# API reference

- **OpenAPI Version:** `3.2.0`
- **API Version:** `1`

Customer inputs reject unknown JSON and query fields. Validation errors contain errors entries with field paths, codes and messages. Conflicts report already_exists, limit_reached or invalid_state, with the detail in message; a refused rename adds lockedUntil. Per-IP and per-token limits apply; honor Retry-After on throttling and in-flight creates. An in-flight create returns 409 idempotency_in_progress; retry it with the same Idempotency-Key. Customer resources expose one id containing the immutable public project identifier used in pid path parameters. Timestamps are RFC 3339 strings normalized to UTC; money fields ending in Micros are micro-USD. Customer PATCH accepts JSON Merge Patch: omitted members remain unchanged, arrays replace, and null clears nullable members. A nullable member has no implicit value beyond the declared null; required fields without a schema default must be supplied. Stored state and billing calculations retain their existing units. A vanity name is a mutable project field. Renames lock further changes for the seven-day grace window; old names redirect during that window. Use this API at <https://api.moq.pro/v1>. Device login uses the OAuth error envelope and form encoding. Signup, login, passkey enrolment, and email change remain human sign-in flows outside this spec. Fleet operations under /cluster also remain outside this spec. Admin operations require a token with admin authority from a current site admin. The /v1 compatibility and deprecation policy is at <https://doc.moq.pro/api-versioning>.

## Contents

**Operations**

- [GET /projects](#scalar-operation-get-projects)
- [POST /projects](#scalar-operation-post-projects)
- [GET /projects/{pid}](#scalar-operation-get-projectspid)
- [PATCH /projects/{pid}](#scalar-operation-patch-projectspid)
- [DELETE /projects/{pid}](#scalar-operation-delete-projectspid)
- [GET /projects/{pid}/keys](#scalar-operation-get-projectspidkeys)
- [POST /projects/{pid}/keys](#scalar-operation-post-projectspidkeys)
- [POST /projects/{pid}/keys/import](#scalar-operation-post-projectspidkeysimport)
- [GET /projects/{pid}/keys/{kid}/private](#scalar-operation-get-projectspidkeyskidprivate)
- [POST /projects/{pid}/keys/{kid}/token](#scalar-operation-post-projectspidkeyskidtoken)
- [POST /projects/{pid}/keys/{kid}/disable](#scalar-operation-post-projectspidkeyskiddisable)
- [POST /projects/{pid}/keys/{kid}/enable](#scalar-operation-post-projectspidkeyskidenable)
- [DELETE /projects/{pid}/keys/{kid}](#scalar-operation-delete-projectspidkeyskid)
- [GET /projects/{pid}/limits](#scalar-operation-get-projectspidlimits)
- [PATCH /projects/{pid}/limits](#scalar-operation-patch-projectspidlimits)
- [GET /projects/{pid}/auth/endpoint](#scalar-operation-get-projectspidauthendpoint)
- [PUT /projects/{pid}/auth/endpoint](#scalar-operation-put-projectspidauthendpoint)
- [DELETE /projects/{pid}/auth/endpoint](#scalar-operation-delete-projectspidauthendpoint)
- [POST /projects/{pid}/auth/endpoint/test](#scalar-operation-post-projectspidauthendpointtest)
- [POST /projects/{pid}/live/token](#scalar-operation-post-projectspidlivetoken)
- [GET /projects/{pid}/recording/rules](#scalar-operation-get-projectspidrecordingrules)
- [POST /projects/{pid}/recording/rules](#scalar-operation-post-projectspidrecordingrules)
- [DELETE /projects/{pid}/recording/rules/{id}](#scalar-operation-delete-projectspidrecordingrulesid)
- [PATCH /projects/{pid}/recording/rules/{id}](#scalar-operation-patch-projectspidrecordingrulesid)
- [DELETE /projects/{pid}/recording/rules/{id}/sessions](#scalar-operation-delete-projectspidrecordingrulesidsessions)
- [GET /projects/{pid}/recording/sessions](#scalar-operation-get-projectspidrecordingsessions)
- [GET /projects/{pid}/recording/sessions/{id}](#scalar-operation-get-projectspidrecordingsessionsid)
- [DELETE /projects/{pid}/recording/sessions/{id}](#scalar-operation-delete-projectspidrecordingsessionsid)
- [GET /projects/{pid}/recording/sessions/{id}/play](#scalar-operation-get-projectspidrecordingsessionsidplay)
- [GET /projects/{pid}/recording/s3](#scalar-operation-get-projectspidrecordings3)
- [GET /projects/{pid}/agent/rules](#scalar-operation-get-projectspidagentrules)
- [POST /projects/{pid}/agent/rules](#scalar-operation-post-projectspidagentrules)
- [DELETE /projects/{pid}/agent/rules/{id}](#scalar-operation-delete-projectspidagentrulesid)
- [PATCH /projects/{pid}/agent/rules/{id}](#scalar-operation-patch-projectspidagentrulesid)
- [GET /projects/{pid}/sync/targets](#scalar-operation-get-projectspidsynctargets)
- [POST /projects/{pid}/sync/targets](#scalar-operation-post-projectspidsynctargets)
- [PATCH /projects/{pid}/sync/targets/{id}](#scalar-operation-patch-projectspidsynctargetsid)
- [DELETE /projects/{pid}/sync/targets/{id}](#scalar-operation-delete-projectspidsynctargetsid)
- [GET /projects/{pid}/webhooks](#scalar-operation-get-projectspidwebhooks)
- [POST /projects/{pid}/webhooks](#scalar-operation-post-projectspidwebhooks)
- [PATCH /projects/{pid}/webhooks/{id}](#scalar-operation-patch-projectspidwebhooksid)
- [DELETE /projects/{pid}/webhooks/{id}](#scalar-operation-delete-projectspidwebhooksid)
- [POST /projects/{pid}/webhooks/{id}/test](#scalar-operation-post-projectspidwebhooksidtest)
- [GET /projects/{pid}/webhooks/{id}/deliveries](#scalar-operation-get-projectspidwebhooksiddeliveries)
- [GET /projects/{pid}/usage](#scalar-operation-get-projectspidusage)
- [GET /projects/{pid}/usage/current](#scalar-operation-get-projectspidusagecurrent)
- [GET /projects/{pid}/invoices](#scalar-operation-get-projectspidinvoices)
- [GET /projects/{pid}/spend-cap](#scalar-operation-get-projectspidspend-cap)
- [PATCH /projects/{pid}/spend-cap](#scalar-operation-patch-projectspidspend-cap)
- [GET /projects/{pid}/payment](#scalar-operation-get-projectspidpayment)
- [DELETE /projects/{pid}/payment](#scalar-operation-delete-projectspidpayment)
- [GET /projects/{pid}/credit](#scalar-operation-get-projectspidcredit)
- [POST /projects/{pid}/payment/setup-link](#scalar-operation-post-projectspidpaymentsetup-link)
- [POST /projects/{pid}/payment/setup-intent](#scalar-operation-post-projectspidpaymentsetup-intent)
- [POST /projects/{pid}/payment/confirm](#scalar-operation-post-projectspidpaymentconfirm)
- [POST /projects/{pid}/payment/portal](#scalar-operation-post-projectspidpaymentportal)
- [POST /projects/{pid}/payment/retry](#scalar-operation-post-projectspidpaymentretry)
- [PATCH /projects/{pid}/payment/contact](#scalar-operation-patch-projectspidpaymentcontact)
- [PATCH /projects/{pid}/payment/statements](#scalar-operation-patch-projectspidpaymentstatements)
- [GET /projects/{pid}/payment/identity](#scalar-operation-get-projectspidpaymentidentity)
- [PATCH /projects/{pid}/payment/identity](#scalar-operation-patch-projectspidpaymentidentity)
- [POST /projects/{pid}/payment/net-terms](#scalar-operation-post-projectspidpaymentnet-terms)
- [DELETE /projects/{pid}/payment/net-terms](#scalar-operation-delete-projectspidpaymentnet-terms)
- [POST /projects/{pid}/payment/credit-limit](#scalar-operation-post-projectspidpaymentcredit-limit)
- [GET /projects/{pid}/notifications/live](#scalar-operation-get-projectspidnotificationslive)
- [POST /projects/{pid}/notifications/dismiss](#scalar-operation-post-projectspidnotificationsdismiss)
- [GET /account/tokens](#scalar-operation-get-accounttokens)
- [POST /account/tokens](#scalar-operation-post-accounttokens)
- [DELETE /account/tokens/current](#scalar-operation-delete-accounttokenscurrent)
- [DELETE /account/tokens/{id}](#scalar-operation-delete-accounttokensid)
- [PATCH /account/tokens/{id}](#scalar-operation-patch-accounttokensid)
- [GET /account](#scalar-operation-get-account)
- [PATCH /account](#scalar-operation-patch-account)
- [DELETE /account](#scalar-operation-delete-account)
- [GET /account/usage](#scalar-operation-get-accountusage)
- [GET /account/export](#scalar-operation-get-accountexport)
- [GET /account/deletion-preview](#scalar-operation-get-accountdeletion-preview)
- [GET /account/passkeys](#scalar-operation-get-accountpasskeys)
- [DELETE /account/passkeys/{id}](#scalar-operation-delete-accountpasskeysid)
- [DELETE /account/sessions/current](#scalar-operation-delete-accountsessionscurrent)
- [DELETE /account/sessions](#scalar-operation-delete-accountsessions)
- [GET /projects/{pid}/members](#scalar-operation-get-projectspidmembers)
- [PATCH /projects/{pid}/members/{accountId}](#scalar-operation-patch-projectspidmembersaccountid)
- [DELETE /projects/{pid}/members/{accountId}](#scalar-operation-delete-projectspidmembersaccountid)
- [GET /projects/{pid}/invites](#scalar-operation-get-projectspidinvites)
- [POST /projects/{pid}/invites](#scalar-operation-post-projectspidinvites)
- [DELETE /projects/{pid}/invites](#scalar-operation-delete-projectspidinvites)
- [POST /projects/{pid}/invites/accept](#scalar-operation-post-projectspidinvitesaccept)
- [GET /projects/{pid}/transfer](#scalar-operation-get-projectspidtransfer)
- [POST /projects/{pid}/transfer](#scalar-operation-post-projectspidtransfer)
- [DELETE /projects/{pid}/transfer](#scalar-operation-delete-projectspidtransfer)
- [POST /projects/{pid}/transfer/accept](#scalar-operation-post-projectspidtransferaccept)
- [POST /projects/{pid}/transfer/decline](#scalar-operation-post-projectspidtransferdecline)
- [POST /oauth/device](#scalar-operation-post-oauthdevice)
- [POST /oauth/token](#scalar-operation-post-oauthtoken)

**Webhooks**

- [POST broadcast.started](#scalar-webhook-post-broadcaststarted)
- [POST broadcast.ended](#scalar-webhook-post-broadcastended)
- [POST recording.started](#scalar-webhook-post-recordingstarted)
- [POST recording.ready](#scalar-webhook-post-recordingready)
- [POST recording.failed](#scalar-webhook-post-recordingfailed)
- [POST webhook.test](#scalar-webhook-post-webhooktest)

**Schemas**

- [AdminProjectPage](#scalar-schema-adminprojectpage)
- [ApiError](#scalar-schema-apierror)
- [CreatedApiToken](#scalar-schema-createdapitoken)
- [Permissions](#scalar-schema-permissions)
- [ApiToken](#scalar-schema-apitoken)
- [AdminProject](#scalar-schema-adminproject)
- [BillingExposure](#scalar-schema-billingexposure)
- [CreditLimitRequest](#scalar-schema-creditlimitrequest)
- [CurrentUsageQuantities](#scalar-schema-currentusagequantities)
- [Usage](#scalar-schema-usage)
- [Project](#scalar-schema-project)
- [Key](#scalar-schema-key)
- [PrivateKey](#scalar-schema-privatekey)
- [Token](#scalar-schema-token)
- [SessionLimits](#scalar-schema-sessionlimits)
- [AuthEndpoint](#scalar-schema-authendpoint)
- [CreatedAuthEndpoint](#scalar-schema-createdauthendpoint)
- [AuthEndpointTest](#scalar-schema-authendpointtest)
- [LiveToken](#scalar-schema-livetoken)
- [RecordingRule](#scalar-schema-recordingrule)
- [Bucket](#scalar-schema-bucket)
- [DeletingRule](#scalar-schema-deletingrule)
- [DeletingRecordings](#scalar-schema-deletingrecordings)
- [Session](#scalar-schema-session)
- [RecordingRuleTotals](#scalar-schema-recordingruletotals)
- [Playback](#scalar-schema-playback)
- [DeletingRecording](#scalar-schema-deletingrecording)
- [S3Credentials](#scalar-schema-s3credentials)
- [AgentRule](#scalar-schema-agentrule)
- [AgentConfig](#scalar-schema-agentconfig)
- [SyncTarget](#scalar-schema-synctarget)
- [Webhook](#scalar-schema-webhook)
- [TestedWebhook](#scalar-schema-testedwebhook)
- [Delivery](#scalar-schema-delivery)
- [Allowance](#scalar-schema-allowance)
- [CurrentUsage](#scalar-schema-currentusage)
- [Pool](#scalar-schema-pool)
- [Invoice](#scalar-schema-invoice)
- [SpendCap](#scalar-schema-spendcap)
- [Payment](#scalar-schema-payment)
- [BillingStatus](#scalar-schema-billingstatus)
- [BillingIdentity](#scalar-schema-billingidentity)
- [NotificationFrame](#scalar-schema-notificationframe)
- [PermissionsInput](#scalar-schema-permissionsinput)
- [Account](#scalar-schema-account)
- [AccountUsage](#scalar-schema-accountusage)
- [AccountAllowance](#scalar-schema-accountallowance)
- [AccountExport](#scalar-schema-accountexport)
- [AccountDeletionPreview](#scalar-schema-accountdeletionpreview)
- [AccountDeletionStep](#scalar-schema-accountdeletionstep)
- [Passkey](#scalar-schema-passkey)
- [ProjectMember](#scalar-schema-projectmember)
- [ProjectInvitation](#scalar-schema-projectinvitation)
- [OwnershipTransfer](#scalar-schema-ownershiptransfer)
- [CreditLimitRequestResponse](#scalar-schema-creditlimitrequestresponse)

<a id="scalar-context-global-servers"></a>

## Servers

- **URL:** `https://api.moq.wtf/v1`

## Operations

<a id="scalar-operation-get-projects"></a>

### List Projects

- **Method:** `GET`
- **Path:** `/projects`
- **Operation ID:** `listProjects`
- **Tags:** Projects

Token permissions: settings:read. Ordered by createdAt ascending, then id ascending.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Query parameters

- **`limit`**: `integer`, minimum: `1`, maximum: `200`

  Maximum items per page, from 1 to 200.
- **`cursor`**: `string`

  Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.
- **`name`**: `string`, minLength: `1`, maxLength: `32`, pattern: `^[a-z0-9]([a-z0-9-]*[a-z0-9])?$`

  Only the project with this name, or a former name still redirecting to it, when the caller can read it. An id never matches; read it with getProject.

#### Responses

##### 200 Success

**Content type:** `application/json`

- **`items` (required)**: array of [Project](#scalar-schema-project)

  This page's items, in the list's documented order.
- **`nextCursor` (required)**: `string | null`

  Pass as cursor to continue, or null when there are no more items.

<a id="scalar-example-1"></a>

**Generated example:**

```json
{
  "items": [
    {
      "id": "",
      "name": null,
      "billingExempt": true,
      "domain": null,
      "domainRole": "",
      "createdAt": "2026-10-22T12:00:00.000Z",
      "role": "",
      "public": {
        "v": 1,
        "publish": [
          ""
        ],
        "subscribe": [
          ""
        ]
      },
      "enforcedPublic": {
        "v": 1,
        "publish": [
          ""
        ],
        "subscribe": [
          ""
        ]
      },
      "redirects": [
        {
          "path": "",
          "expiresAt": "2026-10-22T12:00:00.000Z"
        }
      ],
      "suspension": {
        "paused": true,
        "banned": true,
        "freeLimited": true,
        "capReached": true,
        "suspended": true,
        "overFreeTier": true,
        "spendCapReached": true
      },
      "ownerEmail": null
    }
  ],
  "nextCursor": null
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

<a id="scalar-example-2"></a>

**Generated example:**

```json
{
  "code": "",
  "message": "",
  "errors": [
    {
      "path": [
        ""
      ],
      "code": "",
      "message": ""
    }
  ],
  "lockedUntil": ""
}
```

<a id="scalar-operation-post-projects"></a>

### Create Project

- **Method:** `POST`
- **Path:** `/projects`
- **Operation ID:** `createProject`
- **Tags:** Projects

Token permissions: lifecycle:write.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Header parameters

- **`Idempotency-Key` (required)**: `string`, minLength: `1`, maxLength: `512`

  Required on every POST create. A quoted Structured Field string containing 1 to 255 ASCII characters, for example "request-123". Keep the same key and exact request bytes when retrying. Results, including one-time private material, replay for 24 hours per caller. Interrupted attempts recover within that window. Changed input returns 422; an attempt that cannot yet recover returns 409 idempotency_in_progress.

#### Request body

**Required:** `true`

**Content type:** `application/json`

no additional properties

- **`name`**: `string`, minLength: `4`, maxLength: `32`, pattern: `^[a-z0-9]([a-z0-9-]*[a-z0-9])?$`

  Optional mutable project name. Omit for an unnamed project reachable only by its immutable id.

<a id="scalar-example-3"></a>

**Generated example:**

```json
{
  "name": "example"
}
```

#### Responses

##### 201 Created

**Content type:** `application/json`

[Project](#scalar-schema-project)

<a id="scalar-example-4"></a>

**Generated example:**

```json
{
  "id": "",
  "name": null,
  "billingExempt": true,
  "domain": null,
  "domainRole": "",
  "createdAt": "2026-10-22T12:00:00.000Z",
  "role": "",
  "public": {
    "v": 1,
    "publish": [
      ""
    ],
    "subscribe": [
      ""
    ]
  },
  "enforcedPublic": {
    "v": 1,
    "publish": [
      ""
    ],
    "subscribe": [
      ""
    ]
  },
  "redirects": [
    {
      "path": "",
      "expiresAt": "2026-10-22T12:00:00.000Z"
    }
  ],
  "suspension": {
    "paused": true,
    "banned": true,
    "freeLimited": true,
    "capReached": true,
    "suspended": true,
    "overFreeTier": true,
    "spendCapReached": true
  },
  "ownerEmail": null
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-get-projectspid"></a>

### Get Project

- **Method:** `GET`
- **Path:** `/projects/{pid}`
- **Operation ID:** `getProject`
- **Tags:** Projects

Token permissions: settings:read. Project role: read.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Responses

##### 200 Success

**Content type:** `application/json`

[Project](#scalar-schema-project)

[Generated example](#scalar-example-4)

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-patch-projectspid"></a>

### Update Project

- **Method:** `PATCH`
- **Path:** `/projects/{pid}`
- **Operation ID:** `updateProject`
- **Tags:** Projects

Token permissions: settings:write. Project role: write.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Request body

JSON Merge Patch: omitted fields remain unchanged, arrays replace, and null clears nullable fields.

**Required:** `true`

**Content type:** `application/json`

no additional properties

- **`domainEnabled`**: `boolean`

  Enable access from the caller verified email domain; false disables it. Omit to leave unchanged.
- **`domainRole`**: `string`, possible values: `"write", "read"`

  Role for matching verified-domain members. Omit to leave unchanged; owner requires an explicit membership.
- **`name`**: `string | null`, minLength: `4`, maxLength: `32`, pattern: `^[a-z0-9]([a-z0-9-]*[a-z0-9])?$`

  New project name, or null to clear it. The previous name redirects for a grace period.
- **`public`**: `object | null`, no additional properties

  Version 1 patterns. A bare path is exact; a trailing \*\* segment matches its subtree. Null clears public access.
  - **`v` (required)**: `number`, possible values: `1`

    Pattern version 1: `foo` matches only `foo`; use `foo/**` for a subtree.
  - **`publish`**: `array | null`, maxItems: `32`

    Patterns anyone may publish to. Omitted keeps the saved list; null clears it.

    **Items:**

    `string`, maxLength: `256`

    At most 256 UTF-16 code units (JavaScript string.length). A legacy prefix must also fit after subtree expansion.
  - **`subscribe`**: `array | null`, maxItems: `32`

    Patterns anyone may subscribe to. Omitted keeps the saved list; null clears it.

    **Items:**

    `string`, maxLength: `256`

    At most 256 UTF-16 code units (JavaScript string.length). A legacy prefix must also fit after subtree expansion.

<a id="scalar-example-5"></a>

**Generated example:**

```json
{
  "name": null,
  "domainEnabled": true,
  "domainRole": "write",
  "public": {
    "v": 1,
    "publish": [
      ""
    ],
    "subscribe": [
      ""
    ]
  }
}
```

**Content type:** `application/merge-patch+json`

no additional properties

- **`domainEnabled`**: `boolean`

  Enable access from the caller verified email domain; false disables it. Omit to leave unchanged.
- **`domainRole`**: `string`, possible values: `"write", "read"`

  Role for matching verified-domain members. Omit to leave unchanged; owner requires an explicit membership.
- **`name`**: `string | null`, minLength: `4`, maxLength: `32`, pattern: `^[a-z0-9]([a-z0-9-]*[a-z0-9])?$`

  New project name, or null to clear it. The previous name redirects for a grace period.
- **`public`**: `object | null`, no additional properties

  Version 1 patterns. A bare path is exact; a trailing \*\* segment matches its subtree. Null clears public access.
  - **`v` (required)**: `number`, possible values: `1`

    Pattern version 1: `foo` matches only `foo`; use `foo/**` for a subtree.
  - **`publish`**: `array | null`, maxItems: `32`

    Patterns anyone may publish to. Omitted keeps the saved list; null clears it.

    **Items:**

    `string`, maxLength: `256`

    At most 256 UTF-16 code units (JavaScript string.length). A legacy prefix must also fit after subtree expansion.
  - **`subscribe`**: `array | null`, maxItems: `32`

    Patterns anyone may subscribe to. Omitted keeps the saved list; null clears it.

    **Items:**

    `string`, maxLength: `256`

    At most 256 UTF-16 code units (JavaScript string.length). A legacy prefix must also fit after subtree expansion.

<a id="scalar-example-6"></a>

**Generated example:**

```json
{
  "name": null,
  "domainEnabled": true,
  "domainRole": "write",
  "public": {
    "v": 1,
    "publish": [
      ""
    ],
    "subscribe": [
      ""
    ]
  }
}
```

#### Responses

##### 200 Success

**Content type:** `application/json`

[Project](#scalar-schema-project)

[Generated example](#scalar-example-4)

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-delete-projectspid"></a>

### Delete Project

- **Method:** `DELETE`
- **Path:** `/projects/{pid}`
- **Operation ID:** `deleteProject`
- **Tags:** Projects

Token permissions: lifecycle:write. Project role: owner.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Responses

##### 204 Success. The response has no body.

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-get-projectspidkeys"></a>

### List Keys

- **Method:** `GET`
- **Path:** `/projects/{pid}/keys`
- **Operation ID:** `listKeys`
- **Tags:** Keys

Token permissions: keys:read. Project role: write. Ordered by createdAt ascending, then id ascending.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Query parameters

- **`limit`**: `integer`, minimum: `1`, maximum: `200`

  Maximum items per page, from 1 to 200.
- **`cursor`**: `string`

  Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.

#### Responses

##### 200 Success

**Content type:** `application/json`

- **`items` (required)**: array of [Key](#scalar-schema-key)

  This page's items, in the list's documented order.
- **`nextCursor` (required)**: `string | null`

  Pass as cursor to continue, or null when there are no more items.

<a id="scalar-example-7"></a>

**Generated example:**

```json
{
  "items": [
    {
      "id": "",
      "algorithm": "",
      "keyType": "",
      "label": null,
      "disabledAt": "2026-10-22T12:00:00.000Z",
      "createdAt": "2026-10-22T12:00:00.000Z",
      "scope": {
        "root": "",
        "publish": [
          ""
        ],
        "subscribe": [
          ""
        ],
        "put": [
          ""
        ],
        "get": [
          ""
        ]
      },
      "privateKey": ""
    }
  ],
  "nextCursor": null
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-post-projectspidkeys"></a>

### Create Key

- **Method:** `POST`
- **Path:** `/projects/{pid}/keys`
- **Operation ID:** `createKey`
- **Tags:** Keys

Token permissions: keys:write. Project role: write.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Header parameters

- **`Idempotency-Key` (required)**: `string`, minLength: `1`, maxLength: `512`

  Required on every POST create. A quoted Structured Field string containing 1 to 255 ASCII characters, for example "request-123". Keep the same key and exact request bytes when retrying. Results, including one-time private material, replay for 24 hours per caller. Interrupted attempts recover within that window. Changed input returns 422; an attempt that cannot yet recover returns 409 idempotency_in_progress.

#### Request body

**Required:** `true`

**Content type:** `application/json`

no additional properties

- **`algorithm` (required)**: `string`, possible values: `"HS256", "HS384", "HS512", "ES256", "ES384", "RS256", "RS384", "RS512", "PS256", "PS384", "PS512", "EdDSA"`

  Signing algorithm. HS\* keys are symmetric secrets; the others are asymmetric key pairs.
- **`label`**: `string`

  Optional human-readable label.
- **`publish`**: `array of string`

  Immutable publish scope, patterns relative to the project. Omit both lists for no scope.
- **`subscribe`**: `array of string`

  Immutable subscribe scope, patterns relative to the project. Omit both lists for no scope.

<a id="scalar-example-8"></a>

**Generated example:**

```json
{
  "algorithm": "HS256",
  "label": "",
  "publish": [
    ""
  ],
  "subscribe": [
    ""
  ]
}
```

#### Responses

##### 201 Created

**Content type:** `application/json`

[Key](#scalar-schema-key)

<a id="scalar-example-9"></a>

**Generated example:**

```json
{
  "id": "",
  "algorithm": "",
  "keyType": "",
  "label": null,
  "disabledAt": "2026-10-22T12:00:00.000Z",
  "createdAt": "2026-10-22T12:00:00.000Z",
  "scope": {
    "root": "",
    "publish": [
      ""
    ],
    "subscribe": [
      ""
    ],
    "put": [
      ""
    ],
    "get": [
      ""
    ]
  },
  "privateKey": ""
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-post-projectspidkeysimport"></a>

### Import Key

- **Method:** `POST`
- **Path:** `/projects/{pid}/keys/import`
- **Operation ID:** `importKey`
- **Tags:** Keys

Token permissions: keys:write. Project role: write.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Header parameters

- **`Idempotency-Key` (required)**: `string`, minLength: `1`, maxLength: `512`

  Required on every POST create. A quoted Structured Field string containing 1 to 255 ASCII characters, for example "request-123". Keep the same key and exact request bytes when retrying. Results, including one-time private material, replay for 24 hours per caller. Interrupted attempts recover within that window. Changed input returns 422; an attempt that cannot yet recover returns 409 idempotency_in_progress.

#### Request body

**Required:** `true`

**Content type:** `application/json`

no additional properties

- **`publicKey` (required)**: `string`

  Serialized public JWK. Private key parameters are refused.
- **`label`**: `string`

  Optional human-readable label.

<a id="scalar-example-10"></a>

**Generated example:**

```json
{
  "label": "",
  "publicKey": "{\"kty\":\"EC\",\"crv\":\"P-256\",...}"
}
```

#### Responses

##### 201 Created

**Content type:** `application/json`

[Key](#scalar-schema-key)

[Generated example](#scalar-example-9)

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-get-projectspidkeyskidprivate"></a>

### Get Private Key

- **Method:** `GET`
- **Path:** `/projects/{pid}/keys/{kid}/private`
- **Operation ID:** `getPrivateKey`
- **Tags:** Keys

Token permissions: keys:write. Project role: write.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.
- **`kid` (required)**: `string`, minLength: `1`

  The key's id field.

#### Responses

##### 200 Success

**Content type:** `application/json`

[PrivateKey](#scalar-schema-privatekey)

<a id="scalar-example-11"></a>

**Generated example:**

```json
{
  "privateKey": ""
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-post-projectspidkeyskidtoken"></a>

### Mint Token

- **Method:** `POST`
- **Path:** `/projects/{pid}/keys/{kid}/token`
- **Operation ID:** `mintToken`
- **Tags:** Keys

Token permissions: keys:write. Project role: write.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.
- **`kid` (required)**: `string`, minLength: `1`

  The key's id field.

#### Header parameters

- **`Idempotency-Key` (required)**: `string`, minLength: `1`, maxLength: `512`

  Required on every POST create. A quoted Structured Field string containing 1 to 255 ASCII characters, for example "request-123". Keep the same key and exact request bytes when retrying. Results, including one-time private material, replay for 24 hours per caller. Interrupted attempts recover within that window. Changed input returns 422; an attempt that cannot yet recover returns 409 idempotency_in_progress.

#### Request body

**Required:** `true`

**Content type:** `application/json`

no additional properties

- **`exp`**: `string`, format: `date-time`

  Expiry strictly in the future, at most 30 days from the request. Omitted means the token never expires.
- **`publish`**: `array of string`

  Patterns the token may publish, relative to its root; must fit the key scope.
- **`root`**: `string`

  Optional connection root claim. Defaults to the key scope root when scoped, otherwise the immutable project id; an explicit root must fit the key scope.
- **`subscribe`**: `array of string`

  Patterns the token may subscribe to, relative to its root; must fit the key scope.

<a id="scalar-example-12"></a>

**Generated example:**

```json
{
  "root": "",
  "publish": [
    ""
  ],
  "subscribe": [
    ""
  ],
  "exp": "2026-10-22T12:00:00.000Z"
}
```

#### Responses

##### 200 Success

**Content type:** `application/json`

[Token](#scalar-schema-token)

<a id="scalar-example-13"></a>

**Generated example:**

```json
{
  "token": ""
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-post-projectspidkeyskiddisable"></a>

### Disable Key

- **Method:** `POST`
- **Path:** `/projects/{pid}/keys/{kid}/disable`
- **Operation ID:** `disableKey`
- **Tags:** Keys

Token permissions: keys:write. Project role: write.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.
- **`kid` (required)**: `string`, minLength: `1`

  The key's id field.

#### Responses

##### 200 Success

**Content type:** `application/json`

[Key](#scalar-schema-key)

[Generated example](#scalar-example-9)

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-post-projectspidkeyskidenable"></a>

### Enable Key

- **Method:** `POST`
- **Path:** `/projects/{pid}/keys/{kid}/enable`
- **Operation ID:** `enableKey`
- **Tags:** Keys

Token permissions: keys:write. Project role: write.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.
- **`kid` (required)**: `string`, minLength: `1`

  The key's id field.

#### Responses

##### 200 Success

**Content type:** `application/json`

[Key](#scalar-schema-key)

[Generated example](#scalar-example-9)

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-delete-projectspidkeyskid"></a>

### Delete Key

- **Method:** `DELETE`
- **Path:** `/projects/{pid}/keys/{kid}`
- **Operation ID:** `deleteKey`
- **Tags:** Keys

Token permissions: keys:write. Project role: write.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.
- **`kid` (required)**: `string`, minLength: `1`

  The key's id field.

#### Responses

##### 204 Success. The response has no body.

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-get-projectspidlimits"></a>

### Get Session Limits

- **Method:** `GET`
- **Path:** `/projects/{pid}/limits`
- **Operation ID:** `getSessionLimits`
- **Tags:** Limits

Token permissions: settings:read. Project role: read. Caps on the live sessions one token, and one remote address, may hold on each relay. A change applies to the next connect and never closes a running session.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Responses

##### 200 Success

**Content type:** `application/json`

[SessionLimits](#scalar-schema-sessionlimits)

<a id="scalar-example-14"></a>

**Generated example:**

```json
{
  "token": null,
  "remote": null
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-patch-projectspidlimits"></a>

### Set Session Limits

- **Method:** `PATCH`
- **Path:** `/projects/{pid}/limits`
- **Operation ID:** `setSessionLimits`
- **Tags:** Limits

Token permissions: settings:write. Project role: write.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Request body

JSON Merge Patch: omitted fields remain unchanged, arrays replace, and null clears nullable fields.

**Required:** `true`

**Content type:** `application/json`

no additional properties

- **`remote`**: `integer | null`, minimum: `1`, maximum: `1000000`

  Live sessions one remote address (an IPv4 address or an IPv6 /64) may hold on each relay, from 1 to 1000000; null is unlimited.
- **`token`**: `integer | null`, minimum: `1`, maximum: `1000000`

  Live sessions one token may hold on each relay, from 1 to 1000000; null is unlimited.

<a id="scalar-example-15"></a>

**Generated example:**

```json
{
  "token": null,
  "remote": null
}
```

**Content type:** `application/merge-patch+json`

no additional properties

- **`remote`**: `integer | null`, minimum: `1`, maximum: `1000000`

  Live sessions one remote address (an IPv4 address or an IPv6 /64) may hold on each relay, from 1 to 1000000; null is unlimited.
- **`token`**: `integer | null`, minimum: `1`, maximum: `1000000`

  Live sessions one token may hold on each relay, from 1 to 1000000; null is unlimited.

<a id="scalar-example-16"></a>

**Generated example:**

```json
{
  "token": null,
  "remote": null
}
```

#### Responses

##### 200 Success

**Content type:** `application/json`

[SessionLimits](#scalar-schema-sessionlimits)

[Generated example](#scalar-example-14)

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-get-projectspidauthendpoint"></a>

### Get Auth Endpoint

- **Method:** `GET`
- **Path:** `/projects/{pid}/auth/endpoint`
- **Operation ID:** `getAuthEndpoint`
- **Tags:** Auth

Token permissions: keys:read. Project role: write. Your own HTTPS endpoint that decides tokenless sessions, which replaces the project's public rules while set. Sessions with a MoQ token are still verified against the project's keys. A token without keys:write sees only the URL's origin, since its path can carry a credential.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Responses

##### 200 Success

**Content type:** `application/json`

[AuthEndpoint](#scalar-schema-authendpoint)

<a id="scalar-example-17"></a>

**Generated example:**

```json
{
  "url": null
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-put-projectspidauthendpoint"></a>

### Set Auth Endpoint

- **Method:** `PUT`
- **Path:** `/projects/{pid}/auth/endpoint`
- **Operation ID:** `setAuthEndpoint`
- **Tags:** Auth

Token permissions: keys:write. Project role: write. Saves the endpoint. The save that creates it returns 201 with its one-time signing secret; a later save changes the URL, keeps the secret, and returns 200. Retry with the same Idempotency-Key to recover the secret from a lost reply.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Header parameters

- **`Idempotency-Key` (required)**: `string`, minLength: `1`, maxLength: `512`

  Required on every POST create. A quoted Structured Field string containing 1 to 255 ASCII characters, for example "request-123". Keep the same key and exact request bytes when retrying. Results, including one-time private material, replay for 24 hours per caller. Interrupted attempts recover within that window. Changed input returns 422; an attempt that cannot yet recover returns 409 idempotency_in_progress.

#### Request body

**Required:** `true`

**Content type:** `application/json`

no additional properties

- **`url` (required)**: `string`, minLength: `1`, maxLength: `2000`

  An https URL to a public host name, with no IP address and no user:password.

<a id="scalar-example-18"></a>

**Generated example:**

```json
{
  "url": ""
}
```

#### Responses

##### 200 Success

**Content type:** `application/json`

[AuthEndpoint](#scalar-schema-authendpoint)

[Generated example](#scalar-example-17)

##### 201 Created

**Content type:** `application/json`

[CreatedAuthEndpoint](#scalar-schema-createdauthendpoint)

<a id="scalar-example-19"></a>

**Generated example:**

```json
{
  "url": "",
  "secret": ""
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-delete-projectspidauthendpoint"></a>

### Delete Auth Endpoint

- **Method:** `DELETE`
- **Path:** `/projects/{pid}/auth/endpoint`
- **Operation ID:** `deleteAuthEndpoint`
- **Tags:** Auth

Token permissions: keys:write. Project role: write. The saved public rules apply again, and the signing secret is discarded.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Responses

##### 204 Success. The response has no body.

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-post-projectspidauthendpointtest"></a>

### Test Auth Endpoint

- **Method:** `POST`
- **Path:** `/projects/{pid}/auth/endpoint/test`
- **Operation ID:** `testAuthEndpoint`
- **Tags:** Auth

Token permissions: keys:write. Project role: write. Sends the saved endpoint one signed connect request, as a relay would, and returns what it answered. Admits no one. Refused with 404 before an endpoint is saved.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Request body

**Required:** `true`

**Content type:** `application/json`

no additional properties

- **`path`**: `string`, maxLength: `1000`

  The path a viewer dials, relative to the project. Defaults to the project root.
- **`query`**: `string`, maxLength: `4000`

  The query string the viewer presents, without the leading ?. jwt is reserved for MoQ tokens.

<a id="scalar-example-20"></a>

**Generated example:**

```json
{
  "path": "",
  "query": ""
}
```

#### Responses

##### 200 Success

**Content type:** `application/json`

[AuthEndpointTest](#scalar-schema-authendpointtest)

<a id="scalar-example-21"></a>

**Generated example:**

```json
{
  "request": {
    "id": "",
    "event": "",
    "node": "",
    "transport": "",
    "path": "",
    "query": ""
  },
  "answer": {
    "kind": "",
    "grant": {
      "additionalProperty": null
    }
  }
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-post-projectspidlivetoken"></a>

### Mint Live Token

- **Method:** `POST`
- **Path:** `/projects/{pid}/live/token`
- **Operation ID:** `mintLiveToken`
- **Tags:** Live

Token permissions: keys:write. Project role: read. Relay tokens that watch the project's broadcasts and its live dashboard statistics for one hour; mint again before expiresAt. A credential, so it needs keys:write. Refused with 403 while the project is paused; see Project.suspension.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Responses

##### 200 Success

**Content type:** `application/json`

[LiveToken](#scalar-schema-livetoken)

<a id="scalar-example-22"></a>

**Generated example:**

```json
{
  "project": {
    "token": "",
    "root": ""
  },
  "stats": {
    "token": "",
    "root": ""
  },
  "expiresAt": "2026-10-22T12:00:00.000Z"
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-get-projectspidrecordingrules"></a>

### List Recording Rules

- **Method:** `GET`
- **Path:** `/projects/{pid}/recording/rules`
- **Operation ID:** `listRecordingRules`
- **Tags:** Recording

Token permissions: media:read. Project role: read. Ordered by createdAt ascending, then id ascending.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Query parameters

- **`limit`**: `integer`, minimum: `1`, maximum: `200`

  Maximum items per page, from 1 to 200.
- **`cursor`**: `string`

  Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.

#### Responses

##### 200 Success

**Content type:** `application/json`

- **`items` (required)**: array of [RecordingRule](#scalar-schema-recordingrule)

  This page's items, in the list's documented order.
- **`nextCursor` (required)**: `string | null`

  Pass as cursor to continue, or null when there are no more items.

<a id="scalar-example-23"></a>

**Generated example:**

```json
{
  "items": [
    {
      "id": "",
      "label": null,
      "patterns": [
        ""
      ],
      "vodEnabled": true,
      "retentionDays": 1,
      "createdAt": "2026-10-22T12:00:00.000Z",
      "updatedAt": "2026-10-22T12:00:00.000Z",
      "bucketId": null,
      "deleting": true,
      "uploadRefusal": null,
      "bucket": {
        "endpoint": "",
        "region": "",
        "bucket": "",
        "destination": "",
        "delivery": true
      },
      "deletePending": 0
    }
  ],
  "nextCursor": null
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-post-projectspidrecordingrules"></a>

### Create Recording Rule

- **Method:** `POST`
- **Path:** `/projects/{pid}/recording/rules`
- **Operation ID:** `createRecordingRule`
- **Tags:** Recording

Token permissions: media:write. Project role: write.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Header parameters

- **`Idempotency-Key` (required)**: `string`, minLength: `1`, maxLength: `512`

  Required on every POST create. A quoted Structured Field string containing 1 to 255 ASCII characters, for example "request-123". Keep the same key and exact request bytes when retrying. Results, including one-time private material, replay for 24 hours per caller. Interrupted attempts recover within that window. Changed input returns 422; an attempt that cannot yet recover returns 409 idempotency_in_progress.

#### Request body

**Required:** `true`

**Content type:** `application/json`

no additional properties

- **`patterns` (required)**: `array`, minItems: `1`, maxItems: `32`

  Broadcast path patterns relative to the project, 1 to 32; a PATCH replaces the list.

  **Items:**

  `string`, maxLength: `200`
- **`bucket`**: `object`, no additional properties

  Omit for managed storage; supply customer bucket credentials once for BYOB. BYOB owns retention. Cardless projects cannot start recording after exhausting their account allowance.
  - **`accessKeyId` (required)**: `string`, minLength: `1`, maxLength: `200`

    Access key id with write access.
  - **`bucket` (required)**: `string`, minLength: `1`, maxLength: `200`

    S3 bucket name.
  - **`endpoint` (required)**: `string`, minLength: `1`, maxLength: `400`

    S3-compatible endpoint, an https URL on a public host.
  - **`region` (required)**: `string`, minLength: `1`, maxLength: `100`

    S3 region used for signing.
  - **`secretAccessKey` (required)**: `string`, minLength: `1`, maxLength: `400`

    Secret access key; write-only, encrypted at rest and never returned.
  - **`delivery`**: `boolean`

    Serve playback from the bucket, which needs read access too. Defaults to false.
  - **`destination`**: `string`, maxLength: `400`

    Object prefix inside the bucket; omitted is the root.
- **`label`**: `string`, maxLength: `80`

  Optional display label, at most 80 characters.
- **`retentionDays`**: default: `7`

  Managed retention in days. More than 7 days requires a payment method; BYOB retention is owned by the bucket owner.

  **Any of:**
  - `number`, possible values: `1`
  - `number`, possible values: `7`
  - `number`, possible values: `30`
  - `number`, possible values: `90`
  - `number`, possible values: `180`
  - `number`, possible values: `365`
- **`vodEnabled`**: `boolean`

  Whether matching broadcasts are recorded. Defaults to true.

<a id="scalar-example-24"></a>

**Generated example:**

```json
{
  "label": "",
  "patterns": [
    ""
  ],
  "vodEnabled": true,
  "retentionDays": 7,
  "bucket": {
    "endpoint": "",
    "region": "",
    "bucket": "",
    "accessKeyId": "",
    "secretAccessKey": "",
    "destination": "",
    "delivery": true
  }
}
```

#### Responses

##### 201 Created

**Content type:** `application/json`

[RecordingRule](#scalar-schema-recordingrule)

<a id="scalar-example-25"></a>

**Generated example:**

```json
{
  "id": "",
  "label": null,
  "patterns": [
    ""
  ],
  "vodEnabled": true,
  "retentionDays": 1,
  "createdAt": "2026-10-22T12:00:00.000Z",
  "updatedAt": "2026-10-22T12:00:00.000Z",
  "bucketId": null,
  "deleting": true,
  "uploadRefusal": null,
  "bucket": {
    "endpoint": "",
    "region": "",
    "bucket": "",
    "destination": "",
    "delivery": true
  },
  "deletePending": 0
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-delete-projectspidrecordingrulesid"></a>

### Delete Recording Rule

- **Method:** `DELETE`
- **Path:** `/projects/{pid}/recording/rules/{id}`
- **Operation ID:** `deleteRecordingRule`
- **Tags:** Recording

Token permissions: media:write. Project role: write. Disable the rule first. Refused with 409 invalid_state while the rule has recordings not yet being deleted; delete them first, one at a time or with deleteRuleRecordings. The rule stays listed with deleting set until its storage drains in the background.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.
- **`id` (required)**: `string`, minLength: `1`

  The resource's id field.

#### Responses

##### 202 Accepted. The body is a receipt; the work completes asynchronously.

**Content type:** `application/json`

[DeletingRule](#scalar-schema-deletingrule)

<a id="scalar-example-26"></a>

**Generated example:**

```json
{
  "status": "",
  "pending": 1
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-patch-projectspidrecordingrulesid"></a>

### Update Recording Rule

- **Method:** `PATCH`
- **Path:** `/projects/{pid}/recording/rules/{id}`
- **Operation ID:** `updateRecordingRule`
- **Tags:** Recording

Token permissions: media:write. Project role: write.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.
- **`id` (required)**: `string`, minLength: `1`

  The resource's id field.

#### Request body

JSON Merge Patch: omitted fields remain unchanged, arrays replace, and null clears nullable fields.

**Required:** `true`

**Content type:** `application/json`

no additional properties

- **`bucket`**: `string | null`

  Rename the customer bucket; customer buckets only.
- **`delivery`**: `boolean`

  Serve playback from the customer bucket, which needs read access; re-probed when enabled.
- **`region`**: `string | null`

  Change the customer bucket's region; customer buckets only.
- **`retentionDays`**

  Managed retention in days. More than 7 days requires a payment method; BYOB retention is owned by the bucket owner.

  **Any of:**
  - `number`, possible values: `1`
  - `number`, possible values: `7`
  - `number`, possible values: `30`
  - `number`, possible values: `90`
  - `number`, possible values: `180`
  - `number`, possible values: `365`
- **`vodEnabled`**: `boolean`

  Whether matching broadcasts are recorded.

<a id="scalar-example-27"></a>

**Generated example:**

```json
{
  "vodEnabled": true,
  "retentionDays": 7,
  "bucket": null,
  "region": null,
  "delivery": true
}
```

**Content type:** `application/merge-patch+json`

no additional properties

- **`bucket`**: `string | null`

  Rename the customer bucket; customer buckets only.
- **`delivery`**: `boolean`

  Serve playback from the customer bucket, which needs read access; re-probed when enabled.
- **`region`**: `string | null`

  Change the customer bucket's region; customer buckets only.
- **`retentionDays`**

  Managed retention in days. More than 7 days requires a payment method; BYOB retention is owned by the bucket owner.

  **Any of:**
  - `number`, possible values: `1`
  - `number`, possible values: `7`
  - `number`, possible values: `30`
  - `number`, possible values: `90`
  - `number`, possible values: `180`
  - `number`, possible values: `365`
- **`vodEnabled`**: `boolean`

  Whether matching broadcasts are recorded.

<a id="scalar-example-28"></a>

**Generated example:**

```json
{
  "vodEnabled": true,
  "retentionDays": 7,
  "bucket": null,
  "region": null,
  "delivery": true
}
```

#### Responses

##### 200 Success

**Content type:** `application/json`

[RecordingRule](#scalar-schema-recordingrule)

[Generated example](#scalar-example-25)

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-delete-projectspidrecordingrulesidsessions"></a>

### Delete Rule Recordings

- **Method:** `DELETE`
- **Path:** `/projects/{pid}/recording/rules/{id}/sessions`
- **Operation ID:** `deleteRuleRecordings`
- **Tags:** Recording

Token permissions: media:write. Project role: write. Deletes every recording the rule holds and keeps the rule. Completes asynchronously: each recording leaves the listing once queued (a live one after its recorder stops), and its storage drains in the background, metered until it is gone.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.
- **`id` (required)**: `string`, minLength: `1`

  The resource's id field.

#### Responses

##### 202 Accepted. The body is a receipt; the work completes asynchronously.

**Content type:** `application/json`

[DeletingRecordings](#scalar-schema-deletingrecordings)

<a id="scalar-example-29"></a>

**Generated example:**

```json
{
  "status": "",
  "pending": 1
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-get-projectspidrecordingsessions"></a>

### List Recordings

- **Method:** `GET`
- **Path:** `/projects/{pid}/recording/sessions`
- **Operation ID:** `listRecordings`
- **Tags:** Recording

Token permissions: media:read. Project role: read. Lists recordings newest first, a page at a time. To keep a copy current, pass the sinceCursor of an earlier response as since: the response holds the recordings that changed after it and the ids removed since, and its own sinceCursor continues from there.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Query parameters

- **`limit`**: `integer`, minimum: `1`, maximum: `200`

  Maximum items per page, from 1 to 200.
- **`cursor`**: `string`

  Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.
- **`since`**: `string`

  Opaque sinceCursor from an earlier response. Returns the recordings that changed after it, in items, and the ids that left the listing, in removed, instead of a page. Not combined with cursor.

#### Responses

##### 200 Success

**Content type:** `application/json`

- **`items` (required)**: array of [Session](#scalar-schema-session)

  This page's items, in the list's documented order.
- **`nextCursor` (required)**: `string | null`

  Pass as cursor to continue, or null when there are no more items.
- **`removed` (required)**: `array of string`

  With since: ids of recordings that left the listing after it, such as by deletion. Otherwise empty.
- **`reset` (required)**: `boolean`

  With since: true when its changes are no longer available, or more than limit changed. items and nextCursor are then the first page, which replaces every recording you hold.
- **`sinceCursor` (required)**: `string | null`

  Pass as since to get the changes after this response. Null on a page continued with cursor: the first page's covers the pages after it.
- **`totals` (required)**: array of [RecordingRuleTotals](#scalar-schema-recordingruletotals) | `null`

  Totals per recording rule over every recording listed, not just this page, as of sinceCursor. A rule without recordings is absent. Null on a page continued with cursor.

<a id="scalar-example-30"></a>

**Generated example:**

```json
{
  "items": [
    {
      "id": "",
      "broadcastPath": "",
      "status": "",
      "startedAt": "2026-10-22T12:00:00.000Z",
      "endedAt": "2026-10-22T12:00:00.000Z",
      "playlistUrl": null,
      "region": null,
      "bytes": 1,
      "durationMs": 1,
      "deleting": true,
      "cleanupFailed": true,
      "exportStatus": null,
      "exportFailure": null,
      "regression": null
    }
  ],
  "nextCursor": null,
  "removed": [
    ""
  ],
  "reset": true,
  "sinceCursor": null,
  "totals": [
    {
      "ruleId": "",
      "recordings": 0,
      "live": 0,
      "bytes": 0
    }
  ]
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-get-projectspidrecordingsessionsid"></a>

### Get Recording

- **Method:** `GET`
- **Path:** `/projects/{pid}/recording/sessions/{id}`
- **Operation ID:** `getRecording`
- **Tags:** Recording

Token permissions: media:read. Project role: read.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.
- **`id` (required)**: `string`, minLength: `1`

  The recording's id, percent-encoded as one path segment: each `/` is `%2F`.

#### Responses

##### 200 Success

**Content type:** `application/json`

[Session](#scalar-schema-session)

<a id="scalar-example-31"></a>

**Generated example:**

```json
{
  "id": "",
  "broadcastPath": "",
  "status": "",
  "startedAt": "2026-10-22T12:00:00.000Z",
  "endedAt": "2026-10-22T12:00:00.000Z",
  "playlistUrl": null,
  "region": null,
  "bytes": 1,
  "durationMs": 1,
  "deleting": true,
  "cleanupFailed": true,
  "exportStatus": null,
  "exportFailure": null,
  "regression": null
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-delete-projectspidrecordingsessionsid"></a>

### Delete Recording

- **Method:** `DELETE`
- **Path:** `/projects/{pid}/recording/sessions/{id}`
- **Operation ID:** `deleteRecording`
- **Tags:** Recording

Token permissions: media:write. Project role: write. Completes asynchronously: the recording leaves the listing once queued (a live one after its recorder stops), and its storage drains in the background, metered until it is gone.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.
- **`id` (required)**: `string`, minLength: `1`

  The recording's id, percent-encoded as one path segment: each `/` is `%2F`.

#### Responses

##### 202 Accepted. The body is a receipt; the work completes asynchronously.

**Content type:** `application/json`

[DeletingRecording](#scalar-schema-deletingrecording)

<a id="scalar-example-32"></a>

**Generated example:**

```json
{
  "status": ""
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-get-projectspidrecordingsessionsidplay"></a>

### Play Recording

- **Method:** `GET`
- **Path:** `/projects/{pid}/recording/sessions/{id}/play`
- **Operation ID:** `playRecording`
- **Tags:** Recording

Token permissions: media:read. Project role: read. Returns the recording's playlist URL with a short-lived token for fetching it and its media. Refused with 409 when the recording's own bucket does not allow delivery.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.
- **`id` (required)**: `string`, minLength: `1`

  The recording's id, percent-encoded as one path segment: each `/` is `%2F`.

#### Responses

##### 200 Success

**Content type:** `application/json`

[Playback](#scalar-schema-playback)

<a id="scalar-example-33"></a>

**Generated example:**

```json
{
  "url": "",
  "token": "",
  "expiresAt": "2026-10-22T12:00:00.000Z",
  "live": true,
  "span": {
    "start": "2026-10-22T12:00:00.000Z",
    "end": "2026-10-22T12:00:00.000Z",
    "segments": 1
  },
  "initial": {
    "start": "2026-10-22T12:00:00.000Z",
    "end": "2026-10-22T12:00:00.000Z"
  }
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-get-projectspidrecordings3"></a>

### Get Recording Credentials

- **Method:** `GET`
- **Path:** `/projects/{pid}/recording/s3`
- **Operation ID:** `getRecordingCredentials`
- **Tags:** Recording

Token permissions: keys:write. Project role: owner. S3 credentials for tools such as aws-cli and rclone to list and download managed recordings at endpoint, path-style. They grant nothing alone: every request also needs a subscribe token from mintToken as aws_session_token (x-amz-security-token), which limits reads to the broadcasts it subscribes to and expires with it. Any region signs; bucket is only a label. The same project returns the same credentials. Downloads are metered as delivery; recordings in your own bucket are not listed.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Responses

##### 200 Success

**Content type:** `application/json`

[S3Credentials](#scalar-schema-s3credentials)

<a id="scalar-example-34"></a>

**Generated example:**

```json
{
  "endpoint": "",
  "region": "",
  "bucket": "",
  "accessKeyId": "",
  "secretAccessKey": ""
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-get-projectspidagentrules"></a>

### List Agent Rules

- **Method:** `GET`
- **Path:** `/projects/{pid}/agent/rules`
- **Operation ID:** `listAgentRules`
- **Tags:** Agent

Token permissions: media:read. Project role: read. Ordered by createdAt ascending, then id ascending.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Query parameters

- **`limit`**: `integer`, minimum: `1`, maximum: `200`

  Maximum items per page, from 1 to 200.
- **`cursor`**: `string`

  Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.

#### Responses

##### 200 Success

**Content type:** `application/json`

- **`items` (required)**: array of [AgentRule](#scalar-schema-agentrule)

  This page's items, in the list's documented order.
- **`nextCursor` (required)**: `string | null`

  Pass as cursor to continue, or null when there are no more items.

<a id="scalar-example-35"></a>

**Generated example:**

```json
{
  "items": [
    {
      "id": "",
      "patterns": [
        ""
      ],
      "enabled": true,
      "config": {
        "systemPrompt": "",
        "greeting": null,
        "turnDetection": "silero",
        "maxSessions": 4,
        "stt": {
          "provider": "",
          "model": null,
          "hasKey": true,
          "options": {
            "additionalProperty": ""
          }
        },
        "llm": {
          "provider": "",
          "model": null,
          "hasKey": true,
          "options": {
            "additionalProperty": ""
          }
        },
        "tts": {
          "provider": "",
          "model": null,
          "hasKey": true,
          "options": {
            "additionalProperty": ""
          }
        }
      },
      "createdAt": "2026-10-22T12:00:00.000Z",
      "updatedAt": "2026-10-22T12:00:00.000Z"
    }
  ],
  "nextCursor": null
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-post-projectspidagentrules"></a>

### Create Agent Rule

- **Method:** `POST`
- **Path:** `/projects/{pid}/agent/rules`
- **Operation ID:** `createAgentRule`
- **Tags:** Agent

Token permissions: media:write. Project role: write.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Header parameters

- **`Idempotency-Key` (required)**: `string`, minLength: `1`, maxLength: `512`

  Required on every POST create. A quoted Structured Field string containing 1 to 255 ASCII characters, for example "request-123". Keep the same key and exact request bytes when retrying. Results, including one-time private material, replay for 24 hours per caller. Interrupted attempts recover within that window. Changed input returns 422; an attempt that cannot yet recover returns 409 idempotency_in_progress.

#### Request body

**Required:** `true`

**Content type:** `application/json`

no additional properties

- **`config` (required)**: `object`, no additional properties

  Full voice configuration with write-only provider keys. Creating or enabling a voice rule requires paid collection (card, net terms, or billing exemption).
  - **`llm` (required)**: `object`, no additional properties

    Language-model provider.
    - **`apiKey` (required)**: `string`, minLength: `1`, maxLength: `400`

      Your provider API key; write-only, encrypted at rest and never returned.
    - **`provider` (required)**: `string`, possible values: `"openai"`

      Provider id.
    - **`model`**: `string`, maxLength: `80`

      Provider model; omitted uses its default.
    - **`options`**: `object`

      Provider-specific options, checked against that provider's allowlist.

      **Additional properties:**

      `string | number | boolean`
  - **`stt` (required)**: `object`, no additional properties

    Speech-to-text provider.
    - **`apiKey` (required)**: `string`, minLength: `1`, maxLength: `400`

      Your provider API key; write-only, encrypted at rest and never returned.
    - **`provider` (required)**: `string`, possible values: `"deepgram"`

      Provider id.
    - **`model`**: `string`, maxLength: `80`

      Provider model; omitted uses its default.
    - **`options`**: `object`

      Provider-specific options, checked against that provider's allowlist.

      **Additional properties:**

      `string | number | boolean`
  - **`systemPrompt` (required)**: `string`, maxLength: `8000`

    Instruction text, at most 8000 characters.
  - **`tts` (required)**: `object`, no additional properties

    Text-to-speech provider.
    - **`apiKey` (required)**: `string`, minLength: `1`, maxLength: `400`

      Your provider API key; write-only, encrypted at rest and never returned.
    - **`provider` (required)**: `string`, possible values: `"cartesia"`

      Provider id.
    - **`model`**: `string`, maxLength: `80`

      Provider model; omitted uses its default.
    - **`options`**: `object`

      Provider-specific options, checked against that provider's allowlist.

      **Additional properties:**

      `string | number | boolean`
  - **`greeting`**: `string | null`, maxLength: `2000`

    Initial spoken greeting, at most 2000 characters; null or omitted for none.
  - **`maxSessions`**: `number`

    Simultaneous sessions for this rule, 1 to 4. Defaults to the project ceiling.
  - **`turnDetection`**: `string`, possible values: `"silero", "stt_endpointing", "client"`

    Turn detection strategy. Defaults to silero.
- **`patterns` (required)**: `array`, minItems: `1`, maxItems: `32`

  Broadcast path patterns relative to the project, 1 to 32; a PATCH replaces the list.

  **Items:**

  `string`, maxLength: `200`
- **`enabled`**: `boolean`

  Whether the rule applies. Defaults to true on create.

<a id="scalar-example-36"></a>

**Generated example:**

```json
{
  "patterns": [
    ""
  ],
  "enabled": true,
  "config": {
    "stt": {
      "provider": "deepgram",
      "model": "",
      "apiKey": "",
      "options": {
        "additionalProperty": ""
      }
    },
    "llm": {
      "provider": "openai",
      "model": "",
      "apiKey": "",
      "options": {
        "additionalProperty": ""
      }
    },
    "tts": {
      "provider": "cartesia",
      "model": "",
      "apiKey": "",
      "options": {
        "additionalProperty": ""
      }
    },
    "systemPrompt": "",
    "greeting": null,
    "turnDetection": "silero",
    "maxSessions": 1
  }
}
```

#### Responses

##### 201 Created

**Content type:** `application/json`

[AgentRule](#scalar-schema-agentrule)

<a id="scalar-example-37"></a>

**Generated example:**

```json
{
  "id": "",
  "patterns": [
    ""
  ],
  "enabled": true,
  "config": {
    "systemPrompt": "",
    "greeting": null,
    "turnDetection": "silero",
    "maxSessions": 4,
    "stt": {
      "provider": "",
      "model": null,
      "hasKey": true,
      "options": {
        "additionalProperty": ""
      }
    },
    "llm": {
      "provider": "",
      "model": null,
      "hasKey": true,
      "options": {
        "additionalProperty": ""
      }
    },
    "tts": {
      "provider": "",
      "model": null,
      "hasKey": true,
      "options": {
        "additionalProperty": ""
      }
    }
  },
  "createdAt": "2026-10-22T12:00:00.000Z",
  "updatedAt": "2026-10-22T12:00:00.000Z"
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-delete-projectspidagentrulesid"></a>

### Delete Agent Rule

- **Method:** `DELETE`
- **Path:** `/projects/{pid}/agent/rules/{id}`
- **Operation ID:** `deleteAgentRule`
- **Tags:** Agent

Token permissions: media:write. Project role: write.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.
- **`id` (required)**: `string`, minLength: `1`

  The resource's id field.

#### Responses

##### 204 Success. The response has no body.

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-patch-projectspidagentrulesid"></a>

### Update Agent Rule

- **Method:** `PATCH`
- **Path:** `/projects/{pid}/agent/rules/{id}`
- **Operation ID:** `updateAgentRule`
- **Tags:** Agent

Token permissions: media:write. Project role: write.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.
- **`id` (required)**: `string`, minLength: `1`

  The resource's id field.

#### Request body

JSON Merge Patch: omitted fields remain unchanged, arrays replace, and null clears nullable fields.

**Required:** `true`

**Content type:** `application/json`

no additional properties

- **`config`**: `object`, no additional properties

  Voice configuration changes. Changing a provider requires its apiKey.
  - **`greeting`**: `string | null`, maxLength: `2000`

    Initial spoken greeting, at most 2000 characters; null or omitted for none.
  - **`llm`**: `object`, no additional properties

    Language-model changes.
    - **`apiKey`**: `string`, minLength: `1`, maxLength: `400`

      Your provider API key; write-only, encrypted at rest and never returned.
    - **`model`**: `string | null`, maxLength: `80`

      Provider model; null restores its default.
    - **`options`**: `object | null`

      Merge-patched provider options; a null member or null clears.

      **Additional properties:**

      `string | number | boolean | null`
    - **`provider`**: `string`, possible values: `"openai"`

      Provider id.
  - **`maxSessions`**: `number`

    Simultaneous sessions for this rule, 1 to 4. Defaults to the project ceiling.
  - **`stt`**: `object`, no additional properties

    Speech-to-text changes.
    - **`apiKey`**: `string`, minLength: `1`, maxLength: `400`

      Your provider API key; write-only, encrypted at rest and never returned.
    - **`model`**: `string | null`, maxLength: `80`

      Provider model; null restores its default.
    - **`options`**: `object | null`

      Merge-patched provider options; a null member or null clears.

      **Additional properties:**

      `string | number | boolean | null`
    - **`provider`**: `string`, possible values: `"deepgram"`

      Provider id.
  - **`systemPrompt`**: `string`, maxLength: `8000`

    Instruction text, at most 8000 characters.
  - **`tts`**: `object`, no additional properties

    Text-to-speech changes.
    - **`apiKey`**: `string`, minLength: `1`, maxLength: `400`

      Your provider API key; write-only, encrypted at rest and never returned.
    - **`model`**: `string | null`, maxLength: `80`

      Provider model; null restores its default.
    - **`options`**: `object | null`

      Merge-patched provider options; a null member or null clears.

      **Additional properties:**

      `string | number | boolean | null`
    - **`provider`**: `string`, possible values: `"cartesia"`

      Provider id.
  - **`turnDetection`**: `string`, possible values: `"silero", "stt_endpointing", "client"`

    Turn detection strategy. Defaults to silero.
- **`enabled`**: `boolean`

  Whether the rule applies. Defaults to true on create.

<a id="scalar-example-38"></a>

**Generated example:**

```json
{
  "enabled": true,
  "config": {
    "stt": {
      "provider": "deepgram",
      "model": null,
      "apiKey": "",
      "options": null
    },
    "llm": {
      "provider": "openai",
      "model": null,
      "apiKey": "",
      "options": null
    },
    "tts": {
      "provider": "cartesia",
      "model": null,
      "apiKey": "",
      "options": null
    },
    "systemPrompt": "",
    "greeting": null,
    "turnDetection": "silero",
    "maxSessions": 1
  }
}
```

**Content type:** `application/merge-patch+json`

no additional properties

- **`config`**: `object`, no additional properties

  Voice configuration changes. Changing a provider requires its apiKey.
  - **`greeting`**: `string | null`, maxLength: `2000`

    Initial spoken greeting, at most 2000 characters; null or omitted for none.
  - **`llm`**: `object`, no additional properties

    Language-model changes.
    - **`apiKey`**: `string`, minLength: `1`, maxLength: `400`

      Your provider API key; write-only, encrypted at rest and never returned.
    - **`model`**: `string | null`, maxLength: `80`

      Provider model; null restores its default.
    - **`options`**: `object | null`

      Merge-patched provider options; a null member or null clears.

      **Additional properties:**

      `string | number | boolean | null`
    - **`provider`**: `string`, possible values: `"openai"`

      Provider id.
  - **`maxSessions`**: `number`

    Simultaneous sessions for this rule, 1 to 4. Defaults to the project ceiling.
  - **`stt`**: `object`, no additional properties

    Speech-to-text changes.
    - **`apiKey`**: `string`, minLength: `1`, maxLength: `400`

      Your provider API key; write-only, encrypted at rest and never returned.
    - **`model`**: `string | null`, maxLength: `80`

      Provider model; null restores its default.
    - **`options`**: `object | null`

      Merge-patched provider options; a null member or null clears.

      **Additional properties:**

      `string | number | boolean | null`
    - **`provider`**: `string`, possible values: `"deepgram"`

      Provider id.
  - **`systemPrompt`**: `string`, maxLength: `8000`

    Instruction text, at most 8000 characters.
  - **`tts`**: `object`, no additional properties

    Text-to-speech changes.
    - **`apiKey`**: `string`, minLength: `1`, maxLength: `400`

      Your provider API key; write-only, encrypted at rest and never returned.
    - **`model`**: `string | null`, maxLength: `80`

      Provider model; null restores its default.
    - **`options`**: `object | null`

      Merge-patched provider options; a null member or null clears.

      **Additional properties:**

      `string | number | boolean | null`
    - **`provider`**: `string`, possible values: `"cartesia"`

      Provider id.
  - **`turnDetection`**: `string`, possible values: `"silero", "stt_endpointing", "client"`

    Turn detection strategy. Defaults to silero.
- **`enabled`**: `boolean`

  Whether the rule applies. Defaults to true on create.

<a id="scalar-example-39"></a>

**Generated example:**

```json
{
  "enabled": true,
  "config": {
    "stt": {
      "provider": "deepgram",
      "model": null,
      "apiKey": "",
      "options": null
    },
    "llm": {
      "provider": "openai",
      "model": null,
      "apiKey": "",
      "options": null
    },
    "tts": {
      "provider": "cartesia",
      "model": null,
      "apiKey": "",
      "options": null
    },
    "systemPrompt": "",
    "greeting": null,
    "turnDetection": "silero",
    "maxSessions": 1
  }
}
```

#### Responses

##### 200 Success

**Content type:** `application/json`

[AgentRule](#scalar-schema-agentrule)

[Generated example](#scalar-example-37)

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-get-projectspidsynctargets"></a>

### List Sync Targets

- **Method:** `GET`
- **Path:** `/projects/{pid}/sync/targets`
- **Operation ID:** `listSyncTargets`
- **Tags:** Sync

Token permissions: media:read. Project role: read. Ordered by createdAt ascending, then id ascending.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Query parameters

- **`limit`**: `integer`, minimum: `1`, maximum: `200`

  Maximum items per page, from 1 to 200.
- **`cursor`**: `string`

  Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.

#### Responses

##### 200 Success

**Content type:** `application/json`

- **`items` (required)**: array of [SyncTarget](#scalar-schema-synctarget)

  This page's items, in the list's documented order.
- **`nextCursor` (required)**: `string | null`

  Pass as cursor to continue, or null when there are no more items.

<a id="scalar-example-40"></a>

**Generated example:**

```json
{
  "items": [
    {
      "id": "",
      "kind": "",
      "label": "",
      "publishPrefixes": [
        ""
      ],
      "publishConfigured": true,
      "publishOrigin": null,
      "createdAt": "2026-10-22T12:00:00.000Z",
      "updatedAt": "2026-10-22T12:00:00.000Z"
    }
  ],
  "nextCursor": null
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-post-projectspidsynctargets"></a>

### Create Sync Target

- **Method:** `POST`
- **Path:** `/projects/{pid}/sync/targets`
- **Operation ID:** `createSyncTarget`
- **Tags:** Sync

Token permissions: media:write. Project role: write.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Header parameters

- **`Idempotency-Key` (required)**: `string`, minLength: `1`, maxLength: `512`

  Required on every POST create. A quoted Structured Field string containing 1 to 255 ASCII characters, for example "request-123". Keep the same key and exact request bytes when retrying. Results, including one-time private material, replay for 24 hours per caller. Interrupted attempts recover within that window. Changed input returns 422; an attempt that cannot yet recover returns 409 idempotency_in_progress.

#### Request body

**Required:** `true`

**Content type:** `application/json`

no additional properties

- **`kind` (required)**: `string`, possible values: `"cloudflare", "custom"`

  Immutable target kind: cloudflare or custom.
- **`label`**: `string`, maxLength: `80`

  Human-readable label, trimmed; empty means unset.
- **`publishPrefixes`**: `array`, maxItems: `20`

  Broadcast path prefixes relative to the project to publish to the target.

  **Items:**

  `string`, maxLength: `200`
- **`publishUrl`**: `string | null`, maxLength: `2000`

  Publish URL including its credential; write-only. null clears it.

<a id="scalar-example-41"></a>

**Generated example:**

```json
{
  "kind": "cloudflare",
  "label": "",
  "publishPrefixes": [
    ""
  ],
  "publishUrl": null
}
```

#### Responses

##### 201 Created

**Content type:** `application/json`

[SyncTarget](#scalar-schema-synctarget)

<a id="scalar-example-42"></a>

**Generated example:**

```json
{
  "id": "",
  "kind": "",
  "label": "",
  "publishPrefixes": [
    ""
  ],
  "publishConfigured": true,
  "publishOrigin": null,
  "createdAt": "2026-10-22T12:00:00.000Z",
  "updatedAt": "2026-10-22T12:00:00.000Z"
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-patch-projectspidsynctargetsid"></a>

### Update Sync Target

- **Method:** `PATCH`
- **Path:** `/projects/{pid}/sync/targets/{id}`
- **Operation ID:** `updateSyncTarget`
- **Tags:** Sync

Token permissions: media:write. Project role: write.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.
- **`id` (required)**: `string`, minLength: `1`

  The resource's id field.

#### Request body

JSON Merge Patch: omitted fields remain unchanged, arrays replace, and null clears nullable fields.

**Required:** `true`

**Content type:** `application/json`

no additional properties

- **`expectedUpdatedAt`**: `string`, format: `date-time`

  The updatedAt of the version being edited. Omit only for an intentional last-writer-wins update.
- **`label`**: `string`, maxLength: `80`

  Human-readable label, trimmed; empty means unset.
- **`publishPrefixes`**: `array`, maxItems: `20`

  Broadcast path prefixes relative to the project to publish to the target.

  **Items:**

  `string`, maxLength: `200`
- **`publishUrl`**: `string | null`, maxLength: `2000`

  Publish URL including its credential; write-only. null clears it.

<a id="scalar-example-43"></a>

**Generated example:**

```json
{
  "label": "",
  "publishPrefixes": [
    ""
  ],
  "publishUrl": null,
  "expectedUpdatedAt": "2026-10-22T12:00:00.000Z"
}
```

**Content type:** `application/merge-patch+json`

no additional properties

- **`expectedUpdatedAt`**: `string`, format: `date-time`

  The updatedAt of the version being edited. Omit only for an intentional last-writer-wins update.
- **`label`**: `string`, maxLength: `80`

  Human-readable label, trimmed; empty means unset.
- **`publishPrefixes`**: `array`, maxItems: `20`

  Broadcast path prefixes relative to the project to publish to the target.

  **Items:**

  `string`, maxLength: `200`
- **`publishUrl`**: `string | null`, maxLength: `2000`

  Publish URL including its credential; write-only. null clears it.

<a id="scalar-example-44"></a>

**Generated example:**

```json
{
  "label": "",
  "publishPrefixes": [
    ""
  ],
  "publishUrl": null,
  "expectedUpdatedAt": "2026-10-22T12:00:00.000Z"
}
```

#### Responses

##### 200 Success

**Content type:** `application/json`

[SyncTarget](#scalar-schema-synctarget)

[Generated example](#scalar-example-42)

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-delete-projectspidsynctargetsid"></a>

### Delete Sync Target

- **Method:** `DELETE`
- **Path:** `/projects/{pid}/sync/targets/{id}`
- **Operation ID:** `deleteSyncTarget`
- **Tags:** Sync

Token permissions: media:write. Project role: write.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.
- **`id` (required)**: `string`, minLength: `1`

  The resource's id field.

#### Responses

##### 204 Success. The response has no body.

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-get-projectspidwebhooks"></a>

### List Webhooks

- **Method:** `GET`
- **Path:** `/projects/{pid}/webhooks`
- **Operation ID:** `listWebhooks`
- **Tags:** Webhooks

Token permissions: media:read. Project role: write. Ordered by createdAt ascending, then id ascending.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Query parameters

- **`limit`**: `integer`, minimum: `1`, maximum: `200`

  Maximum items per page, from 1 to 200.
- **`cursor`**: `string`

  Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.

#### Responses

##### 200 Success

**Content type:** `application/json`

- **`dropped` (required)**: `number`

  Events dropped because the delivery queue was full.
- **`items` (required)**: array of [Webhook](#scalar-schema-webhook)

  This page's items, in the list's documented order.
- **`nextCursor` (required)**: `string | null`

  Pass as cursor to continue, or null when there are no more items.

<a id="scalar-example-45"></a>

**Generated example:**

```json
{
  "items": [
    {
      "id": "",
      "url": "",
      "events": [
        ""
      ],
      "pattern": "",
      "format": "",
      "enabled": true,
      "disabledReason": null,
      "secret": "",
      "failures": 1,
      "createdAt": "2026-10-22T12:00:00.000Z",
      "updatedAt": "2026-10-22T12:00:00.000Z"
    }
  ],
  "nextCursor": null,
  "dropped": 1
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-post-projectspidwebhooks"></a>

### Create Webhook

- **Method:** `POST`
- **Path:** `/projects/{pid}/webhooks`
- **Operation ID:** `createWebhook`
- **Tags:** Webhooks

Token permissions: media:write, keys:write. Project role: write.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Header parameters

- **`Idempotency-Key` (required)**: `string`, minLength: `1`, maxLength: `512`

  Required on every POST create. A quoted Structured Field string containing 1 to 255 ASCII characters, for example "request-123". Keep the same key and exact request bytes when retrying. Results, including one-time private material, replay for 24 hours per caller. Interrupted attempts recover within that window. Changed input returns 422; an attempt that cannot yet recover returns 409 idempotency_in_progress.

#### Request body

**Required:** `true`

**Content type:** `application/json`

no additional properties

- **`events` (required)**: `array`, minItems: `1`, maxItems: `5`

  Event names to deliver; at least one.

  **Items:**

  `string`, possible values: `"broadcast.started", "broadcast.ended", "recording.started", "recording.ready", "recording.failed"`
- **`url` (required)**: `string`, minLength: `1`, maxLength: `2000`

  HTTPS endpoint on a public host that receives signed deliveries.
- **`format`**: `string`, possible values: `"json", "slack", "discord"`

  Delivery body, fixed at creation: `json` (default) is the signed envelope; `slack` and `discord` post an unsigned message to that platform's incoming-webhook URL.
- **`pattern`**: `string`, maxLength: `500`

  Broadcast path pattern relative to the project. Defaults to `**` on create.

<a id="scalar-example-46"></a>

**Generated example:**

```json
{
  "url": "",
  "events": [
    "broadcast.started"
  ],
  "pattern": "",
  "format": "json"
}
```

#### Responses

##### 201 Created

**Content type:** `application/json`

[Webhook](#scalar-schema-webhook)

<a id="scalar-example-47"></a>

**Generated example:**

```json
{
  "id": "",
  "url": "",
  "events": [
    ""
  ],
  "pattern": "",
  "format": "",
  "enabled": true,
  "disabledReason": null,
  "secret": "",
  "failures": 1,
  "createdAt": "2026-10-22T12:00:00.000Z",
  "updatedAt": "2026-10-22T12:00:00.000Z"
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-patch-projectspidwebhooksid"></a>

### Update Webhook

- **Method:** `PATCH`
- **Path:** `/projects/{pid}/webhooks/{id}`
- **Operation ID:** `updateWebhook`
- **Tags:** Webhooks

Token permissions: media:write. Project role: write.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.
- **`id` (required)**: `string`, minLength: `1`

  The resource's id field.

#### Request body

JSON Merge Patch: omitted fields remain unchanged, arrays replace, and null clears nullable fields.

**Required:** `true`

**Content type:** `application/json`

no additional properties

- **`enabled`**: `boolean`

  true re-enables an endpoint that repeated failures disabled.
- **`events`**: `array`, minItems: `1`, maxItems: `5`

  Event names to deliver; at least one.

  **Items:**

  `string`, possible values: `"broadcast.started", "broadcast.ended", "recording.started", "recording.ready", "recording.failed"`
- **`pattern`**: `string`, maxLength: `500`

  Broadcast path pattern relative to the project. Defaults to `**` on create.
- **`url`**: `string`, minLength: `1`, maxLength: `2000`

  HTTPS endpoint on a public host that receives signed deliveries.

<a id="scalar-example-48"></a>

**Generated example:**

```json
{
  "url": "",
  "events": [
    "broadcast.started"
  ],
  "pattern": "",
  "enabled": true
}
```

**Content type:** `application/merge-patch+json`

no additional properties

- **`enabled`**: `boolean`

  true re-enables an endpoint that repeated failures disabled.
- **`events`**: `array`, minItems: `1`, maxItems: `5`

  Event names to deliver; at least one.

  **Items:**

  `string`, possible values: `"broadcast.started", "broadcast.ended", "recording.started", "recording.ready", "recording.failed"`
- **`pattern`**: `string`, maxLength: `500`

  Broadcast path pattern relative to the project. Defaults to `**` on create.
- **`url`**: `string`, minLength: `1`, maxLength: `2000`

  HTTPS endpoint on a public host that receives signed deliveries.

<a id="scalar-example-49"></a>

**Generated example:**

```json
{
  "url": "",
  "events": [
    "broadcast.started"
  ],
  "pattern": "",
  "enabled": true
}
```

#### Responses

##### 200 Success

**Content type:** `application/json`

[Webhook](#scalar-schema-webhook)

[Generated example](#scalar-example-47)

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-delete-projectspidwebhooksid"></a>

### Delete Webhook

- **Method:** `DELETE`
- **Path:** `/projects/{pid}/webhooks/{id}`
- **Operation ID:** `deleteWebhook`
- **Tags:** Webhooks

Token permissions: media:write. Project role: write.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.
- **`id` (required)**: `string`, minLength: `1`

  The resource's id field.

#### Responses

##### 204 Success. The response has no body.

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-post-projectspidwebhooksidtest"></a>

### Test Webhook

- **Method:** `POST`
- **Path:** `/projects/{pid}/webhooks/{id}/test`
- **Operation ID:** `testWebhook`
- **Tags:** Webhooks

Token permissions: media:write. Project role: write.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.
- **`id` (required)**: `string`, minLength: `1`

  The resource's id field.

#### Header parameters

- **`Idempotency-Key` (required)**: `string`, minLength: `1`, maxLength: `512`

  Required on every POST create. A quoted Structured Field string containing 1 to 255 ASCII characters, for example "request-123". Keep the same key and exact request bytes when retrying. Results, including one-time private material, replay for 24 hours per caller. Interrupted attempts recover within that window. Changed input returns 422; an attempt that cannot yet recover returns 409 idempotency_in_progress.

#### Responses

##### 202 Accepted. The body is a receipt; the work completes asynchronously.

**Content type:** `application/json`

[TestedWebhook](#scalar-schema-testedwebhook)

<a id="scalar-example-50"></a>

**Generated example:**

```json
{
  "eventId": ""
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-get-projectspidwebhooksiddeliveries"></a>

### List Webhook Deliveries

- **Method:** `GET`
- **Path:** `/projects/{pid}/webhooks/{id}/deliveries`
- **Operation ID:** `listWebhookDeliveries`
- **Tags:** Webhooks

Token permissions: media:read. Project role: write. Ordered by createdAt descending, then id descending.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.
- **`id` (required)**: `string`, minLength: `1`

  The resource's id field.

#### Query parameters

- **`limit`**: `integer`, minimum: `1`, maximum: `200`

  Maximum items per page, from 1 to 200.
- **`cursor`**: `string`

  Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.

#### Responses

##### 200 Success

**Content type:** `application/json`

- **`items` (required)**: array of [Delivery](#scalar-schema-delivery)

  This page's items, in the list's documented order.
- **`nextCursor` (required)**: `string | null`

  Pass as cursor to continue, or null when there are no more items.

<a id="scalar-example-51"></a>

**Generated example:**

```json
{
  "items": [
    {
      "id": "",
      "eventId": "",
      "type": "",
      "state": "",
      "attempts": 1,
      "status": null,
      "error": null,
      "createdAt": "2026-10-22T12:00:00.000Z",
      "updatedAt": "2026-10-22T12:00:00.000Z"
    }
  ],
  "nextCursor": null
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-get-projectspidusage"></a>

### List Usage

- **Method:** `GET`
- **Path:** `/projects/{pid}/usage`
- **Operation ID:** `listUsage`
- **Tags:** Usage

Token permissions: usage:read. Project role: read. Ordered by periodStart ascending. A cursor retains the resolved from/to window and period for all pages.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Query parameters

- **`limit`**: `integer`, minimum: `1`, maximum: `200`

  Maximum items per page, from 1 to 200.
- **`cursor`**: `string`

  Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.
- **`period`**: `string`, possible values: `"minute", "hour", "day", "month", "year", "forever"`, default: `"day"`

  UTC calendar aggregation grain; defaults to day. from/to select whole periods by their start, without clipping quantities.
- **`from`**: `string`, format: `date-time`

  Inclusive RFC 3339 period-start bound. Defaults to 30 days before to.
- **`to`**: `string`, format: `date-time`

  Exclusive RFC 3339 period-start bound. Defaults to the request time; periods are selected by start, not clipped.

#### Responses

##### 200 Success

**Content type:** `application/json`

- **`allowances` (required)**: `object`

  Monthly pages only: each settled month's frozen allowance by its periodStart, or null while the account split is pending. Members only.

  **Additional properties:**

  [Allowance](#scalar-schema-allowance)
- **`from` (required)**: `string`, format: `date-time`

  RFC 3339 timestamp with a timezone; responses are normalized to UTC.
- **`items` (required)**: array of [Usage](#scalar-schema-usage)

  This page's items, in the list's documented order.
- **`nextCursor` (required)**: `string | null`

  Pass as cursor to continue, or null when there are no more items.
- **`period` (required)**: `string`

  The resolved aggregation grain.
- **`project` (required)**: `string`

  The project's immutable id.
- **`to` (required)**: `string`, format: `date-time`

  RFC 3339 timestamp with a timezone; responses are normalized to UTC.

<a id="scalar-example-52"></a>

**Generated example:**

```json
{
  "items": [
    {
      "periodStart": "2026-10-22T12:00:00.000Z",
      "periodEnd": "2026-10-22T12:00:00.000Z",
      "egressExternalBytes": 1000000,
      "ingressBytes": 1000000,
      "storedGbSeconds": 60,
      "retainedDeltaBytes": 1000000,
      "vodRecordingBytes": 1000000,
      "vodEgressBytes": 1000000,
      "transcode480Seconds": 60,
      "transcode720Seconds": 60,
      "transcode1080Seconds": 60,
      "agentSeconds": 60,
      "webtransportEgressBytes": 1000000,
      "webtransportIngressBytes": 1000000,
      "websocketEgressBytes": 1000000,
      "websocketIngressBytes": 1000000,
      "rtmpEgressBytes": 1000000,
      "rtmpIngressBytes": 1000000,
      "srtEgressBytes": 1000000,
      "srtIngressBytes": 1000000,
      "webrtcEgressBytes": 1000000,
      "webrtcIngressBytes": 1000000,
      "syncEgressBytes": 1000000,
      "syncIngressBytes": 1000000,
      "hlsEgressBytes": 1000000,
      "hlsIngressBytes": 1000000,
      "byobEgressBytes": 1000000,
      "byobIngressBytes": 1000000,
      "egressFrames": 60,
      "ingressFrames": 60,
      "egressGroups": 60,
      "ingressGroups": 60,
      "egressDatagrams": 60,
      "ingressDatagrams": 60,
      "egressSubscriptions": 60,
      "ingressSubscriptions": 60,
      "egressFetches": 60,
      "ingressFetches": 60,
      "egressAnnounced": 60,
      "ingressAnnounced": 60,
      "connectedSeconds": 60,
      "peakConnections": 60,
      "costMicros": 60,
      "partial": false
    }
  ],
  "nextCursor": null,
  "project": "",
  "period": "",
  "from": "2026-10-22T12:00:00.000Z",
  "to": "2026-10-22T12:00:00.000Z",
  "allowances": {
    "additionalProperty": {
      "egressBytes": 1000000000,
      "storedGbSeconds": 86400
    }
  }
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-get-projectspidusagecurrent"></a>

### Get Current Usage

- **Method:** `GET`
- **Path:** `/projects/{pid}/usage/current`
- **Operation ID:** `getCurrentUsage`
- **Tags:** Usage

Token permissions: usage:read. Project role: read.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Responses

##### 200 Success

**Content type:** `application/json`

[CurrentUsage](#scalar-schema-currentusage)

<a id="scalar-example-53"></a>

**Generated example:**

```json
{
  "project": "",
  "usage": {
    "periodStart": "2026-10-22T12:00:00.000Z",
    "periodEnd": "2026-10-22T12:00:00.000Z",
    "egressExternalBytes": 1000000,
    "ingressBytes": 1000000,
    "storedGbSeconds": 60,
    "retainedDeltaBytes": 1000000,
    "vodRecordingBytes": 1000000,
    "vodEgressBytes": 1000000,
    "transcode480Seconds": 60,
    "transcode720Seconds": 60,
    "transcode1080Seconds": 60,
    "agentSeconds": 60,
    "webtransportEgressBytes": 1000000,
    "webtransportIngressBytes": 1000000,
    "websocketEgressBytes": 1000000,
    "websocketIngressBytes": 1000000,
    "rtmpEgressBytes": 1000000,
    "rtmpIngressBytes": 1000000,
    "srtEgressBytes": 1000000,
    "srtIngressBytes": 1000000,
    "webrtcEgressBytes": 1000000,
    "webrtcIngressBytes": 1000000,
    "syncEgressBytes": 1000000,
    "syncIngressBytes": 1000000,
    "hlsEgressBytes": 1000000,
    "hlsIngressBytes": 1000000,
    "byobEgressBytes": 1000000,
    "byobIngressBytes": 1000000,
    "egressFrames": 60,
    "ingressFrames": 60,
    "egressGroups": 60,
    "ingressGroups": 60,
    "egressDatagrams": 60,
    "ingressDatagrams": 60,
    "egressSubscriptions": 60,
    "ingressSubscriptions": 60,
    "egressFetches": 60,
    "ingressFetches": 60,
    "egressAnnounced": 60,
    "ingressAnnounced": 60,
    "connectedSeconds": 60,
    "peakConnections": 60,
    "costMicros": 60,
    "partial": false,
    "freeAllowance": {
      "egressBytes": 1,
      "storedGbSeconds": 1
    },
    "freeEgressBytes": 1,
    "freeVodEgressBytes": 1,
    "freeStoredGbSeconds": 1,
    "freeRecordingBytes": 0,
    "freeMicros": 1,
    "netMicros": 1
  },
  "pool": {
    "periodStart": "2026-10-22T12:00:00.000Z",
    "available": {
      "egressBytes": 1000000000,
      "storedGbSeconds": 86400
    },
    "projected": {
      "egressBytes": 1000000000,
      "storedGbSeconds": 86400
    },
    "drawn": {
      "egressBytes": 1000000000,
      "storedGbSeconds": 86400
    },
    "projects": 1
  }
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-get-projectspidinvoices"></a>

### List Invoices

- **Method:** `GET`
- **Path:** `/projects/{pid}/invoices`
- **Operation ID:** `listInvoices`
- **Tags:** Invoices

Token permissions: billing:read. Project role: owner. Ordered by periodStart descending.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Query parameters

- **`limit`**: `integer`, minimum: `1`, maximum: `200`

  Maximum items per page, from 1 to 200.
- **`cursor`**: `string`

  Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.

#### Responses

##### 200 Success

**Content type:** `application/json`

- **`items` (required)**: array of [Invoice](#scalar-schema-invoice)

  This page's items, in the list's documented order.
- **`nextCursor` (required)**: `string | null`

  Pass as cursor to continue, or null when there are no more items.

<a id="scalar-example-54"></a>

**Generated example:**

```json
{
  "items": [
    {
      "periodStart": "2026-10-22T12:00:00.000Z",
      "grossMicros": 1000000,
      "freeMicros": 1000000,
      "netMicros": 1000000,
      "creditAppliedMicros": 1000000,
      "principalMicros": 1000000,
      "amountChargedMicros": 1000000,
      "status": "",
      "hostedInvoiceUrl": null,
      "poolProjects": null,
      "createdAt": "2026-10-22T12:00:00.000Z"
    }
  ],
  "nextCursor": null
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-get-projectspidspend-cap"></a>

### Get Spend Cap

- **Method:** `GET`
- **Path:** `/projects/{pid}/spend-cap`
- **Operation ID:** `getSpendCap`
- **Tags:** Spend-cap

Token permissions: billing:read. Project role: owner.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Responses

##### 200 Success

**Content type:** `application/json`

[SpendCap](#scalar-schema-spendcap)

<a id="scalar-example-55"></a>

**Generated example:**

```json
{
  "spendCapMicros": null,
  "suspended": true,
  "overFreeTier": true,
  "spendCapReached": true,
  "capReached": true,
  "freeLimited": true,
  "seq": 1,
  "notifications": [
    {
      "id": "",
      "category": "",
      "level": 1,
      "severity": "",
      "dismissible": true,
      "capReached": true
    }
  ]
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-patch-projectspidspend-cap"></a>

### Set Spend Cap

- **Method:** `PATCH`
- **Path:** `/projects/{pid}/spend-cap`
- **Operation ID:** `setSpendCap`
- **Tags:** Spend-cap

Token permissions: billing:write. Project role: owner.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Request body

JSON Merge Patch: omitted fields remain unchanged, arrays replace, and null clears nullable fields.

**Required:** `true`

**Content type:** `application/json`

no additional properties

- **`spendCapMicros` (required)**: `number | null`, minimum: `1000000`, maximum: `1000000000000`

  Monthly spend cap in micro-USD (1,000,000 = USD 1), from USD 1 to USD 1,000,000; null removes it.

<a id="scalar-example-56"></a>

**Generated example:**

```json
{
  "spendCapMicros": null
}
```

**Content type:** `application/merge-patch+json`

no additional properties

- **`spendCapMicros` (required)**: `number | null`, minimum: `1000000`, maximum: `1000000000000`

  Monthly spend cap in micro-USD (1,000,000 = USD 1), from USD 1 to USD 1,000,000; null removes it.

<a id="scalar-example-57"></a>

**Generated example:**

```json
{
  "spendCapMicros": null
}
```

#### Responses

##### 200 Success

**Content type:** `application/json`

[SpendCap](#scalar-schema-spendcap)

[Generated example](#scalar-example-55)

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-get-projectspidpayment"></a>

### Get Payment

- **Method:** `GET`
- **Path:** `/projects/{pid}/payment`
- **Operation ID:** `getPayment`
- **Tags:** Payment

Token permissions: billing:read. Project role: owner. The card summary, gate, contact, and spend cap.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Responses

##### 200 Success

**Content type:** `application/json`

[Payment](#scalar-schema-payment)

<a id="scalar-example-58"></a>

**Generated example:**

```json
{
  "spendCapMicros": null,
  "suspended": true,
  "overFreeTier": true,
  "spendCapReached": true,
  "capReached": true,
  "freeLimited": true,
  "seq": 1,
  "notifications": [
    {
      "id": "",
      "category": "",
      "level": 1,
      "severity": "",
      "dismissible": true,
      "capReached": true
    }
  ],
  "enabled": true,
  "billingExempt": true,
  "publishableKey": "",
  "exposure": {
    "periodStart": "2026-10-22T12:00:00.000Z",
    "monthNetMicros": 1000000,
    "spendCapMicros": 1000000,
    "spendCapRemainingMicros": 1000000,
    "spendCapReached": true,
    "creditLimitMicros": 1000000,
    "creditLimitRemainingMicros": 1000000,
    "invoicedPrincipalMicros": 1000000,
    "accruedPrincipalMicros": 1000000,
    "absorbedMicros": 1000000,
    "headroomMicros": 1000000,
    "headroomSource": null,
    "usableHeadroomMicros": 1000000,
    "identityHold": true
  },
  "billingContact": "",
  "billingRecipient": "",
  "forgivenStatements": true,
  "hasPaymentMethod": true,
  "collectionMode": "",
  "hasStripeCustomer": true,
  "netTerms": "",
  "netTermsDays": 1,
  "creditLimitRequest": {
    "creditLimitMicros": 1000000,
    "reason": "",
    "requestedAt": "2026-10-22T12:00:00.000Z"
  },
  "paymentLabel": null,
  "paymentLast4": null
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-delete-projectspidpayment"></a>

### Remove Payment Method

- **Method:** `DELETE`
- **Path:** `/projects/{pid}/payment`
- **Operation ID:** `removePaymentMethod`
- **Tags:** Payment

Token permissions: billing:write. Project role: owner.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Responses

##### 200 Success

**Content type:** `application/json`

[BillingStatus](#scalar-schema-billingstatus)

<a id="scalar-example-59"></a>

**Generated example:**

```json
{
  "suspended": true,
  "overFreeTier": true,
  "spendCapReached": true,
  "capReached": true,
  "freeLimited": true,
  "seq": 1,
  "notifications": [
    {
      "id": "",
      "category": "",
      "level": 1,
      "severity": "",
      "dismissible": true,
      "capReached": true
    }
  ]
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-get-projectspidcredit"></a>

### Get Credit Balance

- **Method:** `GET`
- **Path:** `/projects/{pid}/credit`
- **Operation ID:** `getCreditBalance`
- **Tags:** Credit

Token permissions: billing:read. Project role: read.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Responses

##### 200 Success

**Content type:** `application/json`

- **`balanceMicros` (required)**: `integer`

  Available in-app credit in micro-USD.
- **`project` (required)**: `string`

  Immutable project id.

<a id="scalar-example-60"></a>

**Generated example:**

```json
{
  "project": "",
  "balanceMicros": 1
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-post-projectspidpaymentsetup-link"></a>

### Create Payment Setup Link

- **Method:** `POST`
- **Path:** `/projects/{pid}/payment/setup-link`
- **Operation ID:** `createPaymentSetupLink`
- **Tags:** Payment

Token permissions: billing:write. Project role: owner. Hand this Stripe-hosted card-entry link to the cardholder. A signed Stripe completion records the card only while the issuer still owns the same billing account. No payment is taken by Checkout.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Responses

##### 200 Success

**Content type:** `application/json`

- **`url` (required)**: `string`, format: `uri`

  Stripe-hosted URL. Share only with the person managing payment.

<a id="scalar-example-61"></a>

**Generated example:**

```json
{
  "url": ""
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-post-projectspidpaymentsetup-intent"></a>

### Create Payment Setup Intent

- **Method:** `POST`
- **Path:** `/projects/{pid}/payment/setup-intent`
- **Operation ID:** `createPaymentSetupIntent`
- **Tags:** Payment

Token permissions: billing:write. Project role: owner. Embedded alternative to the hosted setup link. Confirm through Stripe.js, then call confirmPaymentMethod.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Responses

##### 200 Success

**Content type:** `application/json`

- **`clientSecret` (required)**: `string`

  Stripe SetupIntent secret for the card-entry browser only.

<a id="scalar-example-62"></a>

**Generated example:**

```json
{
  "clientSecret": ""
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-post-projectspidpaymentconfirm"></a>

### Confirm Payment Method

- **Method:** `POST`
- **Path:** `/projects/{pid}/payment/confirm`
- **Operation ID:** `confirmPaymentMethod`
- **Tags:** Payment

Token permissions: billing:write. Project role: owner.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Request body

**Required:** `true`

**Content type:** `application/json`

no additional properties

- **`setupIntentId` (required)**: `string`, minLength: `1`

  The SetupIntent confirmed by Stripe.js.

<a id="scalar-example-63"></a>

**Generated example:**

```json
{
  "setupIntentId": ""
}
```

#### Responses

##### 200 Success

**Content type:** `application/json`

**All of:**

- [BillingStatus](#scalar-schema-billingstatus)
- `object`
  - **`paymentLabel` (required)**: `string`

    Saved method label.
  - **`paymentLast4` (required)**: `string | null`

    Last four digits, or null.

<a id="scalar-example-64"></a>

**Generated example:**

```json
{
  "suspended": true,
  "overFreeTier": true,
  "spendCapReached": true,
  "capReached": true,
  "freeLimited": true,
  "seq": 1,
  "notifications": [
    {
      "id": "",
      "category": "",
      "level": 1,
      "severity": "",
      "dismissible": true,
      "capReached": true
    }
  ],
  "paymentLabel": "",
  "paymentLast4": null
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-post-projectspidpaymentportal"></a>

### Create Billing Portal Link

- **Method:** `POST`
- **Path:** `/projects/{pid}/payment/portal`
- **Operation ID:** `createBillingPortalLink`
- **Tags:** Payment

Token permissions: billing:write. Project role: owner.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Responses

##### 200 Success

**Content type:** `application/json`

- **`url` (required)**: `string`, format: `uri`

  Stripe-hosted URL. Share only with the person managing payment.

<a id="scalar-example-65"></a>

**Generated example:**

```json
{
  "url": ""
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-post-projectspidpaymentretry"></a>

### Pay Now

- **Method:** `POST`
- **Path:** `/projects/{pid}/payment/retry`
- **Operation ID:** `payNow`
- **Tags:** Payment

Token permissions: billing:write. Project role: owner. Collect outstanding invoices using the card on file, through the existing settlement rules.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Responses

##### 200 Success

**Content type:** `application/json`

[BillingStatus](#scalar-schema-billingstatus)

[Generated example](#scalar-example-59)

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-patch-projectspidpaymentcontact"></a>

### Set Billing Contact

- **Method:** `PATCH`
- **Path:** `/projects/{pid}/payment/contact`
- **Operation ID:** `setBillingContact`
- **Tags:** Payment

Token permissions: billing:write. Project role: owner.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Request body

JSON Merge Patch: omitted fields remain unchanged, arrays replace, and null clears nullable fields.

**Required:** `true`

**Content type:** `application/json`

no additional properties

- **`contact` (required)**: `string`, maxLength: `320`

  Billing email; empty restores the owner's address.

<a id="scalar-example-66"></a>

**Generated example:**

```json
{
  "contact": ""
}
```

**Content type:** `application/merge-patch+json`

no additional properties

- **`contact` (required)**: `string`, maxLength: `320`

  Billing email; empty restores the owner's address.

<a id="scalar-example-67"></a>

**Generated example:**

```json
{
  "contact": ""
}
```

#### Responses

##### 200 Success

**Content type:** `application/json`

- **`billingContact` (required)**: `string`

  Explicit billing email; empty uses the owner's address.
- **`billingRecipient` (required)**: `string`

  Resolved address receiving billing mail.

<a id="scalar-example-68"></a>

**Generated example:**

```json
{
  "billingContact": "",
  "billingRecipient": ""
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-patch-projectspidpaymentstatements"></a>

### Set Statement Preferences

- **Method:** `PATCH`
- **Path:** `/projects/{pid}/payment/statements`
- **Operation ID:** `setStatementPreferences`
- **Tags:** Payment

Token permissions: billing:write. Project role: owner. Turn the monthly statement for months with nothing to pay on or off.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Request body

JSON Merge Patch: omitted fields remain unchanged, arrays replace, and null clears nullable fields.

**Required:** `true`

**Content type:** `application/json`

no additional properties

- **`forgivenStatements` (required)**: `boolean`

  Send the monthly statement for months with nothing to pay. Months that charge or owe always send.

<a id="scalar-example-69"></a>

**Generated example:**

```json
{
  "forgivenStatements": true
}
```

**Content type:** `application/merge-patch+json`

no additional properties

- **`forgivenStatements` (required)**: `boolean`

  Send the monthly statement for months with nothing to pay. Months that charge or owe always send.

<a id="scalar-example-70"></a>

**Generated example:**

```json
{
  "forgivenStatements": true
}
```

#### Responses

##### 200 Success

**Content type:** `application/json`

- **`forgivenStatements` (required)**: `boolean`

  Send the monthly statement for months with nothing to pay. Months that charge or owe always send.

<a id="scalar-example-71"></a>

**Generated example:**

```json
{
  "forgivenStatements": true
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-get-projectspidpaymentidentity"></a>

### Get Billing Identity

- **Method:** `GET`
- **Path:** `/projects/{pid}/payment/identity`
- **Operation ID:** `getBillingIdentity`
- **Tags:** Payment

Token permissions: billing:read. Project role: owner.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Responses

##### 200 Success

**Content type:** `application/json`

- **`identity` (required)**: [BillingIdentity](#scalar-schema-billingidentity)

  Stripe legal identity, or null before a billing account exists.

<a id="scalar-example-72"></a>

**Generated example:**

```json
{
  "identity": {
    "companyName": "",
    "address": {
      "line1": "",
      "line2": "",
      "city": "",
      "state": "",
      "postalCode": "",
      "country": ""
    },
    "taxId": {
      "type": "",
      "value": ""
    }
  }
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-patch-projectspidpaymentidentity"></a>

### Set Billing Identity

- **Method:** `PATCH`
- **Path:** `/projects/{pid}/payment/identity`
- **Operation ID:** `setBillingIdentity`
- **Tags:** Payment

Token permissions: billing:write. Project role: owner. Supply the complete identity; empty address fields clear them and null clears the tax ID.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Request body

JSON Merge Patch: omitted fields remain unchanged, arrays replace, and null clears nullable fields.

**Required:** `true`

**Content type:** `application/json`

no additional properties

- **`address` (required)**: `object`, no additional properties

  Billing address; empty fields clear them.
  - **`city` (required)**: `string`, maxLength: `200`

    City.
  - **`country` (required)**

    ISO 3166-1 alpha-2 country; empty clears the address.

    **Any of:**
    - `string`, possible values: `""`
    - `string`, pattern: `^[A-Za-z]{2}$`
  - **`line1` (required)**: `string`, maxLength: `200`

    Street line 1.
  - **`line2` (required)**: `string`, maxLength: `200`

    Street line 2.
  - **`postalCode` (required)**: `string`, maxLength: `30`

    Postal code.
  - **`state` (required)**: `string`, maxLength: `200`

    State or region.
- **`companyName` (required)**: `string`, maxLength: `200`

  The legal entity billed; empty falls back to the email.
- **`taxId` (required)**: `object | null`, no additional properties

  One tax registration, or null to clear it.
  - **`type` (required)**: `string`, possible values: `"eu_vat", "gb_vat", "ch_vat", "no_vat", "us_ein", "ca_bn", "ca_gst_hst", "au_abn", "nz_gst", "jp_ct", "kr_brn", "sg_uen", "in_gst", "br_cnpj", "mx_rfc", "za_vat", "ae_trn"`

    Stripe tax id type.
  - **`value` (required)**: `string`, maxLength: `50`

    The registration number.

<a id="scalar-example-73"></a>

**Generated example:**

```json
{
  "companyName": "",
  "address": {
    "line1": "",
    "line2": "",
    "city": "",
    "state": "",
    "postalCode": "",
    "country": ""
  },
  "taxId": {
    "type": "eu_vat",
    "value": ""
  }
}
```

**Content type:** `application/merge-patch+json`

no additional properties

- **`address` (required)**: `object`, no additional properties

  Billing address; empty fields clear them.
  - **`city` (required)**: `string`, maxLength: `200`

    City.
  - **`country` (required)**

    ISO 3166-1 alpha-2 country; empty clears the address.

    **Any of:**
    - `string`, possible values: `""`
    - `string`, pattern: `^[A-Za-z]{2}$`
  - **`line1` (required)**: `string`, maxLength: `200`

    Street line 1.
  - **`line2` (required)**: `string`, maxLength: `200`

    Street line 2.
  - **`postalCode` (required)**: `string`, maxLength: `30`

    Postal code.
  - **`state` (required)**: `string`, maxLength: `200`

    State or region.
- **`companyName` (required)**: `string`, maxLength: `200`

  The legal entity billed; empty falls back to the email.
- **`taxId` (required)**: `object | null`, no additional properties

  One tax registration, or null to clear it.
  - **`type` (required)**: `string`, possible values: `"eu_vat", "gb_vat", "ch_vat", "no_vat", "us_ein", "ca_bn", "ca_gst_hst", "au_abn", "nz_gst", "jp_ct", "kr_brn", "sg_uen", "in_gst", "br_cnpj", "mx_rfc", "za_vat", "ae_trn"`

    Stripe tax id type.
  - **`value` (required)**: `string`, maxLength: `50`

    The registration number.

<a id="scalar-example-74"></a>

**Generated example:**

```json
{
  "companyName": "",
  "address": {
    "line1": "",
    "line2": "",
    "city": "",
    "state": "",
    "postalCode": "",
    "country": ""
  },
  "taxId": {
    "type": "eu_vat",
    "value": ""
  }
}
```

#### Responses

##### 200 Success

**Content type:** `application/json`

- **`identity` (required)**: [BillingIdentity](#scalar-schema-billingidentity)

  Stripe legal identity, or null before a billing account exists.

<a id="scalar-example-75"></a>

**Generated example:**

```json
{
  "identity": {
    "companyName": "",
    "address": {
      "line1": "",
      "line2": "",
      "city": "",
      "state": "",
      "postalCode": "",
      "country": ""
    },
    "taxId": {
      "type": "",
      "value": ""
    }
  }
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-post-projectspidpaymentnet-terms"></a>

### Request Net Terms

- **Method:** `POST`
- **Path:** `/projects/{pid}/payment/net-terms`
- **Operation ID:** `requestNetTerms`
- **Tags:** Payment

Token permissions: billing:write. Project role: owner.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Responses

##### 200 Success

**Content type:** `application/json`

- **`netTerms` (required)**: `string`

  Net-terms approval state; empty means no request. Known values: "", "pending", "active", "cancelling", "revoking". Additional values may appear.

<a id="scalar-example-76"></a>

**Generated example:**

```json
{
  "netTerms": ""
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-delete-projectspidpaymentnet-terms"></a>

### Cancel Net Terms

- **Method:** `DELETE`
- **Path:** `/projects/{pid}/payment/net-terms`
- **Operation ID:** `cancelNetTerms`
- **Tags:** Payment

Token permissions: billing:write. Project role: owner.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Responses

##### 200 Success

**Content type:** `application/json`

- **`netTerms` (required)**: `string`

  Net-terms approval state; empty means no request. Known values: "", "pending", "active", "cancelling", "revoking". Additional values may appear.

<a id="scalar-example-77"></a>

**Generated example:**

```json
{
  "netTerms": ""
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-post-projectspidpaymentcredit-limit"></a>

### Request Credit Limit

- **Method:** `POST`
- **Path:** `/projects/{pid}/payment/credit-limit`
- **Operation ID:** `requestCreditLimit`
- **Tags:** Payment

Token permissions: billing:write. Project role: owner. Request an increase; the approved limit and charges do not change until an operator approves it.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Request body

**Required:** `true`

**Content type:** `application/json`

no additional properties

- **`creditLimitMicros` (required)**: `integer`, maximum: `1000000000000`, exclusiveMinimum: `0`

  Requested credit limit in micro-USD, above the current limit.
- **`reason` (required)**: `string`, maxLength: `500`

  Why the higher limit is needed.

<a id="scalar-example-78"></a>

**Generated example:**

```json
{
  "creditLimitMicros": 1,
  "reason": ""
}
```

#### Responses

##### 200 Success

**Content type:** `application/json`

- **`creditLimitRequest` (required)**: [CreditLimitRequestResponse](#scalar-schema-creditlimitrequestresponse)

  The owner's pending request, or null.

<a id="scalar-example-79"></a>

**Generated example:**

```json
{
  "creditLimitRequest": {
    "creditLimitMicros": 1000000,
    "reason": "",
    "requestedAt": "2026-10-22T12:00:00.000Z"
  }
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-get-projectspidnotificationslive"></a>

### Watch Notifications

- **Method:** `GET`
- **Path:** `/projects/{pid}/notifications/live`
- **Operation ID:** `watchNotifications`
- **Tags:** Notifications

Token permissions: settings:read. Project role: read. A read-only WebSocket of the project's billing notices, for the members and public viewers getProject admits. Open it as a WebSocket handshake; any other request is refused with 426. A browser authenticates with its session cookie from the dashboard's origin; an agent sends its token in the Authorization header. On connect and after every change the server sends a NotificationFrame as a JSON text message; apply a frame only when its seq exceeds the last one applied. The server answers the text message ping with pong and ignores any other message, so ping an idle socket to detect a dead connection (the dashboard pings every 30 seconds and redials after 95 seconds of silence). A socket receives frames for ten minutes after its handshake; the next change after that closes it with code 1000 instead, so reconnect, which authorizes again. Hide a notice with dismissNotification.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Responses

##### 101 Switching Protocols: the socket is open. Each server message is a NotificationFrame; see x-websocket.

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-post-projectspidnotificationsdismiss"></a>

### Dismiss Notification

- **Method:** `POST`
- **Path:** `/projects/{pid}/notifications/dismiss`
- **Operation ID:** `dismissNotification`
- **Tags:** Notifications

Token permissions: settings:write. Project role: write. Hides an advisory billing notice from every member until the billing month ends. Returns the project's current notices.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Request body

**Required:** `true`

**Content type:** `application/json`

no additional properties

- **`id` (required)**: `string`, maxLength: `64`

  The notice's id. Only a notice whose dismissible is true can be dismissed.

<a id="scalar-example-80"></a>

**Generated example:**

```json
{
  "id": ""
}
```

#### Responses

##### 200 Success

**Content type:** `application/json`

[NotificationFrame](#scalar-schema-notificationframe)

<a id="scalar-example-81"></a>

**Generated example:**

```json
{
  "seq": 1,
  "notifications": [
    {
      "id": "",
      "category": "",
      "level": 1,
      "severity": "",
      "dismissible": true,
      "capReached": true
    }
  ]
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-get-accounttokens"></a>

### List Tokens

- **Method:** `GET`
- **Path:** `/account/tokens`
- **Operation ID:** `listTokens`
- **Tags:** Tokens

Token permissions: credentials:read. Unrevoked tokens; an expired one stays listed until revoked. Ordered by createdAt descending, then id descending.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Query parameters

- **`limit`**: `integer`, minimum: `1`, maximum: `200`

  Maximum items per page, from 1 to 200.
- **`cursor`**: `string`

  Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.

#### Responses

##### 200 Success

**Content type:** `application/json`

- **`items` (required)**: array of [ApiToken](#scalar-schema-apitoken)

  This page's items, in the list's documented order.
- **`nextCursor` (required)**: `string | null`

  Pass as cursor to continue, or null when there are no more items.

<a id="scalar-example-82"></a>

**Generated example:**

```json
{
  "items": [
    {
      "id": "",
      "name": "",
      "prefix": "",
      "permissions": {
        "settings": "",
        "keys": "",
        "media": "",
        "usage": "",
        "lifecycle": "",
        "members": "",
        "billing": "",
        "credentials": "",
        "account": "",
        "admin": ""
      },
      "projects": [
        ""
      ],
      "audience": null,
      "clientId": null,
      "expiresAt": "2026-10-22T12:00:00.000Z",
      "lastUsedAt": "2026-10-22T12:00:00.000Z",
      "createdAt": "2026-10-22T12:00:00.000Z"
    }
  ],
  "nextCursor": null
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-post-accounttokens"></a>

### Create Token

- **Method:** `POST`
- **Path:** `/account/tokens`
- **Operation ID:** `createToken`
- **Tags:** Tokens

Token permissions: credentials:write.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Header parameters

- **`Idempotency-Key` (required)**: `string`, minLength: `1`, maxLength: `512`

  Required on every POST create. A quoted Structured Field string containing 1 to 255 ASCII characters, for example "request-123". Keep the same key and exact request bytes when retrying. Results, including one-time private material, replay for 24 hours per caller. Interrupted attempts recover within that window. Changed input returns 422; an attempt that cannot yet recover returns 409 idempotency_in_progress.

#### Request body

**Required:** `true`

**Content type:** `application/json`

no additional properties

- **`name` (required)**: `string`, minLength: `1`, maxLength: `100`

  Human-readable label.
- **`permissions` (required)**: [PermissionsInput](#scalar-schema-permissionsinput)

  A level per permission family; an omitted family is none. See Permissions.
- **`expiresAt`**: `string | null`, format: `date-time`

  Expiry in the future. Omit or null for never; a token with admin expires within 90 days.
- **`projects`**: `(array of string) | null`, minItems: `1`, maxItems: `100`

  Project ids to restrict it to, which allows only the project families. Omit or null for every project the account reaches.

<a id="scalar-example-83"></a>

**Generated example:**

```json
{
  "name": "github-actions",
  "permissions": {
    "settings": "none",
    "keys": "none",
    "media": "none",
    "usage": "none",
    "lifecycle": "none",
    "members": "none",
    "billing": "none",
    "credentials": "none",
    "account": "none",
    "admin": "none"
  },
  "projects": [
    ""
  ],
  "expiresAt": "2026-10-22T12:00:00.000Z"
}
```

#### Responses

##### 201 Created

**Content type:** `application/json`

[CreatedApiToken](#scalar-schema-createdapitoken)

<a id="scalar-example-84"></a>

**Generated example:**

```json
{
  "id": "",
  "name": "",
  "prefix": "",
  "permissions": {
    "settings": "",
    "keys": "",
    "media": "",
    "usage": "",
    "lifecycle": "",
    "members": "",
    "billing": "",
    "credentials": "",
    "account": "",
    "admin": ""
  },
  "projects": [
    ""
  ],
  "audience": null,
  "clientId": null,
  "expiresAt": "2026-10-22T12:00:00.000Z",
  "lastUsedAt": "2026-10-22T12:00:00.000Z",
  "createdAt": "2026-10-22T12:00:00.000Z",
  "secret": ""
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-delete-accounttokenscurrent"></a>

### Revoke Current Token

- **Method:** `DELETE`
- **Path:** `/account/tokens/current`
- **Operation ID:** `revokeCurrentToken`
- **Tags:** Tokens

Any token. Revokes the calling token: logout for an agent. A session cookie is refused.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Responses

##### 204 Success. The response has no body.

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-delete-accounttokensid"></a>

### Revoke Token

- **Method:** `DELETE`
- **Path:** `/account/tokens/{id}`
- **Operation ID:** `revokeToken`
- **Tags:** Tokens

Token permissions: credentials:write. Anything still using it gets 401 right away. A token revokes itself without credentials.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`id` (required)**: `string`, minLength: `1`

  The token's id field.

#### Responses

##### 204 Success. The response has no body.

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-patch-accounttokensid"></a>

### Update Token

- **Method:** `PATCH`
- **Path:** `/account/tokens/{id}`
- **Operation ID:** `updateToken`
- **Tags:** Tokens

Token permissions: credentials:write. Narrows a token in place: a family may drop and never rise, and a project list may only lose projects. Restricting an all-projects token to projects needs every family outside the project ones at none.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`id` (required)**: `string`, minLength: `1`

  The token's id field.

#### Request body

JSON Merge Patch: omitted fields remain unchanged, arrays replace, and null clears nullable fields.

**Required:** `true`

**Content type:** `application/json`

no additional properties

- **`permissions`**

  **All of:**
  - [PermissionsInput](#scalar-schema-permissionsinput)

    A level per permission family; an omitted family is none. See Permissions.
  - `any`

    Lower levels for the families named; omitted ones stay.
- **`projects`**: `array of string`, minItems: `1`, maxItems: `100`

  The projects it keeps: a subset of its list, or any of yours for an all-projects token.

<a id="scalar-example-85"></a>

**Generated example:**

```json
{
  "permissions": {
    "settings": "none",
    "keys": "none",
    "media": "none",
    "usage": "none",
    "lifecycle": "none",
    "members": "none",
    "billing": "none",
    "credentials": "none",
    "account": "none",
    "admin": "none"
  },
  "projects": [
    ""
  ]
}
```

**Content type:** `application/merge-patch+json`

no additional properties

- **`permissions`**

  **All of:**
  - [PermissionsInput](#scalar-schema-permissionsinput)

    A level per permission family; an omitted family is none. See Permissions.
  - `any`

    Lower levels for the families named; omitted ones stay.
- **`projects`**: `array of string`, minItems: `1`, maxItems: `100`

  The projects it keeps: a subset of its list, or any of yours for an all-projects token.

<a id="scalar-example-86"></a>

**Generated example:**

```json
{
  "permissions": {
    "settings": "none",
    "keys": "none",
    "media": "none",
    "usage": "none",
    "lifecycle": "none",
    "members": "none",
    "billing": "none",
    "credentials": "none",
    "account": "none",
    "admin": "none"
  },
  "projects": [
    ""
  ]
}
```

#### Responses

##### 200 Success

**Content type:** `application/json`

[ApiToken](#scalar-schema-apitoken)

<a id="scalar-example-87"></a>

**Generated example:**

```json
{
  "id": "",
  "name": "",
  "prefix": "",
  "permissions": {
    "settings": "",
    "keys": "",
    "media": "",
    "usage": "",
    "lifecycle": "",
    "members": "",
    "billing": "",
    "credentials": "",
    "account": "",
    "admin": ""
  },
  "projects": [
    ""
  ],
  "audience": null,
  "clientId": null,
  "expiresAt": "2026-10-22T12:00:00.000Z",
  "lastUsedAt": "2026-10-22T12:00:00.000Z",
  "createdAt": "2026-10-22T12:00:00.000Z"
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-get-account"></a>

### Get Account

- **Method:** `GET`
- **Path:** `/account`
- **Operation ID:** `getAccount`
- **Tags:** Account

Token permissions: account:read.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Responses

##### 200 Success

**Content type:** `application/json`

[Account](#scalar-schema-account)

<a id="scalar-example-88"></a>

**Generated example:**

```json
{
  "id": "",
  "email": "",
  "name": "",
  "isAdmin": true,
  "ownedProjectCap": 1,
  "domainLoginRequired": true,
  "domainAccessExpiresAt": 1
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-patch-account"></a>

### Update Account

- **Method:** `PATCH`
- **Path:** `/account`
- **Operation ID:** `updateAccount`
- **Tags:** Account

Token permissions: account:write.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Request body

JSON Merge Patch: omitted fields remain unchanged, arrays replace, and null clears nullable fields.

**Required:** `true`

**Content type:** `application/json`

no additional properties

- **`name`**: `string`, minLength: `2`, maxLength: `100`

  Account display name, between 2 and 100 characters. Omit to leave unchanged.

<a id="scalar-example-89"></a>

**Generated example:**

```json
{
  "name": ""
}
```

**Content type:** `application/merge-patch+json`

no additional properties

- **`name`**: `string`, minLength: `2`, maxLength: `100`

  Account display name, between 2 and 100 characters. Omit to leave unchanged.

<a id="scalar-example-90"></a>

**Generated example:**

```json
{
  "name": ""
}
```

#### Responses

##### 200 Success

**Content type:** `application/json`

[Account](#scalar-schema-account)

[Generated example](#scalar-example-88)

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-delete-account"></a>

### Delete Account

- **Method:** `DELETE`
- **Path:** `/account`
- **Operation ID:** `deleteAccount`
- **Tags:** Account

Token permissions: account:write. Deletes the account and its owned projects in bounded steps. Every owned project is checked before any is deleted, then they are deleted a page at a time, each re-checked first. With Prefer: respond-async, a 202 means more remains: repeat with its nextStep as step until a 204. Banned projects or unpaid usage refuse deletion. Preview the affected projects first with getAccountDeletionPreview.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Query parameters

- **`step`**: `string`

  Opaque nextStep from the previous 202 response; omit to start deleting.

#### Header parameters

- **`Prefer`**: `string`

  Send respond-async (RFC 7240) to accept a 202 continuation. Without it, or a step, an account too large for one request is refused with 409 before anything is deleted.

#### Responses

##### 202 Not finished. Repeat the request with nextStep as step to continue.

**Content type:** `application/json`

[AccountDeletionStep](#scalar-schema-accountdeletionstep)

<a id="scalar-example-91"></a>

**Generated example:**

```json
{
  "nextStep": ""
}
```

##### 204 Success. The response has no body.

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-get-accountusage"></a>

### Get Account Usage

- **Method:** `GET`
- **Path:** `/account/usage`
- **Operation ID:** `getAccountUsage`
- **Tags:** Usage

Token permissions: billing:read.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Responses

##### 200 Success

**Content type:** `application/json`

[AccountUsage](#scalar-schema-accountusage)

<a id="scalar-example-92"></a>

**Generated example:**

```json
{
  "periodStart": "2026-10-22T12:00:00.000Z",
  "pooled": true,
  "allowance": {
    "egressBytes": 1000000000,
    "storedGbSeconds": 86400
  },
  "drawn": {
    "egressBytes": 1000000000,
    "storedGbSeconds": 86400
  },
  "projects": [
    {
      "id": "",
      "name": null,
      "costMicros": 0,
      "egressBytes": 1,
      "storedGbSeconds": 1,
      "share": {
        "egressBytes": 1000000000,
        "storedGbSeconds": 86400
      },
      "freeMicros": 0,
      "netMicros": 0
    }
  ]
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-get-accountexport"></a>

### Export Account

- **Method:** `GET`
- **Path:** `/account/export`
- **Operation ID:** `exportAccount`
- **Tags:** Export

Token permissions: account:read. Exports the account and every project it can see, in id order, a page at a time. Each page reads the account as it stands; keep the first page's.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Query parameters

- **`limit`**: `integer`, minimum: `1`, maximum: `200`

  Maximum items per page, from 1 to 200.
- **`cursor`**: `string`

  Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.

#### Responses

##### 200 Success

**Content type:** `application/json`

[AccountExport](#scalar-schema-accountexport)

<a id="scalar-example-93"></a>

**Generated example:**

```json
{
  "items": [
    {
      "id": "",
      "name": null,
      "role": "",
      "public": {
        "v": 1,
        "publish": [
          ""
        ],
        "subscribe": [
          ""
        ]
      },
      "domain": null,
      "domainRole": "",
      "createdAt": "2026-10-22T12:00:00.000Z",
      "billingIdentity": {
        "companyName": "",
        "address": {
          "line1": "",
          "line2": "",
          "city": "",
          "state": "",
          "postalCode": "",
          "country": ""
        },
        "taxId": {
          "type": "",
          "value": ""
        }
      },
      "usage": {
        "currentMonth": {
          "periodStart": "2026-10-22T12:00:00.000Z",
          "periodEnd": "2026-10-22T12:00:00.000Z",
          "egressExternalBytes": 1000000,
          "ingressBytes": 1000000,
          "storedGbSeconds": 60,
          "retainedDeltaBytes": 1000000,
          "vodRecordingBytes": 1000000,
          "vodEgressBytes": 1000000,
          "transcode480Seconds": 60,
          "transcode720Seconds": 60,
          "transcode1080Seconds": 60,
          "agentSeconds": 60,
          "webtransportEgressBytes": 1000000,
          "webtransportIngressBytes": 1000000,
          "websocketEgressBytes": 1000000,
          "websocketIngressBytes": 1000000,
          "rtmpEgressBytes": 1000000,
          "rtmpIngressBytes": 1000000,
          "srtEgressBytes": 1000000,
          "srtIngressBytes": 1000000,
          "webrtcEgressBytes": 1000000,
          "webrtcIngressBytes": 1000000,
          "syncEgressBytes": 1000000,
          "syncIngressBytes": 1000000,
          "hlsEgressBytes": 1000000,
          "hlsIngressBytes": 1000000,
          "byobEgressBytes": 1000000,
          "byobIngressBytes": 1000000,
          "egressFrames": 60,
          "ingressFrames": 60,
          "egressGroups": 60,
          "ingressGroups": 60,
          "egressDatagrams": 60,
          "ingressDatagrams": 60,
          "egressSubscriptions": 60,
          "ingressSubscriptions": 60,
          "egressFetches": 60,
          "ingressFetches": 60,
          "egressAnnounced": 60,
          "ingressAnnounced": 60,
          "connectedSeconds": 60,
          "peakConnections": 60,
          "costMicros": 60,
          "partial": false,
          "freeAllowance": {
            "egressBytes": 1,
            "storedGbSeconds": 1
          },
          "freeEgressBytes": 1,
          "freeVodEgressBytes": 1,
          "freeStoredGbSeconds": 1,
          "freeRecordingBytes": 0,
          "freeMicros": 1,
          "netMicros": 1
        }
      }
    }
  ],
  "nextCursor": null,
  "exportedAt": "2026-10-22T12:00:00.000Z",
  "account": {
    "id": "",
    "email": "",
    "name": "",
    "isAdmin": true,
    "ownedProjectCap": 1,
    "domainLoginRequired": true,
    "domainAccessExpiresAt": 1
  }
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-get-accountdeletion-preview"></a>

### Get Account Deletion Preview

- **Method:** `GET`
- **Path:** `/account/deletion-preview`
- **Operation ID:** `getAccountDeletionPreview`
- **Tags:** Deletion-preview

Token permissions: account:read. Lists the owned projects account deletion would destroy, in id order, a page at a time.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Query parameters

- **`limit`**: `integer`, minimum: `1`, maximum: `200`

  Maximum items per page, from 1 to 200.
- **`cursor`**: `string`

  Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.

#### Responses

##### 200 Success

**Content type:** `application/json`

[AccountDeletionPreview](#scalar-schema-accountdeletionpreview)

<a id="scalar-example-94"></a>

**Generated example:**

```json
{
  "items": [
    {
      "id": "",
      "name": "",
      "outstanding": true,
      "banned": true,
      "otherMembers": 0
    }
  ],
  "nextCursor": null,
  "projects": [
    {
      "id": "",
      "name": "",
      "outstanding": true,
      "banned": true,
      "otherMembers": 0
    }
  ]
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-get-accountpasskeys"></a>

### List Passkeys

- **Method:** `GET`
- **Path:** `/account/passkeys`
- **Operation ID:** `listPasskeys`
- **Tags:** Passkeys

Token permissions: credentials:read. Passkeys that sign in to the account. Enrol one from the dashboard; that stays human. Ordered by createdAt descending, then id descending.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Query parameters

- **`limit`**: `integer`, minimum: `1`, maximum: `200`

  Maximum items per page, from 1 to 200.
- **`cursor`**: `string`

  Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.

#### Responses

##### 200 Success

**Content type:** `application/json`

- **`items` (required)**: array of [Passkey](#scalar-schema-passkey)

  This page's items, in the list's documented order.
- **`nextCursor` (required)**: `string | null`

  Pass as cursor to continue, or null when there are no more items.

<a id="scalar-example-95"></a>

**Generated example:**

```json
{
  "items": [
    {
      "id": "",
      "createdAt": "2026-10-22T12:00:00.000Z",
      "lastUsedAt": "2026-10-22T12:00:00.000Z",
      "stale": true
    }
  ],
  "nextCursor": null
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-delete-accountpasskeysid"></a>

### Delete Passkey

- **Method:** `DELETE`
- **Path:** `/account/passkeys/{id}`
- **Operation ID:** `deletePasskey`
- **Tags:** Passkeys

Token permissions: credentials:write. It stops signing in at once; sessions it already started continue.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`id` (required)**: `string`, minLength: `1`

  The passkey's id field.

#### Responses

##### 204 Success. The response has no body.

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-delete-accountsessionscurrent"></a>

### Revoke Current Session

- **Method:** `DELETE`
- **Path:** `/account/sessions/current`
- **Operation ID:** `revokeCurrentSession`
- **Tags:** Sessions

Any token. Signs out the calling browser session: logout for a person. The response clears its cookie. A token is refused; it revokes itself with revokeCurrentToken.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Responses

##### 204 Success. The response has no body.

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-delete-accountsessions"></a>

### Revoke Sessions

- **Method:** `DELETE`
- **Path:** `/account/sessions`
- **Operation ID:** `revokeSessions`
- **Tags:** Sessions

Token permissions: credentials:write. Signs out everywhere: ends every browser session and revokes every API token and approved device grant on the account, the calling token included. A browser caller stays signed in: the response reissues its cookie, still bound to the session's original absolute expiry.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Responses

##### 204 Success. The response has no body.

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-get-projectspidmembers"></a>

### List Members

- **Method:** `GET`
- **Path:** `/projects/{pid}/members`
- **Operation ID:** `listMembers`
- **Tags:** Members

Token permissions: members:read. Project role: read. Ordered by createdAt ascending, then accountId ascending.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Query parameters

- **`limit`**: `integer`, minimum: `1`, maximum: `200`

  Maximum items per page, from 1 to 200.
- **`cursor`**: `string`

  Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.

#### Responses

##### 200 Success

**Content type:** `application/json`

- **`items` (required)**: array of [ProjectMember](#scalar-schema-projectmember)

  This page's items, in the list's documented order.
- **`nextCursor` (required)**: `string | null`

  Pass as cursor to continue, or null when there are no more items.

<a id="scalar-example-96"></a>

**Generated example:**

```json
{
  "items": [
    {
      "accountId": "",
      "email": "",
      "role": "",
      "createdAt": ""
    }
  ],
  "nextCursor": null
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-patch-projectspidmembersaccountid"></a>

### Update Member

- **Method:** `PATCH`
- **Path:** `/projects/{pid}/members/{accountId}`
- **Operation ID:** `updateMember`
- **Tags:** Members

Token permissions: members:write. Project role: owner.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.
- **`accountId` (required)**: `string`, minLength: `1`

  Member account id from the roster.

#### Request body

JSON Merge Patch: omitted fields remain unchanged, arrays replace, and null clears nullable fields.

**Required:** `true`

**Content type:** `application/json`

no additional properties

- **`role` (required)**: `string`, possible values: `"read", "write"`

  Member access; ownership moves only through an accepted transfer.

<a id="scalar-example-97"></a>

**Generated example:**

```json
{
  "role": "read"
}
```

**Content type:** `application/merge-patch+json`

no additional properties

- **`role` (required)**: `string`, possible values: `"read", "write"`

  Member access; ownership moves only through an accepted transfer.

<a id="scalar-example-98"></a>

**Generated example:**

```json
{
  "role": "read"
}
```

#### Responses

##### 204 Success. The response has no body.

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-delete-projectspidmembersaccountid"></a>

### Remove Member

- **Method:** `DELETE`
- **Path:** `/projects/{pid}/members/{accountId}`
- **Operation ID:** `removeMember`
- **Tags:** Members

Token permissions: members:write. Project role: owner.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.
- **`accountId` (required)**: `string`, minLength: `1`

  Member account id from the roster.

#### Responses

##### 204 Success. The response has no body.

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-get-projectspidinvites"></a>

### List Invitations

- **Method:** `GET`
- **Path:** `/projects/{pid}/invites`
- **Operation ID:** `listInvitations`
- **Tags:** Invites

Token permissions: members:read. Project role: read. Ordered by createdAt ascending, then email ascending.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Query parameters

- **`limit`**: `integer`, minimum: `1`, maximum: `200`

  Maximum items per page, from 1 to 200.
- **`cursor`**: `string`

  Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.

#### Responses

##### 200 Success

**Content type:** `application/json`

- **`items` (required)**: array of [ProjectInvitation](#scalar-schema-projectinvitation)

  This page's items, in the list's documented order.
- **`nextCursor` (required)**: `string | null`

  Pass as cursor to continue, or null when there are no more items.

<a id="scalar-example-99"></a>

**Generated example:**

```json
{
  "items": [
    {
      "email": "",
      "role": "",
      "createdAt": ""
    }
  ],
  "nextCursor": null
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-post-projectspidinvites"></a>

### Invite Member

- **Method:** `POST`
- **Path:** `/projects/{pid}/invites`
- **Operation ID:** `inviteMember`
- **Tags:** Invites

Token permissions: members:write. Project role: owner. Existing accounts join immediately; other addresses receive a pending invite claimed on sign-in. Re-inviting a member leaves its role unchanged. Each call may resend mail under the existing inviter and recipient budgets; Idempotency-Key is not supported for this action. A 429 with Retry-After saves the pending invite but sends no email.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Request body

**Required:** `true`

**Content type:** `application/json`

no additional properties

- **`email` (required)**: `string`, format: `email`

  Email address to invite.
- **`role`**: `string`, possible values: `"read", "write"`

  Access to grant; defaults to write.

<a id="scalar-example-100"></a>

**Generated example:**

```json
{
  "email": "",
  "role": "read"
}
```

#### Responses

##### 201 Created

**Content type:** `application/json`

- **`pending` (required)**: `boolean`

  True when the email has no account yet. The invite is claimed on sign-in or by acceptInvitation.

<a id="scalar-example-101"></a>

**Generated example:**

```json
{
  "pending": true
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-delete-projectspidinvites"></a>

### Revoke Invitation

- **Method:** `DELETE`
- **Path:** `/projects/{pid}/invites`
- **Operation ID:** `revokeInvitation`
- **Tags:** Invites

Token permissions: members:write. Project role: owner.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Request body

**Required:** `true`

**Content type:** `application/json`

no additional properties

- **`email` (required)**: `string`, format: `email`

  Invited email address.

<a id="scalar-example-102"></a>

**Generated example:**

```json
{
  "email": ""
}
```

#### Responses

##### 204 Success. The response has no body.

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-post-projectspidinvitesaccept"></a>

### Accept Invitation

- **Method:** `POST`
- **Path:** `/projects/{pid}/invites/accept`
- **Operation ID:** `acceptInvitation`
- **Tags:** Invites

Token permissions: members:write. Claims only the authenticated account's email invitation. No membership is needed; a token cannot accept for another account. Sign-in also claims pending invitations automatically.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Responses

##### 204 Success. The response has no body.

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-get-projectspidtransfer"></a>

### Get Ownership Transfer

- **Method:** `GET`
- **Path:** `/projects/{pid}/transfer`
- **Operation ID:** `getOwnershipTransfer`
- **Tags:** Transfer

Token permissions: members:read. Project role: read.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Responses

##### 200 Success

**Content type:** `application/json`

[OwnershipTransfer](#scalar-schema-ownershiptransfer)

<a id="scalar-example-103"></a>

**Generated example:**

```json
{
  "pending": {
    "toAccountId": "",
    "toEmail": "",
    "fromAccountId": "",
    "fromEmail": ""
  }
}
```

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-post-projectspidtransfer"></a>

### Offer Ownership Transfer

- **Method:** `POST`
- **Path:** `/projects/{pid}/transfer`
- **Operation ID:** `offerOwnershipTransfer`
- **Tags:** Transfer

Token permissions: members:write. Project role: owner. Replaces the outstanding offer; banned projects cannot be transferred.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Request body

**Required:** `true`

**Content type:** `application/json`

no additional properties

- **`email` (required)**: `string`, format: `email`

  Existing direct Editor member to receive ownership.

<a id="scalar-example-104"></a>

**Generated example:**

```json
{
  "email": ""
}
```

#### Responses

##### 201 Created

**Content type:** `application/json`

[OwnershipTransfer](#scalar-schema-ownershiptransfer)

[Generated example](#scalar-example-103)

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-delete-projectspidtransfer"></a>

### Cancel Ownership Transfer

- **Method:** `DELETE`
- **Path:** `/projects/{pid}/transfer`
- **Operation ID:** `cancelOwnershipTransfer`
- **Tags:** Transfer

Token permissions: members:write. Project role: owner.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Responses

##### 204 Success. The response has no body.

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-post-projectspidtransferaccept"></a>

### Accept Ownership Transfer

- **Method:** `POST`
- **Path:** `/projects/{pid}/transfer/accept`
- **Operation ID:** `acceptOwnershipTransfer`
- **Tags:** Transfer

Token permissions: members:write. Project role: read. Only the recipient's own account can accept. Existing owner caps, ban, net-terms and billing handoff checks apply.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Request body

**Required:** `true`

**Content type:** `application/json`

no additional properties

- **`sameEntity` (required)**: `boolean`

  True retains the billed company and removes its card; false clears billing identity and writes off unpaid invoices. The recipient chooses explicitly.

<a id="scalar-example-105"></a>

**Generated example:**

```json
{
  "sameEntity": true
}
```

#### Responses

##### 204 Success. The response has no body.

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-post-projectspidtransferdecline"></a>

### Decline Ownership Transfer

- **Method:** `POST`
- **Path:** `/projects/{pid}/transfer/decline`
- **Operation ID:** `declineOwnershipTransfer`
- **Tags:** Transfer

Token permissions: members:write. Project role: read.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

- **bearerAuth**: HTTP bearer (`moqpro_`)

  An account API token from the dashboard's account settings, `moqpro_` and 46 base62 characters. A token holds a level (none, read, or write) per permission family, and each operation's x-permissions names the levels it needs. The project families are settings, keys (keys & secrets), media (recording, transcode, agent, sync, webhooks), and usage; keys:write is the only level that reveals a secret or mints a credential. The owner families are lifecycle (create and delete projects), members (members, invites, transfer), and billing; the account families are credentials (tokens, passkeys, sessions) and account (profile, export, deletion). A token restricted to projects reaches only those and holds only project families. Either way, the account's role on each project still applies. Any token can revoke itself.

Or:

- **sessionCookie**: API key in cookie `__Host-moq_session`

  The dashboard's session.

#### Path parameters

- **`pid` (required)**: `string`, minLength: `1`

  Immutable public project id from Project.id; vanity names are refused.

#### Responses

##### 204 Success. The response has no body.

##### 4XX, 5XX

- `4XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.
- `5XX` Request refused. 400 invalid input; 401 authentication; 403 permission; 404 missing resource; 409 conflict (see code; idempotency_in_progress means in flight, keep the same key); 415 unsupported JSON media type; 422 changed idempotent input; 429 rate limited; 500 internal error; 503 unavailable.

**Content type:** `application/json`

[ApiError](#scalar-schema-apierror)

[Generated example](#scalar-example-2)

<a id="scalar-operation-post-oauthdevice"></a>

### Start device login

- **Method:** `POST`
- **Path:** `/oauth/device`
- **Operation ID:** `authorizeDevice`
- **Tags:** OAuth

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

No authentication required.

#### Request body

**Required:** `true`

**Content type:** `application/x-www-form-urlencoded`

no additional properties

- **`client_id` (required)**: `string`, possible values: `"moq-pro"`

  This CLI's registered OAuth client id.
- **`name`**: `string`, minLength: `1`, maxLength: `100`

  Requester-chosen token name; defaults to moq-pro CLI.
- **`projects`**: `string`, minLength: `1`, maxLength: `5000`

  Space-separated project ids. Omit for all current and future projects the account can access.
- **`scope`**: `string`, default: `"write"`, minLength: `1`, maxLength: `1000`

  Permissions requested, space-separated: family:level entries such as media:write or billing:read, or the presets read and write, which set every project family (settings, keys, media, usage) at that level; write on all projects adds lifecycle:write. The highest level asked for a family wins. Families outside the project ones need all projects; admin is never granted here.

<a id="scalar-example-106"></a>

**Generated example:**

```json
{
  "client_id": "moq-pro",
  "name": "",
  "scope": "write",
  "projects": ""
}
```

#### Responses

##### 200 Show the URL and code to the human. Never approve automatically.

**Content type:** `application/json`

- **`device_code` (required)**: `string`

  Secret polling code for the token exchange; never show it.
- **`expires_in` (required)**: `integer`

  Seconds until the device_code expires.
- **`interval` (required)**: `integer`

  Minimum seconds between token polls.
- **`user_code` (required)**: `string`

  Short code the human confirms in the browser.
- **`verification_uri` (required)**: `string`, format: `uri`

  Where the human approves the login.
- **`verification_uri_complete` (required)**: `string`, format: `uri`

  verification_uri with the user_code filled in.

<a id="scalar-example-107"></a>

**Generated example:**

```json
{
  "device_code": "",
  "user_code": "",
  "verification_uri": "",
  "verification_uri_complete": "",
  "expires_in": 1,
  "interval": 1
}
```

##### 400 Invalid client, scope, or request.

**Content type:** `application/json`

- **`error` (required)**: `string`

  RFC 6749 error code.
- **`error_description`**: `string`

  Human-readable explanation.

<a id="scalar-example-108"></a>

**Generated example:**

```json
{
  "error": "",
  "error_description": ""
}
```

##### 429 IP rate limit reached. Stop and start login again later.

**Content type:** `application/json`

- **`error` (required)**: `string`

  RFC 6749 error code.
- **`error_description`**: `string`

  Human-readable explanation.

<a id="scalar-example-109"></a>

**Generated example:**

```json
{
  "error": "",
  "error_description": ""
}
```

<a id="scalar-operation-post-oauthtoken"></a>

### Poll device login

- **Method:** `POST`
- **Path:** `/oauth/token`
- **Operation ID:** `exchangeDeviceCode`
- **Tags:** OAuth

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

No authentication required.

#### Request body

**Required:** `true`

**Content type:** `application/x-www-form-urlencoded`

no additional properties

- **`client_id` (required)**: `string`, possible values: `"moq-pro"`

  This CLI's registered OAuth client id.
- **`device_code` (required)**: `string`, pattern: `^[A-Za-z0-9_-]{43}$`

  The device_code from the device login response.
- **`grant_type` (required)**: `string`, possible values: `"urn:ietf:params:oauth:grant-type:device_code"`

  The RFC 8628 device-code grant type.

<a id="scalar-example-110"></a>

**Generated example:**

```json
{
  "client_id": "moq-pro",
  "grant_type": "urn:ietf:params:oauth:grant-type:device_code",
  "device_code": ""
}
```

#### Responses

##### 200 An ordinary, revocable account token that expires after 90 days. No refresh token; log in again.

**Content type:** `application/json`

- **`access_token` (required)**: `string`

  Account API token for the Authorization: Bearer header.
- **`scope` (required)**: `string`

  Permissions granted, space-separated family:level entries for each family above none.
- **`token_type` (required)**: `string`

  Always Bearer. Known values: "Bearer". Additional values may appear.

<a id="scalar-example-111"></a>

**Generated example:**

```json
{
  "access_token": "",
  "token_type": "",
  "scope": ""
}
```

##### 400 authorization_pending: keep polling at interval. slow_down: increase interval by 5 seconds for all subsequent polls. access_denied, expired_token, invalid_grant: stop.

**Content type:** `application/json`

- **`error` (required)**: `string`

  RFC 6749 error code.
- **`error_description`**: `string`

  Human-readable explanation.

<a id="scalar-example-112"></a>

**Generated example:**

```json
{
  "error": "",
  "error_description": ""
}
```

##### 429 IP rate limit reached. Stop and start login again later.

**Content type:** `application/json`

- **`error` (required)**: `string`

  RFC 6749 error code.
- **`error_description`**: `string`

  Human-readable explanation.

<a id="scalar-example-113"></a>

**Generated example:**

```json
{
  "error": "",
  "error_description": ""
}
```

## Webhooks

<a id="scalar-webhook-post-broadcaststarted"></a>

### broadcast.started

- **Method:** `POST`
- **Webhook:** `broadcast.started`

Signed JSON delivery to your configured endpoint. Verify the raw request body and Moq-Signature before processing. Delivery, retry and rotation contract: <https://doc.moq.pro/webhooks>.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

No authentication required.

#### Header parameters

- **`Moq-Signature` (required)**: `string`

  t=,v1=\<lowercase hex HMAC-SHA256(secret, t + '.' + raw body)>. Reject timestamps more than 300 seconds from your server time.

#### Request body

**Required:** `true`

**Content type:** `application/json`

- **`created_at` (required)**: `string`

  When the API emitted this event, in UTC.
- **`data` (required)**: `object`
  - **`at` (required)**: `string`

    Source observation time in UTC, independent of the envelope creation time.
  - **`path` (required)**: `string`

    Broadcast path relative to the project.
  - **`backfill`**: `boolean`

    A live path discovered in a snapshot, including when started events turn on. Known values: true. Additional values may appear.
- **`id` (required)**: `string`

  Stable event identifier. Deduplicate on this, including across endpoints.
- **`project` (required)**: `string`

  Immutable project pid.
- **`type` (required)**: `string`

  Known values: "broadcast.started". Additional values may appear.

<a id="scalar-example-114"></a>

**Generated example:**

```json
{
  "id": "",
  "type": "",
  "created_at": "",
  "project": "",
  "data": {
    "path": "",
    "at": "",
    "backfill": true
  }
}
```

#### Responses

##### 2XX Event durably accepted. Any non-2xx response fails this attempt; transient failures may retry.

<a id="scalar-webhook-post-broadcastended"></a>

### broadcast.ended

- **Method:** `POST`
- **Webhook:** `broadcast.ended`

Signed JSON delivery to your configured endpoint. Verify the raw request body and Moq-Signature before processing. Delivery, retry and rotation contract: <https://doc.moq.pro/webhooks>.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

No authentication required.

#### Header parameters

- **`Moq-Signature` (required)**: `string`

  t=,v1=\<lowercase hex HMAC-SHA256(secret, t + '.' + raw body)>. Reject timestamps more than 300 seconds from your server time.

#### Request body

**Required:** `true`

**Content type:** `application/json`

- **`created_at` (required)**: `string`

  When the API emitted this event, in UTC.
- **`data` (required)**: `object`
  - **`at` (required)**: `string`

    Source observation time in UTC, independent of the envelope creation time.
  - **`path` (required)**: `string`

    Broadcast path relative to the project.
  - **`reason` (required)**: `string`

    Known values: "unannounced", "connection_lost". Additional values may appear.
- **`id` (required)**: `string`

  Stable event identifier. Deduplicate on this, including across endpoints.
- **`project` (required)**: `string`

  Immutable project pid.
- **`type` (required)**: `string`

  Known values: "broadcast.ended". Additional values may appear.

<a id="scalar-example-115"></a>

**Generated example:**

```json
{
  "id": "",
  "type": "",
  "created_at": "",
  "project": "",
  "data": {
    "path": "",
    "at": "",
    "reason": ""
  }
}
```

#### Responses

##### 2XX Event durably accepted. Any non-2xx response fails this attempt; transient failures may retry.

<a id="scalar-webhook-post-recordingstarted"></a>

### recording.started

- **Method:** `POST`
- **Webhook:** `recording.started`

Signed JSON delivery to your configured endpoint. Verify the raw request body and Moq-Signature before processing. Delivery, retry and rotation contract: <https://doc.moq.pro/webhooks>.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

No authentication required.

#### Header parameters

- **`Moq-Signature` (required)**: `string`

  t=,v1=\<lowercase hex HMAC-SHA256(secret, t + '.' + raw body)>. Reject timestamps more than 300 seconds from your server time.

#### Request body

**Required:** `true`

**Content type:** `application/json`

- **`created_at` (required)**: `string`

  When the API emitted this event, in UTC.
- **`data` (required)**: `object`
  - **`at` (required)**: `string`

    Source observation time in UTC, independent of the envelope creation time.
  - **`generationId` (required)**: `string`

    Physical generation: //.
  - **`path` (required)**: `string`

    Broadcast path relative to the project.
  - **`recordingId` (required)**: `string`

    Logical recording: /.
  - **`bucket`**: `string`

    Customer bucket name. Omitted for managed recordings.
  - **`masterPlaylist`**: `string`

    Master playlist URI relative to objectPrefix, or the managed playback URL's directory.
  - **`objectPrefix`**: `string`

    Generation's object-key prefix, including its trailing /. BYOB only.
  - **`renditions`**: `array`

    Empty at start; exported renditions when ready, acknowledged media renditions when failed.

    **Items:**
    - **`kind` (required)**: `string`

      Known values: "video", "audio". Additional values may appear.
    - **`name` (required)**: `string`
    - **`playlist` (required)**: `string`

      Rendition playlist URI relative to the same directory as masterPlaylist.
    - **`height`**: `integer`, exclusiveMinimum: `0`

      Video height in pixels, when known.
    - **`width`**: `integer`, exclusiveMinimum: `0`

      Video width in pixels, when known.
- **`id` (required)**: `string`

  Stable event identifier. Deduplicate on this, including across endpoints.
- **`project` (required)**: `string`

  Immutable project pid.
- **`type` (required)**: `string`

  Known values: "recording.started". Additional values may appear.

<a id="scalar-example-116"></a>

**Generated example:**

```json
{
  "id": "",
  "type": "",
  "created_at": "",
  "project": "",
  "data": {
    "path": "",
    "at": "",
    "recordingId": "",
    "generationId": "",
    "bucket": "",
    "objectPrefix": "",
    "masterPlaylist": "",
    "renditions": [
      {
        "name": "",
        "kind": "",
        "playlist": "",
        "width": 1,
        "height": 1
      }
    ]
  }
}
```

#### Responses

##### 2XX Event durably accepted. Any non-2xx response fails this attempt; transient failures may retry.

<a id="scalar-webhook-post-recordingready"></a>

### recording.ready

- **Method:** `POST`
- **Webhook:** `recording.ready`

Signed JSON delivery to your configured endpoint. Verify the raw request body and Moq-Signature before processing. Delivery, retry and rotation contract: <https://doc.moq.pro/webhooks>.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

No authentication required.

#### Header parameters

- **`Moq-Signature` (required)**: `string`

  t=,v1=\<lowercase hex HMAC-SHA256(secret, t + '.' + raw body)>. Reject timestamps more than 300 seconds from your server time.

#### Request body

**Required:** `true`

**Content type:** `application/json`

- **`created_at` (required)**: `string`

  When the API emitted this event, in UTC.
- **`data` (required)**: `object`
  - **`at` (required)**: `string`

    Source observation time in UTC, independent of the envelope creation time.
  - **`generationId` (required)**: `string`

    Physical generation: //.
  - **`path` (required)**: `string`

    Broadcast path relative to the project.
  - **`recordingId` (required)**: `string`

    Logical recording: /.
  - **`bucket`**: `string`

    Customer bucket name. Omitted for managed recordings.
  - **`durationMs`**: `number`, minimum: `0`

    Recorded media duration in milliseconds, not elapsed wall time.
  - **`masterPlaylist`**: `string`

    Master playlist URI relative to objectPrefix, or the managed playback URL's directory.
  - **`objectPrefix`**: `string`

    Generation's object-key prefix, including its trailing /. BYOB only.
  - **`regression`**: `string | null`

    Diagnostic explaining why this generation ended after its source reused an epoch with conflicting or backwards media; null when none was reported. Not an enum or an export failure. Older queued events may omit it.
  - **`renditions`**: `array`

    Empty at start; exported renditions when ready, acknowledged media renditions when failed.

    **Items:**
    - **`kind` (required)**: `string`

      Known values: "video", "audio". Additional values may appear.
    - **`name` (required)**: `string`
    - **`playlist` (required)**: `string`

      Rendition playlist URI relative to the same directory as masterPlaylist.
    - **`height`**: `integer`, exclusiveMinimum: `0`

      Video height in pixels, when known.
    - **`width`**: `integer`, exclusiveMinimum: `0`

      Video width in pixels, when known.
- **`id` (required)**: `string`

  Stable event identifier. Deduplicate on this, including across endpoints.
- **`project` (required)**: `string`

  Immutable project pid.
- **`type` (required)**: `string`

  Known values: "recording.ready". Additional values may appear.

<a id="scalar-example-117"></a>

**Generated example:**

```json
{
  "id": "",
  "type": "",
  "created_at": "",
  "project": "",
  "data": {
    "path": "",
    "at": "",
    "recordingId": "",
    "generationId": "",
    "bucket": "",
    "objectPrefix": "",
    "masterPlaylist": "",
    "renditions": [
      {
        "name": "",
        "kind": "",
        "playlist": "",
        "width": 1,
        "height": 1
      }
    ],
    "regression": null,
    "durationMs": 0
  }
}
```

#### Responses

##### 2XX Event durably accepted. Any non-2xx response fails this attempt; transient failures may retry.

<a id="scalar-webhook-post-recordingfailed"></a>

### recording.failed

- **Method:** `POST`
- **Webhook:** `recording.failed`

Signed JSON delivery to your configured endpoint. Verify the raw request body and Moq-Signature before processing. Delivery, retry and rotation contract: <https://doc.moq.pro/webhooks>.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

No authentication required.

#### Header parameters

- **`Moq-Signature` (required)**: `string`

  t=,v1=\<lowercase hex HMAC-SHA256(secret, t + '.' + raw body)>. Reject timestamps more than 300 seconds from your server time.

#### Request body

**Required:** `true`

**Content type:** `application/json`

- **`created_at` (required)**: `string`

  When the API emitted this event, in UTC.
- **`data` (required)**: `object`
  - **`at` (required)**: `string`

    Source observation time in UTC, independent of the envelope creation time.
  - **`generationId` (required)**: `string`

    Physical generation: //.
  - **`mediaWritten` (required)**: `boolean`

    Whether this generation acknowledged any uploaded media before failing.
  - **`path` (required)**: `string`

    Broadcast path relative to the project.
  - **`reason` (required)**: `string`

    Bucket refused an upload, export could not be rendered, or platform storage/export failed. Known values: "bucket_refusal", "export_failure", "platform_error". Additional values may appear.
  - **`recordingId` (required)**: `string`

    Logical recording: /.
  - **`bucket`**: `string`

    Customer bucket name. Omitted for managed recordings.
  - **`masterPlaylist`**: `string`

    Master playlist URI relative to objectPrefix, or the managed playback URL's directory.
  - **`objectPrefix`**: `string`

    Generation's object-key prefix, including its trailing /. BYOB only.
  - **`regression`**: `string | null`

    Diagnostic explaining why this generation ended after its source reused an epoch with conflicting or backwards media; null when none was reported. Not an enum or an export failure. Older queued events may omit it.
  - **`renditions`**: `array`

    Empty at start; exported renditions when ready, acknowledged media renditions when failed.

    **Items:**
    - **`kind` (required)**: `string`

      Known values: "video", "audio". Additional values may appear.
    - **`name` (required)**: `string`
    - **`playlist` (required)**: `string`

      Rendition playlist URI relative to the same directory as masterPlaylist.
    - **`height`**: `integer`, exclusiveMinimum: `0`

      Video height in pixels, when known.
    - **`width`**: `integer`, exclusiveMinimum: `0`

      Video width in pixels, when known.
- **`id` (required)**: `string`

  Stable event identifier. Deduplicate on this, including across endpoints.
- **`project` (required)**: `string`

  Immutable project pid.
- **`type` (required)**: `string`

  Known values: "recording.failed". Additional values may appear.

<a id="scalar-example-118"></a>

**Generated example:**

```json
{
  "id": "",
  "type": "",
  "created_at": "",
  "project": "",
  "data": {
    "path": "",
    "at": "",
    "recordingId": "",
    "generationId": "",
    "bucket": "",
    "objectPrefix": "",
    "masterPlaylist": "",
    "renditions": [
      {
        "name": "",
        "kind": "",
        "playlist": "",
        "width": 1,
        "height": 1
      }
    ],
    "regression": null,
    "reason": "",
    "mediaWritten": true
  }
}
```

#### Responses

##### 2XX Event durably accepted. Any non-2xx response fails this attempt; transient failures may retry.

<a id="scalar-webhook-post-webhooktest"></a>

### webhook.test

- **Method:** `POST`
- **Webhook:** `webhook.test`

Signed JSON delivery to your configured endpoint. Verify the raw request body and Moq-Signature before processing. Delivery, retry and rotation contract: <https://doc.moq.pro/webhooks>.

**Servers:** [Inherited servers](#scalar-context-global-servers)

#### Authentication

No authentication required.

#### Header parameters

- **`Moq-Signature` (required)**: `string`

  t=,v1=\<lowercase hex HMAC-SHA256(secret, t + '.' + raw body)>. Reject timestamps more than 300 seconds from your server time.

#### Request body

**Required:** `true`

**Content type:** `application/json`

- **`created_at` (required)**: `string`

  When the API emitted this event, in UTC.
- **`data` (required)**: `object`
  - **`endpoint` (required)**: `string`

    The endpoint receiving this test delivery.
- **`id` (required)**: `string`

  Stable event identifier. Deduplicate on this, including across endpoints.
- **`project` (required)**: `string`

  Immutable project pid.
- **`type` (required)**: `string`

  Known values: "webhook.test". Additional values may appear.

<a id="scalar-example-119"></a>

**Generated example:**

```json
{
  "id": "",
  "type": "",
  "created_at": "",
  "project": "",
  "data": {
    "endpoint": ""
  }
}
```

#### Responses

##### 2XX Event durably accepted. Any non-2xx response fails this attempt; transient failures may retry.

## Schemas

<a id="scalar-schema-adminprojectpage"></a>

### AdminProjectPage

**Type:** `object`

- **`items` (required)**: `array`

  This page's items, in the list's documented order.

  **Items:**

  **Any of:**
  - `object`
    - **`account` (required)**: `object | null`

      The account: the project's owner, or the signup on a project-less row.
      - **`createdAt` (required)**: `string`, format: `date-time`

        When the account signed up. RFC 3339, UTC.
      - **`email` (required)**: `string`

        Account sign-in email.
      - **`id` (required)**: `string`

        Account id.
    - **`billingKnown` (required)**: `boolean`

      False until this project's billing fields are first published: billingRecipient through stripeRefused are then unknown, not empty, and the row counts as attention.
    - **`billingRecipient` (required)**: `string | null`

      Where invoices are mailed, or null.
    - **`createdAt` (required)**: `string`, format: `date-time`

      When the project was created. RFC 3339, UTC.
    - **`creditLimitMicros` (required)**: `integer | null`

      Net-terms credit limit, or null when unset. Micro-USD (1,000,000 = USD 1).
    - **`creditLimitRequestMicros` (required)**: `integer | null`

      Owner's pending credit-limit request, or null. Micro-USD (1,000,000 = USD 1).
    - **`hasPaymentMethod` (required)**: `boolean`

      A card is on file.
    - **`id` (required)**: `string`

      Immutable public project id, the {pid} path parameter.
    - **`invoicedDaysPastDue` (required)**: `integer`, minimum: `0`

      Days the oldest unpaid net-terms invoice is past due.
    - **`invoicedMicros` (required)**: `integer`

      Invoiced on net terms and unpaid. Micro-USD (1,000,000 = USD 1).
    - **`name` (required)**: `string | null`

      Project name, or null when unnamed.
    - **`netTerms` (required)**: `string`

      Net-terms state: empty for card billing, then pending approval, active (invoiced on terms), cancelling, or revoking. Known values: "", "pending", "active", "cancelling", "revoking". Additional values may appear.
    - **`ownerEmail` (required)**: `string | null`

      Owner's email, or null for an ownerless project.
    - **`spendCapMicros` (required)**: `integer | null`

      Owner's monthly spend cap, or null for none. Micro-USD (1,000,000 = USD 1).
    - **`staleAt` (required)**: `string | null`, format: `date-time`

      When the heartbeat counts as stale, or null when quiesced. RFC 3339, UTC.
    - **`stripeRefused` (required)**: `array of string`

      Stripe ids queued for removal that Stripe refused; remove each by hand.
    - **`stuckHeartbeats` (required)**: `integer`, minimum: `0`

      Consecutive billing heartbeats that failed to publish.
    - **`ticking` (required)**: `boolean`

      The billing heartbeat still has work, so updatedAt can go stale.
    - **`uncollectibleMicros` (required)**: `integer`

      Debt Stripe stopped collecting that nobody has written off. Micro-USD (1,000,000 = USD 1).
    - **`updatedAt` (required)**: `string | null`, format: `date-time`

      When the figures were last published, or null if never. RFC 3339, UTC.
    - **`usage` (required)**: `object | null`

      This month's published figures, or null when the latest summary is older (the project is idle).
      - **`absorbedMicros` (required)**: `integer`

        Metered past the spend cap or credit limit; never billed. Micro-USD (1,000,000 = USD 1).
      - **`costMicros` (required)**: `integer`

        Month-to-date metered cost before the free tier. Micro-USD (1,000,000 = USD 1).
      - **`egressExternalBytes` (required)**: `integer`, minimum: `0`

        Live delivery to viewers this month. Decimal bytes.
      - **`freeMicros` (required)**: `integer`

        Month-to-date cost the free tier covered. Micro-USD (1,000,000 = USD 1).
      - **`freeTierFraction` (required)**: `number`, minimum: `0`

        Share of the free allowance used, where 1 is exhausted; saturates rather than overflowing.
      - **`netMicros` (required)**: `integer`

        Month-to-date billable net after the free tier. Micro-USD (1,000,000 = USD 1).
      - **`storedGbSeconds` (required)**: `number`, minimum: `0`

        Managed storage held this month, GB·second, decimal GB.
      - **`vodEgressBytes` (required)**: `integer`, minimum: `0`

        Recording delivery this month. Decimal bytes.
      - **`vodRecordingBytes` (required)**: `integer`, minimum: `0`

        Managed recording written this month. Decimal bytes.
      - **`freeAllowance`**: `object`

        The allowance the figures were measured against; absent from older summaries.
        - **`egressBytes` (required)**: `integer`, minimum: `0`

          Delivery allowance this month. Decimal bytes.
        - **`storedGbSeconds` (required)**: `number`, minimum: `0`

          Storage allowance this month, GB·second, decimal GB.
  - `object`

    sort=newest only: an account without a live project.
    - **`account` (required)**: `object`

      The account: the project's owner, or the signup on a project-less row.
      - **`createdAt` (required)**: `string`, format: `date-time`

        When the account signed up. RFC 3339, UTC.
      - **`email` (required)**: `string`

        Account sign-in email.
      - **`id` (required)**: `string`

        Account id.
    - **`id` (required)**: `null`

      Null: this account owns no live project.
- **`month` (required)**: `string`, format: `date-time`

  Start of the billing month the figures cover. RFC 3339, UTC.
- **`nextCursor` (required)**: `string | null`

  Pass as cursor to continue, or null when there are no more items.
- **`now` (required)**: `string`, format: `date-time`

  Server time of the read. RFC 3339, UTC.
- **`oldestUpdatedAt` (required)**: `string | null`, format: `date-time`

  Oldest publish among this page's ticking rows, or null when none has one. RFC 3339, UTC.
- **`totals` (required)**: `object`

  The month's platform totals over the usage listing, whatever the sort or page.
  - **`absorbedMicros` (required)**: `integer`

    Metered past caps and limits this month; never billed. Micro-USD (1,000,000 = USD 1).
  - **`attention` (required)**: `integer`, minimum: `0`

    Listed projects in an attention state.
  - **`billed` (required)**: `integer`, minimum: `0`

    Listed projects that owe money.
  - **`costMicros` (required)**: `integer`

    Metered cost this month. Micro-USD (1,000,000 = USD 1).
  - **`egressExternalBytes` (required)**: `integer`, minimum: `0`

    Live delivery this month. Decimal bytes.
  - **`listed` (required)**: `integer`, minimum: `0`

    Projects in the usage listing.
  - **`netMicros` (required)**: `integer`

    Billable net this month. Micro-USD (1,000,000 = USD 1).
  - **`storedGbSeconds` (required)**: `number`, minimum: `0`

    Managed storage this month, GB·second, decimal GB.

<a id="scalar-example-120"></a>

**Generated example:**

```json
{
  "items": [
    {
      "account": {
        "id": "",
        "email": "",
        "createdAt": "2026-10-22T12:00:00.000Z"
      },
      "id": "p123example",
      "name": null,
      "ownerEmail": null,
      "billingKnown": true,
      "billingRecipient": null,
      "netTerms": "",
      "creditLimitMicros": 1000000,
      "spendCapMicros": 1000000,
      "creditLimitRequestMicros": 1000000,
      "hasPaymentMethod": true,
      "invoicedMicros": 1000000,
      "invoicedDaysPastDue": 0,
      "uncollectibleMicros": 1000000,
      "stuckHeartbeats": 0,
      "stripeRefused": [
        ""
      ],
      "createdAt": "2026-10-22T12:00:00.000Z",
      "updatedAt": "2026-10-22T12:00:00.000Z",
      "ticking": true,
      "staleAt": "2026-10-22T12:00:00.000Z",
      "usage": {
        "egressExternalBytes": 0,
        "vodEgressBytes": 0,
        "storedGbSeconds": 0,
        "vodRecordingBytes": 0,
        "costMicros": 1000000,
        "freeMicros": 1000000,
        "netMicros": 1000000,
        "absorbedMicros": 1000000,
        "freeTierFraction": 0,
        "freeAllowance": {
          "egressBytes": 0,
          "storedGbSeconds": 0
        }
      }
    }
  ],
  "nextCursor": null,
  "month": "2026-10-22T12:00:00.000Z",
  "now": "2026-10-22T12:00:00.000Z",
  "oldestUpdatedAt": "2026-10-22T12:00:00.000Z",
  "totals": {
    "listed": 0,
    "billed": 0,
    "attention": 0,
    "egressExternalBytes": 0,
    "storedGbSeconds": 0,
    "costMicros": 1000000,
    "netMicros": 1000000,
    "absorbedMicros": 1000000
  }
}
```

<a id="scalar-schema-apierror"></a>

### ApiError

**Type:** `object`

- **`code` (required)**: `string`

  Known values: "invalid_request", "unauthenticated", "forbidden", "not_found", "conflict", "rate_limited", "unavailable", "internal_error", "already_exists", "limit_reached", "invalid_state", "idempotency_in_progress". Additional values may appear.
- **`message` (required)**: `string`

  Human-readable explanation; do not match on it.
- **`errors`**: `array`

  Each invalid input value, on a 400 validation refusal.

  **Items:**
  - **`code` (required)**: `string`

    Validation issue code, such as invalid_type or unrecognized_keys.
  - **`message` (required)**: `string`

    Human-readable explanation of the issue.
  - **`path` (required)**: `array of (string | number)`

    Location of the invalid value; empty for the whole input.
- **`lockedUntil`**: `string`, format: `date-time`

  When a refused rename may be retried, RFC 3339; set only for a rename lock.

<a id="scalar-example-121"></a>

**Generated example:**

```json
{
  "code": "",
  "message": "",
  "errors": [
    {
      "path": [
        ""
      ],
      "code": "",
      "message": ""
    }
  ],
  "lockedUntil": ""
}
```

<a id="scalar-schema-createdapitoken"></a>

### CreatedApiToken

**All of:**

- [ApiToken](#scalar-schema-apitoken)
- `object`
  - **`secret` (required)**: `string`

    The token, on creation only. Only its hash is kept; save it before discarding the response.

<a id="scalar-schema-permissions"></a>

### Permissions

**Type:** `object`, no additional properties

A level per permission family: none, read, or write. Project families (settings, keys, media, usage) reach the token's projects; the rest need an all-projects token. keys write is the only level that reveals a secret or mints a credential.

- **`account` (required)**: `string`

  Account: profile, data export, and deletion. Known values: "none", "read", "write". Additional values may appear.
- **`admin` (required)**: `string`

  Site admin: operator routes, for a site admin. Known values: "none", "read", "write". Additional values may appear.
- **`billing` (required)**: `string`

  Billing: payment, spend caps, invoices, credit, and account usage. Known values: "none", "read", "write". Additional values may appear.
- **`credentials` (required)**: `string`

  Credentials: API tokens, passkeys, and sessions. Known values: "none", "read", "write". Additional values may appear.
- **`keys` (required)**: `string`

  Keys & secrets: signing keys and the auth endpoint; write also reveals every secret and mints relay tokens. Known values: "none", "read", "write". Additional values may appear.
- **`lifecycle` (required)**: `string`

  Project lifecycle: create and delete projects. Known values: "none", "write". Additional values may appear.
- **`media` (required)**: `string`

  Media rules: recording, transcode, agent, sync, and webhooks. Known values: "none", "read", "write". Additional values may appear.
- **`members` (required)**: `string`

  Members & invites: members, invitations, and ownership transfer. Known values: "none", "read", "write". Additional values may appear.
- **`settings` (required)**: `string`

  Project settings: the project itself, its session limits, and its notices. Known values: "none", "read", "write". Additional values may appear.
- **`usage` (required)**: `string`

  Usage & stats: metered usage. Known values: "none", "read". Additional values may appear.

<a id="scalar-example-122"></a>

**Generated example:**

```json
{
  "settings": "",
  "keys": "",
  "media": "",
  "usage": "",
  "lifecycle": "",
  "members": "",
  "billing": "",
  "credentials": "",
  "account": "",
  "admin": ""
}
```

<a id="scalar-schema-apitoken"></a>

### ApiToken

**Type:** `object`

- **`audience` (required)**: `string | null`

  mcp for a token an MCP client signed in for, the only kind /mcp accepts; null otherwise. Known values: "mcp", null. Additional values may appear.
- **`clientId` (required)**: `string | null`

  The client ID metadata document URL of the MCP client the token was issued to, or null. Its host is who published the client; the name is the client's own claim.
- **`createdAt` (required)**: `string`, format: `date-time`

  RFC 3339 timestamp with a timezone; responses are normalized to UTC.
- **`expiresAt` (required)**: `string | null`, format: `date-time`

  When it stops working, or null for never.
- **`id` (required)**: `string`

  Opaque resource identifier.
- **`lastUsedAt` (required)**: `string | null`, format: `date-time`

  Last authenticated call, to the minute, or null before any.
- **`name` (required)**: `string`

  Human-readable label.
- **`permissions` (required)**: [Permissions](#scalar-schema-permissions)

  A level per permission family: none, read, or write. Project families (settings, keys, media, usage) reach the token's projects; the rest need an all-projects token. keys write is the only level that reveals a secret or mints a credential.
- **`prefix` (required)**: `string`

  The secret's first characters, to recognize it; never enough to use it.
- **`projects` (required)**: `(array of string) | null`

  The project ids it is restricted to, or null for every project the account reaches.

<a id="scalar-example-123"></a>

**Generated example:**

```json
{
  "id": "",
  "name": "",
  "prefix": "",
  "permissions": {
    "settings": "",
    "keys": "",
    "media": "",
    "usage": "",
    "lifecycle": "",
    "members": "",
    "billing": "",
    "credentials": "",
    "account": "",
    "admin": ""
  },
  "projects": [
    ""
  ],
  "audience": null,
  "clientId": null,
  "expiresAt": "2026-10-22T12:00:00.000Z",
  "lastUsedAt": "2026-10-22T12:00:00.000Z",
  "createdAt": "2026-10-22T12:00:00.000Z"
}
```

<a id="scalar-schema-adminproject"></a>

### AdminProject

**Type:** `object`

- **`banned` (required)**: `boolean`

  Sticky abuse ban set by an operator.
- **`billingExempt` (required)**: `boolean`

  A platform project that is never billed.
- **`billingRecipient` (required)**: `string | null`

  Where invoices are mailed, or null.
- **`capReached` (required)**: `boolean`

  The owner's spend cap is reached and gates traffic.
- **`createdAt` (required)**: `string`, format: `date-time`

  When the project was created. RFC 3339, UTC.
- **`creditBalanceMicros` (required)**: `integer`

  Unspent credit. Micro-USD (1,000,000 = USD 1).
- **`creditLedger` (required)**: `array`

  Every credit grant, draw, claw-back, and restore.

  **Items:**
  - **`actor` (required)**: `string`

    Operator account id, or empty for a system-recorded draw.
  - **`actorEmail` (required)**: `string | null`

    The actor's email, or null.
  - **`amountMicros` (required)**: `integer`

    Signed: grants and restores are positive, draws and claw-backs negative. Micro-USD (1,000,000 = USD 1).
  - **`createdAt` (required)**: `string`, format: `date-time`

    When the entry was recorded. RFC 3339, UTC.
  - **`id` (required)**: `string`

    Ledger entry id.
  - **`kind` (required)**: `string`

    grant (operator), applied (drawn against an invoice), revoked (operator claw-back), or restored (a draw returned when its invoice was forgiven). Known values: "grant", "applied", "revoked", "restored". Additional values may appear.
  - **`periodStart` (required)**: `string | null`, format: `date-time`

    The billing month an applied entry drew against, or null. RFC 3339, UTC.
  - **`reason` (required)**: `string`

    The operator's note; empty for system entries.
- **`creditLimitRequest` (required)**: [CreditLimitRequestResponse](#scalar-schema-creditlimitrequestresponse)

  The owner's pending request, or null.
- **`enforceSuspension` (required)**: `boolean`

  Whether a suspended project's traffic is currently cut off.
- **`exposure` (required)**: [BillingExposure](#scalar-schema-billingexposure)
- **`freeLimited` (required)**: `boolean`

  The free-tier limit gates this project's traffic.
- **`hasPaymentMethod` (required)**: `boolean`

  A card is on file.
- **`id` (required)**: `string`

  Immutable public project id, the {pid} path parameter.
- **`invoices` (required)**: `array`

  The project's invoices.

  **Items:**
  - **`amountChargedMicros` (required)**: `integer`

    What was charged. Micro-USD (1,000,000 = USD 1).
  - **`amountRemainingMicros` (required)**: `integer | null`

    Stripe's unpaid remainder, or null until a reconcile reports it. Micro-USD (1,000,000 = USD 1).
  - **`creditAppliedMicros` (required)**: `integer`

    Credit drawn against this invoice. Micro-USD (1,000,000 = USD 1).
  - **`freeMicros` (required)**: `integer`

    Cost the free tier covered. Micro-USD (1,000,000 = USD 1).
  - **`grossMicros` (required)**: `integer`

    Metered cost before the free tier. Micro-USD (1,000,000 = USD 1).
  - **`heldReleased` (required)**: `boolean`

    An operator released this held invoice for charging.
  - **`netMicros` (required)**: `integer`

    Billable net after the free tier. Micro-USD (1,000,000 = USD 1).
  - **`periodStart` (required)**: `string`, format: `date-time`

    Start of the billed month; the {periodStart} path parameter. RFC 3339, UTC.
  - **`principalMicros` (required)**: `integer`

    Exactly what Stripe is asked to collect. Micro-USD (1,000,000 = USD 1).
  - **`status` (required)**: `string`

    Invoice ledger state: pending, paid, failed, no_card, held, invoiced, forgiven, invoice_void, invoice_uncollectible, invoice_writeoff_pending, or invoice_written_off.
  - **`stripeInvoiceId` (required)**: `string | null`

    The Stripe invoice id, or null when none was issued.
  - **`superseded` (required)**: `array`

    Voided Stripe invoices this month was reissued from, oldest first.

    **Items:**
    - **`invoiceId` (required)**: `string`

      The ledger row that moved on.
    - **`pending` (required)**: `boolean`

      The replacement is still a draft the customer has not been sent.
    - **`reason` (required)**: `string`

      Why it was reissued, such as recipient.
    - **`replacedBy` (required)**: `string`

      The Stripe invoice that replaced it.
    - **`stripeInvoiceId` (required)**: `string`

      The voided Stripe invoice.
    - **`supersededAt` (required)**: `string`, format: `date-time`

      When it was voided. RFC 3339, UTC.
- **`name` (required)**: `string | null`

  Project name, or null when unnamed.
- **`netTerms` (required)**: `string`

  Net-terms state: empty for card billing, then pending approval, active (invoiced on terms), cancelling, or revoking. Known values: "", "pending", "active", "cancelling", "revoking". Additional values may appear.
- **`netTermsActorEmail` (required)**: `string | null`

  Who approved net terms, or null.
- **`netTermsAt` (required)**: `string | null`, format: `date-time`

  When net terms last changed state, or null if never. RFC 3339, UTC.
- **`netTermsDays` (required)**: `integer`, minimum: `0`

  The agreed payment window in days.
- **`ownerEmail` (required)**: `string | null`

  Owner's email, or null for an ownerless project.
- **`ownerIsAdmin` (required)**: `boolean`

  The owner is a site admin, who bypasses the cap.
- **`ownerOwnedProjects` (required)**: `integer`, minimum: `0`

  Project slots the owner holds; can exceed the cap.
- **`ownerProjectCap` (required)**: `integer | null`

  How many projects the owner may own, or null without an owner.
- **`stripeRefused` (required)**: `array of string`

  Stripe ids queued for removal that Stripe refused; remove each by hand.
- **`suspended` (required)**: `boolean`

  Billing suspension flag; gates traffic only while enforceSuspension is true.
- **`usage` (required)**: nullable

  Month-to-date usage and cost, or null when the billing read failed.

  **All of:**
  - [CurrentUsageQuantities](#scalar-schema-currentusagequantities)
  - `object`
    - **`egressInternalBytes` (required)**: `number`

      Relay-to-relay bytes inside the fleet, an internal cost input; never billed.
    - **`announceCursorSeconds`**: `number`

      Relay announce work in seconds, an internal cost input; never billed.
    - **`announceRouteSeconds`**: `number`

      Relay announce work in seconds, an internal cost input; never billed.
    - **`moqWireEgressBytes`**: `number`, default: `0`

      Wire bytes including transport overhead, an internal cost input; never billed.
    - **`rtmpWireEgressBytes`**: `number`, default: `0`

      Wire bytes including transport overhead, an internal cost input; never billed.
    - **`srtWireEgressBytes`**: `number`, default: `0`

      Wire bytes including transport overhead, an internal cost input; never billed.
    - **`syncWireEgressBytes`**: `number`, default: `0`

      Wire bytes including transport overhead, an internal cost input; never billed.
    - **`webrtcWireEgressBytes`**: `number`, default: `0`

      Wire bytes including transport overhead, an internal cost input; never billed.
    - **`websocketWireEgressBytes`**: `number`, default: `0`

      Wire bytes including transport overhead, an internal cost input; never billed.
    - **`webtransportWireEgressBytes`**: `number`, default: `0`

      Wire bytes including transport overhead, an internal cost input; never billed.
    - **`wireEgressExternalBytes`**: `number`, default: `0`

      Wire bytes including transport overhead, an internal cost input; never billed.
    - **`wireIngressBytes`**: `number`, default: `0`

      Wire bytes including transport overhead, an internal cost input; never billed.

<a id="scalar-example-124"></a>

**Generated example:**

```json
{
  "id": "p123example",
  "name": null,
  "ownerEmail": null,
  "billingRecipient": null,
  "ownerProjectCap": null,
  "ownerOwnedProjects": 0,
  "ownerIsAdmin": true,
  "suspended": true,
  "freeLimited": true,
  "capReached": true,
  "enforceSuspension": true,
  "banned": true,
  "billingExempt": true,
  "hasPaymentMethod": true,
  "stripeRefused": [
    ""
  ],
  "netTerms": "",
  "netTermsDays": 0,
  "netTermsAt": "2026-10-22T12:00:00.000Z",
  "exposure": {
    "periodStart": "2026-10-22T12:00:00.000Z",
    "monthNetMicros": 1000000,
    "spendCapMicros": 1000000,
    "spendCapRemainingMicros": 1000000,
    "spendCapReached": true,
    "creditLimitMicros": 1000000,
    "creditLimitRemainingMicros": 1000000,
    "invoicedPrincipalMicros": 1000000,
    "accruedPrincipalMicros": 1000000,
    "absorbedMicros": 1000000,
    "headroomMicros": 1000000,
    "headroomSource": null,
    "usableHeadroomMicros": 1000000,
    "identityHold": true
  },
  "creditLimitRequest": {
    "creditLimitMicros": 1000000,
    "reason": "",
    "requestedAt": "2026-10-22T12:00:00.000Z"
  },
  "netTermsActorEmail": null,
  "createdAt": "2026-10-22T12:00:00.000Z",
  "usage": {
    "periodStart": "2026-10-22T12:00:00.000Z",
    "periodEnd": "2026-10-22T12:00:00.000Z",
    "egressExternalBytes": 1000000,
    "ingressBytes": 1000000,
    "storedGbSeconds": 60,
    "retainedDeltaBytes": 1000000,
    "vodRecordingBytes": 1000000,
    "vodEgressBytes": 1000000,
    "transcode480Seconds": 60,
    "transcode720Seconds": 60,
    "transcode1080Seconds": 60,
    "agentSeconds": 60,
    "webtransportEgressBytes": 1000000,
    "webtransportIngressBytes": 1000000,
    "websocketEgressBytes": 1000000,
    "websocketIngressBytes": 1000000,
    "rtmpEgressBytes": 1000000,
    "rtmpIngressBytes": 1000000,
    "srtEgressBytes": 1000000,
    "srtIngressBytes": 1000000,
    "webrtcEgressBytes": 1000000,
    "webrtcIngressBytes": 1000000,
    "syncEgressBytes": 1000000,
    "syncIngressBytes": 1000000,
    "hlsEgressBytes": 1000000,
    "hlsIngressBytes": 1000000,
    "byobEgressBytes": 1000000,
    "byobIngressBytes": 1000000,
    "egressFrames": 60,
    "ingressFrames": 60,
    "egressGroups": 60,
    "ingressGroups": 60,
    "egressDatagrams": 60,
    "ingressDatagrams": 60,
    "egressSubscriptions": 60,
    "ingressSubscriptions": 60,
    "egressFetches": 60,
    "ingressFetches": 60,
    "egressAnnounced": 60,
    "ingressAnnounced": 60,
    "connectedSeconds": 60,
    "peakConnections": 60,
    "costMicros": 60,
    "partial": false,
    "freeAllowance": {
      "egressBytes": 1,
      "storedGbSeconds": 1
    },
    "freeEgressBytes": 1,
    "freeVodEgressBytes": 1,
    "freeStoredGbSeconds": 1,
    "freeRecordingBytes": 0,
    "freeMicros": 1,
    "netMicros": 1,
    "egressInternalBytes": 1,
    "wireEgressExternalBytes": 0,
    "wireIngressBytes": 0,
    "moqWireEgressBytes": 0,
    "webtransportWireEgressBytes": 0,
    "websocketWireEgressBytes": 0,
    "rtmpWireEgressBytes": 0,
    "srtWireEgressBytes": 0,
    "webrtcWireEgressBytes": 0,
    "syncWireEgressBytes": 0,
    "announceRouteSeconds": 1,
    "announceCursorSeconds": 1
  },
  "creditBalanceMicros": 1000000,
  "creditLedger": [
    {
      "id": "",
      "amountMicros": 1000000,
      "kind": "",
      "reason": "",
      "actor": "",
      "periodStart": "2026-10-22T12:00:00.000Z",
      "createdAt": "2026-10-22T12:00:00.000Z",
      "actorEmail": null
    }
  ],
  "invoices": [
    {
      "periodStart": "2026-10-22T12:00:00.000Z",
      "grossMicros": 1000000,
      "freeMicros": 1000000,
      "netMicros": 1000000,
      "creditAppliedMicros": 1000000,
      "amountChargedMicros": 1000000,
      "principalMicros": 1000000,
      "amountRemainingMicros": 1000000,
      "status": "",
      "heldReleased": true,
      "stripeInvoiceId": null,
      "superseded": [
        {
          "stripeInvoiceId": "",
          "invoiceId": "",
          "replacedBy": "",
          "reason": "",
          "supersededAt": "2026-10-22T12:00:00.000Z",
          "pending": true
        }
      ]
    }
  ]
}
```

<a id="scalar-schema-billingexposure"></a>

### BillingExposure

**Type:** `object | null`

- **`absorbedMicros` (required)**: `integer`, minimum: `0`

  Metered past the cap or limit; never billed.
- **`accruedPrincipalMicros` (required)**: `integer`, minimum: `0`

  Accrued principal not yet invoiced.
- **`creditLimitMicros` (required)**: `integer | null`, minimum: `0`

  Operator ceiling, or null for none.
- **`creditLimitRemainingMicros` (required)**: `integer | null`, minimum: `0`

  Remaining operator ceiling, or null for none.
- **`headroomMicros` (required)**: `integer | null`, minimum: `0`

  The lower remaining ceiling, or null when neither is set.
- **`headroomSource` (required)**: `string | null`

  Which ceiling supplies the headroom, or null. Known values: "cap", "limit", null. Additional values may appear.
- **`identityHold` (required)**: `boolean`

  The approved billing identity no longer matches Stripe.
- **`invoicedPrincipalMicros` (required)**: `integer`, minimum: `0`

  Invoiced and unpaid principal.
- **`monthNetMicros` (required)**: `integer`, minimum: `0`

  Month-to-date billable net, clamped to the cap, before credit.
- **`periodStart` (required)**: `string`, format: `date-time`

  Start of the month measured.
- **`spendCapMicros` (required)**: `integer | null`, minimum: `0`

  Owner ceiling, or null for none.
- **`spendCapReached` (required)**: `boolean`

  The spend cap is exhausted.
- **`spendCapRemainingMicros` (required)**: `integer | null`, minimum: `0`

  Remaining owner ceiling, or null for none.
- **`usableHeadroomMicros` (required)**: `integer | null`, minimum: `0`

  What a new period can admit; zero while held or below the minimum charge.

<a id="scalar-schema-creditlimitrequest"></a>

### CreditLimitRequest

**Type:** `object | null`

The owner's pending request, or null.

- **`creditLimitMicros` (required)**: `integer`, minimum: `0`

  The requested credit limit in micro-USD.
- **`reason` (required)**: `string`

  Why the owner needs a higher limit.
- **`requestedAt` (required)**: `string`, format: `date-time`

  When the owner requested the increase.

<a id="scalar-schema-currentusagequantities"></a>

### CurrentUsageQuantities

**All of:**

- [Usage](#scalar-schema-usage)
- `object`
  - **`freeEgressBytes` (required)**: `number`

    Live delivery payload bytes covered by the shared account allowance, capped at that allowance. Do not add to egressExternalBytes.
  - **`freeMicros` (required)**: `number`

    Allowance credit in micro-USD (1000000 = USD 1), the difference between frozen gross costMicros and netMicros. Account credit is separate.
  - **`freeStoredGbSeconds` (required)**: `number`

    Managed-storage GB·second covered by the storage allowance, capped at that allowance. Already part of storedGbSeconds.
  - **`freeVodEgressBytes` (required)**: `number`

    Recording-delivery payload bytes covered by the same delivery allowance after live delivery takes its share. Already part of vodEgressBytes.
  - **`netMicros` (required)**: `number`

    Current customer charge after allowances in micro-USD, before account credit; between zero and frozen costMicros. Uses the lesser of current overage and frozen gross less allowance value.
  - **`freeAllowance`**: `object`

    This project's share of account allowance: egressBytes is delivery payload bytes and storedGbSeconds is managed-storage GB·second. Absent on older responses means the whole monthly allowance, not zero.
    - **`egressBytes` (required)**: `number`

      Delivery allowance in payload bytes, decimal.
    - **`storedGbSeconds` (required)**: `number`

      Managed-storage allowance in GB·second, decimal GB.
  - **`freeRecordingBytes`**: `number`, default: `0`

    Managed recording payload bytes covered by the archive allowance. Already part of vodRecordingBytes; defaults to zero on older responses.

<a id="scalar-schema-usage"></a>

### Usage

**Type:** `object`

- **`costMicros` (required)**: `number`

  Frozen gross customer cost in micro-USD (1000000 = USD 1), before allowances and account credit. Never recomputed by this projection.
- **`egressExternalBytes` (required)**: `number`

  Live payload bytes delivered outside the fleet, a billable quantity. Includes the live protocol subsets; do not add those subsets again.
- **`periodStart` (required)**: `string`, format: `date-time`

  RFC 3339 timestamp with a timezone; responses are normalized to UTC.
- **`storedGbSeconds` (required)**: `number`

  Managed storage integral in GB·second (decimal GB); divide by 86400 for GB·days. This is the billable storage quantity.
- **`agentSeconds`**: `number`

  Live voice agent-seconds, a billable quantity. Provider API charges are separate; absent on older history means zero.
- **`byobEgressBytes`**: `number`, default: `0`

  Customer-bucket upload payload bytes. A display subset of vodEgressBytes in current history; older billing eras include it in vodRecordingBytes or egressExternalBytes instead. Never add this subset to its containing total or charge it again. Missing older-history values mean zero.
- **`byobIngressBytes`**: `number`, default: `0`

  Structurally zero: customer-bucket uploads have no ingress side. Included for the uniform transport split; never billed separately.
- **`connectedSeconds`**: `number`

  Connection-seconds summed over the period. Display only, never billed. Absence on older history or live/open rows means unavailable, not zero.
- **`egressAnnounced`**: `number`, default: `0`

  Delivery operation count, display only and never billed. Datagrams are a subset of groups; subscriptions and fetches are per track, not viewer counts. Missing older-history values mean zero.
- **`egressDatagrams`**: `number`, default: `0`

  Delivery operation count, display only and never billed. Datagrams are a subset of groups; subscriptions and fetches are per track, not viewer counts. Missing older-history values mean zero.
- **`egressFetches`**: `number`, default: `0`

  Delivery operation count, display only and never billed. Datagrams are a subset of groups; subscriptions and fetches are per track, not viewer counts. Missing older-history values mean zero.
- **`egressFrames`**: `number`, default: `0`

  Delivery operation count, display only and never billed. Datagrams are a subset of groups; subscriptions and fetches are per track, not viewer counts. Missing older-history values mean zero.
- **`egressGroups`**: `number`, default: `0`

  Delivery operation count, display only and never billed. Datagrams are a subset of groups; subscriptions and fetches are per track, not viewer counts. Missing older-history values mean zero.
- **`egressSubscriptions`**: `number`, default: `0`

  Delivery operation count, display only and never billed. Datagrams are a subset of groups; subscriptions and fetches are per track, not viewer counts. Missing older-history values mean zero.
- **`hlsEgressBytes`**: `number`, default: `0`

  Payload bytes for this transport, a display subset of egressExternalBytes. Never add to its parent total or bill separately. Missing older-history values mean zero; ingress is free.
- **`hlsIngressBytes`**: `number`, default: `0`

  Payload bytes for this transport, a display subset of ingressBytes. Never add to its parent total or bill separately. Missing older-history values mean zero; ingress is free.
- **`ingressAnnounced`**: `number`, default: `0`

  Delivery operation count, display only and never billed. Datagrams are a subset of groups; subscriptions and fetches are per track, not viewer counts. Missing older-history values mean zero.
- **`ingressBytes`**: `number`

  Publisher payload bytes received. Display only, never billed; absent on older history means zero.
- **`ingressDatagrams`**: `number`, default: `0`

  Delivery operation count, display only and never billed. Datagrams are a subset of groups; subscriptions and fetches are per track, not viewer counts. Missing older-history values mean zero.
- **`ingressFetches`**: `number`, default: `0`

  Delivery operation count, display only and never billed. Datagrams are a subset of groups; subscriptions and fetches are per track, not viewer counts. Missing older-history values mean zero.
- **`ingressFrames`**: `number`, default: `0`

  Delivery operation count, display only and never billed. Datagrams are a subset of groups; subscriptions and fetches are per track, not viewer counts. Missing older-history values mean zero.
- **`ingressGroups`**: `number`, default: `0`

  Delivery operation count, display only and never billed. Datagrams are a subset of groups; subscriptions and fetches are per track, not viewer counts. Missing older-history values mean zero.
- **`ingressSubscriptions`**: `number`, default: `0`

  Delivery operation count, display only and never billed. Datagrams are a subset of groups; subscriptions and fetches are per track, not viewer counts. Missing older-history values mean zero.
- **`partial`**: `boolean`

  Whether the source period is incomplete. Omitted means a complete historical period.
- **`peakConnections`**: `number`

  Peak simultaneous connection count (maximum over constituent minutes). Display only, never billed. Absence means unavailable, not zero.
- **`periodEnd`**: `string`, format: `date-time`

  RFC 3339 timestamp with a timezone; responses are normalized to UTC.
- **`retainedDeltaBytes`**: `number`

  Signed managed-storage byte change over the period. Display only; negative means deletion/expiry. Absence means unknown, not zero.
- **`rtmpEgressBytes`**: `number`

  Payload bytes for this transport, a display subset of egressExternalBytes. Never add to its parent total or bill separately. Missing older-history values mean zero; ingress is free.
- **`rtmpIngressBytes`**: `number`

  Payload bytes for this transport, a display subset of ingressBytes. Never add to its parent total or bill separately. Missing older-history values mean zero; ingress is free.
- **`srtEgressBytes`**: `number`

  Payload bytes for this transport, a display subset of egressExternalBytes. Never add to its parent total or bill separately. Missing older-history values mean zero; ingress is free.
- **`srtIngressBytes`**: `number`

  Payload bytes for this transport, a display subset of ingressBytes. Never add to its parent total or bill separately. Missing older-history values mean zero; ingress is free.
- **`syncEgressBytes`**: `number`, default: `0`

  Payload bytes for this transport, a display subset of egressExternalBytes. Never add to its parent total or bill separately. Missing older-history values mean zero; ingress is free.
- **`syncIngressBytes`**: `number`, default: `0`

  Payload bytes for this transport, a display subset of ingressBytes. Never add to its parent total or bill separately. Missing older-history values mean zero; ingress is free.
- **`transcode1080Seconds`**: `number`

  Encoded rendition-seconds at this resolution, a billable quantity. Absent on older history means zero.
- **`transcode480Seconds`**: `number`

  Encoded rendition-seconds at this resolution, a billable quantity. Absent on older history means zero.
- **`transcode720Seconds`**: `number`

  Encoded rendition-seconds at this resolution, a billable quantity. Absent on older history means zero.
- **`vodEgressBytes`**: `number`

  Recording-delivery bytes, a billable quantity including playback and BYOB uploads. Shares the delivery allowance with live egress; absent on older history means zero.
- **`vodRecordingBytes`**: `number`, default: `0`

  Managed recording bytes written, a billable quantity. Older history may include BYOB writes; the BYOB subset overlaps rather than adding another charge.
- **`webrtcEgressBytes`**: `number`

  Payload bytes for this transport, a display subset of egressExternalBytes. Never add to its parent total or bill separately. Missing older-history values mean zero; ingress is free.
- **`webrtcIngressBytes`**: `number`

  Payload bytes for this transport, a display subset of ingressBytes. Never add to its parent total or bill separately. Missing older-history values mean zero; ingress is free.
- **`websocketEgressBytes`**: `number`, default: `0`

  Payload bytes for this transport, a display subset of egressExternalBytes. Never add to its parent total or bill separately. Missing older-history values mean zero; ingress is free.
- **`websocketIngressBytes`**: `number`, default: `0`

  Payload bytes for this transport, a display subset of ingressBytes. Never add to its parent total or bill separately. Missing older-history values mean zero; ingress is free.
- **`webtransportEgressBytes`**: `number`, default: `0`

  Payload bytes for this transport, a display subset of egressExternalBytes. Never add to its parent total or bill separately. Missing older-history values mean zero; ingress is free.
- **`webtransportIngressBytes`**: `number`, default: `0`

  Payload bytes for this transport, a display subset of ingressBytes. Never add to its parent total or bill separately. Missing older-history values mean zero; ingress is free.

<a id="scalar-example-125"></a>

**Generated example:**

```json
{
  "periodStart": "2026-10-22T12:00:00.000Z",
  "periodEnd": "2026-10-22T12:00:00.000Z",
  "egressExternalBytes": 1000000,
  "ingressBytes": 1000000,
  "storedGbSeconds": 60,
  "retainedDeltaBytes": 1000000,
  "vodRecordingBytes": 1000000,
  "vodEgressBytes": 1000000,
  "transcode480Seconds": 60,
  "transcode720Seconds": 60,
  "transcode1080Seconds": 60,
  "agentSeconds": 60,
  "webtransportEgressBytes": 1000000,
  "webtransportIngressBytes": 1000000,
  "websocketEgressBytes": 1000000,
  "websocketIngressBytes": 1000000,
  "rtmpEgressBytes": 1000000,
  "rtmpIngressBytes": 1000000,
  "srtEgressBytes": 1000000,
  "srtIngressBytes": 1000000,
  "webrtcEgressBytes": 1000000,
  "webrtcIngressBytes": 1000000,
  "syncEgressBytes": 1000000,
  "syncIngressBytes": 1000000,
  "hlsEgressBytes": 1000000,
  "hlsIngressBytes": 1000000,
  "byobEgressBytes": 1000000,
  "byobIngressBytes": 1000000,
  "egressFrames": 60,
  "ingressFrames": 60,
  "egressGroups": 60,
  "ingressGroups": 60,
  "egressDatagrams": 60,
  "ingressDatagrams": 60,
  "egressSubscriptions": 60,
  "ingressSubscriptions": 60,
  "egressFetches": 60,
  "ingressFetches": 60,
  "egressAnnounced": 60,
  "ingressAnnounced": 60,
  "connectedSeconds": 60,
  "peakConnections": 60,
  "costMicros": 60,
  "partial": false
}
```

<a id="scalar-schema-project"></a>

### Project

**Type:** `object`

- **`billingExempt` (required)**: `boolean`

  Whether this project is exempt from billing.
- **`createdAt` (required)**: `string`, format: `date-time`

  RFC 3339 timestamp with a timezone; responses are normalized to UTC.
- **`domain` (required)**: `string | null`

  Verified email domain admitted to this project, or null.
- **`domainRole` (required)**: `string`

  Role granted to matching verified email domains.
- **`id` (required)**: `string`

  Immutable public project identifier used in URLs; never a database row id.
- **`name` (required)**: `string | null`

  Mutable project name, or null for an unnamed project.
- **`enforcedPublic`**

  The public rules the edge and recording playback enforce: null while an auth endpoint decides.

  **Any of:**
  - `object`, no additional properties
    - **`v` (required)**: `number`, const: `1`

      Pattern version 1: `foo` matches only `foo`; use `foo/**` for a subtree.
    - **`publish`**: `array of string`

      Patterns anyone may publish to; omitted grants none.
    - **`subscribe`**: `array of string`

      Patterns anyone may subscribe to; omitted grants none.
  - `object`, no additional properties
    - **`publish`**: `array of string`

      Patterns anyone may publish to; omitted grants none.
    - **`subscribe`**: `array of string`

      Patterns anyone may subscribe to; omitted grants none.
    - **`v`**: `number`, const: `0`

      Legacy prefix version: `foo` matches the subtree `foo/**`.
  - `null`
- **`ownerEmail`**: `string | null`

  The owner's email, whom members ask to lift a billing pause; null for an anonymous viewer. getProject only.
- **`public`**

  Anonymous publish/subscribe patterns as saved; null disables anonymous grants. Kept while an auth endpoint replaces them.

  **Any of:**
  - `object`, no additional properties
    - **`v` (required)**: `number`, const: `1`

      Pattern version 1: `foo` matches only `foo`; use `foo/**` for a subtree.
    - **`publish`**: `array of string`

      Patterns anyone may publish to; omitted grants none.
    - **`subscribe`**: `array of string`

      Patterns anyone may subscribe to; omitted grants none.
  - `object`, no additional properties
    - **`publish`**: `array of string`

      Patterns anyone may publish to; omitted grants none.
    - **`subscribe`**: `array of string`

      Patterns anyone may subscribe to; omitted grants none.
    - **`v`**: `number`, const: `0`

      Legacy prefix version: `foo` matches the subtree `foo/**`.
  - `null`
- **`redirects`**: `array`

  Previous names retained as redirects until their RFC 3339 expiry.

  **Items:**
  - **`expiresAt` (required)**: `string`, format: `date-time`

    RFC 3339 timestamp with a timezone; responses are normalized to UTC.
  - **`path` (required)**: `string`

    The previous name.
- **`role`**: `string`

  The current caller role on this project.
- **`suspension`**: `object`

  Whether and why the project is paused or warned about billing. getProject only.
  - **`banned` (required)**: `boolean`

    Paused by a moq.pro operator. Contact support.
  - **`capReached` (required)**: `boolean`

    Paused because the project reached its monthly spend cap. Lifts next month, or when the owner raises the cap.
  - **`freeLimited` (required)**: `boolean`

    Paused because the project has no card on file and used twice its share of the account monthly free tier. Lifts when the owner adds a card.
  - **`overFreeTier` (required)**: `boolean`

    A warning: the project has no card on file and is over its share of the account monthly free tier. It pauses at twice that share.
  - **`paused` (required)**: `boolean`

    Whether the project is refused now: new connections, playback, Sync, and live tokens. The other fields say why.
  - **`spendCapReached` (required)**: `boolean`

    The project reached its monthly spend cap.
  - **`suspended` (required)**: `boolean`

    Billing suspended the project, for an overdue invoice or the no-card limit. paused says whether that is enforced.

<a id="scalar-example-126"></a>

**Generated example:**

```json
{
  "id": "",
  "name": null,
  "billingExempt": true,
  "domain": null,
  "domainRole": "",
  "createdAt": "2026-10-22T12:00:00.000Z",
  "role": "",
  "public": {
    "v": 1,
    "publish": [
      ""
    ],
    "subscribe": [
      ""
    ]
  },
  "enforcedPublic": {
    "v": 1,
    "publish": [
      ""
    ],
    "subscribe": [
      ""
    ]
  },
  "redirects": [
    {
      "path": "",
      "expiresAt": "2026-10-22T12:00:00.000Z"
    }
  ],
  "suspension": {
    "paused": true,
    "banned": true,
    "freeLimited": true,
    "capReached": true,
    "suspended": true,
    "overFreeTier": true,
    "spendCapReached": true
  },
  "ownerEmail": null
}
```

<a id="scalar-schema-key"></a>

### Key

**Type:** `object`

- **`algorithm` (required)**: `string`

  Signing algorithm used by this key.
- **`createdAt` (required)**: `string`, format: `date-time`

  RFC 3339 timestamp with a timezone; responses are normalized to UTC.
- **`disabledAt` (required)**: `string | null`, format: `date-time`

  RFC 3339 timestamp with a timezone; responses are normalized to UTC.
- **`id` (required)**: `string`

  Opaque resource identifier.
- **`keyType` (required)**: `string`

  Symmetric shared secret or asymmetric signing key. Known values: "symmetric", "asymmetric". Additional values may appear.
- **`label` (required)**: `string | null`

  Optional human-readable label; null means unset.
- **`privateKey`**: `string`

  One-time private key on creation only; absent from lists and state updates. Save it before discarding the response.
- **`scope`**: `object`

  Immutable upper bound on token grants from this key.
  - **`root` (required)**: `string`

    Connection root carried by this stored key scope, often the project name or immutable id. Required when scope is present; no implicit default.
  - **`get`**: `array of string`

    Legacy subscribe grants, if present on an older stored key.
  - **`publish`**: `array of string`

    Allowed publish patterns relative to the root.
  - **`put`**: `array of string`

    Legacy publish grants, if present on an older stored key.
  - **`subscribe`**: `array of string`

    Allowed subscribe patterns relative to the root.

<a id="scalar-example-127"></a>

**Generated example:**

```json
{
  "id": "",
  "algorithm": "",
  "keyType": "",
  "label": null,
  "disabledAt": "2026-10-22T12:00:00.000Z",
  "createdAt": "2026-10-22T12:00:00.000Z",
  "scope": {
    "root": "",
    "publish": [
      ""
    ],
    "subscribe": [
      ""
    ],
    "put": [
      ""
    ],
    "get": [
      ""
    ]
  },
  "privateKey": ""
}
```

<a id="scalar-schema-privatekey"></a>

### PrivateKey

**Type:** `object`

- **`privateKey` (required)**: `string`

  The key's private JWK, serialized. It signs tokens as this key; keep it secret.

<a id="scalar-example-128"></a>

**Generated example:**

```json
{
  "privateKey": ""
}
```

<a id="scalar-schema-token"></a>

### Token

**Type:** `object`

- **`token` (required)**: `string`

  Signed JWT for the relay, as the `jwt` query parameter or a bearer.

<a id="scalar-example-129"></a>

**Generated example:**

```json
{
  "token": ""
}
```

<a id="scalar-schema-sessionlimits"></a>

### SessionLimits

**Type:** `object`

- **`remote` (required)**: `integer | null`, minimum: `1`, maximum: `1000000`

  Live sessions one remote address (an IPv4 address or an IPv6 /64) may hold on each relay, from 1 to 1000000; null is unlimited.
- **`token` (required)**: `integer | null`, minimum: `1`, maximum: `1000000`

  Live sessions one token may hold on each relay, from 1 to 1000000; null is unlimited.

<a id="scalar-example-130"></a>

**Generated example:**

```json
{
  "token": null,
  "remote": null
}
```

<a id="scalar-schema-authendpoint"></a>

### AuthEndpoint

**Type:** `object`

- **`url` (required)**: `string | null`

  The https URL asked to admit each tokenless session, or null when the project's public rules decide.

<a id="scalar-example-131"></a>

**Generated example:**

```json
{
  "url": null
}
```

<a id="scalar-schema-createdauthendpoint"></a>

### CreatedAuthEndpoint

**Type:** `object`

- **`secret` (required)**: `string`

  One-time signing secret, returned only by the save that creates the endpoint. The endpoint verifies each request's Moq-Signature with it, as for webhooks. Save it before discarding the response.
- **`url` (required)**: `string`

  The https URL asked to admit each tokenless session.

<a id="scalar-example-132"></a>

**Generated example:**

```json
{
  "url": "",
  "secret": ""
}
```

<a id="scalar-schema-authendpointtest"></a>

### AuthEndpointTest

**Type:** `object`

- **`answer` (required)**

  What the endpoint answered.

  **One of:**
  - `object`
    - **`grant` (required)**: `object`

      The grant the endpoint answered, as the edge reads it.

      **Additional properties:**

      `any`
    - **`kind` (required)**: `string`

      The endpoint admitted the session. Known values: "grant". Additional values may appear.
  - `object`
    - **`kind` (required)**: `string`

      The endpoint refused the session. Known values: "refused". Additional values may appear.
    - **`status` (required)**: `integer`

      The refusing HTTP status: 401 or 403.
  - `object`
    - **`error` (required)**: `string`

      What is wrong with the answer.
    - **`kind` (required)**: `string`

      The endpoint answered 2xx without a valid grant, which refuses the session. Known values: "invalid". Additional values may appear.
  - `object`
    - **`error` (required)**: `string`

      What failed.
    - **`kind` (required)**: `string`

      The endpoint failed to answer. Known values: "unavailable". Additional values may appear.
    - **`status`**: `integer`

      The HTTP status, when the endpoint answered.
- **`request` (required)**: `object`

  The signed request the endpoint received.
  - **`event` (required)**: `string`

    The event asked about. Known values: "connect". Additional values may appear.
  - **`id` (required)**: `string`

    Random id for this test session.
  - **`node` (required)**: `string`

    The relay asking; moq.pro-test for a test.
  - **`path` (required)**: `string`

    The dialed path, relative to the project.
  - **`transport` (required)**: `string`

    How the session reached the relay.
  - **`query`**: `string`

    The raw query string, without the leading ?.

<a id="scalar-example-133"></a>

**Generated example:**

```json
{
  "request": {
    "id": "",
    "event": "",
    "node": "",
    "transport": "",
    "path": "",
    "query": ""
  },
  "answer": {
    "kind": "",
    "grant": {
      "additionalProperty": null
    }
  }
}
```

<a id="scalar-schema-livetoken"></a>

### LiveToken

**Type:** `object`

- **`expiresAt` (required)**: `string`, format: `date-time`

  RFC 3339 timestamp with a timezone; responses are normalized to UTC.
- **`project` (required)**: `object`

  Watches the project's broadcasts.
  - **`root` (required)**: `string`

    The connection path to dial on the relay, which the token is scoped to.
  - **`token` (required)**: `string`

    A relay JWT that subscribes to everything under root.
- **`stats` (required)**: `object`

  Watches the project's live dashboard statistics.
  - **`root` (required)**: `string`

    The connection path to dial on the relay, which the token is scoped to.
  - **`token` (required)**: `string`

    A relay JWT that subscribes to everything under root.

<a id="scalar-example-134"></a>

**Generated example:**

```json
{
  "project": {
    "token": "",
    "root": ""
  },
  "stats": {
    "token": "",
    "root": ""
  },
  "expiresAt": "2026-10-22T12:00:00.000Z"
}
```

<a id="scalar-schema-recordingrule"></a>

### RecordingRule

**Type:** `object`

- **`bucket` (required)**: [Bucket](#scalar-schema-bucket)

  Customer bucket metadata, or null for managed storage.
- **`bucketId` (required)**: `string | null`

  Opaque customer bucket id, or null for managed storage.
- **`createdAt` (required)**: `string`, format: `date-time`

  RFC 3339 timestamp with a timezone; responses are normalized to UTC.
- **`deletePending` (required)**: `integer`, minimum: `0`

  Recordings still waiting for this rule deletion to drain; zero when idle.
- **`deleting` (required)**: `boolean`

  Whether asynchronous deletion is draining this resource.
- **`id` (required)**: `string`

  Opaque resource identifier.
- **`label` (required)**: `string | null`

  Optional human-readable label; null means unset.
- **`patterns` (required)**: `array of string`

  Broadcast path patterns matched by this rule; arrays replace the existing list when patched.
- **`retentionDays` (required)**: `number`

  Managed recording retention in days; one of the declared retention presets. Defaults to 7 on create.
- **`updatedAt` (required)**: `string`, format: `date-time`

  RFC 3339 timestamp with a timezone; responses are normalized to UTC.
- **`uploadRefusal` (required)**: `string | null`

  Why the bucket refused an upload, which stopped recording until the rule is re-enabled; null when recording.
- **`vodEnabled` (required)**: `boolean`

  Whether this rule records matching broadcasts. Defaults to true on create.

<a id="scalar-example-135"></a>

**Generated example:**

```json
{
  "id": "",
  "label": null,
  "patterns": [
    ""
  ],
  "vodEnabled": true,
  "retentionDays": 1,
  "createdAt": "2026-10-22T12:00:00.000Z",
  "updatedAt": "2026-10-22T12:00:00.000Z",
  "bucketId": null,
  "deleting": true,
  "uploadRefusal": null,
  "bucket": {
    "endpoint": "",
    "region": "",
    "bucket": "",
    "destination": "",
    "delivery": true
  },
  "deletePending": 0
}
```

<a id="scalar-schema-bucket"></a>

### Bucket

**Type:** `object | null`

Customer bucket metadata, or null for managed storage.

- **`bucket` (required)**: `string`

  S3 bucket name. Managed storage is represented by null on the enclosing recording rule bucket field.
- **`delivery` (required)**: `boolean`

  Whether customer-bucket playback/delivery is enabled.
- **`destination` (required)**: `string`

  Object prefix inside the customer bucket.
- **`endpoint` (required)**: `string`

  S3-compatible endpoint URL. Callers with keys:write and the write role see the configured URL, which may carry credentials; others see only its origin.
- **`region` (required)**: `string`

  S3 region used for signing.

<a id="scalar-schema-deletingrule"></a>

### DeletingRule

**Type:** `object`

- **`pending` (required)**: `number`

  Recordings still draining before the rule disappears.
- **`status` (required)**: `string`

  The rule's storage is draining. Known values: "deleting". Additional values may appear.

<a id="scalar-example-136"></a>

**Generated example:**

```json
{
  "status": "",
  "pending": 1
}
```

<a id="scalar-schema-deletingrecordings"></a>

### DeletingRecordings

**Type:** `object`

- **`pending` (required)**: `number`

  How many of the rule's recordings are being deleted.
- **`status` (required)**: `string`

  The recordings are draining. Known values: "deleting". Additional values may appear.

<a id="scalar-example-137"></a>

**Generated example:**

```json
{
  "status": "",
  "pending": 1
}
```

<a id="scalar-schema-session"></a>

### Session

**Type:** `object`

- **`broadcastPath` (required)**: `string`

  Broadcast path relative to the project.
- **`bytes` (required)**: `number`

  Recorded media bytes, excluding transport overhead.
- **`cleanupFailed` (required)**: `boolean`

  Whether object deletion needs another attempt.
- **`deleting` (required)**: `boolean`

  Whether asynchronous deletion is draining this resource.
- **`durationMs` (required)**: `number`

  Recorded media duration in milliseconds.
- **`endedAt` (required)**: `string | null`, format: `date-time`

  RFC 3339 timestamp with a timezone; responses are normalized to UTC.
- **`exportFailure` (required)**: `string | null`

  Export failure reason, or null.
- **`exportStatus` (required)**: `string | null`

  Bucket export state, or null if not applicable. Known values: "pending", "ready", "failed", null. Additional values may appear.
- **`id` (required)**: `string`

  Opaque resource identifier.
- **`playlistUrl` (required)**: `string | null`

  Playback playlist URL, or null until media is ready.
- **`region` (required)**: `string | null`

  S3 region used for signing.
- **`regression` (required)**: `string | null`

  Diagnostic from the newest retained generation by generation number that reported conflicting or backwards media under a reused source epoch; null if none did. Free text, not an enum. A later generation may be live; committed media can still play or export successfully.
- **`startedAt` (required)**: `string`, format: `date-time`

  RFC 3339 timestamp with a timezone; responses are normalized to UTC.
- **`status` (required)**: `string`

  Recording lifecycle: LIVE is ingesting, ENDED has closed, and DELETED is removed; deleting separately marks an asynchronous drain. Known values: "LIVE", "ENDED", "DELETED". Additional values may appear.

<a id="scalar-example-138"></a>

**Generated example:**

```json
{
  "id": "",
  "broadcastPath": "",
  "status": "",
  "startedAt": "2026-10-22T12:00:00.000Z",
  "endedAt": "2026-10-22T12:00:00.000Z",
  "playlistUrl": null,
  "region": null,
  "bytes": 1,
  "durationMs": 1,
  "deleting": true,
  "cleanupFailed": true,
  "exportStatus": null,
  "exportFailure": null,
  "regression": null
}
```

<a id="scalar-schema-recordingruletotals"></a>

### RecordingRuleTotals

**Type:** `object`

- **`bytes` (required)**: `integer`, minimum: `0`

  Their stored size. A live recording's size trails what it has written so far.
- **`live` (required)**: `integer`, minimum: `0`

  Of those, recordings still in progress.
- **`recordings` (required)**: `integer`, minimum: `0`

  Recordings the rule holds, live ones included.
- **`ruleId` (required)**: `string`

  The recording rule that holds these recordings.

<a id="scalar-example-139"></a>

**Generated example:**

```json
{
  "ruleId": "",
  "recordings": 0,
  "live": 0,
  "bytes": 0
}
```

<a id="scalar-schema-playback"></a>

### Playback

**Type:** `object`

- **`expiresAt` (required)**: `string`, format: `date-time`

  When the token expires. Call again for a fresh one.
- **`initial` (required)**: `object | null`

  The window the playlist starts with; an omitted bound is open.
  - **`end`**: `string`, format: `date-time`

    RFC 3339 timestamp with a timezone; responses are normalized to UTC.
  - **`start`**: `string`, format: `date-time`

    RFC 3339 timestamp with a timezone; responses are normalized to UTC.
- **`live` (required)**: `boolean`

  The recording is still ingesting, so the playlist grows.
- **`span` (required)**: `object | null`

  The retained media span, and the most segments one rendition holds across it.
  - **`end` (required)**: `string`, format: `date-time`

    RFC 3339 timestamp with a timezone; responses are normalized to UTC.
  - **`segments` (required)**: `number`

    The most segments one rendition holds across the span.
  - **`start` (required)**: `string`, format: `date-time`

    RFC 3339 timestamp with a timezone; responses are normalized to UTC.
- **`token` (required)**: `string`

  Grants this recording's playlists and media. Send it on every request as `Authorization: Bearer` or the `jwt` query parameter.
- **`url` (required)**: `string`

  The recording's HLS playlist.

<a id="scalar-example-140"></a>

**Generated example:**

```json
{
  "url": "",
  "token": "",
  "expiresAt": "2026-10-22T12:00:00.000Z",
  "live": true,
  "span": {
    "start": "2026-10-22T12:00:00.000Z",
    "end": "2026-10-22T12:00:00.000Z",
    "segments": 1
  },
  "initial": {
    "start": "2026-10-22T12:00:00.000Z",
    "end": "2026-10-22T12:00:00.000Z"
  }
}
```

<a id="scalar-schema-deletingrecording"></a>

### DeletingRecording

**Type:** `object`

- **`status` (required)**: `string`

  `stopping` while a live recording's recorder stops; its storage drains after. Known values: "deleting", "stopping". Additional values may appear.

<a id="scalar-example-141"></a>

**Generated example:**

```json
{
  "status": ""
}
```

<a id="scalar-schema-s3credentials"></a>

### S3Credentials

**Type:** `object`

- **`accessKeyId` (required)**: `string`

  S3 access-key identifier. Requires write authority and must be kept private.
- **`bucket` (required)**: `string`

  S3 bucket name. Managed storage is represented by null on the enclosing recording rule bucket field.
- **`endpoint` (required)**: `string`

  S3-compatible endpoint URL for these credentials. This operation requires write authority.
- **`region` (required)**: `string`

  S3 region used for signing.
- **`secretAccessKey` (required)**: `string`

  S3 signing secret. Requires write authority; never place it in a public URL.

<a id="scalar-example-142"></a>

**Generated example:**

```json
{
  "endpoint": "",
  "region": "",
  "bucket": "",
  "accessKeyId": "",
  "secretAccessKey": ""
}
```

<a id="scalar-schema-agentrule"></a>

### AgentRule

**Type:** `object`

- **`config` (required)**: [AgentConfig](#scalar-schema-agentconfig)

  Full keyless voice configuration. Provider keys are write-only and stay encrypted in storage.
- **`createdAt` (required)**: `string`, format: `date-time`

  RFC 3339 timestamp with a timezone; responses are normalized to UTC.
- **`enabled` (required)**: `boolean`

  Whether this configuration is enabled.
- **`id` (required)**: `string`

  Opaque resource identifier.
- **`patterns` (required)**: `array of string`

  Broadcast path patterns matched by this rule; arrays replace the existing list when patched.
- **`updatedAt` (required)**: `string`, format: `date-time`

  RFC 3339 timestamp with a timezone; responses are normalized to UTC.

<a id="scalar-example-143"></a>

**Generated example:**

```json
{
  "id": "",
  "patterns": [
    ""
  ],
  "enabled": true,
  "config": {
    "systemPrompt": "",
    "greeting": null,
    "turnDetection": "silero",
    "maxSessions": 4,
    "stt": {
      "provider": "",
      "model": null,
      "hasKey": true,
      "options": {
        "additionalProperty": ""
      }
    },
    "llm": {
      "provider": "",
      "model": null,
      "hasKey": true,
      "options": {
        "additionalProperty": ""
      }
    },
    "tts": {
      "provider": "",
      "model": null,
      "hasKey": true,
      "options": {
        "additionalProperty": ""
      }
    }
  },
  "createdAt": "2026-10-22T12:00:00.000Z",
  "updatedAt": "2026-10-22T12:00:00.000Z"
}
```

<a id="scalar-schema-agentconfig"></a>

### AgentConfig

**Type:** `object`

Full keyless voice configuration. Provider keys are write-only and stay encrypted in storage.

- **`greeting` (required)**: `string | null`

  Initial spoken greeting, or null for none; null in PATCH clears it.
- **`llm` (required)**: `object`

  Language-model provider configuration; apiKey is never returned.
  - **`hasKey` (required)**: `boolean`

    Whether an encrypted provider key is configured; never the key itself.
  - **`model` (required)**: `string | null`

    Provider model, or null for its default.
  - **`options` (required)**: `object`

    Provider-specific options. Only the selected provider options are supported.

    **Additional properties:**

    `string | number | boolean`
  - **`provider` (required)**: `string`

    Provider identifier.
- **`maxSessions` (required)**: `integer`, minimum: `1`, maximum: `4`

  Maximum simultaneous sessions for this rule, from 1 to the project ceiling. Defaults to that ceiling on create.
- **`stt` (required)**: `object`

  Speech-to-text provider configuration; apiKey is never returned.
  - **`hasKey` (required)**: `boolean`

    Whether an encrypted provider key is configured; never the key itself.
  - **`model` (required)**: `string | null`

    Provider model, or null for its default.
  - **`options` (required)**: `object`

    Provider-specific options. Only the selected provider options are supported.

    **Additional properties:**

    `string | number | boolean`
  - **`provider` (required)**: `string`

    Provider identifier.
- **`systemPrompt` (required)**: `string`, maxLength: `8000`

  Instruction text, at most 8000 characters; required on create and unchanged when omitted in PATCH.
- **`tts` (required)**: `object`

  Text-to-speech provider configuration; apiKey is never returned.
  - **`hasKey` (required)**: `boolean`

    Whether an encrypted provider key is configured; never the key itself.
  - **`model` (required)**: `string | null`

    Provider model, or null for its default.
  - **`options` (required)**: `object`

    Provider-specific options. Only the selected provider options are supported.

    **Additional properties:**

    `string | number | boolean`
  - **`provider` (required)**: `string`

    Provider identifier.
- **`turnDetection` (required)**: `string`

  Turn detection strategy. Defaults to silero on create. Known values: "silero", "stt_endpointing", "client". Additional values may appear.

<a id="scalar-example-144"></a>

**Generated example:**

```json
{
  "systemPrompt": "",
  "greeting": null,
  "turnDetection": "silero",
  "maxSessions": 4,
  "stt": {
    "provider": "",
    "model": null,
    "hasKey": true,
    "options": {
      "additionalProperty": ""
    }
  },
  "llm": {
    "provider": "",
    "model": null,
    "hasKey": true,
    "options": {
      "additionalProperty": ""
    }
  },
  "tts": {
    "provider": "",
    "model": null,
    "hasKey": true,
    "options": {
      "additionalProperty": ""
    }
  }
}
```

<a id="scalar-schema-synctarget"></a>

### SyncTarget

**Type:** `object`

- **`createdAt` (required)**: `string`, format: `date-time`

  RFC 3339 timestamp with a timezone; responses are normalized to UTC.
- **`id` (required)**: `string`

  Opaque resource identifier.
- **`kind` (required)**: `string`

  Immutable sync target kind: cloudflare or custom. Known values: "cloudflare", "custom". Additional values may appear.
- **`label` (required)**: `string`

  Trimmed human-readable label; an empty string means unset. Defaults to an empty string on create.
- **`publishConfigured` (required)**: `boolean`

  Whether a publish credential is configured.
- **`publishOrigin` (required)**: `string | null`

  Credential-free publish origin, or null when withheld/unconfigured.
- **`publishPrefixes` (required)**: `array of string`

  Published path prefixes relative to the project.
- **`updatedAt` (required)**: `string`, format: `date-time`

  RFC 3339 timestamp with a timezone; responses are normalized to UTC.

<a id="scalar-example-145"></a>

**Generated example:**

```json
{
  "id": "",
  "kind": "",
  "label": "",
  "publishPrefixes": [
    ""
  ],
  "publishConfigured": true,
  "publishOrigin": null,
  "createdAt": "2026-10-22T12:00:00.000Z",
  "updatedAt": "2026-10-22T12:00:00.000Z"
}
```

<a id="scalar-schema-webhook"></a>

### Webhook

**Type:** `object`

- **`createdAt` (required)**: `string`, format: `date-time`

  RFC 3339 timestamp with a timezone; responses are normalized to UTC.
- **`disabledReason` (required)**: `string | null`

  Reason for automatic disable, or null.
- **`enabled` (required)**: `boolean`

  Whether this configuration is enabled.
- **`events` (required)**: `array`

  Subscribed event names.

  **Items:**

  `string`

  Known values: "broadcast.started", "broadcast.ended", "recording.started", "recording.ready", "recording.failed". Additional values may appear.
- **`failures` (required)**: `number`

  Consecutive delivery failures.
- **`format` (required)**: `string`

  Delivery body: the signed `json` envelope, or an unsigned `slack` or `discord` message. Known values: "json", "slack", "discord". Additional values may appear.
- **`id` (required)**: `string`

  Opaque resource identifier.
- **`pattern` (required)**: `string`

  Broadcast path pattern for this webhook.
- **`updatedAt` (required)**: `string`, format: `date-time`

  RFC 3339 timestamp with a timezone; responses are normalized to UTC.
- **`url` (required)**: `string`

  Webhook HTTPS endpoint URL; may carry a credential and requires write authority.
- **`secret`**: `string`

  One-time signing secret on creation only; never returned by reads or updates.

<a id="scalar-example-146"></a>

**Generated example:**

```json
{
  "id": "",
  "url": "",
  "events": [
    ""
  ],
  "pattern": "",
  "format": "",
  "enabled": true,
  "disabledReason": null,
  "secret": "",
  "failures": 1,
  "createdAt": "2026-10-22T12:00:00.000Z",
  "updatedAt": "2026-10-22T12:00:00.000Z"
}
```

<a id="scalar-schema-testedwebhook"></a>

### TestedWebhook

**Type:** `object`

- **`eventId` (required)**: `string`

  The queued test event; find its delivery by this id.

<a id="scalar-example-147"></a>

**Generated example:**

```json
{
  "eventId": ""
}
```

<a id="scalar-schema-delivery"></a>

### Delivery

**Type:** `object`

- **`attempts` (required)**: `number`

  Number of delivery attempts.
- **`createdAt` (required)**: `string`, format: `date-time`

  RFC 3339 timestamp with a timezone; responses are normalized to UTC.
- **`error` (required)**: `string | null`

  Last delivery error, or null.
- **`eventId` (required)**: `string`

  Opaque webhook event identifier.
- **`id` (required)**: `string`

  Opaque resource identifier.
- **`state` (required)**: `string`

  Delivery state. Known values: "pending", "delivered", "failed". Additional values may appear.
- **`status` (required)**: `number | null`

  Last webhook response HTTP status code, or null before an HTTP response (including transport failures).
- **`type` (required)**: `string`

  Webhook event type.
- **`updatedAt` (required)**: `string`, format: `date-time`

  RFC 3339 timestamp with a timezone; responses are normalized to UTC.

<a id="scalar-example-148"></a>

**Generated example:**

```json
{
  "id": "",
  "eventId": "",
  "type": "",
  "state": "",
  "attempts": 1,
  "status": null,
  "error": null,
  "createdAt": "2026-10-22T12:00:00.000Z",
  "updatedAt": "2026-10-22T12:00:00.000Z"
}
```

<a id="scalar-schema-allowance"></a>

### Allowance

**Type:** `object | null`

- **`egressBytes` (required)**: `number`, minimum: `0`

  Shared delivery allowance in payload bytes (live plus recording delivery); decimal GB = 1000000000 bytes.
- **`storedGbSeconds` (required)**: `number`, minimum: `0`

  Shared managed-storage allowance in GB·second, decimal GB.

<a id="scalar-schema-currentusage"></a>

### CurrentUsage

**Type:** `object`

- **`pool` (required)**: [Pool](#scalar-schema-pool)
- **`project` (required)**: `string`

  The project's immutable id.
- **`usage` (required)**: [CurrentUsageQuantities](#scalar-schema-currentusagequantities)

<a id="scalar-example-149"></a>

**Generated example:**

```json
{
  "project": "",
  "usage": {
    "periodStart": "2026-10-22T12:00:00.000Z",
    "periodEnd": "2026-10-22T12:00:00.000Z",
    "egressExternalBytes": 1000000,
    "ingressBytes": 1000000,
    "storedGbSeconds": 60,
    "retainedDeltaBytes": 1000000,
    "vodRecordingBytes": 1000000,
    "vodEgressBytes": 1000000,
    "transcode480Seconds": 60,
    "transcode720Seconds": 60,
    "transcode1080Seconds": 60,
    "agentSeconds": 60,
    "webtransportEgressBytes": 1000000,
    "webtransportIngressBytes": 1000000,
    "websocketEgressBytes": 1000000,
    "websocketIngressBytes": 1000000,
    "rtmpEgressBytes": 1000000,
    "rtmpIngressBytes": 1000000,
    "srtEgressBytes": 1000000,
    "srtIngressBytes": 1000000,
    "webrtcEgressBytes": 1000000,
    "webrtcIngressBytes": 1000000,
    "syncEgressBytes": 1000000,
    "syncIngressBytes": 1000000,
    "hlsEgressBytes": 1000000,
    "hlsIngressBytes": 1000000,
    "byobEgressBytes": 1000000,
    "byobIngressBytes": 1000000,
    "egressFrames": 60,
    "ingressFrames": 60,
    "egressGroups": 60,
    "ingressGroups": 60,
    "egressDatagrams": 60,
    "ingressDatagrams": 60,
    "egressSubscriptions": 60,
    "ingressSubscriptions": 60,
    "egressFetches": 60,
    "ingressFetches": 60,
    "egressAnnounced": 60,
    "ingressAnnounced": 60,
    "connectedSeconds": 60,
    "peakConnections": 60,
    "costMicros": 60,
    "partial": false,
    "freeAllowance": {
      "egressBytes": 1,
      "storedGbSeconds": 1
    },
    "freeEgressBytes": 1,
    "freeVodEgressBytes": 1,
    "freeStoredGbSeconds": 1,
    "freeRecordingBytes": 0,
    "freeMicros": 1,
    "netMicros": 1
  },
  "pool": {
    "periodStart": "2026-10-22T12:00:00.000Z",
    "available": {
      "egressBytes": 1000000000,
      "storedGbSeconds": 86400
    },
    "projected": {
      "egressBytes": 1000000000,
      "storedGbSeconds": 86400
    },
    "drawn": {
      "egressBytes": 1000000000,
      "storedGbSeconds": 86400
    },
    "projects": 1
  }
}
```

<a id="scalar-schema-pool"></a>

### Pool

**Type:** `object | null`

- **`available` (required)**

  **All of:**
  - [Allowance](#scalar-schema-allowance)
  - `any`

    Remaining account allowance available to this project now.
- **`drawn` (required)**

  **All of:**
  - [Allowance](#scalar-schema-allowance)
  - `any`

    Account allowance already consumed by this project this month.
- **`periodStart` (required)**: `string`, format: `date-time`

  RFC 3339 timestamp with a timezone; responses are normalized to UTC.
- **`projected` (required)**

  **All of:**
  - [Allowance](#scalar-schema-allowance)
  - `any`

    Projected account allowance share available to this project at month end.
- **`projects` (required)**: `number`

  Number of projects sharing the allowance.

<a id="scalar-schema-invoice"></a>

### Invoice

**Type:** `object`

- **`amountChargedMicros` (required)**: `integer`, minimum: `0`

  Micro-USD (1,000,000 = USD 1); never cents.
- **`createdAt` (required)**: `string`, format: `date-time`

  RFC 3339 timestamp with a timezone; responses are normalized to UTC.
- **`creditAppliedMicros` (required)**: `integer`, minimum: `0`

  Micro-USD (1,000,000 = USD 1); never cents.
- **`freeMicros` (required)**: `integer`, minimum: `0`

  Micro-USD (1,000,000 = USD 1); never cents.
- **`grossMicros` (required)**: `integer`, minimum: `0`

  Micro-USD (1,000,000 = USD 1); never cents.
- **`hostedInvoiceUrl` (required)**: `string | null`

  Stripe hosted invoice URL, or null when unavailable or withheld by caller authority.
- **`netMicros` (required)**: `integer`, minimum: `0`

  Micro-USD (1,000,000 = USD 1); never cents.
- **`periodStart` (required)**: `string`, format: `date-time`

  RFC 3339 timestamp with a timezone; responses are normalized to UTC.
- **`poolProjects` (required)**: `number | null`

  Number of projects sharing this invoice allowance; null for older invoices.
- **`principalMicros` (required)**: `integer`, minimum: `0`

  Micro-USD (1,000,000 = USD 1); never cents.
- **`status` (required)**: `string`

  Invoice ledger state: pending collection, paid, failed collection, no_card, held review, invoiced net terms, forgiven, invoice_void, invoice_uncollectible, invoice_writeoff_pending, or invoice_written_off.

<a id="scalar-example-150"></a>

**Generated example:**

```json
{
  "periodStart": "2026-10-22T12:00:00.000Z",
  "grossMicros": 1000000,
  "freeMicros": 1000000,
  "netMicros": 1000000,
  "creditAppliedMicros": 1000000,
  "principalMicros": 1000000,
  "amountChargedMicros": 1000000,
  "status": "",
  "hostedInvoiceUrl": null,
  "poolProjects": null,
  "createdAt": "2026-10-22T12:00:00.000Z"
}
```

<a id="scalar-schema-spendcap"></a>

### SpendCap

**Type:** `object`

- **`overFreeTier` (required)**: `boolean`

  Whether usage exceeds the account free allowance.
- **`spendCapMicros` (required)**: `number | null`

  Monthly spending cap in micro-USD, or null to remove the cap.
- **`spendCapReached` (required)**: `boolean`

  Whether the spending cap is reached.
- **`suspended` (required)**: `boolean`

  Whether the project is suspended.
- **`capReached`**: `boolean`

  Whether a capacity cap is reached.
- **`freeLimited`**: `boolean`

  Whether the free-tier connection limit applies.
- **`notifications`**: `array`

  At most one billing notice for the dashboard to show.

  **Items:**
  - **`category` (required)**: `string`

    What the notice is about. Known values: "account", "usage", "spend". Additional values may appear.
  - **`dismissible` (required)**: `boolean`

    Whether the dashboard may hide it.
  - **`id` (required)**: `string`

    Opaque resource identifier.
  - **`level` (required)**: `number`

    Which notice in its category: a usage percent, a spend milestone in USD, or an account state.
  - **`severity` (required)**: `string`

    blocking for a suspension notice, else advisory. Known values: "advisory", "blocking". Additional values may appear.
  - **`capReached`**: `boolean`

    Whether a capacity cap is reached.
- **`seq`**: `number`

  Monotonically increasing notice snapshot sequence.

<a id="scalar-example-151"></a>

**Generated example:**

```json
{
  "spendCapMicros": null,
  "suspended": true,
  "overFreeTier": true,
  "spendCapReached": true,
  "capReached": true,
  "freeLimited": true,
  "seq": 1,
  "notifications": [
    {
      "id": "",
      "category": "",
      "level": 1,
      "severity": "",
      "dismissible": true,
      "capReached": true
    }
  ]
}
```

<a id="scalar-schema-payment"></a>

### Payment

**All of:**

- [SpendCap](#scalar-schema-spendcap)
- `object`
  - **`billingContact` (required)**: `string`

    Explicit billing email; empty uses the owner's address.
  - **`billingExempt` (required)**: `boolean`

    The project is exempt from billing.
  - **`billingRecipient` (required)**: `string`

    Resolved billing email; empty when none is available.
  - **`collectionMode` (required)**: `string`

    How the project pays. Known values: "none", "exempt", "charge_automatically", "send_invoice". Additional values may appear.
  - **`creditLimitRequest` (required)**

    **All of:**
    - [CreditLimitRequestResponse](#scalar-schema-creditlimitrequestresponse)

      The owner's pending request, or null.
    - `any`

      Pending request for a higher credit limit, or null.
  - **`enabled` (required)**: `boolean`

    Stripe billing is configured.
  - **`exposure` (required)**

    **All of:**
    - [BillingExposure](#scalar-schema-billingexposure)
    - `any`

      The current cap and credit-limit headroom.
  - **`forgivenStatements` (required)**: `boolean`

    The monthly statement is sent for months with nothing to pay. Months that charge or owe always send.
  - **`hasPaymentMethod` (required)**: `boolean`

    A payment method is on file.
  - **`hasStripeCustomer` (required)**: `boolean`

    A Stripe billing account exists.
  - **`netTerms` (required)**: `string`

    Net-terms approval state; empty means no request. Known values: "", "pending", "active", "cancelling", "revoking". Additional values may appear.
  - **`netTermsDays` (required)**: `number`

    Approved payment window in days.
  - **`paymentLabel` (required)**: `string | null`

    Payment method label, or null.
  - **`paymentLast4` (required)**: `string | null`

    Last four digits of the payment method, or null.
  - **`publishableKey` (required)**: `string`

    Stripe publishable key for embedded card entry; empty when unconfigured.

<a id="scalar-schema-billingstatus"></a>

### BillingStatus

**Type:** `object`

- **`overFreeTier` (required)**: `boolean`

  Whether usage exceeds the account free allowance.
- **`spendCapReached` (required)**: `boolean`

  Whether the spending cap is reached.
- **`suspended` (required)**: `boolean`

  Whether the project is suspended.
- **`capReached`**: `boolean`

  Whether a capacity cap is reached.
- **`freeLimited`**: `boolean`

  Whether the free-tier connection limit applies.
- **`notifications`**: `array`

  At most one billing notice for the dashboard to show.

  **Items:**
  - **`category` (required)**: `string`

    What the notice is about. Known values: "account", "usage", "spend". Additional values may appear.
  - **`dismissible` (required)**: `boolean`

    Whether the dashboard may hide it.
  - **`id` (required)**: `string`

    Opaque resource identifier.
  - **`level` (required)**: `number`

    Which notice in its category: a usage percent, a spend milestone in USD, or an account state.
  - **`severity` (required)**: `string`

    blocking for a suspension notice, else advisory. Known values: "advisory", "blocking". Additional values may appear.
  - **`capReached`**: `boolean`

    Whether a capacity cap is reached.
- **`seq`**: `number`

  Monotonically increasing notice snapshot sequence.

<a id="scalar-example-152"></a>

**Generated example:**

```json
{
  "suspended": true,
  "overFreeTier": true,
  "spendCapReached": true,
  "capReached": true,
  "freeLimited": true,
  "seq": 1,
  "notifications": [
    {
      "id": "",
      "category": "",
      "level": 1,
      "severity": "",
      "dismissible": true,
      "capReached": true
    }
  ]
}
```

<a id="scalar-schema-billingidentity"></a>

### BillingIdentity

**Type:** `object | null`, no additional properties

Stripe legal identity, or null before a billing account exists.

- **`address` (required)**: `object`, no additional properties

  Billing address; empty fields clear them.
  - **`city` (required)**: `string`, maxLength: `200`

    City.
  - **`country` (required)**

    ISO 3166-1 alpha-2 country; empty clears the address.

    **Any of:**
    - `string`

      Known values: "". Additional values may appear.
    - `string`, pattern: `^[A-Za-z]{2}$`
  - **`line1` (required)**: `string`, maxLength: `200`

    Street line 1.
  - **`line2` (required)**: `string`, maxLength: `200`

    Street line 2.
  - **`postalCode` (required)**: `string`, maxLength: `30`

    Postal code.
  - **`state` (required)**: `string`, maxLength: `200`

    State or region.
- **`companyName` (required)**: `string`, maxLength: `200`

  The legal entity billed; empty falls back to the email.
- **`taxId` (required)**: `object | null`, no additional properties

  One tax registration, or null to clear it.
  - **`type` (required)**: `string`

    Stripe tax id type. Known values: "eu_vat", "gb_vat", "ch_vat", "no_vat", "us_ein", "ca_bn", "ca_gst_hst", "au_abn", "nz_gst", "jp_ct", "kr_brn", "sg_uen", "in_gst", "br_cnpj", "mx_rfc", "za_vat", "ae_trn". Additional values may appear.
  - **`value` (required)**: `string`, maxLength: `50`

    The registration number.

<a id="scalar-schema-notificationframe"></a>

### NotificationFrame

**Type:** `object`

- **`notifications` (required)**: `array`

  At most one billing notice for the dashboard to show.

  **Items:**
  - **`category` (required)**: `string`

    What the notice is about. Known values: "account", "usage", "spend". Additional values may appear.
  - **`dismissible` (required)**: `boolean`

    Whether the dashboard may hide it.
  - **`id` (required)**: `string`

    Opaque resource identifier.
  - **`level` (required)**: `number`

    Which notice in its category: a usage percent, a spend milestone in USD, or an account state.
  - **`severity` (required)**: `string`

    blocking for a suspension notice, else advisory. Known values: "advisory", "blocking". Additional values may appear.
  - **`capReached`**: `boolean`

    Whether a capacity cap is reached.
- **`seq` (required)**: `number`

  Monotonically increasing notice snapshot sequence.

<a id="scalar-example-153"></a>

**Generated example:**

```json
{
  "seq": 1,
  "notifications": [
    {
      "id": "",
      "category": "",
      "level": 1,
      "severity": "",
      "dismissible": true,
      "capReached": true
    }
  ]
}
```

<a id="scalar-schema-permissionsinput"></a>

### PermissionsInput

**Type:** `object`, no additional properties

A level per permission family; an omitted family is none. See Permissions.

- **`account`**: `string`, possible values: `"none", "read", "write"`

  Account: profile, data export, and deletion.
- **`admin`**: `string`, possible values: `"none", "read", "write"`

  Site admin: operator routes, for a site admin.
- **`billing`**: `string`, possible values: `"none", "read", "write"`

  Billing: payment, spend caps, invoices, credit, and account usage.
- **`credentials`**: `string`, possible values: `"none", "read", "write"`

  Credentials: API tokens, passkeys, and sessions.
- **`keys`**: `string`, possible values: `"none", "read", "write"`

  Keys & secrets: signing keys and the auth endpoint; write also reveals every secret and mints relay tokens.
- **`lifecycle`**: `string`, possible values: `"none", "write"`

  Project lifecycle: create and delete projects.
- **`media`**: `string`, possible values: `"none", "read", "write"`

  Media rules: recording, transcode, agent, sync, and webhooks.
- **`members`**: `string`, possible values: `"none", "read", "write"`

  Members & invites: members, invitations, and ownership transfer.
- **`settings`**: `string`, possible values: `"none", "read", "write"`

  Project settings: the project itself, its session limits, and its notices.
- **`usage`**: `string`, possible values: `"none", "read"`

  Usage & stats: metered usage.

<a id="scalar-example-154"></a>

**Generated example:**

```json
{
  "settings": "none",
  "keys": "none",
  "media": "none",
  "usage": "none",
  "lifecycle": "none",
  "members": "none",
  "billing": "none",
  "credentials": "none",
  "account": "none",
  "admin": "none"
}
```

<a id="scalar-schema-account"></a>

### Account

**Type:** `object`

- **`email` (required)**: `string`

  Verified sign-in email. Changing it requires human sign-in verification.
- **`id` (required)**: `string`

  The authenticated account's identifier.
- **`isAdmin` (required)**: `boolean`

  Whether the account is a site administrator.
- **`name` (required)**: `string`

  Account display name.
- **`ownedProjectCap` (required)**: `number`

  Maximum number of projects this account may own.
- **`domainAccessExpiresAt`**: `number`

  When the latest Google Workspace sign-in stops granting domain access, in Unix milliseconds; absent without one. Returned by getAccount.
- **`domainLoginRequired`**: `boolean`

  Whether projects shared with the email's domain stay hidden until a Google Workspace sign-in for that domain within the last 7 days. Returned by getAccount.

<a id="scalar-example-155"></a>

**Generated example:**

```json
{
  "id": "",
  "email": "",
  "name": "",
  "isAdmin": true,
  "ownedProjectCap": 1,
  "domainLoginRequired": true,
  "domainAccessExpiresAt": 1
}
```

<a id="scalar-schema-accountusage"></a>

### AccountUsage

**Type:** `object`

- **`allowance` (required)**: [AccountAllowance](#scalar-schema-accountallowance)

  Monthly free allowance, shared when pooled is true.
- **`drawn` (required)**

  **All of:**
  - [AccountAllowance](#scalar-schema-accountallowance)

    Monthly free allowance, shared when pooled is true.
  - `any`

    Allowance consumed, including usage from departed projects.
- **`periodStart` (required)**: `string`, format: `date-time`

  RFC 3339 timestamp with a timezone; responses are normalized to UTC.
- **`pooled` (required)**: `boolean`

  Whether the projects share one account allowance this month.
- **`projects` (required)**: `array`

  Owned billable projects, oldest first. Shares are estimates until settlement.

  **Items:**
  - **`costMicros` (required)**: `integer`, minimum: `0`

    Gross cost before the free allowance, in micro-USD.
  - **`egressBytes` (required)**: `number`

    Live and recording delivery payload bytes.
  - **`freeMicros` (required)**: `integer`, minimum: `0`

    Free allowance credit, in micro-USD.
  - **`id` (required)**: `string`

    Immutable public project identifier used in URLs; never a database row id.
  - **`name` (required)**: `string | null`

    Mutable project name, or null for an unnamed project.
  - **`netMicros` (required)**: `integer`, minimum: `0`

    Estimated charge after the free allowance, in micro-USD.
  - **`share` (required)**

    **All of:**
    - [AccountAllowance](#scalar-schema-accountallowance)

      Monthly free allowance, shared when pooled is true.
    - `any`

      This project's current share of the free allowance.
  - **`storedGbSeconds` (required)**: `number`

    Managed storage integral in GB·second.

<a id="scalar-example-156"></a>

**Generated example:**

```json
{
  "periodStart": "2026-10-22T12:00:00.000Z",
  "pooled": true,
  "allowance": {
    "egressBytes": 1000000000,
    "storedGbSeconds": 86400
  },
  "drawn": {
    "egressBytes": 1000000000,
    "storedGbSeconds": 86400
  },
  "projects": [
    {
      "id": "",
      "name": null,
      "costMicros": 0,
      "egressBytes": 1,
      "storedGbSeconds": 1,
      "share": {
        "egressBytes": 1000000000,
        "storedGbSeconds": 86400
      },
      "freeMicros": 0,
      "netMicros": 0
    }
  ]
}
```

<a id="scalar-schema-accountallowance"></a>

### AccountAllowance

**Type:** `object`

Monthly free allowance, shared when pooled is true.

- **`egressBytes` (required)**: `number`, minimum: `0`

  Shared delivery allowance in payload bytes (live plus recording delivery); decimal GB = 1000000000 bytes.
- **`storedGbSeconds` (required)**: `number`, minimum: `0`

  Shared managed-storage allowance in GB·second, decimal GB.

<a id="scalar-example-157"></a>

**Generated example:**

```json
{
  "egressBytes": 1000000000,
  "storedGbSeconds": 86400
}
```

<a id="scalar-schema-accountexport"></a>

### AccountExport

**Type:** `object`

- **`account` (required)**: [Account](#scalar-schema-account)
- **`exportedAt` (required)**: `string`, format: `date-time`

  RFC 3339 timestamp with a timezone; responses are normalized to UTC.
- **`items` (required)**: `array`

  This page's items, in the list's documented order.

  **Items:**

  A project visible to this account. Billing identity is included only for owned projects.
  - **`createdAt` (required)**: `string`, format: `date-time`

    RFC 3339 timestamp with a timezone; responses are normalized to UTC.
  - **`domain` (required)**: `string | null`

    Verified email domain admitted to this project, or null.
  - **`domainRole` (required)**: `string`

    Role granted to matching verified email domains.
  - **`id` (required)**: `string`

    Immutable public project identifier used in URLs; never a database row id.
  - **`name` (required)**: `string | null`

    Mutable project name, or null for an unnamed project.
  - **`usage` (required)**: `object`

    Usage for this visible project.
    - **`currentMonth` (required)**

      **All of:**
      - [CurrentUsageQuantities](#scalar-schema-currentusagequantities)
      - `any`

        Current-month usage estimate.
  - **`billingIdentity`**: `object | null`

    Billing identity for owned projects only; null when none is saved.
    - **`address` (required)**: `object`

      The billing address; empty fields are unset.
      - **`city` (required)**: `string`, maxLength: `200`

        City.
      - **`country` (required)**

        ISO 3166-1 alpha-2 country; empty clears the address.

        **Any of:**
        - `string`

          Known values: "". Additional values may appear.
        - `string`, pattern: `^[A-Za-z]{2}$`
      - **`line1` (required)**: `string`, maxLength: `200`

        Street line 1.
      - **`line2` (required)**: `string`, maxLength: `200`

        Street line 2.
      - **`postalCode` (required)**: `string`, maxLength: `30`

        Postal code.
      - **`state` (required)**: `string`, maxLength: `200`

        State or region.
    - **`companyName` (required)**: `string`, maxLength: `200`

      The legal entity billed; empty falls back to the email.
    - **`taxId` (required)**: `object | null`

      One tax registration, or null.
      - **`type` (required)**: `string`

        Stripe tax id type. Known values: "eu_vat", "gb_vat", "ch_vat", "no_vat", "us_ein", "ca_bn", "ca_gst_hst", "au_abn", "nz_gst", "jp_ct", "kr_brn", "sg_uen", "in_gst", "br_cnpj", "mx_rfc", "za_vat", "ae_trn". Additional values may appear.
      - **`value` (required)**: `string`, maxLength: `50`

        The registration number.
  - **`public`**

    Anonymous publish/subscribe patterns as saved; null disables anonymous grants. Kept while an auth endpoint replaces them.

    **Any of:**
    - `object`, no additional properties
      - **`v` (required)**: `number`, const: `1`

        Pattern version 1: `foo` matches only `foo`; use `foo/**` for a subtree.
      - **`publish`**: `array of string`

        Patterns anyone may publish to; omitted grants none.
      - **`subscribe`**: `array of string`

        Patterns anyone may subscribe to; omitted grants none.
    - `object`, no additional properties
      - **`publish`**: `array of string`

        Patterns anyone may publish to; omitted grants none.
      - **`subscribe`**: `array of string`

        Patterns anyone may subscribe to; omitted grants none.
      - **`v`**: `number`, const: `0`

        Legacy prefix version: `foo` matches the subtree `foo/**`.
    - `null`
  - **`role`**: `string`

    The current caller role on this project.
- **`nextCursor` (required)**: `string | null`

  Pass as cursor to continue, or null when there are no more items.

<a id="scalar-example-158"></a>

**Generated example:**

```json
{
  "items": [
    {
      "id": "",
      "name": null,
      "role": "",
      "public": {
        "v": 1,
        "publish": [
          ""
        ],
        "subscribe": [
          ""
        ]
      },
      "domain": null,
      "domainRole": "",
      "createdAt": "2026-10-22T12:00:00.000Z",
      "billingIdentity": {
        "companyName": "",
        "address": {
          "line1": "",
          "line2": "",
          "city": "",
          "state": "",
          "postalCode": "",
          "country": ""
        },
        "taxId": {
          "type": "",
          "value": ""
        }
      },
      "usage": {
        "currentMonth": {
          "periodStart": "2026-10-22T12:00:00.000Z",
          "periodEnd": "2026-10-22T12:00:00.000Z",
          "egressExternalBytes": 1000000,
          "ingressBytes": 1000000,
          "storedGbSeconds": 60,
          "retainedDeltaBytes": 1000000,
          "vodRecordingBytes": 1000000,
          "vodEgressBytes": 1000000,
          "transcode480Seconds": 60,
          "transcode720Seconds": 60,
          "transcode1080Seconds": 60,
          "agentSeconds": 60,
          "webtransportEgressBytes": 1000000,
          "webtransportIngressBytes": 1000000,
          "websocketEgressBytes": 1000000,
          "websocketIngressBytes": 1000000,
          "rtmpEgressBytes": 1000000,
          "rtmpIngressBytes": 1000000,
          "srtEgressBytes": 1000000,
          "srtIngressBytes": 1000000,
          "webrtcEgressBytes": 1000000,
          "webrtcIngressBytes": 1000000,
          "syncEgressBytes": 1000000,
          "syncIngressBytes": 1000000,
          "hlsEgressBytes": 1000000,
          "hlsIngressBytes": 1000000,
          "byobEgressBytes": 1000000,
          "byobIngressBytes": 1000000,
          "egressFrames": 60,
          "ingressFrames": 60,
          "egressGroups": 60,
          "ingressGroups": 60,
          "egressDatagrams": 60,
          "ingressDatagrams": 60,
          "egressSubscriptions": 60,
          "ingressSubscriptions": 60,
          "egressFetches": 60,
          "ingressFetches": 60,
          "egressAnnounced": 60,
          "ingressAnnounced": 60,
          "connectedSeconds": 60,
          "peakConnections": 60,
          "costMicros": 60,
          "partial": false,
          "freeAllowance": {
            "egressBytes": 1,
            "storedGbSeconds": 1
          },
          "freeEgressBytes": 1,
          "freeVodEgressBytes": 1,
          "freeStoredGbSeconds": 1,
          "freeRecordingBytes": 0,
          "freeMicros": 1,
          "netMicros": 1
        }
      }
    }
  ],
  "nextCursor": null,
  "exportedAt": "2026-10-22T12:00:00.000Z",
  "account": {
    "id": "",
    "email": "",
    "name": "",
    "isAdmin": true,
    "ownedProjectCap": 1,
    "domainLoginRequired": true,
    "domainAccessExpiresAt": 1
  }
}
```

<a id="scalar-schema-accountdeletionpreview"></a>

### AccountDeletionPreview

**Type:** `object`

- **`items` (required)**: `array`

  This page's items, in the list's documented order.

  **Items:**

  An owned project that account deletion would destroy, with its current blockers.
  - **`banned` (required)**: `boolean`

    Whether an operator ban blocks deletion.
  - **`id` (required)**: `string`

    Immutable public project identifier used in URLs; never a database row id.
  - **`name` (required)**: `string`

    Display name, falling back to the project id.
  - **`otherMembers` (required)**: `integer`, minimum: `0`

    Other members who would lose access.
  - **`outstanding` (required)**: `boolean`

    Whether unpaid invoices or uninvoiced usage block deletion.
- **`nextCursor` (required)**: `string | null`

  Pass as cursor to continue, or null when there are no more items.
- **`projects` (required)**: `array`

  The same projects as items, kept for clients written before this list paged. Prefer items.

  **Items:**

  An owned project that account deletion would destroy, with its current blockers.
  - **`banned` (required)**: `boolean`

    Whether an operator ban blocks deletion.
  - **`id` (required)**: `string`

    Immutable public project identifier used in URLs; never a database row id.
  - **`name` (required)**: `string`

    Display name, falling back to the project id.
  - **`otherMembers` (required)**: `integer`, minimum: `0`

    Other members who would lose access.
  - **`outstanding` (required)**: `boolean`

    Whether unpaid invoices or uninvoiced usage block deletion.

<a id="scalar-example-159"></a>

**Generated example:**

```json
{
  "items": [
    {
      "id": "",
      "name": "",
      "outstanding": true,
      "banned": true,
      "otherMembers": 0
    }
  ],
  "nextCursor": null,
  "projects": [
    {
      "id": "",
      "name": "",
      "outstanding": true,
      "banned": true,
      "otherMembers": 0
    }
  ]
}
```

<a id="scalar-schema-accountdeletionstep"></a>

### AccountDeletionStep

**Type:** `object`

- **`nextStep` (required)**: `string`

  Repeat deleteAccount with this as step to continue.

<a id="scalar-example-160"></a>

**Generated example:**

```json
{
  "nextStep": ""
}
```

<a id="scalar-schema-passkey"></a>

### Passkey

**Type:** `object`

- **`createdAt` (required)**: `string`, format: `date-time`

  When it was enrolled.
- **`id` (required)**: `string`

  The WebAuthn credential id, base64url; also its removal handle.
- **`lastUsedAt` (required)**: `string | null`, format: `date-time`

  Its last sign-in, or null before any.
- **`stale` (required)**: `boolean`

  True when a later sign-out everywhere, email change, or lockdown ended it: it no longer signs in. Remove it and enrol again.

<a id="scalar-example-161"></a>

**Generated example:**

```json
{
  "id": "",
  "createdAt": "2026-10-22T12:00:00.000Z",
  "lastUsedAt": "2026-10-22T12:00:00.000Z",
  "stale": true
}
```

<a id="scalar-schema-projectmember"></a>

### ProjectMember

**Type:** `object`

- **`accountId` (required)**: `string`

  Member account id.
- **`createdAt` (required)**: `string`, format: `date-time`

  When the account joined the project.
- **`email` (required)**: `string`

  Member email address.
- **`role` (required)**: `string`

  Granted project role. Known values: "owner", "write", "read". Additional values may appear.

<a id="scalar-example-162"></a>

**Generated example:**

```json
{
  "accountId": "",
  "email": "",
  "role": "",
  "createdAt": ""
}
```

<a id="scalar-schema-projectinvitation"></a>

### ProjectInvitation

**Type:** `object`

- **`createdAt` (required)**: `string`, format: `date-time`

  When the invitation was first created.
- **`email` (required)**: `string`

  Invited email address.
- **`role` (required)**: `string`

  Role granted when the invite is claimed. Known values: "owner", "write", "read". Additional values may appear.

<a id="scalar-example-163"></a>

**Generated example:**

```json
{
  "email": "",
  "role": "",
  "createdAt": ""
}
```

<a id="scalar-schema-ownershiptransfer"></a>

### OwnershipTransfer

**Type:** `object`

- **`pending` (required)**: `object | null`

  Outstanding offer, visible only to the owner and recipient; null for other members.
  - **`fromAccountId` (required)**: `string`

    Account offering ownership.
  - **`fromEmail` (required)**: `string`

    Offering owner's email address.
  - **`toAccountId` (required)**: `string`

    Account offered ownership.
  - **`toEmail` (required)**: `string`

    Recipient email address.

<a id="scalar-example-164"></a>

**Generated example:**

```json
{
  "pending": {
    "toAccountId": "",
    "toEmail": "",
    "fromAccountId": "",
    "fromEmail": ""
  }
}
```

<a id="scalar-schema-creditlimitrequestresponse"></a>

### CreditLimitRequestResponse

**Type:** `object | null`

The owner's pending request, or null.

- **`creditLimitMicros` (required)**: `integer`, minimum: `0`

  The requested credit limit in micro-USD.
- **`reason` (required)**: `string`

  Why the owner needs a higher limit.
- **`requestedAt` (required)**: `string`, format: `date-time`

  When the owner requested the increase.

## SDK notification stream

The SDK's hand-written `watchNotifications` async iterator implements the notification WebSocket's `x-websocket` contract. It uses the same SDK client and bearer token as generated operations. See the [agent guide](/agents) for authenticated socket factories and cancellation.

## API versioning

The `/v1` contract grows without requiring existing clients to change.

## Requests

New operations and optional request fields are additive. Existing allowed enum values remain accepted; adding request enum values is additive. A new required request field, a narrower accepted value, a removed operation, or another incompatible change requires `/v2`.

## Responses

New response fields are additive. Ignore fields you do not use. Response enums are extensible, including webhook event types and error codes: the documented values are known values, not a complete set. Accept unknown values and use a safe fallback rather than an exhaustive switch that throws. The `@moq/pro` response types accept future values; request enums still describe the values the server currently accepts.

The [OpenAPI contract](/api.json) marks these sets with `x-extensible-enum`. Removing a response field or changing its type incompatibly requires `/v2`.

## Deprecation

Every announced deprecation appears in the [deprecation ledger](/api-deprecations), with the affected operations, replacement, announcement date, deprecation date and sunset date. Affected responses send `Deprecation`, `Sunset` and a `Link` with `rel="deprecation"` pointing to that entry.

`Deprecation` uses an `@`-prefixed Unix timestamp ([RFC 9745](https://www.rfc-editor.org/rfc/rfc9745.html)); `Sunset` uses an HTTP date ([RFC 8594](https://www.rfc-editor.org/rfc/rfc8594.html)). Deprecation announces migration; it does not change an operation's behavior. An incompatible replacement belongs in `/v2`, and an announced date does not make a breaking `/v1` change additive.
