{"openapi":"3.1.0","servers":[{"url":"https://api.moq.wtf/v1"}],"info":{"title":"MoQ CDN","version":"1","description":"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."},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"moqpro_","description":"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."},"sessionCookie":{"type":"apiKey","in":"cookie","name":"__Host-moq_session","description":"The dashboard's session."}},"schemas":{"AdminProjectPage":{"type":"object","properties":{"items":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"account":{"type":["object","null"],"properties":{"id":{"type":"string","description":"Account id."},"email":{"type":"string","description":"Account sign-in email."},"createdAt":{"type":"string","format":"date-time","description":"When the account signed up. RFC 3339, UTC.","example":"2026-10-22T12:00:00.000Z"}},"required":["id","email","createdAt"],"description":"The account: the project's owner, or the signup on a project-less row."},"id":{"type":"string","description":"Immutable public project id, the {pid} path parameter.","example":"p123example"},"name":{"type":["string","null"],"description":"Project name, or null when unnamed."},"ownerEmail":{"type":["string","null"],"description":"Owner's email, or null for an ownerless project."},"billingKnown":{"type":"boolean","description":"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":{"type":["string","null"],"description":"Where invoices are mailed, or null."},"netTerms":{"type":"string","description":"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.","x-extensible-enum":["","pending","active","cancelling","revoking"]},"creditLimitMicros":{"type":["integer","null"],"description":"Net-terms credit limit, or null when unset. Micro-USD (1,000,000 = USD 1).","example":1000000},"spendCapMicros":{"type":["integer","null"],"description":"Owner's monthly spend cap, or null for none. Micro-USD (1,000,000 = USD 1).","example":1000000},"creditLimitRequestMicros":{"type":["integer","null"],"description":"Owner's pending credit-limit request, or null. Micro-USD (1,000,000 = USD 1).","example":1000000},"hasPaymentMethod":{"type":"boolean","description":"A card is on file."},"invoicedMicros":{"type":"integer","description":"Invoiced on net terms and unpaid. Micro-USD (1,000,000 = USD 1).","example":1000000},"invoicedDaysPastDue":{"type":"integer","minimum":0,"description":"Days the oldest unpaid net-terms invoice is past due."},"uncollectibleMicros":{"type":"integer","description":"Debt Stripe stopped collecting that nobody has written off. Micro-USD (1,000,000 = USD 1).","example":1000000},"stuckHeartbeats":{"type":"integer","minimum":0,"description":"Consecutive billing heartbeats that failed to publish."},"stripeRefused":{"type":"array","items":{"type":"string"},"description":"Stripe ids queued for removal that Stripe refused; remove each by hand."},"createdAt":{"type":"string","format":"date-time","description":"When the project was created. RFC 3339, UTC.","example":"2026-10-22T12:00:00.000Z"},"updatedAt":{"type":["string","null"],"format":"date-time","description":"When the figures were last published, or null if never. RFC 3339, UTC.","example":"2026-10-22T12:00:00.000Z"},"ticking":{"type":"boolean","description":"The billing heartbeat still has work, so updatedAt can go stale."},"staleAt":{"type":["string","null"],"format":"date-time","description":"When the heartbeat counts as stale, or null when quiesced. RFC 3339, UTC.","example":"2026-10-22T12:00:00.000Z"},"usage":{"type":["object","null"],"properties":{"egressExternalBytes":{"type":"integer","minimum":0,"description":"Live delivery to viewers this month. Decimal bytes."},"vodEgressBytes":{"type":"integer","minimum":0,"description":"Recording delivery this month. Decimal bytes."},"storedGbSeconds":{"type":"number","minimum":0,"description":"Managed storage held this month, GB·second, decimal GB."},"vodRecordingBytes":{"type":"integer","minimum":0,"description":"Managed recording written this month. Decimal bytes."},"costMicros":{"type":"integer","description":"Month-to-date metered cost before the free tier. Micro-USD (1,000,000 = USD 1).","example":1000000},"freeMicros":{"type":"integer","description":"Month-to-date cost the free tier covered. Micro-USD (1,000,000 = USD 1).","example":1000000},"netMicros":{"type":"integer","description":"Month-to-date billable net after the free tier. Micro-USD (1,000,000 = USD 1).","example":1000000},"absorbedMicros":{"type":"integer","description":"Metered past the spend cap or credit limit; never billed. Micro-USD (1,000,000 = USD 1).","example":1000000},"freeTierFraction":{"type":"number","minimum":0,"description":"Share of the free allowance used, where 1 is exhausted; saturates rather than overflowing."},"freeAllowance":{"type":"object","properties":{"egressBytes":{"type":"integer","minimum":0,"description":"Delivery allowance this month. Decimal bytes."},"storedGbSeconds":{"type":"number","minimum":0,"description":"Storage allowance this month, GB·second, decimal GB."}},"required":["egressBytes","storedGbSeconds"],"description":"The allowance the figures were measured against; absent from older summaries."}},"required":["egressExternalBytes","vodEgressBytes","storedGbSeconds","vodRecordingBytes","costMicros","freeMicros","netMicros","absorbedMicros","freeTierFraction"],"description":"This month's published figures, or null when the latest summary is older (the project is idle)."}},"required":["account","id","name","ownerEmail","billingKnown","billingRecipient","netTerms","creditLimitMicros","spendCapMicros","creditLimitRequestMicros","hasPaymentMethod","invoicedMicros","invoicedDaysPastDue","uncollectibleMicros","stuckHeartbeats","stripeRefused","createdAt","updatedAt","ticking","staleAt","usage"]},{"type":"object","properties":{"account":{"type":"object","properties":{"id":{"type":"string","description":"Account id."},"email":{"type":"string","description":"Account sign-in email."},"createdAt":{"type":"string","format":"date-time","description":"When the account signed up. RFC 3339, UTC.","example":"2026-10-22T12:00:00.000Z"}},"required":["id","email","createdAt"],"description":"The account: the project's owner, or the signup on a project-less row."},"id":{"type":"null","description":"Null: this account owns no live project."}},"required":["account","id"],"description":"sort=newest only: an account without a live project."}]},"description":"This page's items, in the list's documented order."},"nextCursor":{"type":["string","null"],"description":"Pass as cursor to continue, or null when there are no more items."},"month":{"type":"string","format":"date-time","description":"Start of the billing month the figures cover. RFC 3339, UTC.","example":"2026-10-22T12:00:00.000Z"},"now":{"type":"string","format":"date-time","description":"Server time of the read. RFC 3339, UTC.","example":"2026-10-22T12:00:00.000Z"},"oldestUpdatedAt":{"type":["string","null"],"format":"date-time","description":"Oldest publish among this page's ticking rows, or null when none has one. RFC 3339, UTC.","example":"2026-10-22T12:00:00.000Z"},"totals":{"type":"object","properties":{"listed":{"type":"integer","minimum":0,"description":"Projects in the usage listing."},"billed":{"type":"integer","minimum":0,"description":"Listed projects that owe money."},"attention":{"type":"integer","minimum":0,"description":"Listed projects in an attention state."},"egressExternalBytes":{"type":"integer","minimum":0,"description":"Live delivery this month. Decimal bytes."},"storedGbSeconds":{"type":"number","minimum":0,"description":"Managed storage this month, GB·second, decimal GB."},"costMicros":{"type":"integer","description":"Metered cost this month. Micro-USD (1,000,000 = USD 1).","example":1000000},"netMicros":{"type":"integer","description":"Billable net this month. Micro-USD (1,000,000 = USD 1).","example":1000000},"absorbedMicros":{"type":"integer","description":"Metered past caps and limits this month; never billed. Micro-USD (1,000,000 = USD 1).","example":1000000}},"required":["listed","billed","attention","egressExternalBytes","storedGbSeconds","costMicros","netMicros","absorbedMicros"],"description":"The month's platform totals over the usage listing, whatever the sort or page."}},"required":["items","nextCursor","month","now","oldestUpdatedAt","totals"]},"ApiError":{"type":"object","properties":{"code":{"type":"string","x-extensible-enum":["invalid_request","unauthenticated","forbidden","not_found","conflict","rate_limited","unavailable","internal_error","already_exists","limit_reached","invalid_state","idempotency_in_progress"],"description":"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":{"type":"string","description":"Human-readable explanation; do not match on it."},"errors":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"anyOf":[{"type":"string"},{"type":"number"}]},"description":"Location of the invalid value; empty for the whole input."},"code":{"type":"string","description":"Validation issue code, such as invalid_type or unrecognized_keys."},"message":{"type":"string","description":"Human-readable explanation of the issue."}},"required":["path","code","message"]},"description":"Each invalid input value, on a 400 validation refusal."},"lockedUntil":{"type":"string","format":"date-time","description":"When a refused rename may be retried, RFC 3339; set only for a rename lock."}},"required":["code","message"]},"CreatedApiToken":{"allOf":[{"$ref":"#/components/schemas/ApiToken"},{"type":"object","properties":{"secret":{"type":"string","description":"The token, on creation only. Only its hash is kept; save it before discarding the response."}},"required":["secret"]}]},"Permissions":{"type":"object","properties":{"settings":{"type":"string","description":"Project settings: the project itself, its session limits, and its notices. Known values: \"none\", \"read\", \"write\". Additional values may appear.","x-extensible-enum":["none","read","write"]},"keys":{"type":"string","description":"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.","x-extensible-enum":["none","read","write"]},"media":{"type":"string","description":"Media rules: recording, transcode, agent, sync, and webhooks. Known values: \"none\", \"read\", \"write\". Additional values may appear.","x-extensible-enum":["none","read","write"]},"usage":{"type":"string","description":"Usage & stats: metered usage. Known values: \"none\", \"read\". Additional values may appear.","x-extensible-enum":["none","read"]},"lifecycle":{"type":"string","description":"Project lifecycle: create and delete projects. Known values: \"none\", \"write\". Additional values may appear.","x-extensible-enum":["none","write"]},"members":{"type":"string","description":"Members & invites: members, invitations, and ownership transfer. Known values: \"none\", \"read\", \"write\". Additional values may appear.","x-extensible-enum":["none","read","write"]},"billing":{"type":"string","description":"Billing: payment, spend caps, invoices, credit, and account usage. Known values: \"none\", \"read\", \"write\". Additional values may appear.","x-extensible-enum":["none","read","write"]},"credentials":{"type":"string","description":"Credentials: API tokens, passkeys, and sessions. Known values: \"none\", \"read\", \"write\". Additional values may appear.","x-extensible-enum":["none","read","write"]},"account":{"type":"string","description":"Account: profile, data export, and deletion. Known values: \"none\", \"read\", \"write\". Additional values may appear.","x-extensible-enum":["none","read","write"]},"admin":{"type":"string","description":"Site admin: operator routes, for a site admin. Known values: \"none\", \"read\", \"write\". Additional values may appear.","x-extensible-enum":["none","read","write"]}},"required":["settings","keys","media","usage","lifecycle","members","billing","credentials","account","admin"],"additionalProperties":false,"description":"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."},"ApiToken":{"type":"object","properties":{"id":{"type":"string","description":"Opaque resource identifier."},"name":{"type":"string","description":"Human-readable label."},"prefix":{"type":"string","description":"The secret's first characters, to recognize it; never enough to use it."},"permissions":{"$ref":"#/components/schemas/Permissions"},"projects":{"type":["array","null"],"items":{"type":"string"},"description":"The project ids it is restricted to, or null for every project the account reaches."},"audience":{"type":["string","null"],"description":"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.","x-extensible-enum":["mcp",null]},"clientId":{"type":["string","null"],"description":"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."},"expiresAt":{"type":["string","null"],"format":"date-time","description":"When it stops working, or null for never.","example":"2026-10-22T12:00:00.000Z"},"lastUsedAt":{"type":["string","null"],"format":"date-time","description":"Last authenticated call, to the minute, or null before any.","example":"2026-10-22T12:00:00.000Z"},"createdAt":{"type":"string","format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"}},"required":["id","name","prefix","permissions","projects","audience","clientId","expiresAt","lastUsedAt","createdAt"]},"AdminProject":{"type":"object","properties":{"id":{"type":"string","description":"Immutable public project id, the {pid} path parameter.","example":"p123example"},"name":{"type":["string","null"],"description":"Project name, or null when unnamed."},"ownerEmail":{"type":["string","null"],"description":"Owner's email, or null for an ownerless project."},"billingRecipient":{"type":["string","null"],"description":"Where invoices are mailed, or null."},"ownerProjectCap":{"type":["integer","null"],"description":"How many projects the owner may own, or null without an owner."},"ownerOwnedProjects":{"type":"integer","minimum":0,"description":"Project slots the owner holds; can exceed the cap."},"ownerIsAdmin":{"type":"boolean","description":"The owner is a site admin, who bypasses the cap."},"suspended":{"type":"boolean","description":"Billing suspension flag; gates traffic only while enforceSuspension is true."},"freeLimited":{"type":"boolean","description":"The free-tier limit gates this project's traffic."},"capReached":{"type":"boolean","description":"The owner's spend cap is reached and gates traffic."},"enforceSuspension":{"type":"boolean","description":"Whether a suspended project's traffic is currently cut off."},"banned":{"type":"boolean","description":"Sticky abuse ban set by an operator."},"billingExempt":{"type":"boolean","description":"A platform project that is never billed."},"hasPaymentMethod":{"type":"boolean","description":"A card is on file."},"stripeRefused":{"type":"array","items":{"type":"string"},"description":"Stripe ids queued for removal that Stripe refused; remove each by hand."},"netTerms":{"type":"string","description":"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.","x-extensible-enum":["","pending","active","cancelling","revoking"]},"netTermsDays":{"type":"integer","minimum":0,"description":"The agreed payment window in days."},"netTermsAt":{"type":["string","null"],"format":"date-time","description":"When net terms last changed state, or null if never. RFC 3339, UTC.","example":"2026-10-22T12:00:00.000Z"},"exposure":{"$ref":"#/components/schemas/BillingExposure"},"creditLimitRequest":{"$ref":"#/components/schemas/CreditLimitRequestResponse"},"netTermsActorEmail":{"type":["string","null"],"description":"Who approved net terms, or null."},"createdAt":{"type":"string","format":"date-time","description":"When the project was created. RFC 3339, UTC.","example":"2026-10-22T12:00:00.000Z"},"usage":{"anyOf":[{"allOf":[{"$ref":"#/components/schemas/CurrentUsageQuantities"},{"type":"object","properties":{"egressInternalBytes":{"type":"number","description":"Relay-to-relay bytes inside the fleet, an internal cost input; never billed."},"wireEgressExternalBytes":{"type":"number","default":0,"description":"Wire bytes including transport overhead, an internal cost input; never billed."},"wireIngressBytes":{"type":"number","default":0,"description":"Wire bytes including transport overhead, an internal cost input; never billed."},"moqWireEgressBytes":{"type":"number","default":0,"description":"Wire bytes including transport overhead, an internal cost input; never billed."},"webtransportWireEgressBytes":{"type":"number","default":0,"description":"Wire bytes including transport overhead, an internal cost input; never billed."},"websocketWireEgressBytes":{"type":"number","default":0,"description":"Wire bytes including transport overhead, an internal cost input; never billed."},"rtmpWireEgressBytes":{"type":"number","default":0,"description":"Wire bytes including transport overhead, an internal cost input; never billed."},"srtWireEgressBytes":{"type":"number","default":0,"description":"Wire bytes including transport overhead, an internal cost input; never billed."},"webrtcWireEgressBytes":{"type":"number","default":0,"description":"Wire bytes including transport overhead, an internal cost input; never billed."},"syncWireEgressBytes":{"type":"number","default":0,"description":"Wire bytes including transport overhead, an internal cost input; never billed."},"announceRouteSeconds":{"type":"number","description":"Relay announce work in seconds, an internal cost input; never billed."},"announceCursorSeconds":{"type":"number","description":"Relay announce work in seconds, an internal cost input; never billed."}},"required":["egressInternalBytes"]}]},{"type":"null"}],"description":"Month-to-date usage and cost, or null when the billing read failed."},"creditBalanceMicros":{"type":"integer","description":"Unspent credit. Micro-USD (1,000,000 = USD 1).","example":1000000},"creditLedger":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Ledger entry id."},"amountMicros":{"type":"integer","description":"Signed: grants and restores are positive, draws and claw-backs negative. Micro-USD (1,000,000 = USD 1).","example":1000000},"kind":{"type":"string","description":"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.","x-extensible-enum":["grant","applied","revoked","restored"]},"reason":{"type":"string","description":"The operator's note; empty for system entries."},"actor":{"type":"string","description":"Operator account id, or empty for a system-recorded draw."},"periodStart":{"type":["string","null"],"format":"date-time","description":"The billing month an applied entry drew against, or null. RFC 3339, UTC.","example":"2026-10-22T12:00:00.000Z"},"createdAt":{"type":"string","format":"date-time","description":"When the entry was recorded. RFC 3339, UTC.","example":"2026-10-22T12:00:00.000Z"},"actorEmail":{"type":["string","null"],"description":"The actor's email, or null."}},"required":["id","amountMicros","kind","reason","actor","periodStart","createdAt","actorEmail"]},"description":"Every credit grant, draw, claw-back, and restore."},"invoices":{"type":"array","items":{"type":"object","properties":{"periodStart":{"type":"string","format":"date-time","description":"Start of the billed month; the {periodStart} path parameter. RFC 3339, UTC.","example":"2026-10-22T12:00:00.000Z"},"grossMicros":{"type":"integer","description":"Metered cost before the free tier. Micro-USD (1,000,000 = USD 1).","example":1000000},"freeMicros":{"type":"integer","description":"Cost the free tier covered. Micro-USD (1,000,000 = USD 1).","example":1000000},"netMicros":{"type":"integer","description":"Billable net after the free tier. Micro-USD (1,000,000 = USD 1).","example":1000000},"creditAppliedMicros":{"type":"integer","description":"Credit drawn against this invoice. Micro-USD (1,000,000 = USD 1).","example":1000000},"amountChargedMicros":{"type":"integer","description":"What was charged. Micro-USD (1,000,000 = USD 1).","example":1000000},"principalMicros":{"type":"integer","description":"Exactly what Stripe is asked to collect. Micro-USD (1,000,000 = USD 1).","example":1000000},"amountRemainingMicros":{"type":["integer","null"],"description":"Stripe's unpaid remainder, or null until a reconcile reports it. Micro-USD (1,000,000 = USD 1).","example":1000000},"status":{"type":"string","description":"Invoice ledger state: pending, paid, failed, no_card, held, invoiced, forgiven, invoice_void, invoice_uncollectible, invoice_writeoff_pending, or invoice_written_off."},"heldReleased":{"type":"boolean","description":"An operator released this held invoice for charging."},"stripeInvoiceId":{"type":["string","null"],"description":"The Stripe invoice id, or null when none was issued."},"superseded":{"type":"array","items":{"type":"object","properties":{"stripeInvoiceId":{"type":"string","description":"The voided Stripe invoice."},"invoiceId":{"type":"string","description":"The ledger row that moved on."},"replacedBy":{"type":"string","description":"The Stripe invoice that replaced it."},"reason":{"type":"string","description":"Why it was reissued, such as recipient."},"supersededAt":{"type":"string","format":"date-time","description":"When it was voided. RFC 3339, UTC.","example":"2026-10-22T12:00:00.000Z"},"pending":{"type":"boolean","description":"The replacement is still a draft the customer has not been sent."}},"required":["stripeInvoiceId","invoiceId","replacedBy","reason","supersededAt","pending"]},"description":"Voided Stripe invoices this month was reissued from, oldest first."}},"required":["periodStart","grossMicros","freeMicros","netMicros","creditAppliedMicros","amountChargedMicros","principalMicros","amountRemainingMicros","status","heldReleased","stripeInvoiceId","superseded"]},"description":"The project's invoices."}},"required":["id","name","ownerEmail","billingRecipient","ownerProjectCap","ownerOwnedProjects","ownerIsAdmin","suspended","freeLimited","capReached","enforceSuspension","banned","billingExempt","hasPaymentMethod","stripeRefused","netTerms","netTermsDays","netTermsAt","exposure","creditLimitRequest","netTermsActorEmail","createdAt","usage","creditBalanceMicros","creditLedger","invoices"]},"BillingExposure":{"type":["object","null"],"properties":{"periodStart":{"type":"string","format":"date-time","description":"Start of the month measured.","example":"2026-10-22T12:00:00.000Z"},"monthNetMicros":{"type":"integer","minimum":0,"description":"Month-to-date billable net, clamped to the cap, before credit.","example":1000000},"spendCapMicros":{"type":["integer","null"],"minimum":0,"description":"Owner ceiling, or null for none.","example":1000000},"spendCapRemainingMicros":{"type":["integer","null"],"minimum":0,"description":"Remaining owner ceiling, or null for none.","example":1000000},"spendCapReached":{"type":"boolean","description":"The spend cap is exhausted."},"creditLimitMicros":{"type":["integer","null"],"minimum":0,"description":"Operator ceiling, or null for none.","example":1000000},"creditLimitRemainingMicros":{"type":["integer","null"],"minimum":0,"description":"Remaining operator ceiling, or null for none.","example":1000000},"invoicedPrincipalMicros":{"type":"integer","minimum":0,"description":"Invoiced and unpaid principal.","example":1000000},"accruedPrincipalMicros":{"type":"integer","minimum":0,"description":"Accrued principal not yet invoiced.","example":1000000},"absorbedMicros":{"type":"integer","minimum":0,"description":"Metered past the cap or limit; never billed.","example":1000000},"headroomMicros":{"type":["integer","null"],"minimum":0,"description":"The lower remaining ceiling, or null when neither is set.","example":1000000},"headroomSource":{"type":["string","null"],"description":"Which ceiling supplies the headroom, or null. Known values: \"cap\", \"limit\", null. Additional values may appear.","x-extensible-enum":["cap","limit",null]},"usableHeadroomMicros":{"type":["integer","null"],"minimum":0,"description":"What a new period can admit; zero while held or below the minimum charge.","example":1000000},"identityHold":{"type":"boolean","description":"The approved billing identity no longer matches Stripe."}},"required":["periodStart","monthNetMicros","spendCapMicros","spendCapRemainingMicros","spendCapReached","creditLimitMicros","creditLimitRemainingMicros","invoicedPrincipalMicros","accruedPrincipalMicros","absorbedMicros","headroomMicros","headroomSource","usableHeadroomMicros","identityHold"]},"CreditLimitRequest":{"type":["object","null"],"properties":{"creditLimitMicros":{"type":"integer","minimum":0,"description":"The requested credit limit in micro-USD.","example":1000000},"reason":{"type":"string","description":"Why the owner needs a higher limit."},"requestedAt":{"type":"string","format":"date-time","description":"When the owner requested the increase.","example":"2026-10-22T12:00:00.000Z"}},"required":["creditLimitMicros","reason","requestedAt"],"description":"The owner's pending request, or null."},"CurrentUsageQuantities":{"allOf":[{"$ref":"#/components/schemas/Usage"},{"type":"object","properties":{"freeAllowance":{"type":"object","properties":{"egressBytes":{"type":"number","description":"Delivery allowance in payload bytes, decimal."},"storedGbSeconds":{"type":"number","description":"Managed-storage allowance in GB·second, decimal GB."}},"required":["egressBytes","storedGbSeconds"],"description":"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."},"freeEgressBytes":{"type":"number","description":"Live delivery payload bytes covered by the shared account allowance, capped at that allowance. Do not add to egressExternalBytes."},"freeVodEgressBytes":{"type":"number","description":"Recording-delivery payload bytes covered by the same delivery allowance after live delivery takes its share. Already part of vodEgressBytes."},"freeStoredGbSeconds":{"type":"number","description":"Managed-storage GB·second covered by the storage allowance, capped at that allowance. Already part of storedGbSeconds."},"freeRecordingBytes":{"type":"number","default":0,"description":"Managed recording payload bytes covered by the archive allowance. Already part of vodRecordingBytes; defaults to zero on older responses."},"freeMicros":{"type":"number","description":"Allowance credit in micro-USD (1000000 = USD 1), the difference between frozen gross costMicros and netMicros. Account credit is separate."},"netMicros":{"type":"number","description":"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."}},"required":["freeEgressBytes","freeVodEgressBytes","freeStoredGbSeconds","freeMicros","netMicros"]}]},"Usage":{"type":"object","properties":{"periodStart":{"type":"string","format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"},"periodEnd":{"type":"string","format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"},"egressExternalBytes":{"type":"number","description":"Live payload bytes delivered outside the fleet, a billable quantity. Includes the live protocol subsets; do not add those subsets again.","example":1000000},"ingressBytes":{"type":"number","description":"Publisher payload bytes received. Display only, never billed; absent on older history means zero.","example":1000000},"storedGbSeconds":{"type":"number","description":"Managed storage integral in GB·second (decimal GB); divide by 86400 for GB·days. This is the billable storage quantity.","example":60},"retainedDeltaBytes":{"type":"number","description":"Signed managed-storage byte change over the period. Display only; negative means deletion/expiry. Absence means unknown, not zero.","example":1000000},"vodRecordingBytes":{"type":"number","default":0,"description":"Managed recording bytes written, a billable quantity. Older history may include BYOB writes; the BYOB subset overlaps rather than adding another charge.","example":1000000},"vodEgressBytes":{"type":"number","description":"Recording-delivery bytes, a billable quantity including playback and BYOB uploads. Shares the delivery allowance with live egress; absent on older history means zero.","example":1000000},"transcode480Seconds":{"type":"number","description":"Encoded rendition-seconds at this resolution, a billable quantity. Absent on older history means zero.","example":60},"transcode720Seconds":{"type":"number","description":"Encoded rendition-seconds at this resolution, a billable quantity. Absent on older history means zero.","example":60},"transcode1080Seconds":{"type":"number","description":"Encoded rendition-seconds at this resolution, a billable quantity. Absent on older history means zero.","example":60},"agentSeconds":{"type":"number","description":"Live voice agent-seconds, a billable quantity. Provider API charges are separate; absent on older history means zero.","example":60},"webtransportEgressBytes":{"type":"number","default":0,"description":"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.","example":1000000},"webtransportIngressBytes":{"type":"number","default":0,"description":"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.","example":1000000},"websocketEgressBytes":{"type":"number","default":0,"description":"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.","example":1000000},"websocketIngressBytes":{"type":"number","default":0,"description":"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.","example":1000000},"rtmpEgressBytes":{"type":"number","description":"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.","example":1000000},"rtmpIngressBytes":{"type":"number","description":"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.","example":1000000},"srtEgressBytes":{"type":"number","description":"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.","example":1000000},"srtIngressBytes":{"type":"number","description":"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.","example":1000000},"webrtcEgressBytes":{"type":"number","description":"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.","example":1000000},"webrtcIngressBytes":{"type":"number","description":"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.","example":1000000},"syncEgressBytes":{"type":"number","default":0,"description":"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.","example":1000000},"syncIngressBytes":{"type":"number","default":0,"description":"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.","example":1000000},"hlsEgressBytes":{"type":"number","default":0,"description":"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.","example":1000000},"hlsIngressBytes":{"type":"number","default":0,"description":"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.","example":1000000},"byobEgressBytes":{"type":"number","default":0,"description":"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.","example":1000000},"byobIngressBytes":{"type":"number","default":0,"description":"Structurally zero: customer-bucket uploads have no ingress side. Included for the uniform transport split; never billed separately.","example":1000000},"egressFrames":{"type":"number","default":0,"description":"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.","example":60},"ingressFrames":{"type":"number","default":0,"description":"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.","example":60},"egressGroups":{"type":"number","default":0,"description":"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.","example":60},"ingressGroups":{"type":"number","default":0,"description":"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.","example":60},"egressDatagrams":{"type":"number","default":0,"description":"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.","example":60},"ingressDatagrams":{"type":"number","default":0,"description":"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.","example":60},"egressSubscriptions":{"type":"number","default":0,"description":"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.","example":60},"ingressSubscriptions":{"type":"number","default":0,"description":"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.","example":60},"egressFetches":{"type":"number","default":0,"description":"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.","example":60},"ingressFetches":{"type":"number","default":0,"description":"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.","example":60},"egressAnnounced":{"type":"number","default":0,"description":"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.","example":60},"ingressAnnounced":{"type":"number","default":0,"description":"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.","example":60},"connectedSeconds":{"type":"number","description":"Connection-seconds summed over the period. Display only, never billed. Absence on older history or live/open rows means unavailable, not zero.","example":60},"peakConnections":{"type":"number","description":"Peak simultaneous connection count (maximum over constituent minutes). Display only, never billed. Absence means unavailable, not zero.","example":60},"costMicros":{"type":"number","description":"Frozen gross customer cost in micro-USD (1000000 = USD 1), before allowances and account credit. Never recomputed by this projection.","example":60},"partial":{"type":"boolean","description":"Whether the source period is incomplete. Omitted means a complete historical period.","example":false}},"required":["periodStart","egressExternalBytes","storedGbSeconds","costMicros"]},"Project":{"type":"object","properties":{"id":{"type":"string","description":"Immutable public project identifier used in URLs; never a database row id."},"name":{"type":["string","null"],"description":"Mutable project name, or null for an unnamed project."},"billingExempt":{"type":"boolean","description":"Whether this project is exempt from billing."},"domain":{"type":["string","null"],"description":"Verified email domain admitted to this project, or null."},"domainRole":{"type":"string","description":"Role granted to matching verified email domains."},"createdAt":{"type":"string","format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"},"role":{"type":"string","description":"The current caller role on this project."},"public":{"anyOf":[{"type":"object","properties":{"v":{"type":"number","description":"Pattern version 1: `foo` matches only `foo`; use `foo/**` for a subtree.","const":1},"publish":{"type":"array","items":{"type":"string"},"description":"Patterns anyone may publish to; omitted grants none."},"subscribe":{"type":"array","items":{"type":"string"},"description":"Patterns anyone may subscribe to; omitted grants none."}},"required":["v"],"additionalProperties":false},{"type":"object","properties":{"v":{"type":"number","description":"Legacy prefix version: `foo` matches the subtree `foo/**`.","const":0},"publish":{"type":"array","items":{"type":"string"},"description":"Patterns anyone may publish to; omitted grants none."},"subscribe":{"type":"array","items":{"type":"string"},"description":"Patterns anyone may subscribe to; omitted grants none."}},"additionalProperties":false},{"type":"null"}],"description":"Anonymous publish/subscribe patterns as saved; null disables anonymous grants. Kept while an auth endpoint replaces them."},"enforcedPublic":{"anyOf":[{"type":"object","properties":{"v":{"type":"number","description":"Pattern version 1: `foo` matches only `foo`; use `foo/**` for a subtree.","const":1},"publish":{"type":"array","items":{"type":"string"},"description":"Patterns anyone may publish to; omitted grants none."},"subscribe":{"type":"array","items":{"type":"string"},"description":"Patterns anyone may subscribe to; omitted grants none."}},"required":["v"],"additionalProperties":false},{"type":"object","properties":{"v":{"type":"number","description":"Legacy prefix version: `foo` matches the subtree `foo/**`.","const":0},"publish":{"type":"array","items":{"type":"string"},"description":"Patterns anyone may publish to; omitted grants none."},"subscribe":{"type":"array","items":{"type":"string"},"description":"Patterns anyone may subscribe to; omitted grants none."}},"additionalProperties":false},{"type":"null"}],"description":"The public rules the edge and recording playback enforce: null while an auth endpoint decides."},"redirects":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"The previous name."},"expiresAt":{"type":"string","format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"}},"required":["path","expiresAt"]},"description":"Previous names retained as redirects until their RFC 3339 expiry."},"suspension":{"type":"object","properties":{"paused":{"type":"boolean","description":"Whether the project is refused now: new connections, playback, Sync, and live tokens. The other fields say why."},"banned":{"type":"boolean","description":"Paused by a moq.pro operator. Contact support."},"freeLimited":{"type":"boolean","description":"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."},"capReached":{"type":"boolean","description":"Paused because the project reached its monthly spend cap. Lifts next month, or when the owner raises the cap."},"suspended":{"type":"boolean","description":"Billing suspended the project, for an overdue invoice or the no-card limit. paused says whether that is enforced."},"overFreeTier":{"type":"boolean","description":"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."},"spendCapReached":{"type":"boolean","description":"The project reached its monthly spend cap."}},"required":["paused","banned","freeLimited","capReached","suspended","overFreeTier","spendCapReached"],"description":"Whether and why the project is paused or warned about billing. getProject only."},"ownerEmail":{"type":["string","null"],"description":"The owner's email, whom members ask to lift a billing pause; null for an anonymous viewer. getProject only."}},"required":["id","name","billingExempt","domain","domainRole","createdAt"]},"Key":{"type":"object","properties":{"id":{"type":"string","description":"Opaque resource identifier."},"algorithm":{"type":"string","description":"Signing algorithm used by this key."},"keyType":{"type":"string","description":"Symmetric shared secret or asymmetric signing key. Known values: \"symmetric\", \"asymmetric\". Additional values may appear.","x-extensible-enum":["symmetric","asymmetric"]},"label":{"type":["string","null"],"description":"Optional human-readable label; null means unset."},"disabledAt":{"type":["string","null"],"format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"},"createdAt":{"type":"string","format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"},"scope":{"type":"object","properties":{"root":{"type":"string","description":"Connection root carried by this stored key scope, often the project name or immutable id. Required when scope is present; no implicit default."},"publish":{"type":"array","items":{"type":"string"},"description":"Allowed publish patterns relative to the root."},"subscribe":{"type":"array","items":{"type":"string"},"description":"Allowed subscribe patterns relative to the root."},"put":{"type":"array","items":{"type":"string"},"description":"Legacy publish grants, if present on an older stored key."},"get":{"type":"array","items":{"type":"string"},"description":"Legacy subscribe grants, if present on an older stored key."}},"required":["root"],"description":"Immutable upper bound on token grants from this key."},"privateKey":{"type":"string","description":"One-time private key on creation only; absent from lists and state updates. Save it before discarding the response."}},"required":["id","algorithm","keyType","label","disabledAt","createdAt"]},"PrivateKey":{"type":"object","properties":{"privateKey":{"type":"string","description":"The key's private JWK, serialized. It signs tokens as this key; keep it secret."}},"required":["privateKey"]},"Token":{"type":"object","properties":{"token":{"type":"string","description":"Signed JWT for the relay, as the `jwt` query parameter or a bearer."}},"required":["token"]},"SessionLimits":{"type":"object","properties":{"token":{"type":["integer","null"],"minimum":1,"maximum":1000000,"description":"Live sessions one token may hold on each relay, from 1 to 1000000; null is unlimited."},"remote":{"type":["integer","null"],"minimum":1,"maximum":1000000,"description":"Live sessions one remote address (an IPv4 address or an IPv6 /64) may hold on each relay, from 1 to 1000000; null is unlimited."}},"required":["token","remote"]},"AuthEndpoint":{"type":"object","properties":{"url":{"type":["string","null"],"description":"The https URL asked to admit each tokenless session, or null when the project's public rules decide."}},"required":["url"]},"CreatedAuthEndpoint":{"type":"object","properties":{"url":{"type":"string","description":"The https URL asked to admit each tokenless session."},"secret":{"type":"string","description":"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."}},"required":["url","secret"]},"AuthEndpointTest":{"type":"object","properties":{"request":{"type":"object","properties":{"id":{"type":"string","description":"Random id for this test session."},"event":{"type":"string","description":"The event asked about. Known values: \"connect\". Additional values may appear.","x-extensible-enum":["connect"]},"node":{"type":"string","description":"The relay asking; moq.pro-test for a test."},"transport":{"type":"string","description":"How the session reached the relay."},"path":{"type":"string","description":"The dialed path, relative to the project."},"query":{"type":"string","description":"The raw query string, without the leading ?."}},"required":["id","event","node","transport","path"],"description":"The signed request the endpoint received."},"answer":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","description":"The endpoint admitted the session. Known values: \"grant\". Additional values may appear.","x-extensible-enum":["grant"]},"grant":{"type":"object","additionalProperties":{},"description":"The grant the endpoint answered, as the edge reads it."}},"required":["kind","grant"]},{"type":"object","properties":{"kind":{"type":"string","description":"The endpoint refused the session. Known values: \"refused\". Additional values may appear.","x-extensible-enum":["refused"]},"status":{"type":"integer","description":"The refusing HTTP status: 401 or 403."}},"required":["kind","status"]},{"type":"object","properties":{"kind":{"type":"string","description":"The endpoint answered 2xx without a valid grant, which refuses the session. Known values: \"invalid\". Additional values may appear.","x-extensible-enum":["invalid"]},"error":{"type":"string","description":"What is wrong with the answer."}},"required":["kind","error"]},{"type":"object","properties":{"kind":{"type":"string","description":"The endpoint failed to answer. Known values: \"unavailable\". Additional values may appear.","x-extensible-enum":["unavailable"]},"status":{"type":"integer","description":"The HTTP status, when the endpoint answered."},"error":{"type":"string","description":"What failed."}},"required":["kind","error"]}],"description":"What the endpoint answered."}},"required":["request","answer"]},"LiveToken":{"type":"object","properties":{"project":{"type":"object","properties":{"token":{"type":"string","description":"A relay JWT that subscribes to everything under root."},"root":{"type":"string","description":"The connection path to dial on the relay, which the token is scoped to."}},"required":["token","root"],"description":"Watches the project's broadcasts."},"stats":{"type":"object","properties":{"token":{"type":"string","description":"A relay JWT that subscribes to everything under root."},"root":{"type":"string","description":"The connection path to dial on the relay, which the token is scoped to."}},"required":["token","root"],"description":"Watches the project's live dashboard statistics."},"expiresAt":{"type":"string","format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"}},"required":["project","stats","expiresAt"]},"RecordingRule":{"type":"object","properties":{"id":{"type":"string","description":"Opaque resource identifier."},"label":{"type":["string","null"],"description":"Optional human-readable label; null means unset."},"patterns":{"type":"array","items":{"type":"string"},"description":"Broadcast path patterns matched by this rule; arrays replace the existing list when patched."},"vodEnabled":{"type":"boolean","description":"Whether this rule records matching broadcasts. Defaults to true on create."},"retentionDays":{"type":"number","description":"Managed recording retention in days; one of the declared retention presets. Defaults to 7 on create."},"createdAt":{"type":"string","format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"},"updatedAt":{"type":"string","format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"},"bucketId":{"type":["string","null"],"description":"Opaque customer bucket id, or null for managed storage."},"deleting":{"type":"boolean","description":"Whether asynchronous deletion is draining this resource."},"uploadRefusal":{"type":["string","null"],"description":"Why the bucket refused an upload, which stopped recording until the rule is re-enabled; null when recording."},"bucket":{"$ref":"#/components/schemas/Bucket"},"deletePending":{"type":"integer","minimum":0,"description":"Recordings still waiting for this rule deletion to drain; zero when idle.","example":0}},"required":["id","label","patterns","vodEnabled","retentionDays","createdAt","updatedAt","bucketId","deleting","uploadRefusal","bucket","deletePending"]},"Bucket":{"type":["object","null"],"properties":{"endpoint":{"type":"string","description":"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":{"type":"string","description":"S3 region used for signing."},"bucket":{"type":"string","description":"S3 bucket name. Managed storage is represented by null on the enclosing recording rule bucket field."},"destination":{"type":"string","description":"Object prefix inside the customer bucket."},"delivery":{"type":"boolean","description":"Whether customer-bucket playback/delivery is enabled."}},"required":["endpoint","region","bucket","destination","delivery"],"description":"Customer bucket metadata, or null for managed storage."},"DeletingRule":{"type":"object","properties":{"status":{"type":"string","description":"The rule's storage is draining. Known values: \"deleting\". Additional values may appear.","x-extensible-enum":["deleting"]},"pending":{"type":"number","description":"Recordings still draining before the rule disappears."}},"required":["status","pending"]},"DeletingRecordings":{"type":"object","properties":{"status":{"type":"string","description":"The recordings are draining. Known values: \"deleting\". Additional values may appear.","x-extensible-enum":["deleting"]},"pending":{"type":"number","description":"How many of the rule's recordings are being deleted."}},"required":["status","pending"]},"Session":{"type":"object","properties":{"id":{"type":"string","description":"Opaque resource identifier."},"broadcastPath":{"type":"string","description":"Broadcast path relative to the project."},"status":{"type":"string","description":"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.","x-extensible-enum":["LIVE","ENDED","DELETED"]},"startedAt":{"type":"string","format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"},"endedAt":{"type":["string","null"],"format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"},"playlistUrl":{"type":["string","null"],"description":"Playback playlist URL, or null until media is ready."},"region":{"type":["string","null"],"description":"S3 region used for signing."},"bytes":{"type":"number","description":"Recorded media bytes, excluding transport overhead."},"durationMs":{"type":"number","description":"Recorded media duration in milliseconds."},"deleting":{"type":"boolean","description":"Whether asynchronous deletion is draining this resource."},"cleanupFailed":{"type":"boolean","description":"Whether object deletion needs another attempt."},"exportStatus":{"type":["string","null"],"description":"Bucket export state, or null if not applicable. Known values: \"pending\", \"ready\", \"failed\", null. Additional values may appear.","x-extensible-enum":["pending","ready","failed",null]},"exportFailure":{"type":["string","null"],"description":"Export failure reason, or null."},"regression":{"type":["string","null"],"description":"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."}},"required":["id","broadcastPath","status","startedAt","endedAt","playlistUrl","region","bytes","durationMs","deleting","cleanupFailed","exportStatus","exportFailure","regression"]},"RecordingRuleTotals":{"type":"object","properties":{"ruleId":{"type":"string","description":"The recording rule that holds these recordings."},"recordings":{"type":"integer","minimum":0,"description":"Recordings the rule holds, live ones included."},"live":{"type":"integer","minimum":0,"description":"Of those, recordings still in progress."},"bytes":{"type":"integer","minimum":0,"description":"Their stored size. A live recording's size trails what it has written so far."}},"required":["ruleId","recordings","live","bytes"]},"Playback":{"type":"object","properties":{"url":{"type":"string","description":"The recording's HLS playlist."},"token":{"type":"string","description":"Grants this recording's playlists and media. Send it on every request as `Authorization: Bearer` or the `jwt` query parameter."},"expiresAt":{"type":"string","format":"date-time","description":"When the token expires. Call again for a fresh one.","example":"2026-10-22T12:00:00.000Z"},"live":{"type":"boolean","description":"The recording is still ingesting, so the playlist grows."},"span":{"type":["object","null"],"properties":{"start":{"type":"string","format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"},"end":{"type":"string","format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"},"segments":{"type":"number","description":"The most segments one rendition holds across the span."}},"required":["start","end","segments"],"description":"The retained media span, and the most segments one rendition holds across it."},"initial":{"type":["object","null"],"properties":{"start":{"type":"string","format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"},"end":{"type":"string","format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"}},"description":"The window the playlist starts with; an omitted bound is open."}},"required":["url","token","expiresAt","live","span","initial"]},"DeletingRecording":{"type":"object","properties":{"status":{"type":"string","description":"`stopping` while a live recording's recorder stops; its storage drains after. Known values: \"deleting\", \"stopping\". Additional values may appear.","x-extensible-enum":["deleting","stopping"]}},"required":["status"]},"S3Credentials":{"type":"object","properties":{"endpoint":{"type":"string","description":"S3-compatible endpoint URL for these credentials. This operation requires write authority."},"region":{"type":"string","description":"S3 region used for signing."},"bucket":{"type":"string","description":"S3 bucket name. Managed storage is represented by null on the enclosing recording rule bucket field."},"accessKeyId":{"type":"string","description":"S3 access-key identifier. Requires write authority and must be kept private."},"secretAccessKey":{"type":"string","description":"S3 signing secret. Requires write authority; never place it in a public URL."}},"required":["endpoint","region","bucket","accessKeyId","secretAccessKey"]},"AgentRule":{"type":"object","properties":{"id":{"type":"string","description":"Opaque resource identifier."},"patterns":{"type":"array","items":{"type":"string"},"description":"Broadcast path patterns matched by this rule; arrays replace the existing list when patched."},"enabled":{"type":"boolean","description":"Whether this configuration is enabled."},"config":{"$ref":"#/components/schemas/AgentConfig"},"createdAt":{"type":"string","format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"},"updatedAt":{"type":"string","format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"}},"required":["id","patterns","enabled","config","createdAt","updatedAt"]},"AgentConfig":{"type":"object","properties":{"systemPrompt":{"type":"string","maxLength":8000,"description":"Instruction text, at most 8000 characters; required on create and unchanged when omitted in PATCH."},"greeting":{"type":["string","null"],"description":"Initial spoken greeting, or null for none; null in PATCH clears it."},"turnDetection":{"type":"string","description":"Turn detection strategy. Defaults to silero on create. Known values: \"silero\", \"stt_endpointing\", \"client\". Additional values may appear.","example":"silero","x-extensible-enum":["silero","stt_endpointing","client"]},"maxSessions":{"type":"integer","minimum":1,"maximum":4,"description":"Maximum simultaneous sessions for this rule, from 1 to the project ceiling. Defaults to that ceiling on create.","example":4},"stt":{"type":"object","properties":{"provider":{"type":"string","description":"Provider identifier."},"model":{"type":["string","null"],"description":"Provider model, or null for its default."},"hasKey":{"type":"boolean","description":"Whether an encrypted provider key is configured; never the key itself."},"options":{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"description":"Provider-specific options. Only the selected provider options are supported."}},"required":["provider","model","hasKey","options"],"description":"Speech-to-text provider configuration; apiKey is never returned."},"llm":{"type":"object","properties":{"provider":{"type":"string","description":"Provider identifier."},"model":{"type":["string","null"],"description":"Provider model, or null for its default."},"hasKey":{"type":"boolean","description":"Whether an encrypted provider key is configured; never the key itself."},"options":{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"description":"Provider-specific options. Only the selected provider options are supported."}},"required":["provider","model","hasKey","options"],"description":"Language-model provider configuration; apiKey is never returned."},"tts":{"type":"object","properties":{"provider":{"type":"string","description":"Provider identifier."},"model":{"type":["string","null"],"description":"Provider model, or null for its default."},"hasKey":{"type":"boolean","description":"Whether an encrypted provider key is configured; never the key itself."},"options":{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"description":"Provider-specific options. Only the selected provider options are supported."}},"required":["provider","model","hasKey","options"],"description":"Text-to-speech provider configuration; apiKey is never returned."}},"required":["systemPrompt","greeting","turnDetection","maxSessions","stt","llm","tts"],"description":"Full keyless voice configuration. Provider keys are write-only and stay encrypted in storage."},"SyncTarget":{"type":"object","properties":{"id":{"type":"string","description":"Opaque resource identifier."},"kind":{"type":"string","description":"Immutable sync target kind: cloudflare or custom. Known values: \"cloudflare\", \"custom\". Additional values may appear.","x-extensible-enum":["cloudflare","custom"]},"label":{"type":"string","description":"Trimmed human-readable label; an empty string means unset. Defaults to an empty string on create."},"publishPrefixes":{"type":"array","items":{"type":"string"},"description":"Published path prefixes relative to the project."},"publishConfigured":{"type":"boolean","description":"Whether a publish credential is configured."},"publishOrigin":{"type":["string","null"],"description":"Credential-free publish origin, or null when withheld/unconfigured."},"createdAt":{"type":"string","format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"},"updatedAt":{"type":"string","format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"}},"required":["id","kind","label","publishPrefixes","publishConfigured","publishOrigin","createdAt","updatedAt"]},"Webhook":{"type":"object","properties":{"id":{"type":"string","description":"Opaque resource identifier."},"url":{"type":"string","description":"Webhook HTTPS endpoint URL; may carry a credential and requires write authority."},"events":{"type":"array","items":{"type":"string","x-extensible-enum":["broadcast.started","broadcast.ended","recording.started","recording.ready","recording.failed"],"description":"Known values: \"broadcast.started\", \"broadcast.ended\", \"recording.started\", \"recording.ready\", \"recording.failed\". Additional values may appear."},"description":"Subscribed event names."},"pattern":{"type":"string","description":"Broadcast path pattern for this webhook."},"format":{"type":"string","description":"Delivery body: the signed `json` envelope, or an unsigned `slack` or `discord` message. Known values: \"json\", \"slack\", \"discord\". Additional values may appear.","x-extensible-enum":["json","slack","discord"]},"enabled":{"type":"boolean","description":"Whether this configuration is enabled."},"disabledReason":{"type":["string","null"],"description":"Reason for automatic disable, or null."},"secret":{"type":"string","description":"One-time signing secret on creation only; never returned by reads or updates."},"failures":{"type":"number","description":"Consecutive delivery failures."},"createdAt":{"type":"string","format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"},"updatedAt":{"type":"string","format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"}},"required":["id","url","events","pattern","format","enabled","disabledReason","failures","createdAt","updatedAt"]},"TestedWebhook":{"type":"object","properties":{"eventId":{"type":"string","description":"The queued test event; find its delivery by this id."}},"required":["eventId"]},"Delivery":{"type":"object","properties":{"id":{"type":"string","description":"Opaque resource identifier."},"eventId":{"type":"string","description":"Opaque webhook event identifier."},"type":{"type":"string","description":"Webhook event type."},"state":{"type":"string","description":"Delivery state. Known values: \"pending\", \"delivered\", \"failed\". Additional values may appear.","x-extensible-enum":["pending","delivered","failed"]},"attempts":{"type":"number","description":"Number of delivery attempts."},"status":{"type":["number","null"],"description":"Last webhook response HTTP status code, or null before an HTTP response (including transport failures)."},"error":{"type":["string","null"],"description":"Last delivery error, or null."},"createdAt":{"type":"string","format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"},"updatedAt":{"type":"string","format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"}},"required":["id","eventId","type","state","attempts","status","error","createdAt","updatedAt"]},"Allowance":{"type":["object","null"],"properties":{"egressBytes":{"type":"number","minimum":0,"description":"Shared delivery allowance in payload bytes (live plus recording delivery); decimal GB = 1000000000 bytes.","example":1000000000},"storedGbSeconds":{"type":"number","minimum":0,"description":"Shared managed-storage allowance in GB·second, decimal GB.","example":86400}},"required":["egressBytes","storedGbSeconds"]},"CurrentUsage":{"type":"object","properties":{"project":{"type":"string","description":"The project's immutable id."},"usage":{"$ref":"#/components/schemas/CurrentUsageQuantities"},"pool":{"$ref":"#/components/schemas/Pool"}},"required":["project","usage","pool"]},"Pool":{"type":["object","null"],"properties":{"periodStart":{"type":"string","format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"},"available":{"allOf":[{"$ref":"#/components/schemas/Allowance"},{"description":"Remaining account allowance available to this project now."}]},"projected":{"allOf":[{"$ref":"#/components/schemas/Allowance"},{"description":"Projected account allowance share available to this project at month end."}]},"drawn":{"allOf":[{"$ref":"#/components/schemas/Allowance"},{"description":"Account allowance already consumed by this project this month."}]},"projects":{"type":"number","description":"Number of projects sharing the allowance."}},"required":["periodStart","available","projected","drawn","projects"]},"Invoice":{"type":"object","properties":{"periodStart":{"type":"string","format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"},"grossMicros":{"type":"integer","minimum":0,"description":"Micro-USD (1,000,000 = USD 1); never cents.","example":1000000},"freeMicros":{"type":"integer","minimum":0,"description":"Micro-USD (1,000,000 = USD 1); never cents.","example":1000000},"netMicros":{"type":"integer","minimum":0,"description":"Micro-USD (1,000,000 = USD 1); never cents.","example":1000000},"creditAppliedMicros":{"type":"integer","minimum":0,"description":"Micro-USD (1,000,000 = USD 1); never cents.","example":1000000},"principalMicros":{"type":"integer","minimum":0,"description":"Micro-USD (1,000,000 = USD 1); never cents.","example":1000000},"amountChargedMicros":{"type":"integer","minimum":0,"description":"Micro-USD (1,000,000 = USD 1); never cents.","example":1000000},"status":{"type":"string","description":"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."},"hostedInvoiceUrl":{"type":["string","null"],"description":"Stripe hosted invoice URL, or null when unavailable or withheld by caller authority."},"poolProjects":{"type":["number","null"],"description":"Number of projects sharing this invoice allowance; null for older invoices."},"createdAt":{"type":"string","format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"}},"required":["periodStart","grossMicros","freeMicros","netMicros","creditAppliedMicros","principalMicros","amountChargedMicros","status","hostedInvoiceUrl","poolProjects","createdAt"]},"SpendCap":{"type":"object","properties":{"spendCapMicros":{"type":["number","null"],"description":"Monthly spending cap in micro-USD, or null to remove the cap."},"suspended":{"type":"boolean","description":"Whether the project is suspended."},"overFreeTier":{"type":"boolean","description":"Whether usage exceeds the account free allowance."},"spendCapReached":{"type":"boolean","description":"Whether the spending cap is reached."},"capReached":{"type":"boolean","description":"Whether a capacity cap is reached."},"freeLimited":{"type":"boolean","description":"Whether the free-tier connection limit applies."},"seq":{"type":"number","description":"Monotonically increasing notice snapshot sequence."},"notifications":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Opaque resource identifier."},"category":{"type":"string","description":"What the notice is about. Known values: \"account\", \"usage\", \"spend\". Additional values may appear.","x-extensible-enum":["account","usage","spend"]},"level":{"type":"number","description":"Which notice in its category: a usage percent, a spend milestone in USD, or an account state."},"severity":{"type":"string","description":"blocking for a suspension notice, else advisory. Known values: \"advisory\", \"blocking\". Additional values may appear.","x-extensible-enum":["advisory","blocking"]},"dismissible":{"type":"boolean","description":"Whether the dashboard may hide it."},"capReached":{"type":"boolean","description":"Whether a capacity cap is reached."}},"required":["id","category","level","severity","dismissible"]},"description":"At most one billing notice for the dashboard to show."}},"required":["spendCapMicros","suspended","overFreeTier","spendCapReached"]},"Payment":{"allOf":[{"$ref":"#/components/schemas/SpendCap"},{"type":"object","properties":{"enabled":{"type":"boolean","description":"Stripe billing is configured."},"billingExempt":{"type":"boolean","description":"The project is exempt from billing."},"publishableKey":{"type":"string","description":"Stripe publishable key for embedded card entry; empty when unconfigured."},"exposure":{"allOf":[{"$ref":"#/components/schemas/BillingExposure"},{"description":"The current cap and credit-limit headroom."}]},"billingContact":{"type":"string","description":"Explicit billing email; empty uses the owner's address."},"billingRecipient":{"type":"string","description":"Resolved billing email; empty when none is available."},"forgivenStatements":{"type":"boolean","description":"The monthly statement is sent for months with nothing to pay. Months that charge or owe always send."},"hasPaymentMethod":{"type":"boolean","description":"A payment method is on file."},"collectionMode":{"type":"string","description":"How the project pays. Known values: \"none\", \"exempt\", \"charge_automatically\", \"send_invoice\". Additional values may appear.","x-extensible-enum":["none","exempt","charge_automatically","send_invoice"]},"hasStripeCustomer":{"type":"boolean","description":"A Stripe billing account exists."},"netTerms":{"type":"string","description":"Net-terms approval state; empty means no request. Known values: \"\", \"pending\", \"active\", \"cancelling\", \"revoking\". Additional values may appear.","x-extensible-enum":["","pending","active","cancelling","revoking"]},"netTermsDays":{"type":"number","description":"Approved payment window in days."},"creditLimitRequest":{"allOf":[{"$ref":"#/components/schemas/CreditLimitRequestResponse"},{"description":"Pending request for a higher credit limit, or null."}]},"paymentLabel":{"type":["string","null"],"description":"Payment method label, or null."},"paymentLast4":{"type":["string","null"],"description":"Last four digits of the payment method, or null."}},"required":["enabled","billingExempt","publishableKey","exposure","billingContact","billingRecipient","forgivenStatements","hasPaymentMethod","collectionMode","hasStripeCustomer","netTerms","netTermsDays","creditLimitRequest","paymentLabel","paymentLast4"]}]},"BillingStatus":{"type":"object","properties":{"suspended":{"type":"boolean","description":"Whether the project is suspended."},"overFreeTier":{"type":"boolean","description":"Whether usage exceeds the account free allowance."},"spendCapReached":{"type":"boolean","description":"Whether the spending cap is reached."},"capReached":{"type":"boolean","description":"Whether a capacity cap is reached."},"freeLimited":{"type":"boolean","description":"Whether the free-tier connection limit applies."},"seq":{"type":"number","description":"Monotonically increasing notice snapshot sequence."},"notifications":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Opaque resource identifier."},"category":{"type":"string","description":"What the notice is about. Known values: \"account\", \"usage\", \"spend\". Additional values may appear.","x-extensible-enum":["account","usage","spend"]},"level":{"type":"number","description":"Which notice in its category: a usage percent, a spend milestone in USD, or an account state."},"severity":{"type":"string","description":"blocking for a suspension notice, else advisory. Known values: \"advisory\", \"blocking\". Additional values may appear.","x-extensible-enum":["advisory","blocking"]},"dismissible":{"type":"boolean","description":"Whether the dashboard may hide it."},"capReached":{"type":"boolean","description":"Whether a capacity cap is reached."}},"required":["id","category","level","severity","dismissible"]},"description":"At most one billing notice for the dashboard to show."}},"required":["suspended","overFreeTier","spendCapReached"]},"BillingIdentity":{"type":["object","null"],"properties":{"companyName":{"type":"string","maxLength":200,"description":"The legal entity billed; empty falls back to the email."},"address":{"type":"object","properties":{"line1":{"type":"string","maxLength":200,"description":"Street line 1."},"line2":{"type":"string","maxLength":200,"description":"Street line 2."},"city":{"type":"string","maxLength":200,"description":"City."},"state":{"type":"string","maxLength":200,"description":"State or region."},"postalCode":{"type":"string","maxLength":30,"description":"Postal code."},"country":{"anyOf":[{"type":"string","x-extensible-enum":[""],"description":"Known values: \"\". Additional values may appear."},{"type":"string","pattern":"^[A-Za-z]{2}$"}],"description":"ISO 3166-1 alpha-2 country; empty clears the address."}},"required":["line1","line2","city","state","postalCode","country"],"additionalProperties":false,"description":"Billing address; empty fields clear them."},"taxId":{"type":["object","null"],"properties":{"type":{"type":"string","description":"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.","x-extensible-enum":["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"]},"value":{"type":"string","maxLength":50,"description":"The registration number."}},"required":["type","value"],"additionalProperties":false,"description":"One tax registration, or null to clear it."}},"required":["companyName","address","taxId"],"additionalProperties":false,"description":"Stripe legal identity, or null before a billing account exists."},"NotificationFrame":{"type":"object","properties":{"seq":{"type":"number","description":"Monotonically increasing notice snapshot sequence."},"notifications":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Opaque resource identifier."},"category":{"type":"string","description":"What the notice is about. Known values: \"account\", \"usage\", \"spend\". Additional values may appear.","x-extensible-enum":["account","usage","spend"]},"level":{"type":"number","description":"Which notice in its category: a usage percent, a spend milestone in USD, or an account state."},"severity":{"type":"string","description":"blocking for a suspension notice, else advisory. Known values: \"advisory\", \"blocking\". Additional values may appear.","x-extensible-enum":["advisory","blocking"]},"dismissible":{"type":"boolean","description":"Whether the dashboard may hide it."},"capReached":{"type":"boolean","description":"Whether a capacity cap is reached."}},"required":["id","category","level","severity","dismissible"]},"description":"At most one billing notice for the dashboard to show."}},"required":["seq","notifications"]},"PermissionsInput":{"type":"object","properties":{"settings":{"type":"string","enum":["none","read","write"],"description":"Project settings: the project itself, its session limits, and its notices."},"keys":{"type":"string","enum":["none","read","write"],"description":"Keys & secrets: signing keys and the auth endpoint; write also reveals every secret and mints relay tokens."},"media":{"type":"string","enum":["none","read","write"],"description":"Media rules: recording, transcode, agent, sync, and webhooks."},"usage":{"type":"string","enum":["none","read"],"description":"Usage & stats: metered usage."},"lifecycle":{"type":"string","enum":["none","write"],"description":"Project lifecycle: create and delete projects."},"members":{"type":"string","enum":["none","read","write"],"description":"Members & invites: members, invitations, and ownership transfer."},"billing":{"type":"string","enum":["none","read","write"],"description":"Billing: payment, spend caps, invoices, credit, and account usage."},"credentials":{"type":"string","enum":["none","read","write"],"description":"Credentials: API tokens, passkeys, and sessions."},"account":{"type":"string","enum":["none","read","write"],"description":"Account: profile, data export, and deletion."},"admin":{"type":"string","enum":["none","read","write"],"description":"Site admin: operator routes, for a site admin."}},"additionalProperties":false,"description":"A level per permission family; an omitted family is none. See Permissions."},"Account":{"type":"object","properties":{"id":{"type":"string","description":"The authenticated account's identifier."},"email":{"type":"string","description":"Verified sign-in email. Changing it requires human sign-in verification."},"name":{"type":"string","description":"Account display name."},"isAdmin":{"type":"boolean","description":"Whether the account is a site administrator."},"ownedProjectCap":{"type":"number","description":"Maximum number of projects this account may own."},"domainLoginRequired":{"type":"boolean","description":"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."},"domainAccessExpiresAt":{"type":"number","description":"When the latest Google Workspace sign-in stops granting domain access, in Unix milliseconds; absent without one. Returned by getAccount."}},"required":["id","email","name","isAdmin","ownedProjectCap"]},"AccountUsage":{"type":"object","properties":{"periodStart":{"type":"string","format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"},"pooled":{"type":"boolean","description":"Whether the projects share one account allowance this month."},"allowance":{"$ref":"#/components/schemas/AccountAllowance"},"drawn":{"allOf":[{"$ref":"#/components/schemas/AccountAllowance"},{"description":"Allowance consumed, including usage from departed projects."}]},"projects":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Immutable public project identifier used in URLs; never a database row id."},"name":{"type":["string","null"],"description":"Mutable project name, or null for an unnamed project."},"costMicros":{"type":"integer","minimum":0,"description":"Gross cost before the free allowance, in micro-USD."},"egressBytes":{"type":"number","description":"Live and recording delivery payload bytes."},"storedGbSeconds":{"type":"number","description":"Managed storage integral in GB·second."},"share":{"allOf":[{"$ref":"#/components/schemas/AccountAllowance"},{"description":"This project's current share of the free allowance."}]},"freeMicros":{"type":"integer","minimum":0,"description":"Free allowance credit, in micro-USD."},"netMicros":{"type":"integer","minimum":0,"description":"Estimated charge after the free allowance, in micro-USD."}},"required":["id","name","costMicros","egressBytes","storedGbSeconds","share","freeMicros","netMicros"]},"description":"Owned billable projects, oldest first. Shares are estimates until settlement."}},"required":["periodStart","pooled","allowance","drawn","projects"]},"AccountAllowance":{"type":"object","properties":{"egressBytes":{"type":"number","minimum":0,"description":"Shared delivery allowance in payload bytes (live plus recording delivery); decimal GB = 1000000000 bytes.","example":1000000000},"storedGbSeconds":{"type":"number","minimum":0,"description":"Shared managed-storage allowance in GB·second, decimal GB.","example":86400}},"required":["egressBytes","storedGbSeconds"],"description":"Monthly free allowance, shared when pooled is true."},"AccountExport":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Immutable public project identifier used in URLs; never a database row id."},"name":{"type":["string","null"],"description":"Mutable project name, or null for an unnamed project."},"role":{"type":"string","description":"The current caller role on this project."},"public":{"anyOf":[{"type":"object","properties":{"v":{"type":"number","description":"Pattern version 1: `foo` matches only `foo`; use `foo/**` for a subtree.","const":1},"publish":{"type":"array","items":{"type":"string"},"description":"Patterns anyone may publish to; omitted grants none."},"subscribe":{"type":"array","items":{"type":"string"},"description":"Patterns anyone may subscribe to; omitted grants none."}},"required":["v"],"additionalProperties":false},{"type":"object","properties":{"v":{"type":"number","description":"Legacy prefix version: `foo` matches the subtree `foo/**`.","const":0},"publish":{"type":"array","items":{"type":"string"},"description":"Patterns anyone may publish to; omitted grants none."},"subscribe":{"type":"array","items":{"type":"string"},"description":"Patterns anyone may subscribe to; omitted grants none."}},"additionalProperties":false},{"type":"null"}],"description":"Anonymous publish/subscribe patterns as saved; null disables anonymous grants. Kept while an auth endpoint replaces them."},"domain":{"type":["string","null"],"description":"Verified email domain admitted to this project, or null."},"domainRole":{"type":"string","description":"Role granted to matching verified email domains."},"createdAt":{"type":"string","format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"},"billingIdentity":{"type":["object","null"],"properties":{"companyName":{"type":"string","maxLength":200,"description":"The legal entity billed; empty falls back to the email."},"address":{"type":"object","properties":{"line1":{"type":"string","maxLength":200,"description":"Street line 1."},"line2":{"type":"string","maxLength":200,"description":"Street line 2."},"city":{"type":"string","maxLength":200,"description":"City."},"state":{"type":"string","maxLength":200,"description":"State or region."},"postalCode":{"type":"string","maxLength":30,"description":"Postal code."},"country":{"anyOf":[{"type":"string","x-extensible-enum":[""],"description":"Known values: \"\". Additional values may appear."},{"type":"string","pattern":"^[A-Za-z]{2}$"}],"description":"ISO 3166-1 alpha-2 country; empty clears the address."}},"required":["line1","line2","city","state","postalCode","country"],"description":"The billing address; empty fields are unset."},"taxId":{"type":["object","null"],"properties":{"type":{"type":"string","description":"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.","x-extensible-enum":["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"]},"value":{"type":"string","maxLength":50,"description":"The registration number."}},"required":["type","value"],"description":"One tax registration, or null."}},"required":["companyName","address","taxId"],"description":"Billing identity for owned projects only; null when none is saved."},"usage":{"type":"object","properties":{"currentMonth":{"allOf":[{"$ref":"#/components/schemas/CurrentUsageQuantities"},{"description":"Current-month usage estimate."}]}},"required":["currentMonth"],"description":"Usage for this visible project."}},"required":["id","name","domain","domainRole","createdAt","usage"],"description":"A project visible to this account. Billing identity is included only for owned projects."},"description":"This page's items, in the list's documented order."},"nextCursor":{"type":["string","null"],"description":"Pass as cursor to continue, or null when there are no more items."},"exportedAt":{"type":"string","format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"},"account":{"$ref":"#/components/schemas/Account"}},"required":["items","nextCursor","exportedAt","account"]},"AccountDeletionPreview":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Immutable public project identifier used in URLs; never a database row id."},"name":{"type":"string","description":"Display name, falling back to the project id."},"outstanding":{"type":"boolean","description":"Whether unpaid invoices or uninvoiced usage block deletion."},"banned":{"type":"boolean","description":"Whether an operator ban blocks deletion."},"otherMembers":{"type":"integer","minimum":0,"description":"Other members who would lose access."}},"required":["id","name","outstanding","banned","otherMembers"],"description":"An owned project that account deletion would destroy, with its current blockers."},"description":"This page's items, in the list's documented order."},"nextCursor":{"type":["string","null"],"description":"Pass as cursor to continue, or null when there are no more items."},"projects":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Immutable public project identifier used in URLs; never a database row id."},"name":{"type":"string","description":"Display name, falling back to the project id."},"outstanding":{"type":"boolean","description":"Whether unpaid invoices or uninvoiced usage block deletion."},"banned":{"type":"boolean","description":"Whether an operator ban blocks deletion."},"otherMembers":{"type":"integer","minimum":0,"description":"Other members who would lose access."}},"required":["id","name","outstanding","banned","otherMembers"],"description":"An owned project that account deletion would destroy, with its current blockers."},"description":"The same projects as items, kept for clients written before this list paged. Prefer items."}},"required":["items","nextCursor","projects"]},"AccountDeletionStep":{"type":"object","properties":{"nextStep":{"type":"string","description":"Repeat deleteAccount with this as step to continue."}},"required":["nextStep"]},"Passkey":{"type":"object","properties":{"id":{"type":"string","description":"The WebAuthn credential id, base64url; also its removal handle."},"createdAt":{"type":"string","format":"date-time","description":"When it was enrolled.","example":"2026-10-22T12:00:00.000Z"},"lastUsedAt":{"type":["string","null"],"format":"date-time","description":"Its last sign-in, or null before any.","example":"2026-10-22T12:00:00.000Z"},"stale":{"type":"boolean","description":"True when a later sign-out everywhere, email change, or lockdown ended it: it no longer signs in. Remove it and enrol again."}},"required":["id","createdAt","lastUsedAt","stale"]},"ProjectMember":{"type":"object","properties":{"accountId":{"type":"string","description":"Member account id."},"email":{"type":"string","description":"Member email address."},"role":{"type":"string","description":"Granted project role. Known values: \"owner\", \"write\", \"read\". Additional values may appear.","x-extensible-enum":["owner","write","read"]},"createdAt":{"type":"string","format":"date-time","description":"When the account joined the project."}},"required":["accountId","email","role","createdAt"]},"ProjectInvitation":{"type":"object","properties":{"email":{"type":"string","description":"Invited email address."},"role":{"type":"string","description":"Role granted when the invite is claimed. Known values: \"owner\", \"write\", \"read\". Additional values may appear.","x-extensible-enum":["owner","write","read"]},"createdAt":{"type":"string","format":"date-time","description":"When the invitation was first created."}},"required":["email","role","createdAt"]},"OwnershipTransfer":{"type":"object","properties":{"pending":{"type":["object","null"],"properties":{"toAccountId":{"type":"string","description":"Account offered ownership."},"toEmail":{"type":"string","description":"Recipient email address."},"fromAccountId":{"type":"string","description":"Account offering ownership."},"fromEmail":{"type":"string","description":"Offering owner's email address."}},"required":["toAccountId","toEmail","fromAccountId","fromEmail"],"description":"Outstanding offer, visible only to the owner and recipient; null for other members."}},"required":["pending"]},"CreditLimitRequestResponse":{"type":["object","null"],"properties":{"creditLimitMicros":{"type":"integer","minimum":0,"description":"The requested credit limit in micro-USD.","example":1000000},"reason":{"type":"string","description":"Why the owner needs a higher limit."},"requestedAt":{"type":"string","format":"date-time","description":"When the owner requested the increase.","example":"2026-10-22T12:00:00.000Z"}},"required":["creditLimitMicros","reason","requestedAt"],"description":"The owner's pending request, or null."}},"parameters":{}},"paths":{"/admin/projects":{"get":{"operationId":"listAdminProjects","x-permissions":{"admin":"read"},"description":"Token permissions: admin:read.","tags":["admin"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":200,"description":"Maximum items per page, from 1 to 200."},"required":false,"description":"Maximum items per page, from 1 to 200.","name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page."},"required":false,"description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.","name":"cursor","in":"query"},{"schema":{"type":"string","enum":["usage","newest"],"default":"usage","description":"usage (default): month-to-date usage, busiest first. newest: every account by signup, newest first, with its projects or a project-less row."},"required":false,"description":"usage (default): month-to-date usage, busiest first. newest: every account by signup, newest first, with its projects or a project-less row.","name":"sort","in":"query"},{"schema":{"type":"string","format":"date-time","description":"Inclusive RFC 3339 signup bound, sort=newest only, compared at second precision. Pass the newest account.createdAt seen and deduplicate the overlap by account id.","example":"2026-10-22T12:00:00.000Z"},"required":false,"description":"Inclusive RFC 3339 signup bound, sort=newest only, compared at second precision. Pass the newest account.createdAt seen and deduplicate the overlap by account id.","name":"since","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminProjectPage"}}}},"4XX":{"description":"Request refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"Request failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/admin/projects/deleting":{"get":{"operationId":"listDeletingAdminProjects","description":"Token permissions: admin:read. Fenced projects the hourly sweep is still tearing down, ordered by fence time, then internal id.","x-permissions":{"admin":"read"},"tags":["admin"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":200,"description":"Maximum items per page, from 1 to 200."},"required":false,"description":"Maximum items per page, from 1 to 200.","name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page."},"required":false,"description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.","name":"cursor","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Immutable public project id, the {pid} path parameter.","example":"p123example"},"name":{"type":["string","null"],"description":"Project name, or null when unnamed."},"deletingAt":{"type":"string","format":"date-time","description":"When the project was fenced for deletion. RFC 3339, UTC.","example":"2026-10-22T12:00:00.000Z"}},"required":["id","name","deletingAt"]},"description":"This page's items, in the list's documented order."},"nextCursor":{"type":["string","null"],"description":"Pass as cursor to continue, or null when there are no more items."},"now":{"type":"string","format":"date-time","description":"Server time of the read. RFC 3339, UTC.","example":"2026-10-22T12:00:00.000Z"},"stuckSeconds":{"type":"integer","minimum":0,"description":"Age in seconds past which a teardown counts as stuck."}},"required":["items","nextCursor","now","stuckSeconds"]}}}},"4XX":{"description":"Request refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"Request failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/admin/emails":{"get":{"operationId":"previewAdminEmails","description":"Token permissions: admin:read. Every transactional email template rendered with sample data.","x-permissions":{"admin":"read"},"tags":["admin"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"templates":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Template id."},"label":{"type":"string","description":"Short template name."},"description":{"type":"string","description":"When the email is sent."},"subject":{"type":"string","description":"Rendered subject line."},"html":{"type":"string","description":"Rendered HTML body."},"text":{"type":"string","description":"Rendered plain-text body."}},"required":["id","label","description","subject","html","text"]},"description":"One rendering per template."}},"required":["templates"]}}}},"4XX":{"description":"Request refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"Request failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/admin/alert-test":{"post":{"operationId":"testAdminAlert","description":"Token permissions: admin:write. Emits a real alert line through this deployment's alert path.","x-permissions":{"admin":"write"},"tags":["admin"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"sent":{"type":"boolean","description":"Always true once the alert line is emitted."}},"required":["sent"]}}}},"4XX":{"description":"Request refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"Request failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/admin/seed-platform":{"post":{"operationId":"seedAdminPlatform","description":"Token permissions: admin:write. Idempotently creates the platform projects. A smoke project under an unpinned id is refused.","x-permissions":{"admin":"write"},"tags":["admin"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"demo":{"type":"object","properties":{"slug":{"type":"string","description":"The platform project's reserved name."},"id":{"type":"string","description":"Immutable public project id, the {pid} path parameter.","example":"p123example"}},"required":["slug","id"],"description":"The public demo project."},"try":{"type":"object","properties":{"slug":{"type":"string","description":"The platform project's reserved name."},"id":{"type":"string","description":"Immutable public project id, the {pid} path parameter.","example":"p123example"}},"required":["slug","id"],"description":"The try-it project."},"anon":{"type":"object","properties":{"slug":{"type":"string","description":"The platform project's reserved name."},"id":{"type":"string","description":"Immutable public project id, the {pid} path parameter.","example":"p123example"}},"required":["slug","id"],"description":"The anonymous publishing project."},"wb":{"type":"object","properties":{"slug":{"type":"string","description":"The platform project's reserved name."},"id":{"type":"string","description":"Immutable public project id, the {pid} path parameter.","example":"p123example"}},"required":["slug","id"],"description":"The whiteboard project."},"smoke":{"type":"object","properties":{"slug":{"type":"string","description":"The platform project's reserved name."},"id":{"type":"string","description":"Immutable public project id, the {pid} path parameter.","example":"p123example"}},"required":["slug","id"],"description":"The private smoke-test project."},"voice":{"type":["object","null"],"properties":{"slug":{"type":"string","description":"The platform project's reserved name."},"id":{"type":"string","description":"Immutable public project id, the {pid} path parameter.","example":"p123example"}},"required":["slug","id"],"description":"The voice demo project, or null when unconfigured."}},"required":["demo","try","anon","wb","smoke","voice"]}}}},"4XX":{"description":"Request refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"Request failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/admin/smoke/key":{"get":{"operationId":"getAdminSmokeKey","x-permissions":{"admin":"write","keys":"write"},"description":"Token permissions: admin:write, keys:write. Bootstrap ADMIN_KEY only; account tokens and cookie sessions are refused.","tags":["admin"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"privateKey":{"type":"string","description":"The smoke project's signing JWK, serialized."}},"required":["privateKey"]}}}},"4XX":{"description":"Request refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"Request failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/admin/smoke/token":{"post":{"operationId":"createAdminSmokeToken","description":"Token permissions: admin:write. Bootstrap ADMIN_KEY only; account tokens and cookie sessions are refused. Mints a token owned by the pinned smoke project's site-admin owner, restricted to that project with settings, keys, and media at write and usage at read, expiring in 120 minutes. Revoke it at teardown with revokeCurrentToken.","x-permissions":{"admin":"write"},"tags":["admin"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"runId":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]{0,63}$","description":"Unique smoke run label, stored as the token name."}},"required":["runId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedApiToken"}}}},"4XX":{"description":"Request refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"Request failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/admin/tokens":{"post":{"operationId":"createAdminReadToken","description":"Token permissions: admin:write. Bootstrap ADMIN_KEY only: mints a token for the first admin with every project family and admin at read, expiring in 60 minutes. Revoke it early with revokeCurrentToken.","x-permissions":{"admin":"write"},"tags":["admin"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Human-readable label.","example":"admin-proof"}},"required":["name"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedApiToken"}}}},"4XX":{"description":"Request refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"Request failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/admin/gate-owners/reconcile":{"post":{"operationId":"reconcileAdminGateOwners","description":"Token permissions: admin:write. Recomputes one batch of projects' billing gates and counts the flags that changed.","x-permissions":{"admin":"write"},"tags":["admin"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"after":{"type":"string","format":"uuid","description":"The previous batch's nextCursor; omit to start."},"limit":{"type":"integer","minimum":1,"maximum":25,"default":25,"description":"Projects per batch."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"processed":{"type":"integer","minimum":0,"description":"Projects checked in this batch."},"mismatches":{"type":"object","properties":{"suspended":{"type":"integer","minimum":0,"description":"Suspension flags repaired."},"freeLimited":{"type":"integer","minimum":0,"description":"Free-limit flags repaired."},"capReached":{"type":"integer","minimum":0,"description":"Cap flags repaired."}},"required":["suspended","freeLimited","capReached"],"description":"Billing gate flags this batch changed, by flag."},"nextCursor":{"type":["string","null"],"description":"Pass as after to continue, or null when done."}},"required":["processed","mismatches","nextCursor"]}}}},"4XX":{"description":"Request refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"Request failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/admin/billing-config/reconcile":{"post":{"operationId":"reconcileAdminBillingConfig","description":"Token permissions: admin:write. Seeds one batch of projects' billing owners from their stored configuration, and re-seeds and lists each owner that disagrees with it.","x-permissions":{"admin":"write"},"tags":["admin"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"after":{"type":"string","format":"uuid","description":"The previous batch's nextCursor; omit to start."},"limit":{"type":"integer","minimum":1,"maximum":25,"default":25,"description":"Projects per batch."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"processed":{"type":"integer","minimum":0,"description":"Projects checked in this batch."},"seeded":{"type":"integer","minimum":0,"description":"Owners that held no configuration and were seeded."},"mismatched":{"type":"array","items":{"type":"object","properties":{"projectId":{"type":"string","description":"The project whose owner disagrees."},"fields":{"type":"array","items":{"type":"string"},"description":"Each billing field that differed; the stored value replaced the owner's."}},"required":["projectId","fields"]},"description":"Owners that disagreed with the stored copy, each now re-seeded from it."},"nextCursor":{"type":["string","null"],"description":"Pass as after to continue, or null when done."}},"required":["processed","seeded","mismatched","nextCursor"]}}}},"4XX":{"description":"Request refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"Request failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/admin/secretbox/rekey":{"post":{"operationId":"rekeyAdminSecrets","description":"Token permissions: admin:write. Re-seals one batch of projects' stored secrets under the current secretbox key.","x-permissions":{"admin":"write"},"tags":["admin"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"after":{"type":"string","format":"uuid","description":"The previous batch's nextCursor; omit to start."},"limit":{"type":"integer","minimum":1,"maximum":25,"default":25,"description":"Projects per batch."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"processed":{"type":"integer","minimum":0,"description":"Projects visited in this batch."},"rewritten":{"type":"integer","minimum":0,"description":"Stored secrets re-sealed in this batch."},"nextCursor":{"type":["string","null"],"description":"Pass as after to continue, or null when done."}},"required":["processed","rewritten","nextCursor"]}}}},"4XX":{"description":"Request refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"Request failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/admin/usage-summary/republish":{"post":{"operationId":"republishAdminUsageSummary","description":"Token permissions: admin:write. Republishes one batch of live projects' billing fields into the admin listing, so none reads as unknown.","x-permissions":{"admin":"write"},"tags":["admin"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"after":{"type":"string","format":"uuid","description":"The previous batch's nextCursor; omit to start."},"limit":{"type":"integer","minimum":1,"maximum":100,"default":100,"description":"Projects per batch."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"processed":{"type":"integer","minimum":0,"description":"Projects republished in this batch."},"nextCursor":{"type":["string","null"],"description":"Pass as after to continue, or null when done."}},"required":["processed","nextCursor"]}}}},"4XX":{"description":"Request refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"Request failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/admin/projects/{pid}":{"get":{"operationId":"getAdminProject","x-permissions":{"admin":"read"},"description":"Token permissions: admin:read.","tags":["admin"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminProject"}}}},"4XX":{"description":"Request refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"Request failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/admin/projects/{pid}/usage":{"get":{"operationId":"getAdminProjectUsage","description":"Token permissions: admin:read. Reads one closed UTC calendar month from the usage owner, including internal cost inputs. Missing monthly history is null; missing days are omitted. These are read-time quantities, not a frozen settlement: late reports can change them, including between the separate monthly and daily reads. A retained row does not prove continuous relay reporting or connection-meter coverage. No charges or allowances are recomputed.","x-permissions":{"admin":"read"},"tags":["admin"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","pattern":"^[0-9]{4}-(0[1-9]|1[0-2])$","description":"Closed UTC month, YYYY-MM.","example":"2026-10"},"required":true,"description":"Closed UTC month, YYYY-MM.","name":"month","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"project":{"type":"string","description":"Immutable public project id, the {pid} path parameter.","example":"p123example"},"periodStart":{"type":"string","format":"date-time","description":"Inclusive month start. RFC 3339, UTC.","example":"2026-10-22T12:00:00.000Z"},"periodEnd":{"type":"string","format":"date-time","description":"Exclusive month end. RFC 3339, UTC.","example":"2026-10-22T12:00:00.000Z"},"usage":{"anyOf":[{"allOf":[{"$ref":"#/components/schemas/Usage"},{"type":"object","properties":{"egressInternalBytes":{"type":"number","description":"Relay-to-relay bytes inside the fleet, an internal cost input; never billed."},"wireEgressExternalBytes":{"type":"number","default":0,"description":"Wire bytes including transport overhead, an internal cost input; never billed."},"wireIngressBytes":{"type":"number","default":0,"description":"Wire bytes including transport overhead, an internal cost input; never billed."},"moqWireEgressBytes":{"type":"number","default":0,"description":"Wire bytes including transport overhead, an internal cost input; never billed."},"webtransportWireEgressBytes":{"type":"number","default":0,"description":"Wire bytes including transport overhead, an internal cost input; never billed."},"websocketWireEgressBytes":{"type":"number","default":0,"description":"Wire bytes including transport overhead, an internal cost input; never billed."},"rtmpWireEgressBytes":{"type":"number","default":0,"description":"Wire bytes including transport overhead, an internal cost input; never billed."},"srtWireEgressBytes":{"type":"number","default":0,"description":"Wire bytes including transport overhead, an internal cost input; never billed."},"webrtcWireEgressBytes":{"type":"number","default":0,"description":"Wire bytes including transport overhead, an internal cost input; never billed."},"syncWireEgressBytes":{"type":"number","default":0,"description":"Wire bytes including transport overhead, an internal cost input; never billed."},"announceRouteSeconds":{"type":"number","description":"Relay announce work in seconds, an internal cost input; never billed."},"announceCursorSeconds":{"type":"number","description":"Relay announce work in seconds, an internal cost input; never billed."}},"required":["egressInternalBytes"]}]},{"type":"null"}],"description":"Recorded monthly quantities, or null when no monthly row remains."},"days":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/Usage"},{"type":"object","properties":{"egressInternalBytes":{"type":"number","description":"Relay-to-relay bytes inside the fleet, an internal cost input; never billed."},"wireEgressExternalBytes":{"type":"number","default":0,"description":"Wire bytes including transport overhead, an internal cost input; never billed."},"wireIngressBytes":{"type":"number","default":0,"description":"Wire bytes including transport overhead, an internal cost input; never billed."},"moqWireEgressBytes":{"type":"number","default":0,"description":"Wire bytes including transport overhead, an internal cost input; never billed."},"webtransportWireEgressBytes":{"type":"number","default":0,"description":"Wire bytes including transport overhead, an internal cost input; never billed."},"websocketWireEgressBytes":{"type":"number","default":0,"description":"Wire bytes including transport overhead, an internal cost input; never billed."},"rtmpWireEgressBytes":{"type":"number","default":0,"description":"Wire bytes including transport overhead, an internal cost input; never billed."},"srtWireEgressBytes":{"type":"number","default":0,"description":"Wire bytes including transport overhead, an internal cost input; never billed."},"webrtcWireEgressBytes":{"type":"number","default":0,"description":"Wire bytes including transport overhead, an internal cost input; never billed."},"syncWireEgressBytes":{"type":"number","default":0,"description":"Wire bytes including transport overhead, an internal cost input; never billed."},"announceRouteSeconds":{"type":"number","description":"Relay announce work in seconds, an internal cost input; never billed."},"announceCursorSeconds":{"type":"number","description":"Relay announce work in seconds, an internal cost input; never billed."}},"required":["egressInternalBytes"]}]},"description":"Retained daily rows, ordered by periodStart; missing days are omitted."}},"required":["project","periodStart","periodEnd","usage","days"]}}}},"4XX":{"description":"Request refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"Request failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/admin/projects/{pid}/billing-identity":{"get":{"operationId":"getAdminBillingIdentity","description":"Token permissions: admin:read. The legal identity Stripe holds, or null without a Stripe customer.","x-permissions":{"admin":"read"},"tags":["admin"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":["object","null"],"properties":{"companyName":{"type":"string","maxLength":200,"description":"The legal entity billed; empty falls back to the email."},"address":{"type":"object","properties":{"line1":{"type":"string","maxLength":200,"description":"Street line 1."},"line2":{"type":"string","maxLength":200,"description":"Street line 2."},"city":{"type":"string","maxLength":200,"description":"City."},"state":{"type":"string","maxLength":200,"description":"State or region."},"postalCode":{"type":"string","maxLength":30,"description":"Postal code."},"country":{"anyOf":[{"type":"string","x-extensible-enum":[""],"description":"Known values: \"\". Additional values may appear."},{"type":"string","pattern":"^[A-Za-z]{2}$"}],"description":"ISO 3166-1 alpha-2 country; empty clears the address."}},"required":["line1","line2","city","state","postalCode","country"],"description":"The billing address; empty fields are unset."},"taxId":{"type":["object","null"],"properties":{"type":{"type":"string","description":"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.","x-extensible-enum":["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"]},"value":{"type":"string","maxLength":50,"description":"The registration number."}},"required":["type","value"],"description":"One tax registration, or null."}},"required":["companyName","address","taxId"]}}}},"4XX":{"description":"Request refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"Request failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/admin/projects/{pid}/credit":{"post":{"operationId":"grantAdminCredit","description":"Token permissions: admin:write. Grants (positive) or claws back (negative) credit. A claw-back clamps to the balance.","x-permissions":{"admin":"write"},"tags":["admin"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"amountMicros":{"type":"integer","minimum":-1000000000000,"maximum":1000000000000,"description":"Non-zero signed amount, at most USD 1,000,000 either way. Micro-USD (1,000,000 = USD 1).","example":1000000},"reason":{"type":"string","maxLength":500,"default":"","description":"Note recorded in the ledger."}},"required":["amountMicros"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Immutable public project id, the {pid} path parameter.","example":"p123example"},"balanceMicros":{"type":"integer","description":"Credit balance after the change. Micro-USD (1,000,000 = USD 1).","example":1000000},"entry":{"type":["object","null"],"properties":{"id":{"type":"string","description":"Ledger entry id."},"amountMicros":{"type":"integer","description":"Signed: grants and restores are positive, draws and claw-backs negative. Micro-USD (1,000,000 = USD 1).","example":1000000},"kind":{"type":"string","description":"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.","x-extensible-enum":["grant","applied","revoked","restored"]},"reason":{"type":"string","description":"The operator's note; empty for system entries."},"actor":{"type":"string","description":"Operator account id, or empty for a system-recorded draw."},"periodStart":{"type":["string","null"],"format":"date-time","description":"The billing month an applied entry drew against, or null. RFC 3339, UTC.","example":"2026-10-22T12:00:00.000Z"},"createdAt":{"type":"string","format":"date-time","description":"When the entry was recorded. RFC 3339, UTC.","example":"2026-10-22T12:00:00.000Z"}},"required":["id","amountMicros","kind","reason","actor","periodStart","createdAt"],"description":"The ledger entry, or null when a claw-back found nothing."},"suspended":{"type":"boolean","description":"Whether the project is suspended."},"overFreeTier":{"type":"boolean","description":"Whether usage exceeds the account free allowance."},"spendCapReached":{"type":"boolean","description":"Whether the spending cap is reached."},"capReached":{"type":"boolean","description":"Whether a capacity cap is reached."},"freeLimited":{"type":"boolean","description":"Whether the free-tier connection limit applies."},"seq":{"type":"number","description":"Monotonically increasing notice snapshot sequence."},"notifications":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Opaque resource identifier."},"category":{"type":"string","description":"What the notice is about. Known values: \"account\", \"usage\", \"spend\". Additional values may appear.","x-extensible-enum":["account","usage","spend"]},"level":{"type":"number","description":"Which notice in its category: a usage percent, a spend milestone in USD, or an account state."},"severity":{"type":"string","description":"blocking for a suspension notice, else advisory. Known values: \"advisory\", \"blocking\". Additional values may appear.","x-extensible-enum":["advisory","blocking"]},"dismissible":{"type":"boolean","description":"Whether the dashboard may hide it."},"capReached":{"type":"boolean","description":"Whether a capacity cap is reached."}},"required":["id","category","level","severity","dismissible"]},"description":"At most one billing notice for the dashboard to show."}},"required":["id","balanceMicros","entry","suspended","overFreeTier","spendCapReached"]}}}},"4XX":{"description":"Request refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"Request failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/admin/projects/{pid}/net-terms":{"post":{"operationId":"setAdminNetTerms","x-permissions":{"admin":"write"},"description":"Token permissions: admin:write.","tags":["admin"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["approve","reject","limit"],"description":"approve net terms, reject (revoke) them, or change the credit limit of an approved project."},"days":{"type":"integer","minimum":1,"maximum":180,"default":30,"description":"Payment window for approve, in days."},"creditLimitMicros":{"type":"integer","minimum":1000000,"maximum":1000000000000,"description":"Credit limit, required for approve and limit. Micro-USD (1,000,000 = USD 1).","example":1000000},"answering":{"allOf":[{"$ref":"#/components/schemas/CreditLimitRequest"},{"description":"The owner's request this limit answers, exactly as read."}]}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Immutable public project id, the {pid} path parameter.","example":"p123example"},"netTerms":{"type":"string","description":"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.","x-extensible-enum":["","pending","active","cancelling","revoking"]},"days":{"type":"integer","minimum":0,"description":"The approved payment window."},"creditLimitMicros":{"type":"integer","description":"The credit limit set. Micro-USD (1,000,000 = USD 1).","example":1000000}},"required":["id","netTerms"]}}}},"4XX":{"description":"Request refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"Request failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/admin/projects/{pid}/credit-limit-request/decline":{"post":{"operationId":"declineAdminCreditLimitRequest","description":"Token permissions: admin:write. Declines the pending request exactly as read; a request the owner replaced stays pending.","x-permissions":{"admin":"write"},"tags":["admin"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"creditLimitMicros":{"type":"integer","minimum":0,"description":"The requested credit limit in micro-USD.","example":1000000},"reason":{"type":"string","description":"Why the owner needs a higher limit."},"requestedAt":{"type":"string","format":"date-time","description":"When the owner requested the increase.","example":"2026-10-22T12:00:00.000Z"}},"required":["creditLimitMicros","reason","requestedAt"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Immutable public project id, the {pid} path parameter.","example":"p123example"}},"required":["id"]}}}},"4XX":{"description":"Request refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"Request failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/admin/projects/{pid}/invoices/{periodStart}/reconcile":{"post":{"operationId":"reconcileAdminInvoice","description":"Token permissions: admin:write. Schedules a re-read of the invoice from Stripe.","x-permissions":{"admin":"write"},"tags":["admin"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","format":"date-time","description":"The invoice's periodStart, RFC 3339. Percent-encode the colons.","example":"2026-10-22T12:00:00.000Z"},"required":true,"description":"The invoice's periodStart, RFC 3339. Percent-encode the colons.","name":"periodStart","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"scheduled":{"type":"boolean","description":"Always true once scheduled."}},"required":["scheduled"]}}}},"4XX":{"description":"Request refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"Request failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/admin/projects/{pid}/invoices/{periodStart}/resolve":{"post":{"operationId":"resolveAdminInvoice","description":"Token permissions: admin:write. Releases a held invoice to collection, or forgives it and restores drawn credit.","x-permissions":{"admin":"write"},"tags":["admin"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","format":"date-time","description":"The invoice's periodStart, RFC 3339. Percent-encode the colons.","example":"2026-10-22T12:00:00.000Z"},"required":true,"description":"The invoice's periodStart, RFC 3339. Percent-encode the colons.","name":"periodStart","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["charge","forgive"],"description":"charge or forgive."}},"required":["action"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Immutable public project id, the {pid} path parameter.","example":"p123example"},"periodStart":{"type":"string","format":"date-time","description":"The invoice acted on. RFC 3339, UTC.","example":"2026-10-22T12:00:00.000Z"},"status":{"type":"string","description":"Invoice ledger state: pending, paid, failed, no_card, held, invoiced, forgiven, invoice_void, invoice_uncollectible, invoice_writeoff_pending, or invoice_written_off."}},"required":["id","periodStart","status"]}}}},"4XX":{"description":"Request refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"Request failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/admin/projects/{pid}/invoices/{periodStart}/write-off":{"post":{"operationId":"writeOffAdminInvoice","description":"Token permissions: admin:write. Writes off net-terms debt Stripe will not collect. A payment that lands first wins.","x-permissions":{"admin":"write"},"tags":["admin"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","format":"date-time","description":"The invoice's periodStart, RFC 3339. Percent-encode the colons.","example":"2026-10-22T12:00:00.000Z"},"required":true,"description":"The invoice's periodStart, RFC 3339. Percent-encode the colons.","name":"periodStart","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Immutable public project id, the {pid} path parameter.","example":"p123example"},"periodStart":{"type":"string","format":"date-time","description":"The invoice acted on. RFC 3339, UTC.","example":"2026-10-22T12:00:00.000Z"},"status":{"type":"string","description":"Invoice ledger state: pending, paid, failed, no_card, held, invoiced, forgiven, invoice_void, invoice_uncollectible, invoice_writeoff_pending, or invoice_written_off."}},"required":["id","periodStart","status"]}}}},"4XX":{"description":"Request refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"Request failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/admin/projects/{pid}/owner-cap":{"patch":{"operationId":"setAdminOwnerCap","description":"Token permissions: admin:write. Sets how many projects the project's owner account may own.","x-permissions":{"admin":"write"},"tags":["admin"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"requestBody":{"required":true,"description":"JSON Merge Patch of the sub-resource.","content":{"application/json":{"schema":{"type":"object","properties":{"cap":{"type":"integer","minimum":1,"maximum":1000,"description":"The owner's project cap."}},"required":["cap"],"additionalProperties":false}},"application/merge-patch+json":{"schema":{"type":"object","properties":{"cap":{"type":"integer","minimum":1,"maximum":1000,"description":"The owner's project cap."}},"required":["cap"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Immutable public project id, the {pid} path parameter.","example":"p123example"},"ownerEmail":{"type":"string","description":"The owner whose cap changed."},"ownerProjectCap":{"type":"integer","minimum":0,"description":"The cap now in force."}},"required":["id","ownerEmail","ownerProjectCap"]}}}},"4XX":{"description":"Request refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"Request failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/admin/projects/{pid}/ban":{"patch":{"operationId":"setAdminBan","x-permissions":{"admin":"write"},"description":"Token permissions: admin:write.","tags":["admin"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"requestBody":{"required":true,"description":"JSON Merge Patch of the sub-resource.","content":{"application/json":{"schema":{"type":"object","properties":{"banned":{"type":"boolean","description":"Whether the project is banned."}},"required":["banned"],"additionalProperties":false}},"application/merge-patch+json":{"schema":{"type":"object","properties":{"banned":{"type":"boolean","description":"Whether the project is banned."}},"required":["banned"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Immutable public project id, the {pid} path parameter.","example":"p123example"},"name":{"type":["string","null"],"description":"Project name, or null when unnamed."},"suspended":{"type":"boolean","description":"Billing suspension flag; gates traffic only while enforceSuspension is true."},"banned":{"type":"boolean","description":"Sticky abuse ban set by an operator."}},"required":["id","name","suspended","banned"]}}}},"4XX":{"description":"Request refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"Request failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/admin/projects/{pid}/restore":{"post":{"operationId":"restoreAdminProjectObject","description":"Token permissions: admin:write. Bootstrap ADMIN_KEY only. Restores one Durable Object to a point in time, undoing and refusing a restore that would bring back authority the current state lacks.","x-permissions":{"admin":"write"},"tags":["admin"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["ProjectAuth","ProjectBilling","RecordingProject","Recording"],"description":"The project's Durable Object to restore; only ProjectAuth restores, the rest are refused with a reason."},"at":{"type":"integer","minimum":1,"description":"Target time in epoch seconds, resolved to the nearest restore point. Give at or bookmark."},"bookmark":{"type":"string","pattern":"^[A-Za-z0-9-]{1,256}$","description":"An exact restore point, such as the undo a restore answered. Give at or bookmark."},"accept":{"type":"string","pattern":"^[0-9a-f]{16}$","description":"The fingerprint a refusal printed, to keep exactly the resurrection it named."}},"required":["object"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Immutable public project id, the {pid} path parameter.","example":"p123example"},"object":{"type":"string","description":"The project's Durable Object to restore; only ProjectAuth restores, the rest are refused with a reason. Known values: \"ProjectAuth\", \"ProjectBilling\", \"RecordingProject\", \"Recording\". Additional values may appear.","x-extensible-enum":["ProjectAuth","ProjectBilling","RecordingProject","Recording"]},"bookmark":{"type":"string","description":"The restore point applied."},"undo":{"type":"string","description":"Restore to this bookmark to put back the state the restore replaced."},"removed":{"type":"array","items":{"type":"string"},"description":"Facts the restore took away, such as a key minted since."},"resurrected":{"type":"array","items":{"type":"string"},"description":"Facts the restore brought back; non-empty only under a matching accept."}},"required":["id","object","bookmark","undo","removed","resurrected"]}}}},"4XX":{"description":"Request refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"Request failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects":{"get":{"operationId":"listProjects","x-permissions":{"settings":"read"},"x-project-filter":true,"summary":"List Projects","tags":["Projects"],"description":"Token permissions: settings:read. Ordered by createdAt ascending, then id ascending.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":200,"description":"Maximum items per page, from 1 to 200."},"required":false,"description":"Maximum items per page, from 1 to 200.","name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page."},"required":false,"description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.","name":"cursor","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":32,"pattern":"^[a-z0-9]([a-z0-9-]*[a-z0-9])?$","description":"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.","example":"example"},"required":false,"description":"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.","name":"name","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Project"},"description":"This page's items, in the list's documented order."},"nextCursor":{"type":["string","null"],"description":"Pass as cursor to continue, or null when there are no more items."}},"required":["items","nextCursor"]}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"operationId":"createProject","x-permissions":{"lifecycle":"write"},"summary":"Create Project","tags":["Projects"],"description":"Token permissions: lifecycle:write.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"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."},"required":true,"description":"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.","name":"Idempotency-Key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":4,"maxLength":32,"pattern":"^[a-z0-9]([a-z0-9-]*[a-z0-9])?$","description":"Optional mutable project name. Omit for an unnamed project reachable only by its immutable id.","example":"example"}},"additionalProperties":false}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}":{"get":{"operationId":"getProject","x-permissions":{"settings":"read"},"x-role":"read","summary":"Get Project","tags":["Projects"],"description":"Token permissions: settings:read. Project role: read.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"patch":{"operationId":"updateProject","x-permissions":{"settings":"write"},"x-role":"write","summary":"Update Project","tags":["Projects"],"description":"Token permissions: settings:write. Project role: write.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"requestBody":{"required":true,"description":"JSON Merge Patch: omitted fields remain unchanged, arrays replace, and null clears nullable fields.","content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":["string","null"],"minLength":4,"maxLength":32,"pattern":"^[a-z0-9]([a-z0-9-]*[a-z0-9])?$","description":"New project name, or null to clear it. The previous name redirects for a grace period."},"domainEnabled":{"type":"boolean","description":"Enable access from the caller verified email domain; false disables it. Omit to leave unchanged."},"domainRole":{"type":"string","enum":["write","read"],"description":"Role for matching verified-domain members. Omit to leave unchanged; owner requires an explicit membership."},"public":{"type":["object","null"],"properties":{"v":{"type":"number","enum":[1],"description":"Pattern version 1: `foo` matches only `foo`; use `foo/**` for a subtree."},"publish":{"type":["array","null"],"items":{"type":"string","maxLength":256,"description":"At most 256 UTF-16 code units (JavaScript string.length). A legacy prefix must also fit after subtree expansion."},"maxItems":32,"description":"Patterns anyone may publish to. Omitted keeps the saved list; null clears it."},"subscribe":{"type":["array","null"],"items":{"type":"string","maxLength":256,"description":"At most 256 UTF-16 code units (JavaScript string.length). A legacy prefix must also fit after subtree expansion."},"maxItems":32,"description":"Patterns anyone may subscribe to. Omitted keeps the saved list; null clears it."}},"required":["v"],"additionalProperties":false,"description":"Version 1 patterns. A bare path is exact; a trailing ** segment matches its subtree. Null clears public access."}},"additionalProperties":false}},"application/merge-patch+json":{"schema":{"type":"object","properties":{"name":{"type":["string","null"],"minLength":4,"maxLength":32,"pattern":"^[a-z0-9]([a-z0-9-]*[a-z0-9])?$","description":"New project name, or null to clear it. The previous name redirects for a grace period."},"domainEnabled":{"type":"boolean","description":"Enable access from the caller verified email domain; false disables it. Omit to leave unchanged."},"domainRole":{"type":"string","enum":["write","read"],"description":"Role for matching verified-domain members. Omit to leave unchanged; owner requires an explicit membership."},"public":{"type":["object","null"],"properties":{"v":{"type":"number","enum":[1],"description":"Pattern version 1: `foo` matches only `foo`; use `foo/**` for a subtree."},"publish":{"type":["array","null"],"items":{"type":"string","maxLength":256,"description":"At most 256 UTF-16 code units (JavaScript string.length). A legacy prefix must also fit after subtree expansion."},"maxItems":32,"description":"Patterns anyone may publish to. Omitted keeps the saved list; null clears it."},"subscribe":{"type":["array","null"],"items":{"type":"string","maxLength":256,"description":"At most 256 UTF-16 code units (JavaScript string.length). A legacy prefix must also fit after subtree expansion."},"maxItems":32,"description":"Patterns anyone may subscribe to. Omitted keeps the saved list; null clears it."}},"required":["v"],"additionalProperties":false,"description":"Version 1 patterns. A bare path is exact; a trailing ** segment matches its subtree. Null clears public access."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"delete":{"operationId":"deleteProject","x-permissions":{"lifecycle":"write"},"x-role":"owner","summary":"Delete Project","tags":["Projects"],"description":"Token permissions: lifecycle:write. Project role: owner.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"responses":{"204":{"description":"Success. The response has no body."},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/keys":{"get":{"operationId":"listKeys","x-permissions":{"keys":"read"},"x-role":"write","summary":"List Keys","tags":["Keys"],"description":"Token permissions: keys:read. Project role: write. Ordered by createdAt ascending, then id ascending.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":200,"description":"Maximum items per page, from 1 to 200."},"required":false,"description":"Maximum items per page, from 1 to 200.","name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page."},"required":false,"description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.","name":"cursor","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Key"},"description":"This page's items, in the list's documented order."},"nextCursor":{"type":["string","null"],"description":"Pass as cursor to continue, or null when there are no more items."}},"required":["items","nextCursor"]}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"operationId":"createKey","x-permissions":{"keys":"write"},"x-role":"write","summary":"Create Key","tags":["Keys"],"description":"Token permissions: keys:write. Project role: write.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"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."},"required":true,"description":"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.","name":"Idempotency-Key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"algorithm":{"type":"string","enum":["HS256","HS384","HS512","ES256","ES384","RS256","RS384","RS512","PS256","PS384","PS512","EdDSA"],"description":"Signing algorithm. HS* keys are symmetric secrets; the others are asymmetric key pairs."},"label":{"type":"string","description":"Optional human-readable label."},"publish":{"type":"array","items":{"type":"string"},"description":"Immutable publish scope, patterns relative to the project. Omit both lists for no scope."},"subscribe":{"type":"array","items":{"type":"string"},"description":"Immutable subscribe scope, patterns relative to the project. Omit both lists for no scope."}},"required":["algorithm"],"additionalProperties":false}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Key"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/keys/import":{"post":{"operationId":"importKey","x-permissions":{"keys":"write"},"x-role":"write","summary":"Import Key","tags":["Keys"],"description":"Token permissions: keys:write. Project role: write.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"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."},"required":true,"description":"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.","name":"Idempotency-Key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"label":{"type":"string","description":"Optional human-readable label."},"publicKey":{"type":"string","description":"Serialized public JWK. Private key parameters are refused.","example":"{\"kty\":\"EC\",\"crv\":\"P-256\",...}"}},"required":["publicKey"],"additionalProperties":false}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Key"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/keys/{kid}/private":{"get":{"operationId":"getPrivateKey","x-permissions":{"keys":"write"},"x-role":"write","summary":"Get Private Key","tags":["Keys"],"description":"Token permissions: keys:write. Project role: write.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The key's id field."},"required":true,"description":"The key's id field.","name":"kid","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PrivateKey"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/keys/{kid}/token":{"post":{"operationId":"mintToken","x-permissions":{"keys":"write"},"x-role":"write","summary":"Mint Token","tags":["Keys"],"description":"Token permissions: keys:write. Project role: write.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The key's id field."},"required":true,"description":"The key's id field.","name":"kid","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"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."},"required":true,"description":"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.","name":"Idempotency-Key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"root":{"type":"string","description":"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."},"publish":{"type":"array","items":{"type":"string"},"description":"Patterns the token may publish, relative to its root; must fit the key scope."},"subscribe":{"type":"array","items":{"type":"string"},"description":"Patterns the token may subscribe to, relative to its root; must fit the key scope."},"exp":{"type":"string","format":"date-time","description":"Expiry strictly in the future, at most 30 days from the request. Omitted means the token never expires.","example":"2026-10-22T12:00:00.000Z"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Token"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/keys/{kid}/disable":{"post":{"operationId":"disableKey","x-permissions":{"keys":"write"},"x-role":"write","summary":"Disable Key","tags":["Keys"],"description":"Token permissions: keys:write. Project role: write.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The key's id field."},"required":true,"description":"The key's id field.","name":"kid","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Key"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/keys/{kid}/enable":{"post":{"operationId":"enableKey","x-permissions":{"keys":"write"},"x-role":"write","summary":"Enable Key","tags":["Keys"],"description":"Token permissions: keys:write. Project role: write.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The key's id field."},"required":true,"description":"The key's id field.","name":"kid","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Key"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/keys/{kid}":{"delete":{"operationId":"deleteKey","x-permissions":{"keys":"write"},"x-role":"write","summary":"Delete Key","tags":["Keys"],"description":"Token permissions: keys:write. Project role: write.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The key's id field."},"required":true,"description":"The key's id field.","name":"kid","in":"path"}],"responses":{"204":{"description":"Success. The response has no body."},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/limits":{"get":{"operationId":"getSessionLimits","x-permissions":{"settings":"read"},"x-role":"read","description":"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.","summary":"Get Session Limits","tags":["Limits"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionLimits"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"patch":{"operationId":"setSessionLimits","x-permissions":{"settings":"write"},"x-role":"write","summary":"Set Session Limits","tags":["Limits"],"description":"Token permissions: settings:write. Project role: write.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"requestBody":{"required":true,"description":"JSON Merge Patch: omitted fields remain unchanged, arrays replace, and null clears nullable fields.","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":["integer","null"],"minimum":1,"maximum":1000000,"description":"Live sessions one token may hold on each relay, from 1 to 1000000; null is unlimited."},"remote":{"type":["integer","null"],"minimum":1,"maximum":1000000,"description":"Live sessions one remote address (an IPv4 address or an IPv6 /64) may hold on each relay, from 1 to 1000000; null is unlimited."}},"additionalProperties":false}},"application/merge-patch+json":{"schema":{"type":"object","properties":{"token":{"type":["integer","null"],"minimum":1,"maximum":1000000,"description":"Live sessions one token may hold on each relay, from 1 to 1000000; null is unlimited."},"remote":{"type":["integer","null"],"minimum":1,"maximum":1000000,"description":"Live sessions one remote address (an IPv4 address or an IPv6 /64) may hold on each relay, from 1 to 1000000; null is unlimited."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionLimits"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/auth/endpoint":{"get":{"operationId":"getAuthEndpoint","x-permissions":{"keys":"read"},"x-role":"write","description":"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.","summary":"Get Auth Endpoint","tags":["Auth"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthEndpoint"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"put":{"operationId":"setAuthEndpoint","x-permissions":{"keys":"write"},"x-role":"write","description":"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.","summary":"Set Auth Endpoint","tags":["Auth"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"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."},"required":true,"description":"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.","name":"Idempotency-Key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","minLength":1,"maxLength":2000,"description":"An https URL to a public host name, with no IP address and no user:password."}},"required":["url"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthEndpoint"}}}},"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedAuthEndpoint"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"delete":{"operationId":"deleteAuthEndpoint","x-permissions":{"keys":"write"},"x-role":"write","description":"Token permissions: keys:write. Project role: write. The saved public rules apply again, and the signing secret is discarded.","summary":"Delete Auth Endpoint","tags":["Auth"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"responses":{"204":{"description":"Success. The response has no body."},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/auth/endpoint/test":{"post":{"operationId":"testAuthEndpoint","x-permissions":{"keys":"write"},"x-role":"write","description":"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.","summary":"Test Auth Endpoint","tags":["Auth"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"path":{"type":"string","maxLength":1000,"description":"The path a viewer dials, relative to the project. Defaults to the project root."},"query":{"type":"string","maxLength":4000,"description":"The query string the viewer presents, without the leading ?. jwt is reserved for MoQ tokens."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthEndpointTest"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/live/token":{"post":{"operationId":"mintLiveToken","x-permissions":{"keys":"write"},"x-role":"read","description":"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.","summary":"Mint Live Token","tags":["Live"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LiveToken"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/recording/rules":{"get":{"operationId":"listRecordingRules","x-permissions":{"media":"read"},"x-role":"read","summary":"List Recording Rules","tags":["Recording"],"description":"Token permissions: media:read. Project role: read. Ordered by createdAt ascending, then id ascending.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":200,"description":"Maximum items per page, from 1 to 200."},"required":false,"description":"Maximum items per page, from 1 to 200.","name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page."},"required":false,"description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.","name":"cursor","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/RecordingRule"},"description":"This page's items, in the list's documented order."},"nextCursor":{"type":["string","null"],"description":"Pass as cursor to continue, or null when there are no more items."}},"required":["items","nextCursor"]}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"operationId":"createRecordingRule","x-permissions":{"media":"write"},"x-role":"write","summary":"Create Recording Rule","tags":["Recording"],"description":"Token permissions: media:write. Project role: write.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"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."},"required":true,"description":"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.","name":"Idempotency-Key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"label":{"type":"string","maxLength":80,"description":"Optional display label, at most 80 characters."},"patterns":{"type":"array","items":{"type":"string","maxLength":200},"minItems":1,"maxItems":32,"description":"Broadcast path patterns relative to the project, 1 to 32; a PATCH replaces the list."},"vodEnabled":{"type":"boolean","description":"Whether matching broadcasts are recorded. Defaults to true."},"retentionDays":{"anyOf":[{"type":"number","enum":[1]},{"type":"number","enum":[7]},{"type":"number","enum":[30]},{"type":"number","enum":[90]},{"type":"number","enum":[180]},{"type":"number","enum":[365]}],"default":7,"description":"Managed retention in days. More than 7 days requires a payment method; BYOB retention is owned by the bucket owner.","example":7},"bucket":{"type":"object","properties":{"endpoint":{"type":"string","minLength":1,"maxLength":400,"description":"S3-compatible endpoint, an https URL on a public host."},"region":{"type":"string","minLength":1,"maxLength":100,"description":"S3 region used for signing."},"bucket":{"type":"string","minLength":1,"maxLength":200,"description":"S3 bucket name."},"accessKeyId":{"type":"string","minLength":1,"maxLength":200,"description":"Access key id with write access."},"secretAccessKey":{"type":"string","minLength":1,"maxLength":400,"description":"Secret access key; write-only, encrypted at rest and never returned."},"destination":{"type":"string","maxLength":400,"description":"Object prefix inside the bucket; omitted is the root."},"delivery":{"type":"boolean","description":"Serve playback from the bucket, which needs read access too. Defaults to false."}},"required":["endpoint","region","bucket","accessKeyId","secretAccessKey"],"additionalProperties":false,"description":"Omit for managed storage; supply customer bucket credentials once for BYOB. BYOB owns retention. Cardless projects cannot start recording after exhausting their account allowance."}},"required":["patterns"],"additionalProperties":false}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordingRule"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/recording/rules/{id}":{"patch":{"operationId":"updateRecordingRule","x-permissions":{"media":"write"},"x-role":"write","summary":"Update Recording Rule","tags":["Recording"],"description":"Token permissions: media:write. Project role: write.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The resource's id field."},"required":true,"description":"The resource's id field.","name":"id","in":"path"}],"requestBody":{"required":true,"description":"JSON Merge Patch: omitted fields remain unchanged, arrays replace, and null clears nullable fields.","content":{"application/json":{"schema":{"type":"object","properties":{"vodEnabled":{"type":"boolean","description":"Whether matching broadcasts are recorded."},"retentionDays":{"anyOf":[{"type":"number","enum":[1]},{"type":"number","enum":[7]},{"type":"number","enum":[30]},{"type":"number","enum":[90]},{"type":"number","enum":[180]},{"type":"number","enum":[365]}],"description":"Managed retention in days. More than 7 days requires a payment method; BYOB retention is owned by the bucket owner.","example":7},"bucket":{"type":["string","null"],"description":"Rename the customer bucket; customer buckets only."},"region":{"type":["string","null"],"description":"Change the customer bucket's region; customer buckets only."},"delivery":{"type":"boolean","description":"Serve playback from the customer bucket, which needs read access; re-probed when enabled."}},"additionalProperties":false}},"application/merge-patch+json":{"schema":{"type":"object","properties":{"vodEnabled":{"type":"boolean","description":"Whether matching broadcasts are recorded."},"retentionDays":{"anyOf":[{"type":"number","enum":[1]},{"type":"number","enum":[7]},{"type":"number","enum":[30]},{"type":"number","enum":[90]},{"type":"number","enum":[180]},{"type":"number","enum":[365]}],"description":"Managed retention in days. More than 7 days requires a payment method; BYOB retention is owned by the bucket owner.","example":7},"bucket":{"type":["string","null"],"description":"Rename the customer bucket; customer buckets only."},"region":{"type":["string","null"],"description":"Change the customer bucket's region; customer buckets only."},"delivery":{"type":"boolean","description":"Serve playback from the customer bucket, which needs read access; re-probed when enabled."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordingRule"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"delete":{"operationId":"deleteRecordingRule","x-permissions":{"media":"write"},"x-role":"write","description":"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.","summary":"Delete Recording Rule","tags":["Recording"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The resource's id field."},"required":true,"description":"The resource's id field.","name":"id","in":"path"}],"responses":{"202":{"description":"Accepted. The body is a receipt; the work completes asynchronously.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeletingRule"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/recording/rules/{id}/sessions":{"delete":{"operationId":"deleteRuleRecordings","x-permissions":{"media":"write"},"x-role":"write","description":"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.","summary":"Delete Rule Recordings","tags":["Recording"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The resource's id field."},"required":true,"description":"The resource's id field.","name":"id","in":"path"}],"responses":{"202":{"description":"Accepted. The body is a receipt; the work completes asynchronously.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeletingRecordings"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/recording/sessions":{"get":{"operationId":"listRecordings","x-permissions":{"media":"read"},"x-role":"read","description":"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.","summary":"List Recordings","tags":["Recording"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":200,"description":"Maximum items per page, from 1 to 200."},"required":false,"description":"Maximum items per page, from 1 to 200.","name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page."},"required":false,"description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.","name":"cursor","in":"query"},{"schema":{"type":"string","description":"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."},"required":false,"description":"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.","name":"since","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Session"},"description":"This page's items, in the list's documented order."},"nextCursor":{"type":["string","null"],"description":"Pass as cursor to continue, or null when there are no more items."},"removed":{"type":"array","items":{"type":"string"},"description":"With since: ids of recordings that left the listing after it, such as by deletion. Otherwise empty."},"reset":{"type":"boolean","description":"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":{"type":["string","null"],"description":"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":{"type":["array","null"],"items":{"$ref":"#/components/schemas/RecordingRuleTotals"},"description":"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."}},"required":["items","nextCursor","removed","reset","sinceCursor","totals"]}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/recording/sessions/{id}":{"get":{"operationId":"getRecording","x-permissions":{"media":"read"},"x-role":"read","summary":"Get Recording","tags":["Recording"],"description":"Token permissions: media:read. Project role: read.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The recording's id, percent-encoded as one path segment: each `/` is `%2F`."},"required":true,"description":"The recording's id, percent-encoded as one path segment: each `/` is `%2F`.","name":"id","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Session"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"delete":{"operationId":"deleteRecording","x-permissions":{"media":"write"},"x-role":"write","description":"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.","summary":"Delete Recording","tags":["Recording"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The recording's id, percent-encoded as one path segment: each `/` is `%2F`."},"required":true,"description":"The recording's id, percent-encoded as one path segment: each `/` is `%2F`.","name":"id","in":"path"}],"responses":{"202":{"description":"Accepted. The body is a receipt; the work completes asynchronously.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeletingRecording"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/recording/sessions/{id}/play":{"get":{"operationId":"playRecording","x-permissions":{"media":"read"},"x-role":"read","description":"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.","summary":"Play Recording","tags":["Recording"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The recording's id, percent-encoded as one path segment: each `/` is `%2F`."},"required":true,"description":"The recording's id, percent-encoded as one path segment: each `/` is `%2F`.","name":"id","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Playback"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/recording/s3":{"get":{"operationId":"getRecordingCredentials","x-permissions":{"keys":"write"},"x-role":"owner","description":"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.","summary":"Get Recording Credentials","tags":["Recording"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/S3Credentials"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/agent/rules":{"get":{"operationId":"listAgentRules","x-permissions":{"media":"read"},"x-role":"read","summary":"List Agent Rules","tags":["Agent"],"description":"Token permissions: media:read. Project role: read. Ordered by createdAt ascending, then id ascending.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":200,"description":"Maximum items per page, from 1 to 200."},"required":false,"description":"Maximum items per page, from 1 to 200.","name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page."},"required":false,"description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.","name":"cursor","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/AgentRule"},"description":"This page's items, in the list's documented order."},"nextCursor":{"type":["string","null"],"description":"Pass as cursor to continue, or null when there are no more items."}},"required":["items","nextCursor"]}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"operationId":"createAgentRule","x-permissions":{"media":"write"},"x-role":"write","summary":"Create Agent Rule","tags":["Agent"],"description":"Token permissions: media:write. Project role: write.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"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."},"required":true,"description":"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.","name":"Idempotency-Key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"patterns":{"type":"array","items":{"type":"string","maxLength":200},"minItems":1,"maxItems":32,"description":"Broadcast path patterns relative to the project, 1 to 32; a PATCH replaces the list."},"enabled":{"type":"boolean","description":"Whether the rule applies. Defaults to true on create."},"config":{"type":"object","properties":{"stt":{"type":"object","properties":{"provider":{"type":"string","enum":["deepgram"],"description":"Provider id."},"model":{"type":"string","maxLength":80,"description":"Provider model; omitted uses its default."},"apiKey":{"type":"string","minLength":1,"maxLength":400,"description":"Your provider API key; write-only, encrypted at rest and never returned."},"options":{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"description":"Provider-specific options, checked against that provider's allowlist."}},"required":["provider","apiKey"],"additionalProperties":false,"description":"Speech-to-text provider."},"llm":{"type":"object","properties":{"provider":{"type":"string","enum":["openai"],"description":"Provider id."},"model":{"type":"string","maxLength":80,"description":"Provider model; omitted uses its default."},"apiKey":{"type":"string","minLength":1,"maxLength":400,"description":"Your provider API key; write-only, encrypted at rest and never returned."},"options":{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"description":"Provider-specific options, checked against that provider's allowlist."}},"required":["provider","apiKey"],"additionalProperties":false,"description":"Language-model provider."},"tts":{"type":"object","properties":{"provider":{"type":"string","enum":["cartesia"],"description":"Provider id."},"model":{"type":"string","maxLength":80,"description":"Provider model; omitted uses its default."},"apiKey":{"type":"string","minLength":1,"maxLength":400,"description":"Your provider API key; write-only, encrypted at rest and never returned."},"options":{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"description":"Provider-specific options, checked against that provider's allowlist."}},"required":["provider","apiKey"],"additionalProperties":false,"description":"Text-to-speech provider."},"systemPrompt":{"type":"string","maxLength":8000,"description":"Instruction text, at most 8000 characters."},"greeting":{"type":["string","null"],"maxLength":2000,"description":"Initial spoken greeting, at most 2000 characters; null or omitted for none."},"turnDetection":{"type":"string","enum":["silero","stt_endpointing","client"],"description":"Turn detection strategy. Defaults to silero."},"maxSessions":{"type":"number","description":"Simultaneous sessions for this rule, 1 to 4. Defaults to the project ceiling."}},"required":["stt","llm","tts","systemPrompt"],"additionalProperties":false,"description":"Full voice configuration with write-only provider keys. Creating or enabling a voice rule requires paid collection (card, net terms, or billing exemption)."}},"required":["patterns","config"],"additionalProperties":false}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentRule"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/agent/rules/{id}":{"patch":{"operationId":"updateAgentRule","x-permissions":{"media":"write"},"x-role":"write","summary":"Update Agent Rule","tags":["Agent"],"description":"Token permissions: media:write. Project role: write.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The resource's id field."},"required":true,"description":"The resource's id field.","name":"id","in":"path"}],"requestBody":{"required":true,"description":"JSON Merge Patch: omitted fields remain unchanged, arrays replace, and null clears nullable fields.","content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Whether the rule applies. Defaults to true on create."},"config":{"type":"object","properties":{"stt":{"type":"object","properties":{"provider":{"type":"string","enum":["deepgram"],"description":"Provider id."},"model":{"type":["string","null"],"maxLength":80,"description":"Provider model; null restores its default."},"apiKey":{"type":"string","minLength":1,"maxLength":400,"description":"Your provider API key; write-only, encrypted at rest and never returned."},"options":{"type":["object","null"],"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"null"}]},"description":"Merge-patched provider options; a null member or null clears."}},"additionalProperties":false,"description":"Speech-to-text changes."},"llm":{"type":"object","properties":{"provider":{"type":"string","enum":["openai"],"description":"Provider id."},"model":{"type":["string","null"],"maxLength":80,"description":"Provider model; null restores its default."},"apiKey":{"type":"string","minLength":1,"maxLength":400,"description":"Your provider API key; write-only, encrypted at rest and never returned."},"options":{"type":["object","null"],"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"null"}]},"description":"Merge-patched provider options; a null member or null clears."}},"additionalProperties":false,"description":"Language-model changes."},"tts":{"type":"object","properties":{"provider":{"type":"string","enum":["cartesia"],"description":"Provider id."},"model":{"type":["string","null"],"maxLength":80,"description":"Provider model; null restores its default."},"apiKey":{"type":"string","minLength":1,"maxLength":400,"description":"Your provider API key; write-only, encrypted at rest and never returned."},"options":{"type":["object","null"],"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"null"}]},"description":"Merge-patched provider options; a null member or null clears."}},"additionalProperties":false,"description":"Text-to-speech changes."},"systemPrompt":{"type":"string","maxLength":8000,"description":"Instruction text, at most 8000 characters."},"greeting":{"type":["string","null"],"maxLength":2000,"description":"Initial spoken greeting, at most 2000 characters; null or omitted for none."},"turnDetection":{"type":"string","enum":["silero","stt_endpointing","client"],"description":"Turn detection strategy. Defaults to silero."},"maxSessions":{"type":"number","description":"Simultaneous sessions for this rule, 1 to 4. Defaults to the project ceiling."}},"additionalProperties":false,"description":"Voice configuration changes. Changing a provider requires its apiKey."}},"additionalProperties":false}},"application/merge-patch+json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Whether the rule applies. Defaults to true on create."},"config":{"type":"object","properties":{"stt":{"type":"object","properties":{"provider":{"type":"string","enum":["deepgram"],"description":"Provider id."},"model":{"type":["string","null"],"maxLength":80,"description":"Provider model; null restores its default."},"apiKey":{"type":"string","minLength":1,"maxLength":400,"description":"Your provider API key; write-only, encrypted at rest and never returned."},"options":{"type":["object","null"],"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"null"}]},"description":"Merge-patched provider options; a null member or null clears."}},"additionalProperties":false,"description":"Speech-to-text changes."},"llm":{"type":"object","properties":{"provider":{"type":"string","enum":["openai"],"description":"Provider id."},"model":{"type":["string","null"],"maxLength":80,"description":"Provider model; null restores its default."},"apiKey":{"type":"string","minLength":1,"maxLength":400,"description":"Your provider API key; write-only, encrypted at rest and never returned."},"options":{"type":["object","null"],"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"null"}]},"description":"Merge-patched provider options; a null member or null clears."}},"additionalProperties":false,"description":"Language-model changes."},"tts":{"type":"object","properties":{"provider":{"type":"string","enum":["cartesia"],"description":"Provider id."},"model":{"type":["string","null"],"maxLength":80,"description":"Provider model; null restores its default."},"apiKey":{"type":"string","minLength":1,"maxLength":400,"description":"Your provider API key; write-only, encrypted at rest and never returned."},"options":{"type":["object","null"],"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"null"}]},"description":"Merge-patched provider options; a null member or null clears."}},"additionalProperties":false,"description":"Text-to-speech changes."},"systemPrompt":{"type":"string","maxLength":8000,"description":"Instruction text, at most 8000 characters."},"greeting":{"type":["string","null"],"maxLength":2000,"description":"Initial spoken greeting, at most 2000 characters; null or omitted for none."},"turnDetection":{"type":"string","enum":["silero","stt_endpointing","client"],"description":"Turn detection strategy. Defaults to silero."},"maxSessions":{"type":"number","description":"Simultaneous sessions for this rule, 1 to 4. Defaults to the project ceiling."}},"additionalProperties":false,"description":"Voice configuration changes. Changing a provider requires its apiKey."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentRule"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"delete":{"operationId":"deleteAgentRule","x-permissions":{"media":"write"},"x-role":"write","summary":"Delete Agent Rule","tags":["Agent"],"description":"Token permissions: media:write. Project role: write.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The resource's id field."},"required":true,"description":"The resource's id field.","name":"id","in":"path"}],"responses":{"204":{"description":"Success. The response has no body."},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/sync/targets":{"get":{"operationId":"listSyncTargets","x-permissions":{"media":"read"},"x-role":"read","summary":"List Sync Targets","tags":["Sync"],"description":"Token permissions: media:read. Project role: read. Ordered by createdAt ascending, then id ascending.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":200,"description":"Maximum items per page, from 1 to 200."},"required":false,"description":"Maximum items per page, from 1 to 200.","name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page."},"required":false,"description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.","name":"cursor","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/SyncTarget"},"description":"This page's items, in the list's documented order."},"nextCursor":{"type":["string","null"],"description":"Pass as cursor to continue, or null when there are no more items."}},"required":["items","nextCursor"]}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"operationId":"createSyncTarget","x-permissions":{"media":"write"},"x-role":"write","summary":"Create Sync Target","tags":["Sync"],"description":"Token permissions: media:write. Project role: write.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"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."},"required":true,"description":"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.","name":"Idempotency-Key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"kind":{"type":"string","enum":["cloudflare","custom"],"description":"Immutable target kind: cloudflare or custom."},"label":{"type":"string","maxLength":80,"description":"Human-readable label, trimmed; empty means unset."},"publishPrefixes":{"type":"array","items":{"type":"string","maxLength":200},"maxItems":20,"description":"Broadcast path prefixes relative to the project to publish to the target."},"publishUrl":{"type":["string","null"],"maxLength":2000,"description":"Publish URL including its credential; write-only. null clears it."}},"required":["kind"],"additionalProperties":false}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SyncTarget"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/sync/targets/{id}":{"patch":{"operationId":"updateSyncTarget","x-permissions":{"media":"write"},"x-role":"write","summary":"Update Sync Target","tags":["Sync"],"description":"Token permissions: media:write. Project role: write.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The resource's id field."},"required":true,"description":"The resource's id field.","name":"id","in":"path"}],"requestBody":{"required":true,"description":"JSON Merge Patch: omitted fields remain unchanged, arrays replace, and null clears nullable fields.","content":{"application/json":{"schema":{"type":"object","properties":{"label":{"type":"string","maxLength":80,"description":"Human-readable label, trimmed; empty means unset."},"publishPrefixes":{"type":"array","items":{"type":"string","maxLength":200},"maxItems":20,"description":"Broadcast path prefixes relative to the project to publish to the target."},"publishUrl":{"type":["string","null"],"maxLength":2000,"description":"Publish URL including its credential; write-only. null clears it."},"expectedUpdatedAt":{"type":"string","format":"date-time","description":"The updatedAt of the version being edited. Omit only for an intentional last-writer-wins update.","example":"2026-10-22T12:00:00.000Z"}},"additionalProperties":false}},"application/merge-patch+json":{"schema":{"type":"object","properties":{"label":{"type":"string","maxLength":80,"description":"Human-readable label, trimmed; empty means unset."},"publishPrefixes":{"type":"array","items":{"type":"string","maxLength":200},"maxItems":20,"description":"Broadcast path prefixes relative to the project to publish to the target."},"publishUrl":{"type":["string","null"],"maxLength":2000,"description":"Publish URL including its credential; write-only. null clears it."},"expectedUpdatedAt":{"type":"string","format":"date-time","description":"The updatedAt of the version being edited. Omit only for an intentional last-writer-wins update.","example":"2026-10-22T12:00:00.000Z"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SyncTarget"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"delete":{"operationId":"deleteSyncTarget","x-permissions":{"media":"write"},"x-role":"write","summary":"Delete Sync Target","tags":["Sync"],"description":"Token permissions: media:write. Project role: write.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The resource's id field."},"required":true,"description":"The resource's id field.","name":"id","in":"path"}],"responses":{"204":{"description":"Success. The response has no body."},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/webhooks":{"get":{"operationId":"listWebhooks","x-permissions":{"media":"read"},"x-role":"write","summary":"List Webhooks","tags":["Webhooks"],"description":"Token permissions: media:read. Project role: write. Ordered by createdAt ascending, then id ascending.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":200,"description":"Maximum items per page, from 1 to 200."},"required":false,"description":"Maximum items per page, from 1 to 200.","name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page."},"required":false,"description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.","name":"cursor","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"},"description":"This page's items, in the list's documented order."},"nextCursor":{"type":["string","null"],"description":"Pass as cursor to continue, or null when there are no more items."},"dropped":{"type":"number","description":"Events dropped because the delivery queue was full."}},"required":["items","nextCursor","dropped"]}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"operationId":"createWebhook","x-permissions":{"media":"write","keys":"write"},"x-role":"write","summary":"Create Webhook","tags":["Webhooks"],"description":"Token permissions: media:write, keys:write. Project role: write.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"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."},"required":true,"description":"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.","name":"Idempotency-Key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","minLength":1,"maxLength":2000,"description":"HTTPS endpoint on a public host that receives signed deliveries."},"events":{"type":"array","items":{"type":"string","enum":["broadcast.started","broadcast.ended","recording.started","recording.ready","recording.failed"]},"minItems":1,"maxItems":5,"description":"Event names to deliver; at least one."},"pattern":{"type":"string","maxLength":500,"description":"Broadcast path pattern relative to the project. Defaults to `**` on create."},"format":{"type":"string","enum":["json","slack","discord"],"description":"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."}},"required":["url","events"],"additionalProperties":false}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Webhook"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/webhooks/{id}":{"patch":{"operationId":"updateWebhook","x-permissions":{"media":"write"},"x-role":"write","summary":"Update Webhook","tags":["Webhooks"],"description":"Token permissions: media:write. Project role: write.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The resource's id field."},"required":true,"description":"The resource's id field.","name":"id","in":"path"}],"requestBody":{"required":true,"description":"JSON Merge Patch: omitted fields remain unchanged, arrays replace, and null clears nullable fields.","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","minLength":1,"maxLength":2000,"description":"HTTPS endpoint on a public host that receives signed deliveries."},"events":{"type":"array","items":{"type":"string","enum":["broadcast.started","broadcast.ended","recording.started","recording.ready","recording.failed"]},"minItems":1,"maxItems":5,"description":"Event names to deliver; at least one."},"pattern":{"type":"string","maxLength":500,"description":"Broadcast path pattern relative to the project. Defaults to `**` on create."},"enabled":{"type":"boolean","description":"true re-enables an endpoint that repeated failures disabled."}},"additionalProperties":false}},"application/merge-patch+json":{"schema":{"type":"object","properties":{"url":{"type":"string","minLength":1,"maxLength":2000,"description":"HTTPS endpoint on a public host that receives signed deliveries."},"events":{"type":"array","items":{"type":"string","enum":["broadcast.started","broadcast.ended","recording.started","recording.ready","recording.failed"]},"minItems":1,"maxItems":5,"description":"Event names to deliver; at least one."},"pattern":{"type":"string","maxLength":500,"description":"Broadcast path pattern relative to the project. Defaults to `**` on create."},"enabled":{"type":"boolean","description":"true re-enables an endpoint that repeated failures disabled."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Webhook"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"delete":{"operationId":"deleteWebhook","x-permissions":{"media":"write"},"x-role":"write","summary":"Delete Webhook","tags":["Webhooks"],"description":"Token permissions: media:write. Project role: write.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The resource's id field."},"required":true,"description":"The resource's id field.","name":"id","in":"path"}],"responses":{"204":{"description":"Success. The response has no body."},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/webhooks/{id}/test":{"post":{"operationId":"testWebhook","x-permissions":{"media":"write"},"x-role":"write","summary":"Test Webhook","tags":["Webhooks"],"description":"Token permissions: media:write. Project role: write.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The resource's id field."},"required":true,"description":"The resource's id field.","name":"id","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"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."},"required":true,"description":"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.","name":"Idempotency-Key","in":"header"}],"responses":{"202":{"description":"Accepted. The body is a receipt; the work completes asynchronously.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestedWebhook"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/webhooks/{id}/deliveries":{"get":{"operationId":"listWebhookDeliveries","x-permissions":{"media":"read"},"x-role":"write","summary":"List Webhook Deliveries","tags":["Webhooks"],"description":"Token permissions: media:read. Project role: write. Ordered by createdAt descending, then id descending.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","minLength":1,"description":"The resource's id field."},"required":true,"description":"The resource's id field.","name":"id","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":200,"description":"Maximum items per page, from 1 to 200."},"required":false,"description":"Maximum items per page, from 1 to 200.","name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page."},"required":false,"description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.","name":"cursor","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Delivery"},"description":"This page's items, in the list's documented order."},"nextCursor":{"type":["string","null"],"description":"Pass as cursor to continue, or null when there are no more items."}},"required":["items","nextCursor"]}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/usage":{"get":{"operationId":"listUsage","x-permissions":{"usage":"read"},"x-role":"read","description":"Token permissions: usage:read. Project role: read. Ordered by periodStart ascending. A cursor retains the resolved from/to window and period for all pages.","summary":"List Usage","tags":["Usage"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":200,"description":"Maximum items per page, from 1 to 200."},"required":false,"description":"Maximum items per page, from 1 to 200.","name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page."},"required":false,"description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.","name":"cursor","in":"query"},{"schema":{"type":"string","enum":["minute","hour","day","month","year","forever"],"default":"day","description":"UTC calendar aggregation grain; defaults to day. from/to select whole periods by their start, without clipping quantities.","example":"hour"},"required":false,"description":"UTC calendar aggregation grain; defaults to day. from/to select whole periods by their start, without clipping quantities.","name":"period","in":"query"},{"schema":{"type":"string","format":"date-time","description":"Inclusive RFC 3339 period-start bound. Defaults to 30 days before to."},"required":false,"description":"Inclusive RFC 3339 period-start bound. Defaults to 30 days before to.","name":"from","in":"query"},{"schema":{"type":"string","format":"date-time","description":"Exclusive RFC 3339 period-start bound. Defaults to the request time; periods are selected by start, not clipped."},"required":false,"description":"Exclusive RFC 3339 period-start bound. Defaults to the request time; periods are selected by start, not clipped.","name":"to","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Usage"},"description":"This page's items, in the list's documented order."},"nextCursor":{"type":["string","null"],"description":"Pass as cursor to continue, or null when there are no more items."},"project":{"type":"string","description":"The project's immutable id."},"period":{"type":"string","description":"The resolved aggregation grain."},"from":{"type":"string","format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"},"to":{"type":"string","format":"date-time","description":"RFC 3339 timestamp with a timezone; responses are normalized to UTC.","example":"2026-10-22T12:00:00.000Z"},"allowances":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Allowance"},"description":"Monthly pages only: each settled month's frozen allowance by its periodStart, or null while the account split is pending. Members only."}},"required":["items","nextCursor","project","period","from","to","allowances"]}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/usage/current":{"get":{"operationId":"getCurrentUsage","x-permissions":{"usage":"read"},"x-role":"read","summary":"Get Current Usage","tags":["Usage"],"description":"Token permissions: usage:read. Project role: read.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CurrentUsage"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/invoices":{"get":{"operationId":"listInvoices","x-permissions":{"billing":"read"},"x-role":"owner","summary":"List Invoices","tags":["Invoices"],"description":"Token permissions: billing:read. Project role: owner. Ordered by periodStart descending.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":200,"description":"Maximum items per page, from 1 to 200."},"required":false,"description":"Maximum items per page, from 1 to 200.","name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page."},"required":false,"description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.","name":"cursor","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Invoice"},"description":"This page's items, in the list's documented order."},"nextCursor":{"type":["string","null"],"description":"Pass as cursor to continue, or null when there are no more items."}},"required":["items","nextCursor"]}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/spend-cap":{"get":{"operationId":"getSpendCap","x-permissions":{"billing":"read"},"x-role":"owner","summary":"Get Spend Cap","tags":["Spend-cap"],"description":"Token permissions: billing:read. Project role: owner.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpendCap"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"patch":{"operationId":"setSpendCap","x-permissions":{"billing":"write"},"x-role":"owner","summary":"Set Spend Cap","tags":["Spend-cap"],"description":"Token permissions: billing:write. Project role: owner.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"requestBody":{"required":true,"description":"JSON Merge Patch: omitted fields remain unchanged, arrays replace, and null clears nullable fields.","content":{"application/json":{"schema":{"type":"object","properties":{"spendCapMicros":{"type":["number","null"],"minimum":1000000,"maximum":1000000000000,"description":"Monthly spend cap in micro-USD (1,000,000 = USD 1), from USD 1 to USD 1,000,000; null removes it."}},"required":["spendCapMicros"],"additionalProperties":false}},"application/merge-patch+json":{"schema":{"type":"object","properties":{"spendCapMicros":{"type":["number","null"],"minimum":1000000,"maximum":1000000000000,"description":"Monthly spend cap in micro-USD (1,000,000 = USD 1), from USD 1 to USD 1,000,000; null removes it."}},"required":["spendCapMicros"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpendCap"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/payment":{"get":{"operationId":"getPayment","x-permissions":{"billing":"read"},"x-role":"owner","description":"Token permissions: billing:read. Project role: owner. The card summary, gate, contact, and spend cap.","summary":"Get Payment","tags":["Payment"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Payment"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"delete":{"operationId":"removePaymentMethod","x-permissions":{"billing":"write"},"x-role":"owner","summary":"Remove Payment Method","tags":["Payment"],"description":"Token permissions: billing:write. Project role: owner.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingStatus"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/credit":{"get":{"operationId":"getCreditBalance","x-permissions":{"billing":"read"},"x-role":"read","summary":"Get Credit Balance","tags":["Credit"],"description":"Token permissions: billing:read. Project role: read.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"project":{"type":"string","description":"Immutable project id."},"balanceMicros":{"type":"integer","description":"Available in-app credit in micro-USD."}},"required":["project","balanceMicros"]}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/payment/setup-link":{"post":{"operationId":"createPaymentSetupLink","x-permissions":{"billing":"write"},"x-role":"owner","description":"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.","summary":"Create Payment Setup Link","tags":["Payment"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"Stripe-hosted URL. Share only with the person managing payment."}},"required":["url"]}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/payment/setup-intent":{"post":{"operationId":"createPaymentSetupIntent","x-permissions":{"billing":"write"},"x-role":"owner","description":"Token permissions: billing:write. Project role: owner. Embedded alternative to the hosted setup link. Confirm through Stripe.js, then call confirmPaymentMethod.","summary":"Create Payment Setup Intent","tags":["Payment"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"clientSecret":{"type":"string","description":"Stripe SetupIntent secret for the card-entry browser only."}},"required":["clientSecret"]}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/payment/confirm":{"post":{"operationId":"confirmPaymentMethod","x-permissions":{"billing":"write"},"x-role":"owner","summary":"Confirm Payment Method","tags":["Payment"],"description":"Token permissions: billing:write. Project role: owner.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"setupIntentId":{"type":"string","minLength":1,"description":"The SetupIntent confirmed by Stripe.js."}},"required":["setupIntentId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BillingStatus"},{"type":"object","properties":{"paymentLabel":{"type":"string","description":"Saved method label."},"paymentLast4":{"type":["string","null"],"description":"Last four digits, or null."}},"required":["paymentLabel","paymentLast4"]}]}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/payment/portal":{"post":{"operationId":"createBillingPortalLink","x-permissions":{"billing":"write"},"x-role":"owner","summary":"Create Billing Portal Link","tags":["Payment"],"description":"Token permissions: billing:write. Project role: owner.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"Stripe-hosted URL. Share only with the person managing payment."}},"required":["url"]}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/payment/retry":{"post":{"operationId":"payNow","x-permissions":{"billing":"write"},"x-role":"owner","description":"Token permissions: billing:write. Project role: owner. Collect outstanding invoices using the card on file, through the existing settlement rules.","summary":"Pay Now","tags":["Payment"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingStatus"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/payment/contact":{"patch":{"operationId":"setBillingContact","x-permissions":{"billing":"write"},"x-role":"owner","summary":"Set Billing Contact","tags":["Payment"],"description":"Token permissions: billing:write. Project role: owner.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"requestBody":{"required":true,"description":"JSON Merge Patch: omitted fields remain unchanged, arrays replace, and null clears nullable fields.","content":{"application/json":{"schema":{"type":"object","properties":{"contact":{"type":"string","maxLength":320,"description":"Billing email; empty restores the owner's address."}},"required":["contact"],"additionalProperties":false}},"application/merge-patch+json":{"schema":{"type":"object","properties":{"contact":{"type":"string","maxLength":320,"description":"Billing email; empty restores the owner's address."}},"required":["contact"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"billingContact":{"type":"string","description":"Explicit billing email; empty uses the owner's address."},"billingRecipient":{"type":"string","description":"Resolved address receiving billing mail."}},"required":["billingContact","billingRecipient"]}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/payment/statements":{"patch":{"operationId":"setStatementPreferences","x-permissions":{"billing":"write"},"x-role":"owner","description":"Token permissions: billing:write. Project role: owner. Turn the monthly statement for months with nothing to pay on or off.","summary":"Set Statement Preferences","tags":["Payment"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"requestBody":{"required":true,"description":"JSON Merge Patch: omitted fields remain unchanged, arrays replace, and null clears nullable fields.","content":{"application/json":{"schema":{"type":"object","properties":{"forgivenStatements":{"type":"boolean","description":"Send the monthly statement for months with nothing to pay. Months that charge or owe always send."}},"required":["forgivenStatements"],"additionalProperties":false}},"application/merge-patch+json":{"schema":{"type":"object","properties":{"forgivenStatements":{"type":"boolean","description":"Send the monthly statement for months with nothing to pay. Months that charge or owe always send."}},"required":["forgivenStatements"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"forgivenStatements":{"type":"boolean","description":"Send the monthly statement for months with nothing to pay. Months that charge or owe always send."}},"required":["forgivenStatements"]}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/payment/identity":{"get":{"operationId":"getBillingIdentity","x-permissions":{"billing":"read"},"x-role":"owner","summary":"Get Billing Identity","tags":["Payment"],"description":"Token permissions: billing:read. Project role: owner.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"identity":{"$ref":"#/components/schemas/BillingIdentity"}},"required":["identity"]}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"patch":{"operationId":"setBillingIdentity","x-permissions":{"billing":"write"},"x-role":"owner","description":"Token permissions: billing:write. Project role: owner. Supply the complete identity; empty address fields clear them and null clears the tax ID.","summary":"Set Billing Identity","tags":["Payment"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"requestBody":{"required":true,"description":"JSON Merge Patch: omitted fields remain unchanged, arrays replace, and null clears nullable fields.","content":{"application/json":{"schema":{"type":"object","properties":{"companyName":{"type":"string","maxLength":200,"description":"The legal entity billed; empty falls back to the email."},"address":{"type":"object","properties":{"line1":{"type":"string","maxLength":200,"description":"Street line 1."},"line2":{"type":"string","maxLength":200,"description":"Street line 2."},"city":{"type":"string","maxLength":200,"description":"City."},"state":{"type":"string","maxLength":200,"description":"State or region."},"postalCode":{"type":"string","maxLength":30,"description":"Postal code."},"country":{"anyOf":[{"type":"string","enum":[""]},{"type":"string","pattern":"^[A-Za-z]{2}$"}],"description":"ISO 3166-1 alpha-2 country; empty clears the address."}},"required":["line1","line2","city","state","postalCode","country"],"additionalProperties":false,"description":"Billing address; empty fields clear them."},"taxId":{"type":["object","null"],"properties":{"type":{"type":"string","enum":["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"],"description":"Stripe tax id type."},"value":{"type":"string","maxLength":50,"description":"The registration number."}},"required":["type","value"],"additionalProperties":false,"description":"One tax registration, or null to clear it."}},"required":["companyName","address","taxId"],"additionalProperties":false}},"application/merge-patch+json":{"schema":{"type":"object","properties":{"companyName":{"type":"string","maxLength":200,"description":"The legal entity billed; empty falls back to the email."},"address":{"type":"object","properties":{"line1":{"type":"string","maxLength":200,"description":"Street line 1."},"line2":{"type":"string","maxLength":200,"description":"Street line 2."},"city":{"type":"string","maxLength":200,"description":"City."},"state":{"type":"string","maxLength":200,"description":"State or region."},"postalCode":{"type":"string","maxLength":30,"description":"Postal code."},"country":{"anyOf":[{"type":"string","enum":[""]},{"type":"string","pattern":"^[A-Za-z]{2}$"}],"description":"ISO 3166-1 alpha-2 country; empty clears the address."}},"required":["line1","line2","city","state","postalCode","country"],"additionalProperties":false,"description":"Billing address; empty fields clear them."},"taxId":{"type":["object","null"],"properties":{"type":{"type":"string","enum":["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"],"description":"Stripe tax id type."},"value":{"type":"string","maxLength":50,"description":"The registration number."}},"required":["type","value"],"additionalProperties":false,"description":"One tax registration, or null to clear it."}},"required":["companyName","address","taxId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"identity":{"$ref":"#/components/schemas/BillingIdentity"}},"required":["identity"]}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/payment/net-terms":{"post":{"operationId":"requestNetTerms","x-permissions":{"billing":"write"},"x-role":"owner","summary":"Request Net Terms","tags":["Payment"],"description":"Token permissions: billing:write. Project role: owner.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"netTerms":{"type":"string","description":"Net-terms approval state; empty means no request. Known values: \"\", \"pending\", \"active\", \"cancelling\", \"revoking\". Additional values may appear.","x-extensible-enum":["","pending","active","cancelling","revoking"]}},"required":["netTerms"]}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"delete":{"operationId":"cancelNetTerms","x-permissions":{"billing":"write"},"x-role":"owner","summary":"Cancel Net Terms","tags":["Payment"],"description":"Token permissions: billing:write. Project role: owner.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"netTerms":{"type":"string","description":"Net-terms approval state; empty means no request. Known values: \"\", \"pending\", \"active\", \"cancelling\", \"revoking\". Additional values may appear.","x-extensible-enum":["","pending","active","cancelling","revoking"]}},"required":["netTerms"]}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/payment/credit-limit":{"post":{"operationId":"requestCreditLimit","x-permissions":{"billing":"write"},"x-role":"owner","description":"Token permissions: billing:write. Project role: owner. Request an increase; the approved limit and charges do not change until an operator approves it.","summary":"Request Credit Limit","tags":["Payment"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"creditLimitMicros":{"type":"integer","exclusiveMinimum":0,"maximum":1000000000000,"description":"Requested credit limit in micro-USD, above the current limit."},"reason":{"type":"string","maxLength":500,"description":"Why the higher limit is needed."}},"required":["creditLimitMicros","reason"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"creditLimitRequest":{"$ref":"#/components/schemas/CreditLimitRequestResponse"}},"required":["creditLimitRequest"]}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/notifications/live":{"get":{"operationId":"watchNotifications","x-permissions":{"settings":"read"},"x-role":"read","description":"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.","x-websocket":{"message":{"$ref":"#/components/schemas/NotificationFrame"},"heartbeat":{"send":"ping","receive":"pong"},"lifetimeSeconds":600},"summary":"Watch Notifications","tags":["Notifications"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"responses":{"101":{"description":"Switching Protocols: the socket is open. Each server message is a NotificationFrame; see x-websocket."},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/notifications/dismiss":{"post":{"operationId":"dismissNotification","x-permissions":{"settings":"write"},"x-role":"write","description":"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.","summary":"Dismiss Notification","tags":["Notifications"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","maxLength":64,"description":"The notice's id. Only a notice whose dismissible is true can be dismissed."}},"required":["id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationFrame"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/account/tokens":{"get":{"operationId":"listTokens","x-permissions":{"credentials":"read"},"description":"Token permissions: credentials:read. Unrevoked tokens; an expired one stays listed until revoked. Ordered by createdAt descending, then id descending.","summary":"List Tokens","tags":["Tokens"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":200,"description":"Maximum items per page, from 1 to 200."},"required":false,"description":"Maximum items per page, from 1 to 200.","name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page."},"required":false,"description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.","name":"cursor","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ApiToken"},"description":"This page's items, in the list's documented order."},"nextCursor":{"type":["string","null"],"description":"Pass as cursor to continue, or null when there are no more items."}},"required":["items","nextCursor"]}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"operationId":"createToken","x-permissions":{"credentials":"write"},"summary":"Create Token","tags":["Tokens"],"description":"Token permissions: credentials:write.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"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."},"required":true,"description":"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.","name":"Idempotency-Key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Human-readable label.","example":"github-actions"},"permissions":{"$ref":"#/components/schemas/PermissionsInput"},"projects":{"type":["array","null"],"items":{"type":"string"},"minItems":1,"maxItems":100,"description":"Project ids to restrict it to, which allows only the project families. Omit or null for every project the account reaches."},"expiresAt":{"type":["string","null"],"format":"date-time","description":"Expiry in the future. Omit or null for never; a token with admin expires within 90 days.","example":"2026-10-22T12:00:00.000Z"}},"required":["name","permissions"],"additionalProperties":false}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedApiToken"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/account/tokens/current":{"delete":{"operationId":"revokeCurrentToken","x-permissions":{},"description":"Any token. Revokes the calling token: logout for an agent. A session cookie is refused.","summary":"Revoke Current Token","tags":["Tokens"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"responses":{"204":{"description":"Success. The response has no body."},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/account/tokens/{id}":{"delete":{"operationId":"revokeToken","x-permissions":{"credentials":"write"},"description":"Token permissions: credentials:write. Anything still using it gets 401 right away. A token revokes itself without credentials.","summary":"Revoke Token","tags":["Tokens"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"The token's id field."},"required":true,"description":"The token's id field.","name":"id","in":"path"}],"responses":{"204":{"description":"Success. The response has no body."},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"patch":{"operationId":"updateToken","x-permissions":{"credentials":"write"},"description":"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.","summary":"Update Token","tags":["Tokens"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"The token's id field."},"required":true,"description":"The token's id field.","name":"id","in":"path"}],"requestBody":{"required":true,"description":"JSON Merge Patch: omitted fields remain unchanged, arrays replace, and null clears nullable fields.","content":{"application/json":{"schema":{"type":"object","properties":{"permissions":{"allOf":[{"$ref":"#/components/schemas/PermissionsInput"},{"description":"Lower levels for the families named; omitted ones stay."}]},"projects":{"type":"array","items":{"type":"string"},"minItems":1,"maxItems":100,"description":"The projects it keeps: a subset of its list, or any of yours for an all-projects token."}},"additionalProperties":false}},"application/merge-patch+json":{"schema":{"type":"object","properties":{"permissions":{"allOf":[{"$ref":"#/components/schemas/PermissionsInput"},{"description":"Lower levels for the families named; omitted ones stay."}]},"projects":{"type":"array","items":{"type":"string"},"minItems":1,"maxItems":100,"description":"The projects it keeps: a subset of its list, or any of yours for an all-projects token."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiToken"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/account":{"get":{"operationId":"getAccount","x-permissions":{"account":"read"},"summary":"Get Account","tags":["Account"],"description":"Token permissions: account:read.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Account"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"patch":{"operationId":"updateAccount","x-permissions":{"account":"write"},"summary":"Update Account","tags":["Account"],"description":"Token permissions: account:write.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"requestBody":{"required":true,"description":"JSON Merge Patch: omitted fields remain unchanged, arrays replace, and null clears nullable fields.","content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":2,"maxLength":100,"description":"Account display name, between 2 and 100 characters. Omit to leave unchanged."}},"additionalProperties":false}},"application/merge-patch+json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":2,"maxLength":100,"description":"Account display name, between 2 and 100 characters. Omit to leave unchanged."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Account"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"delete":{"operationId":"deleteAccount","x-permissions":{"account":"write"},"description":"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.","summary":"Delete Account","tags":["Account"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","description":"Opaque nextStep from the previous 202 response; omit to start deleting."},"required":false,"description":"Opaque nextStep from the previous 202 response; omit to start deleting.","name":"step","in":"query"},{"schema":{"type":"string","description":"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."},"required":false,"description":"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.","name":"Prefer","in":"header"}],"responses":{"202":{"description":"Not finished. Repeat the request with nextStep as step to continue.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountDeletionStep"}}}},"204":{"description":"Success. The response has no body."},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/account/usage":{"get":{"operationId":"getAccountUsage","x-permissions":{"billing":"read"},"summary":"Get Account Usage","tags":["Usage"],"description":"Token permissions: billing:read.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountUsage"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/account/export":{"get":{"operationId":"exportAccount","x-permissions":{"account":"read"},"description":"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.","summary":"Export Account","tags":["Export"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":200,"description":"Maximum items per page, from 1 to 200."},"required":false,"description":"Maximum items per page, from 1 to 200.","name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page."},"required":false,"description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.","name":"cursor","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountExport"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/account/deletion-preview":{"get":{"operationId":"getAccountDeletionPreview","x-permissions":{"account":"read"},"description":"Token permissions: account:read. Lists the owned projects account deletion would destroy, in id order, a page at a time.","summary":"Get Account Deletion Preview","tags":["Deletion-preview"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":200,"description":"Maximum items per page, from 1 to 200."},"required":false,"description":"Maximum items per page, from 1 to 200.","name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page."},"required":false,"description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.","name":"cursor","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountDeletionPreview"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/account/passkeys":{"get":{"operationId":"listPasskeys","x-permissions":{"credentials":"read"},"description":"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.","summary":"List Passkeys","tags":["Passkeys"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":200,"description":"Maximum items per page, from 1 to 200."},"required":false,"description":"Maximum items per page, from 1 to 200.","name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page."},"required":false,"description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.","name":"cursor","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Passkey"},"description":"This page's items, in the list's documented order."},"nextCursor":{"type":["string","null"],"description":"Pass as cursor to continue, or null when there are no more items."}},"required":["items","nextCursor"]}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/account/passkeys/{id}":{"delete":{"operationId":"deletePasskey","x-permissions":{"credentials":"write"},"description":"Token permissions: credentials:write. It stops signing in at once; sessions it already started continue.","summary":"Delete Passkey","tags":["Passkeys"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"The passkey's id field."},"required":true,"description":"The passkey's id field.","name":"id","in":"path"}],"responses":{"204":{"description":"Success. The response has no body."},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/account/sessions/current":{"delete":{"operationId":"revokeCurrentSession","x-permissions":{},"description":"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.","summary":"Revoke Current Session","tags":["Sessions"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"responses":{"204":{"description":"Success. The response has no body."},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/account/sessions":{"delete":{"operationId":"revokeSessions","x-permissions":{"credentials":"write"},"description":"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.","summary":"Revoke Sessions","tags":["Sessions"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"responses":{"204":{"description":"Success. The response has no body."},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/members":{"get":{"operationId":"listMembers","x-permissions":{"members":"read"},"x-role":"read","summary":"List Members","tags":["Members"],"description":"Token permissions: members:read. Project role: read. Ordered by createdAt ascending, then accountId ascending.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":200,"description":"Maximum items per page, from 1 to 200."},"required":false,"description":"Maximum items per page, from 1 to 200.","name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page."},"required":false,"description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.","name":"cursor","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ProjectMember"},"description":"This page's items, in the list's documented order."},"nextCursor":{"type":["string","null"],"description":"Pass as cursor to continue, or null when there are no more items."}},"required":["items","nextCursor"]}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/members/{accountId}":{"patch":{"operationId":"updateMember","x-permissions":{"members":"write"},"x-role":"owner","summary":"Update Member","tags":["Members"],"description":"Token permissions: members:write. Project role: owner.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","minLength":1,"description":"Member account id from the roster."},"required":true,"description":"Member account id from the roster.","name":"accountId","in":"path"}],"requestBody":{"required":true,"description":"JSON Merge Patch: omitted fields remain unchanged, arrays replace, and null clears nullable fields.","content":{"application/json":{"schema":{"type":"object","properties":{"role":{"type":"string","enum":["read","write"],"description":"Member access; ownership moves only through an accepted transfer."}},"required":["role"],"additionalProperties":false}},"application/merge-patch+json":{"schema":{"type":"object","properties":{"role":{"type":"string","enum":["read","write"],"description":"Member access; ownership moves only through an accepted transfer."}},"required":["role"],"additionalProperties":false}}}},"responses":{"204":{"description":"Success. The response has no body."},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"delete":{"operationId":"removeMember","x-permissions":{"members":"write"},"x-role":"owner","summary":"Remove Member","tags":["Members"],"description":"Token permissions: members:write. Project role: owner.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"string","minLength":1,"description":"Member account id from the roster."},"required":true,"description":"Member account id from the roster.","name":"accountId","in":"path"}],"responses":{"204":{"description":"Success. The response has no body."},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/invites":{"get":{"operationId":"listInvitations","x-permissions":{"members":"read"},"x-role":"read","summary":"List Invitations","tags":["Invites"],"description":"Token permissions: members:read. Project role: read. Ordered by createdAt ascending, then email ascending.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":200,"description":"Maximum items per page, from 1 to 200."},"required":false,"description":"Maximum items per page, from 1 to 200.","name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page."},"required":false,"description":"Opaque nextCursor from the previous response. Keep the same resource and filters when continuing; omit for the first page.","name":"cursor","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ProjectInvitation"},"description":"This page's items, in the list's documented order."},"nextCursor":{"type":["string","null"],"description":"Pass as cursor to continue, or null when there are no more items."}},"required":["items","nextCursor"]}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"operationId":"inviteMember","x-permissions":{"members":"write"},"x-role":"owner","description":"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.","summary":"Invite Member","tags":["Invites"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email","description":"Email address to invite."},"role":{"type":"string","enum":["read","write"],"description":"Access to grant; defaults to write."}},"required":["email"],"additionalProperties":false}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"pending":{"type":"boolean","description":"True when the email has no account yet. The invite is claimed on sign-in or by acceptInvitation."}},"required":["pending"]}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"delete":{"operationId":"revokeInvitation","x-permissions":{"members":"write"},"x-role":"owner","summary":"Revoke Invitation","tags":["Invites"],"description":"Token permissions: members:write. Project role: owner.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email","description":"Invited email address."}},"required":["email"],"additionalProperties":false}}}},"responses":{"204":{"description":"Success. The response has no body."},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/invites/accept":{"post":{"operationId":"acceptInvitation","x-permissions":{"members":"write"},"description":"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.","summary":"Accept Invitation","tags":["Invites"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"responses":{"204":{"description":"Success. The response has no body."},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/transfer":{"get":{"operationId":"getOwnershipTransfer","x-permissions":{"members":"read"},"x-role":"read","summary":"Get Ownership Transfer","tags":["Transfer"],"description":"Token permissions: members:read. Project role: read.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OwnershipTransfer"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"operationId":"offerOwnershipTransfer","x-permissions":{"members":"write"},"x-role":"owner","description":"Token permissions: members:write. Project role: owner. Replaces the outstanding offer; banned projects cannot be transferred.","summary":"Offer Ownership Transfer","tags":["Transfer"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email","description":"Existing direct Editor member to receive ownership."}},"required":["email"],"additionalProperties":false}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OwnershipTransfer"}}}},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"delete":{"operationId":"cancelOwnershipTransfer","x-permissions":{"members":"write"},"x-role":"owner","summary":"Cancel Ownership Transfer","tags":["Transfer"],"description":"Token permissions: members:write. Project role: owner.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"responses":{"204":{"description":"Success. The response has no body."},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/transfer/accept":{"post":{"operationId":"acceptOwnershipTransfer","x-permissions":{"members":"write"},"x-role":"read","description":"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.","summary":"Accept Ownership Transfer","tags":["Transfer"],"security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"sameEntity":{"type":"boolean","description":"True retains the billed company and removes its card; false clears billing identity and writes off unpaid invoices. The recipient chooses explicitly."}},"required":["sameEntity"],"additionalProperties":false}}}},"responses":{"204":{"description":"Success. The response has no body."},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/projects/{pid}/transfer/decline":{"post":{"operationId":"declineOwnershipTransfer","x-permissions":{"members":"write"},"x-role":"read","summary":"Decline Ownership Transfer","tags":["Transfer"],"description":"Token permissions: members:write. Project role: read.","security":[{"bearerAuth":[]},{"sessionCookie":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"description":"Immutable public project id from Project.id; vanity names are refused.","example":"p123example"},"required":true,"description":"Immutable public project id from Project.id; vanity names are refused.","name":"pid","in":"path"}],"responses":{"204":{"description":"Success. The response has no body."},"4XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"5XX":{"description":"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":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/oauth/device":{"post":{"operationId":"authorizeDevice","tags":["OAuth"],"summary":"Start device login","security":[],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"client_id":{"type":"string","enum":["moq-pro"],"description":"This CLI's registered OAuth client id."},"name":{"type":"string","minLength":1,"maxLength":100,"description":"Requester-chosen token name; defaults to moq-pro CLI."},"scope":{"type":"string","minLength":1,"maxLength":1000,"default":"write","description":"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."},"projects":{"type":"string","minLength":1,"maxLength":5000,"description":"Space-separated project ids. Omit for all current and future projects the account can access."}},"required":["client_id"],"additionalProperties":false}}}},"responses":{"200":{"description":"Show the URL and code to the human. Never approve automatically.","content":{"application/json":{"schema":{"type":"object","properties":{"device_code":{"type":"string","description":"Secret polling code for the token exchange; never show it."},"user_code":{"type":"string","description":"Short code the human confirms in the browser."},"verification_uri":{"type":"string","format":"uri","description":"Where the human approves the login."},"verification_uri_complete":{"type":"string","format":"uri","description":"verification_uri with the user_code filled in."},"expires_in":{"type":"integer","description":"Seconds until the device_code expires."},"interval":{"type":"integer","description":"Minimum seconds between token polls."}},"required":["device_code","user_code","verification_uri","verification_uri_complete","expires_in","interval"]}}}},"400":{"description":"Invalid client, scope, or request.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"RFC 6749 error code."},"error_description":{"type":"string","description":"Human-readable explanation."}},"required":["error"]}}}},"429":{"description":"IP rate limit reached. Stop and start login again later.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"RFC 6749 error code."},"error_description":{"type":"string","description":"Human-readable explanation."}},"required":["error"]}}}}}}},"/oauth/token":{"post":{"operationId":"exchangeDeviceCode","tags":["OAuth"],"summary":"Poll device login","security":[],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"client_id":{"type":"string","enum":["moq-pro"],"description":"This CLI's registered OAuth client id."},"grant_type":{"type":"string","enum":["urn:ietf:params:oauth:grant-type:device_code"],"description":"The RFC 8628 device-code grant type."},"device_code":{"type":"string","pattern":"^[A-Za-z0-9_-]{43}$","description":"The device_code from the device login response."}},"required":["client_id","grant_type","device_code"],"additionalProperties":false}}}},"responses":{"200":{"description":"An ordinary, revocable account token that expires after 90 days. No refresh token; log in again.","content":{"application/json":{"schema":{"type":"object","properties":{"access_token":{"type":"string","description":"Account API token for the Authorization: Bearer header."},"token_type":{"type":"string","description":"Always Bearer. Known values: \"Bearer\". Additional values may appear.","x-extensible-enum":["Bearer"]},"scope":{"type":"string","description":"Permissions granted, space-separated family:level entries for each family above none."}},"required":["access_token","token_type","scope"]}}}},"400":{"description":"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":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"RFC 6749 error code."},"error_description":{"type":"string","description":"Human-readable explanation."}},"required":["error"]}}}},"429":{"description":"IP rate limit reached. Stop and start login again later.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"RFC 6749 error code."},"error_description":{"type":"string","description":"Human-readable explanation."}},"required":["error"]}}}}}}}},"webhooks":{"broadcast.started":{"post":{"summary":"broadcast.started","description":"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.","security":[],"parameters":[{"schema":{"type":"string","description":"t=<Unix seconds>,v1=<lowercase hex HMAC-SHA256(secret, t + '.' + raw body)>. Reject timestamps more than 300 seconds from your server time."},"required":true,"description":"t=<Unix seconds>,v1=<lowercase hex HMAC-SHA256(secret, t + '.' + raw body)>. Reject timestamps more than 300 seconds from your server time.","name":"Moq-Signature","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Stable event identifier. Deduplicate on this, including across endpoints."},"type":{"type":"string","x-extensible-enum":["broadcast.started"],"description":"Known values: \"broadcast.started\". Additional values may appear."},"created_at":{"type":"string","description":"When the API emitted this event, in UTC."},"project":{"type":"string","description":"Immutable project pid."},"data":{"type":"object","properties":{"path":{"type":"string","description":"Broadcast path relative to the project."},"at":{"type":"string","description":"Source observation time in UTC, independent of the envelope creation time."},"backfill":{"type":"boolean","description":"A live path discovered in a snapshot, including when started events turn on. Known values: true. Additional values may appear.","x-extensible-enum":[true]}},"required":["path","at"]}},"required":["id","type","created_at","project","data"]}}}},"responses":{"2XX":{"description":"Event durably accepted. Any non-2xx response fails this attempt; transient failures may retry."}}}},"broadcast.ended":{"post":{"summary":"broadcast.ended","description":"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.","security":[],"parameters":[{"schema":{"type":"string","description":"t=<Unix seconds>,v1=<lowercase hex HMAC-SHA256(secret, t + '.' + raw body)>. Reject timestamps more than 300 seconds from your server time."},"required":true,"description":"t=<Unix seconds>,v1=<lowercase hex HMAC-SHA256(secret, t + '.' + raw body)>. Reject timestamps more than 300 seconds from your server time.","name":"Moq-Signature","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Stable event identifier. Deduplicate on this, including across endpoints."},"type":{"type":"string","x-extensible-enum":["broadcast.ended"],"description":"Known values: \"broadcast.ended\". Additional values may appear."},"created_at":{"type":"string","description":"When the API emitted this event, in UTC."},"project":{"type":"string","description":"Immutable project pid."},"data":{"type":"object","properties":{"path":{"type":"string","description":"Broadcast path relative to the project."},"at":{"type":"string","description":"Source observation time in UTC, independent of the envelope creation time."},"reason":{"type":"string","x-extensible-enum":["unannounced","connection_lost"],"description":"Known values: \"unannounced\", \"connection_lost\". Additional values may appear."}},"required":["path","at","reason"]}},"required":["id","type","created_at","project","data"]}}}},"responses":{"2XX":{"description":"Event durably accepted. Any non-2xx response fails this attempt; transient failures may retry."}}}},"recording.started":{"post":{"summary":"recording.started","description":"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.","security":[],"parameters":[{"schema":{"type":"string","description":"t=<Unix seconds>,v1=<lowercase hex HMAC-SHA256(secret, t + '.' + raw body)>. Reject timestamps more than 300 seconds from your server time."},"required":true,"description":"t=<Unix seconds>,v1=<lowercase hex HMAC-SHA256(secret, t + '.' + raw body)>. Reject timestamps more than 300 seconds from your server time.","name":"Moq-Signature","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Stable event identifier. Deduplicate on this, including across endpoints."},"type":{"type":"string","x-extensible-enum":["recording.started"],"description":"Known values: \"recording.started\". Additional values may appear."},"created_at":{"type":"string","description":"When the API emitted this event, in UTC."},"project":{"type":"string","description":"Immutable project pid."},"data":{"type":"object","properties":{"path":{"type":"string","description":"Broadcast path relative to the project."},"at":{"type":"string","description":"Source observation time in UTC, independent of the envelope creation time."},"recordingId":{"type":"string","description":"Logical recording: <path>/<epoch>."},"generationId":{"type":"string","description":"Physical generation: <path>/<epoch>/<n>."},"bucket":{"type":"string","description":"Customer bucket name. Omitted for managed recordings."},"objectPrefix":{"type":"string","description":"Generation's object-key prefix, including its trailing /. BYOB only."},"masterPlaylist":{"type":"string","description":"Master playlist URI relative to objectPrefix, or the managed playback URL's directory."},"renditions":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"kind":{"type":"string","x-extensible-enum":["video","audio"],"description":"Known values: \"video\", \"audio\". Additional values may appear."},"playlist":{"type":"string","description":"Rendition playlist URI relative to the same directory as masterPlaylist."},"width":{"type":"integer","exclusiveMinimum":0,"description":"Video width in pixels, when known."},"height":{"type":"integer","exclusiveMinimum":0,"description":"Video height in pixels, when known."}},"required":["name","kind","playlist"]},"description":"Empty at start; exported renditions when ready, acknowledged media renditions when failed."}},"required":["path","at","recordingId","generationId"]}},"required":["id","type","created_at","project","data"]}}}},"responses":{"2XX":{"description":"Event durably accepted. Any non-2xx response fails this attempt; transient failures may retry."}}}},"recording.ready":{"post":{"summary":"recording.ready","description":"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.","security":[],"parameters":[{"schema":{"type":"string","description":"t=<Unix seconds>,v1=<lowercase hex HMAC-SHA256(secret, t + '.' + raw body)>. Reject timestamps more than 300 seconds from your server time."},"required":true,"description":"t=<Unix seconds>,v1=<lowercase hex HMAC-SHA256(secret, t + '.' + raw body)>. Reject timestamps more than 300 seconds from your server time.","name":"Moq-Signature","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Stable event identifier. Deduplicate on this, including across endpoints."},"type":{"type":"string","x-extensible-enum":["recording.ready"],"description":"Known values: \"recording.ready\". Additional values may appear."},"created_at":{"type":"string","description":"When the API emitted this event, in UTC."},"project":{"type":"string","description":"Immutable project pid."},"data":{"type":"object","properties":{"path":{"type":"string","description":"Broadcast path relative to the project."},"at":{"type":"string","description":"Source observation time in UTC, independent of the envelope creation time."},"recordingId":{"type":"string","description":"Logical recording: <path>/<epoch>."},"generationId":{"type":"string","description":"Physical generation: <path>/<epoch>/<n>."},"bucket":{"type":"string","description":"Customer bucket name. Omitted for managed recordings."},"objectPrefix":{"type":"string","description":"Generation's object-key prefix, including its trailing /. BYOB only."},"masterPlaylist":{"type":"string","description":"Master playlist URI relative to objectPrefix, or the managed playback URL's directory."},"renditions":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"kind":{"type":"string","x-extensible-enum":["video","audio"],"description":"Known values: \"video\", \"audio\". Additional values may appear."},"playlist":{"type":"string","description":"Rendition playlist URI relative to the same directory as masterPlaylist."},"width":{"type":"integer","exclusiveMinimum":0,"description":"Video width in pixels, when known."},"height":{"type":"integer","exclusiveMinimum":0,"description":"Video height in pixels, when known."}},"required":["name","kind","playlist"]},"description":"Empty at start; exported renditions when ready, acknowledged media renditions when failed."},"regression":{"type":["string","null"],"description":"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."},"durationMs":{"type":"number","minimum":0,"description":"Recorded media duration in milliseconds, not elapsed wall time."}},"required":["path","at","recordingId","generationId"]}},"required":["id","type","created_at","project","data"]}}}},"responses":{"2XX":{"description":"Event durably accepted. Any non-2xx response fails this attempt; transient failures may retry."}}}},"recording.failed":{"post":{"summary":"recording.failed","description":"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.","security":[],"parameters":[{"schema":{"type":"string","description":"t=<Unix seconds>,v1=<lowercase hex HMAC-SHA256(secret, t + '.' + raw body)>. Reject timestamps more than 300 seconds from your server time."},"required":true,"description":"t=<Unix seconds>,v1=<lowercase hex HMAC-SHA256(secret, t + '.' + raw body)>. Reject timestamps more than 300 seconds from your server time.","name":"Moq-Signature","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Stable event identifier. Deduplicate on this, including across endpoints."},"type":{"type":"string","x-extensible-enum":["recording.failed"],"description":"Known values: \"recording.failed\". Additional values may appear."},"created_at":{"type":"string","description":"When the API emitted this event, in UTC."},"project":{"type":"string","description":"Immutable project pid."},"data":{"type":"object","properties":{"path":{"type":"string","description":"Broadcast path relative to the project."},"at":{"type":"string","description":"Source observation time in UTC, independent of the envelope creation time."},"recordingId":{"type":"string","description":"Logical recording: <path>/<epoch>."},"generationId":{"type":"string","description":"Physical generation: <path>/<epoch>/<n>."},"bucket":{"type":"string","description":"Customer bucket name. Omitted for managed recordings."},"objectPrefix":{"type":"string","description":"Generation's object-key prefix, including its trailing /. BYOB only."},"masterPlaylist":{"type":"string","description":"Master playlist URI relative to objectPrefix, or the managed playback URL's directory."},"renditions":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"kind":{"type":"string","x-extensible-enum":["video","audio"],"description":"Known values: \"video\", \"audio\". Additional values may appear."},"playlist":{"type":"string","description":"Rendition playlist URI relative to the same directory as masterPlaylist."},"width":{"type":"integer","exclusiveMinimum":0,"description":"Video width in pixels, when known."},"height":{"type":"integer","exclusiveMinimum":0,"description":"Video height in pixels, when known."}},"required":["name","kind","playlist"]},"description":"Empty at start; exported renditions when ready, acknowledged media renditions when failed."},"regression":{"type":["string","null"],"description":"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."},"reason":{"type":"string","description":"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.","x-extensible-enum":["bucket_refusal","export_failure","platform_error"]},"mediaWritten":{"type":"boolean","description":"Whether this generation acknowledged any uploaded media before failing."}},"required":["path","at","recordingId","generationId","reason","mediaWritten"]}},"required":["id","type","created_at","project","data"]}}}},"responses":{"2XX":{"description":"Event durably accepted. Any non-2xx response fails this attempt; transient failures may retry."}}}},"webhook.test":{"post":{"summary":"webhook.test","description":"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.","security":[],"parameters":[{"schema":{"type":"string","description":"t=<Unix seconds>,v1=<lowercase hex HMAC-SHA256(secret, t + '.' + raw body)>. Reject timestamps more than 300 seconds from your server time."},"required":true,"description":"t=<Unix seconds>,v1=<lowercase hex HMAC-SHA256(secret, t + '.' + raw body)>. Reject timestamps more than 300 seconds from your server time.","name":"Moq-Signature","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Stable event identifier. Deduplicate on this, including across endpoints."},"type":{"type":"string","x-extensible-enum":["webhook.test"],"description":"Known values: \"webhook.test\". Additional values may appear."},"created_at":{"type":"string","description":"When the API emitted this event, in UTC."},"project":{"type":"string","description":"Immutable project pid."},"data":{"type":"object","properties":{"endpoint":{"type":"string","description":"The endpoint receiving this test delivery."}},"required":["endpoint"]}},"required":["id","type","created_at","project","data"]}}}},"responses":{"2XX":{"description":"Event durably accepted. Any non-2xx response fails this attempt; transient failures may retry."}}}}}}