{"openapi":"3.1.0","info":{"title":"Tablecast API","version":"0.0.1","description":"Control plane for Tablecast: orgs, productions, sessions, recordings and integrations.\n\nEvery error response is an RFC 7807 problem document."},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"A Cognito **access** token. The ID token is not an API credential and is rejected: `token_use` is asserted, not assumed."},"serviceToken":{"type":"apiKey","in":"header","name":"x-tablecast-service-token","description":"Shared secret for first-party services (session coordinator, workers)."},"guestToken":{"type":"http","scheme":"Guest","bearerFormat":"JWT","description":"Participant-scoped token returned by `POST /v1/join/{token}`, sent as `Authorization: Guest <token>`. Its subject is a participant id, not a user id — it is deliberately not a `Bearer` credential so that nothing can mistake a guest for a signed-in user (TAB-282)."},"shareToken":{"type":"http","scheme":"Share","description":"A share-link token, sent as `Authorization: Share <token>`. The same token that is in the `/s/<token>` URL, resolved by `GET /v1/s/{token}`. It names one share — one target, at one permission — and can address nothing else; only its sha256 is stored, and revoking the share (or that person's row on it) stops the very next request (TAB-542)."},"apiKeyAuth":{"type":"http","scheme":"bearer","description":"A Tablecast API key, `tck_<handle>_<secret>`, sent as `Authorization: Bearer <key>`. Issued at `POST /v1/orgs/{orgId}/api-keys` and shown once. Its status, scopes and expiry are read on every request, so revoking it takes effect immediately (TAB-68)."},"oauthToken":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"An access token from `POST /v1/oauth/token` (`client_credentials`). One hour, carries the org and the granted scopes, and is verified without a database read — which is why revoking the client stops new tokens rather than existing ones (TAB-69)."}},"schemas":{"GroupRoleMappingInput":{"type":"object","properties":{"group":{"type":"string","minLength":1,"maxLength":200},"orgRole":{"type":"string","enum":["org_admin","org_billing","org_member","org_guest"]},"roleKey":{"type":"string","minLength":2,"maxLength":64},"teamId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"teamRole":{"type":"string","enum":["team_lead","producer","director","host","member","viewer"]}},"required":["group"],"description":"One entry of the group→role mapping: a group in the IdP's `groups` claim, and what holding it means here. Applied on every login, so an edit takes effect at the member's next sign-in."},"CanvaOutputPresetInput":{"type":"object","properties":{"width":{"type":"integer","minimum":16,"maximum":8192},"height":{"type":"integer","minimum":16,"maximum":8192},"format":{"type":"string","enum":["png","mp4","gif"]},"transparent":{"type":"boolean"}},"required":["width","height","format"]},"ChannelAttachmentInput":{"type":"object","properties":{"assetId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"kind":{"type":"string","enum":["media","graphic"]},"name":{"type":"string","maxLength":200},"bytes":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["assetId","kind"]},"EdlEventRecordInput":{"type":"object","properties":{"seq":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"Monotonic per session, from 1"},"clockMs":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Session-clock ms — negative before T0, never wall time"},"actor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"`participants.id`, or null for a system op"},"source":{"type":"string","enum":["ui","midi","companion","osc","api","mcp","system"]},"op":{"type":"array","items":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"description":"RFC 6902 patch array"},"resultingVersion":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"`ProductionState.version` after applying it"},"label":{"type":"string","maxLength":500}},"required":["seq","clockMs","actor","source","op","resultingVersion"],"description":"One row of the append-only switching EDL."},"SceneLayoutInput":{"type":"object","properties":{"type":{"type":"string","enum":["single","pip","grid","split","custom"]},"slots":{"maxItems":16,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64},"rect":{"type":"object","properties":{"x":{"type":"number","minimum":0,"maximum":1},"y":{"type":"number","minimum":0,"maximum":1},"w":{"type":"number","exclusiveMinimum":0,"maximum":1},"h":{"type":"number","exclusiveMinimum":0,"maximum":1}},"required":["x","y","w","h"]},"mask":{"type":"object","properties":{"kind":{"type":"string","enum":["none","rounded","circle","custom"]},"radius":{"type":"number","minimum":0,"maximum":1},"path":{"type":"string","maxLength":4096}},"required":["kind"]},"sourceId":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}]},"zIndex":{"type":"integer","minimum":-1000,"maximum":1000}},"required":["id","rect"]}}},"required":["type","slots"],"description":"Where the pictures go. Rects are 0..1 of the program frame, so one layout renders at 720p and 4K."},"RecordCurationInput":{"type":"object","properties":{"hiddenFromTalent":{"maxItems":256,"type":"array","items":{"type":"string","minLength":1,"maxLength":200},"description":"Source ids the talent grid hides — `p_<participantId>` or `p_<participantId>:<cameraSlug>`."}},"required":["hiddenFromTalent"],"description":"What the record room hides from the on-camera talent's grid (TAB-628). View-only; never a recording control."},"ProblemDetails":{"type":"object","properties":{"type":{"type":"string"},"title":{"type":"string"},"status":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"detail":{"type":"string"},"instance":{"type":"string"},"traceId":{"type":"string"},"errors":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"],"additionalProperties":false}}},"required":["type","title","status","instance","traceId"],"additionalProperties":{},"description":"RFC 7807 problem details. Every error response in this API has this shape, plus any extension members the specific problem type defines."},"LocaleTag":{"type":"string","enum":["en-AU","en-NZ","en-GB","en-US","es","pt-BR"],"description":"A BCP-47 UI locale this product has a catalogue for: en-AU (English (Australia)), en-NZ (English (New Zealand)), en-GB (English (United Kingdom)), en-US (English (United States)), es (Spanish, machine-drafted), pt-BR (Portuguese (Brazil), machine-drafted). Not to be confused with a production's *content* languages, which are a different and much larger set."},"OrgSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"slug":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string","enum":["personal","studio","enterprise"]},"homeRegion":{"type":"string"}},"required":["id","slug","name","kind","homeRegion"],"additionalProperties":false,"description":"An organisation as it appears in the org switcher."},"OrgMembership":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"role":{"type":"string"},"status":{"type":"string"},"joinedVia":{"type":"string"},"organization":{"$ref":"#/components/schemas/OrgSummary"}},"required":["id","orgId","role","status","joinedVia","organization"],"additionalProperties":false,"description":"One person's membership of one organisation."},"UserIdentity":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"kind":{"type":"string"},"providerKey":{"type":"string"},"email":{"anyOf":[{"type":"string"},{"type":"null"}]},"emailVerified":{"type":"boolean"},"isPrimary":{"type":"boolean"},"lastLoginAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","kind","providerKey","email","emailVerified","isPrimary","lastLoginAt"],"additionalProperties":false,"description":"A login attached to this user. Several identities can point at one person (plan §11)."},"UserProfile":{"type":"object","properties":{"displayName":{"type":"string"},"pronouns":{"anyOf":[{"type":"string"},{"type":"null"}]},"title":{"anyOf":[{"type":"string"},{"type":"null"}]},"bio":{"anyOf":[{"type":"string"},{"type":"null"}]},"timezone":{"type":"string"},"locale":{"type":"string","description":"The reader's UI locale. Read as a plain string rather than as `LocaleTag`, deliberately: a row written by a build that shipped a catalogue this one does not have must still be readable, and it renders in `en-AU` rather than failing the response."},"avatarAssetId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"avatarOrgId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"avatarUrl":{"anyOf":[{"type":"string"},{"type":"null"}]},"links":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"lowerThirdDefault":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"required":["displayName","pronouns","title","bio","timezone","locale","avatarAssetId","avatarOrgId","avatarUrl","links","lowerThirdDefault"],"additionalProperties":false,"description":"The editable, human-facing part of a user."},"UserPreferences":{"type":"object","properties":{"theme":{"type":"string"},"notifications":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"defaultDevices":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"recordingDefaults":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"keymap":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"lastOrgId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"lastTeamId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"onboarding":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"required":["theme","notifications","defaultDevices","recordingDefaults","keymap","lastOrgId","lastTeamId","onboarding"],"additionalProperties":false,"description":"Per-user client settings, including the organisation to reopen on next sign-in."},"Me":{"type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"primaryEmail":{"type":"string"},"status":{"type":"string","enum":["active","suspended","deleted"]},"lastSeenAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string"}},"required":["id","primaryEmail","status","lastSeenAt","createdAt"],"additionalProperties":false},"identities":{"type":"array","items":{"$ref":"#/components/schemas/UserIdentity"}},"identityId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"memberships":{"type":"array","items":{"$ref":"#/components/schemas/OrgMembership"}},"profile":{"anyOf":[{"$ref":"#/components/schemas/UserProfile"},{"type":"null"}]},"preferences":{"anyOf":[{"$ref":"#/components/schemas/UserPreferences"},{"type":"null"}]},"platformPermissions":{"type":"array","items":{"type":"string"}}},"required":["user","identities","identityId","memberships","profile","preferences","platformPermissions"],"additionalProperties":false,"description":"Everything the signed-in user is: the person, their logins and their organisations."},"BootstrapResult":{"type":"object","properties":{"created":{"type":"boolean"},"me":{"$ref":"#/components/schemas/Me"}},"required":["created","me"],"additionalProperties":false,"description":"The result of exchanging an access token for a Tablecast account."},"AvatarUpload":{"type":"object","properties":{"assetId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"upload":{"type":"object","properties":{"url":{"type":"string"},"method":{"type":"string","const":"PUT"},"headers":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"expiresAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"expiresInSeconds":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"maxBytes":{"type":"number"}},"required":["url","method","headers","expiresAt","expiresInSeconds","maxBytes"],"additionalProperties":false},"detected":{"type":"object","properties":{"mimeType":{"type":"string"},"label":{"type":"string"}},"required":["mimeType","label"],"additionalProperties":false},"me":{"$ref":"#/components/schemas/Me"}},"required":["assetId","orgId","upload","detected","me"],"additionalProperties":false,"description":"Where to PUT the image, and the profile now pointing at it."},"PushToken":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"token":{"type":"string"},"platform":{"type":"string","enum":["ios","android","web"]},"deviceId":{"anyOf":[{"type":"string"},{"type":"null"}]},"appId":{"anyOf":[{"type":"string"},{"type":"null"}]},"environment":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastSeenAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string"}},"required":["id","token","platform","deviceId","appId","environment","lastSeenAt","createdAt"],"additionalProperties":false,"description":"One device registered to receive push notifications."},"PushTokenList":{"type":"object","properties":{"tokens":{"type":"array","items":{"$ref":"#/components/schemas/PushToken"}}},"required":["tokens"],"additionalProperties":false},"GroupRoleMapping":{"type":"object","properties":{"group":{"type":"string","minLength":1,"maxLength":200},"orgRole":{"type":"string","enum":["org_admin","org_billing","org_member","org_guest"]},"roleKey":{"type":"string","minLength":2,"maxLength":64},"teamId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"teamRole":{"type":"string","enum":["team_lead","producer","director","host","member","viewer"]}},"required":["group"],"additionalProperties":false,"description":"One entry of the group→role mapping: a group in the IdP's `groups` claim, and what holding it means here. Applied on every login, so an edit takes effect at the member's next sign-in."},"IdentityProvider":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"type":{"type":"string","enum":["oidc","saml"]},"name":{"type":"string"},"enabled":{"type":"boolean"},"issuer":{"anyOf":[{"type":"string"},{"type":"null"}]},"metadataUrl":{"anyOf":[{"type":"string"},{"type":"null"}]},"clientId":{"anyOf":[{"type":"string"},{"type":"null"}]},"hasClientSecret":{"type":"boolean"},"entityId":{"anyOf":[{"type":"string"},{"type":"null"}]},"ssoUrl":{"anyOf":[{"type":"string"},{"type":"null"}]},"sloUrl":{"anyOf":[{"type":"string"},{"type":"null"}]},"signingCertificateCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"scopes":{"type":"array","items":{"type":"string"}},"attributeMap":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"groupRoleMap":{"type":"array","items":{"$ref":"#/components/schemas/GroupRoleMapping"}},"jitProvision":{"type":"boolean"},"defaultOrgRole":{"type":"string"},"cognitoProviderName":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastLoginAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastTestAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastTestResult":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","orgId","type","name","enabled","issuer","metadataUrl","clientId","hasClientSecret","entityId","ssoUrl","sloUrl","signingCertificateCount","scopes","attributeMap","groupRoleMap","jitProvision","defaultOrgRole","cognitoProviderName","lastLoginAt","lastTestAt","lastTestResult","createdAt","updatedAt"],"additionalProperties":false,"description":"An OIDC or SAML identity provider this organisation federates with. Never carries the client secret — `hasClientSecret` says whether one is stored, and the value lives in Secrets Manager."},"IdentityProviderList":{"type":"object","properties":{"providers":{"type":"array","items":{"$ref":"#/components/schemas/IdentityProvider"}}},"required":["providers"],"additionalProperties":false},"DeleteIdpResult":{"type":"object","properties":{"suspendedMemberships":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"unroutedDomains":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["suspendedMemberships","unroutedDomains"],"additionalProperties":false},"OrgDomain":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"domain":{"type":"string"},"verified":{"type":"boolean"},"captureMode":{"type":"string","enum":["none","suggest","force"]},"identityProviderId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"verifiedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastCheckedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastCheckError":{"anyOf":[{"type":"string"},{"type":"null"}]},"dnsRecord":{"type":"object","properties":{"host":{"type":"string"},"type":{"type":"string","const":"TXT"},"value":{"type":"string"}},"required":["host","type","value"],"additionalProperties":false},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","orgId","domain","verified","captureMode","identityProviderId","verifiedAt","lastCheckedAt","lastCheckError","dnsRecord","createdAt","updatedAt"],"additionalProperties":false,"description":"An email domain this organisation has claimed. Verified by a DNS TXT record; `captureMode` says what a matching login does — nothing, offer the IdP, or require it."},"OrgDomainList":{"type":"object","properties":{"domains":{"type":"array","items":{"$ref":"#/components/schemas/OrgDomain"}}},"required":["domains"],"additionalProperties":false},"DomainVerification":{"type":"object","properties":{"verified":{"type":"boolean"},"error":{"anyOf":[{"type":"string"},{"type":"null"}]},"domain":{"$ref":"#/components/schemas/OrgDomain"}},"required":["verified","error","domain"],"additionalProperties":false},"TestConnectionResult":{"type":"object","properties":{"ok":{"type":"boolean"},"claims":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"groups":{"type":"array","items":{"type":"string"}},"mappedOrgRole":{"type":"string"},"mappedRoleKeys":{"type":"array","items":{"type":"string"}},"mappedTeamIds":{"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"problems":{"type":"array","items":{"type":"string"}}},"required":["ok","claims","groups","mappedOrgRole","mappedRoleKeys","mappedTeamIds","problems"],"additionalProperties":false,"description":"What a real sign-in through this provider produced, and what it would have granted. Diagnostic only: nothing here is stored except the claim names and the mapping outcome."},"SsoDiscovery":{"type":"object","properties":{"mode":{"type":"string","enum":["none","suggest","force"]},"domain":{"anyOf":[{"type":"string"},{"type":"null"}]},"orgId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"provider":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"name":{"type":"string"},"type":{"type":"string","enum":["oidc","saml"]},"cognitoProviderName":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","type","cognitoProviderName"],"additionalProperties":false},{"type":"null"}]}},"required":["mode","domain","orgId","provider"],"additionalProperties":false,"description":"Where an email address should sign in. Answered without authentication, so it carries the provider's name and nothing about the organisation or whether an account exists."},"StepUpResult":{"type":"object","properties":{"membershipId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"identityId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"bound":{"type":"boolean"},"returnTo":{"type":"string"}},"required":["membershipId","identityId","bound","returnTo"],"additionalProperties":false},"LogoutResult":{"type":"object","properties":{"revokedSessions":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["revokedSessions"],"additionalProperties":false},"ApiKeyStatus":{"type":"string","enum":["active","revoked","expired"]},"ApiKey":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"prefix":{"type":"string"},"scopes":{"type":"array","items":{"type":"string","enum":["read","control","admin"],"description":"`admin` includes `control`, which includes `read`."}},"teamId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"status":{"$ref":"#/components/schemas/ApiKeyStatus"},"rateLimitPerMin":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"createdBy":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"lastUsedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"expiresAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"rotatedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"previousKeyExpiresAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"revokedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]}},"required":["id","name","description","prefix","scopes","teamId","status","rateLimitPerMin","createdAt","createdBy","lastUsedAt","expiresAt","rotatedAt","previousKeyExpiresAt","revokedAt"],"additionalProperties":false,"description":"An API key. The secret half is not here and cannot be read back — a lost key is rotated."},"ApiKeyList":{"type":"object","properties":{"keys":{"type":"array","items":{"$ref":"#/components/schemas/ApiKey"}}},"required":["keys"],"additionalProperties":false},"ApiKeyCreated":{"type":"object","properties":{"key":{"$ref":"#/components/schemas/ApiKey"},"secret":{"type":"string","description":"The whole credential, returned once. Store it now — nothing returns it again."}},"required":["key","secret"],"additionalProperties":false},"ApiKeyRotated":{"type":"object","properties":{"key":{"$ref":"#/components/schemas/ApiKey"},"secret":{"type":"string","description":"The new credential, returned once."},"previousSecretExpiresAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}],"description":"When the old secret stops working. Null when the rotation was immediate."}},"required":["key","secret","previousSecretExpiresAt"],"additionalProperties":false},"OAuthClient":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"clientId":{"type":"string"},"scopes":{"type":"array","items":{"type":"string","enum":["read","control","admin"],"description":"`admin` includes `control`, which includes `read`."}},"status":{"$ref":"#/components/schemas/ApiKeyStatus"},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"createdBy":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"lastUsedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"expiresAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"secretRotatedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"previousSecretExpiresAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"revokedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]}},"required":["id","name","description","clientId","scopes","status","createdAt","createdBy","lastUsedAt","expiresAt","secretRotatedAt","previousSecretExpiresAt","revokedAt"],"additionalProperties":false,"description":"An OAuth 2.0 client for the `client_credentials` grant. The secret is not here."},"OAuthClientList":{"type":"object","properties":{"clients":{"type":"array","items":{"$ref":"#/components/schemas/OAuthClient"}}},"required":["clients"],"additionalProperties":false},"OAuthClientCreated":{"type":"object","properties":{"client":{"$ref":"#/components/schemas/OAuthClient"},"clientSecret":{"type":"string","description":"Returned once, here."}},"required":["client","clientSecret"],"additionalProperties":false},"OAuthClientRotated":{"type":"object","properties":{"client":{"$ref":"#/components/schemas/OAuthClient"},"clientSecret":{"type":"string","description":"The new secret, returned once."},"previousSecretExpiresAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]}},"required":["client","clientSecret","previousSecretExpiresAt"],"additionalProperties":false},"PublicOrganization":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"slug":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string"},"homeRegion":{"type":"string"},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","slug","name","kind","homeRegion","createdAt"],"additionalProperties":false,"description":"The organisation the credential belongs to. A credential can only ever see one."},"PublicTeam":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"slug":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"archivedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","slug","name","description","archivedAt","createdAt"],"additionalProperties":false},"PublicTeamList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PublicTeam"}},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as `cursor` for the next page. Null on the last page."},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["items","nextCursor","limit"],"additionalProperties":false,"description":"The organisation's teams."},"PublicProduction":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"teamId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"slug":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"archivedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","teamId","slug","name","kind","description","archivedAt","createdAt"],"additionalProperties":false,"description":"A show. Soft-deleted productions are never returned."},"PublicProductionList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PublicProduction"}},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as `cursor` for the next page. Null on the last page."},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["items","nextCursor","limit"],"additionalProperties":false,"description":"The organisation's shows."},"PublicSession":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"productionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"title":{"type":"string"},"episodeNo":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"status":{"type":"string"},"scheduledAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"scheduledDurationMin":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"startedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"endedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"sessionClockEpoch":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"integrityStatus":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","productionId","title","episodeNo","status","scheduledAt","scheduledDurationMin","startedAt","endedAt","sessionClockEpoch","integrityStatus","tags","createdAt"],"additionalProperties":false,"description":"An episode of a show. Times inside a session are session-clock milliseconds measured from `sessionClockEpoch` — see ADR-0001."},"PublicSessionList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PublicSession"}},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as `cursor` for the next page. Null on the last page."},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["items","nextCursor","limit"],"additionalProperties":false,"description":"Sessions, newest first."},"PublicParticipant":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"userId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"displayName":{"type":"string"},"role":{"type":"string"},"locationId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"isHidden":{"type":"boolean"},"joinedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"leftAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]}},"required":["id","sessionId","userId","displayName","role","locationId","isHidden","joinedAt","leftAt"],"additionalProperties":false,"description":"Somebody in the room. Deliberately carries no email, no device details and no client capabilities — those are the guest's, not the integration's."},"PublicParticipantList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PublicParticipant"}},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as `cursor` for the next page. Null on the last page."},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["items","nextCursor","limit"],"additionalProperties":false,"description":"The session's participants."},"PublicTrack":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"participantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"kind":{"type":"string"},"origin":{"type":"string"},"status":{"type":"string"},"isPrimary":{"type":"boolean"},"codec":{"anyOf":[{"type":"string"},{"type":"null"}]},"container":{"anyOf":[{"type":"string"},{"type":"null"}]},"width":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"startClockMs":{"anyOf":[{"type":"number"},{"type":"null"}]},"durationMs":{"anyOf":[{"type":"number"},{"type":"null"}]},"bytes":{"anyOf":[{"type":"number"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","sessionId","participantId","kind","origin","status","isPrimary","codec","container","width","height","startClockMs","durationMs","bytes","createdAt"],"additionalProperties":false,"description":"One recorded track. No storage key and no signed URL — downloads are minted by the first-party recordings API, which is where the audit and the expiry live."},"PublicTranscript":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"trackId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"participantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"language":{"anyOf":[{"type":"string"},{"type":"null"}]},"segmentCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"isFinal":{"type":"boolean"},"updatedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","trackId","participantId","language","segmentCount","isFinal","updatedAt"],"additionalProperties":false,"description":"A transcript's metadata. The segments themselves are not on the public surface yet."},"PublicRecording":{"type":"object","properties":{"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"productionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"title":{"type":"string"},"status":{"type":"string"},"integrityStatus":{"type":"string"},"startedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"endedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"trackCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"archivedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]}},"required":["sessionId","productionId","title","status","integrityStatus","startedAt","endedAt","trackCount","archivedAt"],"additionalProperties":false,"description":"A session seen as a recording: the session plus how many tracks it holds."},"PublicRecordingList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PublicRecording"}},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as `cursor` for the next page. Null on the last page."},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["items","nextCursor","limit"],"additionalProperties":false,"description":"Recordings, newest first."},"PublicRecordingDetail":{"type":"object","properties":{"session":{"$ref":"#/components/schemas/PublicSession"},"participants":{"type":"array","items":{"$ref":"#/components/schemas/PublicParticipant"}},"tracks":{"type":"array","items":{"$ref":"#/components/schemas/PublicTrack"}},"transcripts":{"type":"array","items":{"$ref":"#/components/schemas/PublicTranscript"}}},"required":["session","participants","tracks","transcripts"],"additionalProperties":false,"description":"One recording in full: the session, who was in it, what was captured and what was transcribed."},"PublicInviteCreated":{"type":"object","properties":{"inviteId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"role":{"type":"string"},"maxUses":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"expiresAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"token":{"type":"string","description":"Returned once, here. Only its sha256 is stored."},"joinUrl":{"type":"string","format":"uri"}},"required":["inviteId","sessionId","role","maxUses","expiresAt","token","joinUrl"],"additionalProperties":false},"RecordCommandAccepted":{"type":"object","properties":{"commandId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"op":{"type":"string"},"scope":{"type":"string"},"atClockMs":{"type":"number"},"source":{"type":"string"},"acks":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["commandId","sessionId","op","scope","atClockMs","source","acks","createdAt"],"additionalProperties":false,"description":"The persisted command. Devices acknowledge asynchronously; poll the session's commands or subscribe to the realtime channel to see `acks` fill in."},"OAuthTokenResponse":{"type":"object","properties":{"access_token":{"type":"string"},"token_type":{"type":"string","const":"Bearer"},"expires_in":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"scope":{"type":"string"}},"required":["access_token","token_type","expires_in","scope"],"additionalProperties":false},"OAuthError":{"type":"object","properties":{"error":{"type":"string","enum":["invalid_request","invalid_client","invalid_grant","unauthorized_client","unsupported_grant_type","invalid_scope"]},"error_description":{"type":"string"}},"required":["error"],"additionalProperties":false,"description":"RFC 6749 §5.2. Deliberately **not** an RFC 7807 problem document: OAuth clients are written against this shape, and a token endpoint that answers in a different one is a token endpoint every off-the-shelf client library fails against."},"PublicSeries":{"type":"object","properties":{"productionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"slug":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string"}},"required":["productionId","slug","name","kind"],"additionalProperties":false,"description":"The show an episode belongs to. Tablecast has no separate `series` row — a **production is the series** and a session is one episode of it, which is why an episode carries its show inline rather than a `seriesId` pointing at a table that does not exist."},"PublicEpisode":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"productionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"series":{"$ref":"#/components/schemas/PublicSeries"},"title":{"type":"string"},"episodeNo":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"The show's own numbering. Null for an unnumbered one-off."},"status":{"type":"string"},"scheduledAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"scheduledDurationMin":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"startedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"endedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"sessionClockEpoch":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"integrityStatus":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"trackCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Programme tracks. Comms legs are never counted."},"hasTranscript":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","productionId","series","title","episodeNo","status","scheduledAt","scheduledDurationMin","startedAt","endedAt","sessionClockEpoch","integrityStatus","tags","trackCount","hasTranscript","createdAt"],"additionalProperties":false,"description":"A session, framed as an episode of its show. The same row `PublicSession` describes, with the series resolved and the two counts a catalogue needs so listing a season is one call.","examples":[{"id":"979c3d7c-0000-4000-8000-000000000003","productionId":"3f2b0c1a-0000-4000-8000-000000000002","series":{"productionId":"3f2b0c1a-0000-4000-8000-000000000002","slug":"mediaworks-daily","name":"MediaWorks Daily","kind":"podcast"},"title":"Episode 12 — the one about latency","episodeNo":12,"status":"complete","scheduledAt":"2026-08-01T09:15:00.000Z","scheduledDurationMin":60,"startedAt":"2026-08-01T09:15:00.000Z","endedAt":"2026-08-01T10:12:31.000Z","sessionClockEpoch":"2026-08-01T09:15:00.000Z","integrityStatus":"ok","tags":["season-2"],"trackCount":4,"hasTranscript":true,"createdAt":"2026-07-28T02:00:00.000Z"}]},"PublicEpisodeList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PublicEpisode"}},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as `cursor` for the next page. Null on the last page."},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["items","nextCursor","limit"],"additionalProperties":false,"description":"A show's episodes, newest first. Page with `cursor`; `episodeNo` is the show's numbering and not the sort order, because it is nullable and a keyset on a nullable column loses rows."},"PublicRendition":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"trackId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"renderId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"rung":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"720 or 360, or the source's own height when it is shorter. Never upscaled."},"status":{"type":"string"},"baselineStartMs":{"type":"number","description":"Session-clock ms at the first sample. Every `startMs` is on this anchor."},"durationMs":{"anyOf":[{"type":"number"},{"type":"null"}]},"segmentSeconds":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"segmentCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"0 until the rendition is `ready`."},"width":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"codecs":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"RFC 6381, e.g. `avc1.640028,mp4a.40.2`."},"hasPeaks":{"type":"boolean","description":"A merged waveform is available from the rendition-assets endpoint."},"hasSprites":{"type":"boolean","description":"A thumbnail sprite index is available from the rendition-assets endpoint."},"error":{"anyOf":[{"type":"string"},{"type":"null"}]},"readyAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","sessionId","trackId","renderId","rung","status","baselineStartMs","durationMs","segmentSeconds","segmentCount","width","height","codecs","hasPeaks","hasSprites","error","readyAt","createdAt"],"additionalProperties":false,"description":"One rung of a recording's CMAF proxy ladder. No storage keys and no segment list: a caller that wants to play this asks for a **playback ticket**, which presigns a batch.","examples":[{"id":"5e6f7a8b-0000-4000-8000-000000000005","sessionId":"979c3d7c-0000-4000-8000-000000000003","trackId":"1a2b3c4d-0000-4000-8000-000000000004","renderId":null,"rung":720,"status":"ready","baselineStartMs":0,"durationMs":3751000,"segmentSeconds":1,"segmentCount":3751,"width":1280,"height":720,"codecs":"avc1.640028,mp4a.40.2","hasPeaks":true,"hasSprites":true,"error":null,"readyAt":"2026-08-01T10:31:02.000Z","createdAt":"2026-08-01T10:14:00.000Z"}]},"PublicRenditionList":{"type":"object","properties":{"renditions":{"type":"array","items":{"$ref":"#/components/schemas/PublicRendition"}}},"required":["renditions"],"additionalProperties":false,"description":"Every rendition of one recording. Deliberately **not** paginated: a ladder is bounded by the recording (two rungs per ISO plus the programme master), so a cursor would be ceremony over a list that cannot grow without bound."},"PublicPlaybackSegment":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"startMs":{"type":"number","description":"Session-clock ms, absolute — the ruler four cameras share."},"durationMs":{"type":"number"}},"required":["url","startMs","durationMs"],"additionalProperties":false},"PublicPlaybackTicket":{"type":"object","properties":{"renditionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"rung":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"initUrl":{"type":"string","format":"uri","description":"The CMAF init segment. Appended once, before any media segment."},"segments":{"type":"array","items":{"$ref":"#/components/schemas/PublicPlaybackSegment"}},"fromIndex":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"nextIndex":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Pass as `fromIndex` for the next batch. Null at the end."},"expiresAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["renditionId","rung","initUrl","segments","fromIndex","nextIndex","expiresAt"],"additionalProperties":false,"description":"A batch of presigned segment GETs, valid for an hour. Signed through the organisation's own storage target, so an R2 org and an S3 org get the same shape and the player never learns which it is.","examples":[{"renditionId":"5e6f7a8b-0000-4000-8000-000000000005","rung":720,"initUrl":"https://storage.example/…/init.mp4?X-Amz-Signature=…","segments":[{"url":"https://storage.example/…/00000.m4s?X-Amz-Signature=…","startMs":0,"durationMs":1000}],"fromIndex":0,"nextIndex":200,"expiresAt":"2026-08-01T11:31:02.000Z"}]},"PublicRenditionAssetLink":{"type":"object","properties":{"renditionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"kind":{"type":"string"},"url":{"type":"string","format":"uri"},"expiresAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"expiresInSeconds":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["renditionId","kind","url","expiresAt","expiresInSeconds"],"additionalProperties":false,"description":"A one-hour presigned GET for a waveform, a sprite index or one sprite sheet."},"PublicTranscriptSegment":{"type":"object","properties":{"startMs":{"type":"number","description":"Session-clock ms, absolute — `tracks.start_clock_ms + tracks.clock_offset_ms` is already added, so segments from different tracks share one timeline (TAB-600)."},"endMs":{"type":"number"},"text":{"type":"string"}},"required":["startMs","endMs","text"],"additionalProperties":false},"PublicTranscriptDetail":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"trackId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"participantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"speaker":{"type":"string","description":"The speaker's display name, taken from the **track** rather than from diarization: Tablecast records one ISO per person, so who is talking is known exactly."},"language":{"anyOf":[{"type":"string"},{"type":"null"}]},"isFinal":{"type":"boolean"},"timeBase":{"type":"string","const":"session"},"segments":{"type":"array","items":{"$ref":"#/components/schemas/PublicTranscriptSegment"}},"wordsUrl":{"anyOf":[{"type":"string","format":"uri"},{"type":"null"}],"description":"A fifteen-minute presigned GET for the word-level JSON. Null where none has been written."},"wordsExpiresInSeconds":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"vttUrl":{"anyOf":[{"type":"string","format":"uri"},{"type":"null"}],"description":"The same, for the WebVTT sidecar."},"updatedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","sessionId","trackId","participantId","speaker","language","isFinal","timeBase","segments","wordsUrl","wordsExpiresInSeconds","vttUrl","updatedAt"],"additionalProperties":false,"description":"One track's transcript: sentence-level segments inline, word timings behind a short-lived URL the caller fetches directly. Comms legs are excluded by construction — a producer's talkback is not programme and must never reach a transcript.","examples":[{"id":"7d6c5b4a-0000-4000-8000-000000000008","sessionId":"979c3d7c-0000-4000-8000-000000000003","trackId":"1a2b3c4d-0000-4000-8000-000000000004","participantId":"4d5e6f70-0000-4000-8000-000000000009","speaker":"Dan Jackson","language":"en-AU","isFinal":true,"timeBase":"session","segments":[{"startMs":1200,"endMs":4300,"text":"Welcome back to MediaWorks Daily."}],"wordsUrl":"https://storage.example/…/words.json?X-Amz-Signature=…","wordsExpiresInSeconds":900,"vttUrl":null,"updatedAt":"2026-08-01T10:40:00.000Z"}]},"PublicTranscriptList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PublicTranscriptDetail"}},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as `cursor` for the next page. Null on the last page."},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["items","nextCursor","limit"],"additionalProperties":false,"description":"A recording's transcripts, one per programme track."},"PublicAsset":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"teamId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"productionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"kind":{"type":"string"},"source":{"type":"string"},"name":{"type":"string"},"mimeType":{"anyOf":[{"type":"string"},{"type":"null"}]},"bytes":{"anyOf":[{"type":"number"},{"type":"null"}]},"width":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"durationMs":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"assetVersion":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"1 for the first import, +1 on every re-export after an edit."},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"updatedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","teamId","productionId","kind","source","name","mimeType","bytes","width","height","durationMs","assetVersion","createdAt","updatedAt"],"additionalProperties":false,"description":"One file in the organisation's media library — a bumper, a sting, a logo, an imported design. The bytes are fetched through the download endpoint, which mints a signed URL and writes an audit row naming the credential.","examples":[{"id":"9c8d7e6f-0000-4000-8000-000000000006","teamId":null,"productionId":"3f2b0c1a-0000-4000-8000-000000000002","kind":"video","source":"upload","name":"cold-open.mp4","mimeType":"video/mp4","bytes":48211904,"width":1920,"height":1080,"durationMs":12000,"assetVersion":1,"createdAt":"2026-08-01T09:15:00.000Z","updatedAt":"2026-08-01T09:15:00.000Z"}]},"PublicAssetList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PublicAsset"}},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as `cursor` for the next page. Null on the last page."},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["items","nextCursor","limit"],"additionalProperties":false,"description":"The library, newest first."},"PublicDownloadLink":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"expiresAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"expiresInSeconds":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"filename":{"type":"string","description":"Signed into the URL, so it cannot be rewritten by the caller."},"contentType":{"anyOf":[{"type":"string"},{"type":"null"}]},"bytes":{"anyOf":[{"type":"number"},{"type":"null"}]}},"required":["url","expiresAt","expiresInSeconds","filename","contentType","bytes"],"additionalProperties":false,"description":"A presigned GET on the organisation's own bucket. Every call writes an audit row whose actor is the **credential**, not whoever created it; the URL itself is never logged.","examples":[{"url":"https://storage.example/…/cold-open.mp4?X-Amz-Signature=…","expiresAt":"2026-08-01T09:30:00.000Z","expiresInSeconds":900,"filename":"cold-open.mp4","contentType":"video/mp4","bytes":48211904}]},"PublicRenderPreset":{"type":"object","properties":{"id":{"anyOf":[{"type":"string"},{"type":"null"}]},"aspect":{"type":"string"},"width":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"height":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"fps":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"codec":{"type":"string"}},"required":["id","aspect","width","height","fps","codec"],"additionalProperties":false,"description":"The preset as it was resolved when the render was asked for — stored on the row, so a render explains itself after the catalogue has moved on."},"PublicRender":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"kind":{"type":"string"},"mode":{"type":"string","description":"`server` for a worker render, `browser` for the in-page clip export."},"status":{"type":"string"},"preset":{"$ref":"#/components/schemas/PublicRenderPreset"},"progressPct":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"0–100, or null for a step that cannot estimate."},"progressStage":{"anyOf":[{"type":"string"},{"type":"null"}]},"bytes":{"anyOf":[{"type":"number"},{"type":"null"}]},"durationMs":{"anyOf":[{"type":"number"},{"type":"null"}]},"sha256":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Of the output, verified against the object before completion."},"assetId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"The library entry this render was registered as."},"cancelPending":{"type":"boolean","description":"A cancel has been asked for and the worker has not yet confirmed it stopped."},"startedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"finishedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"error":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","sessionId","kind","mode","status","preset","progressPct","progressStage","bytes","durationMs","sha256","assetId","cancelPending","startedAt","finishedAt","error","createdAt"],"additionalProperties":false,"description":"One export. `status` is the durable copy of the worker's progress, so a client that missed every realtime event still shows a bar.","examples":[{"id":"2b3c4d5e-0000-4000-8000-000000000007","sessionId":"979c3d7c-0000-4000-8000-000000000003","kind":"program","mode":"server","status":"succeeded","preset":{"id":"master_16x9","aspect":"16:9","width":1920,"height":1080,"fps":30,"codec":"h264"},"progressPct":100,"progressStage":null,"bytes":1284372992,"durationMs":3751000,"sha256":"6f1c…","assetId":"9c8d7e6f-0000-4000-8000-000000000006","cancelPending":false,"startedAt":"2026-08-01T10:20:00.000Z","finishedAt":"2026-08-01T10:44:12.000Z","error":null,"createdAt":"2026-08-01T10:19:58.000Z"}]},"PublicRenderList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PublicRender"}},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as `cursor` for the next page. Null on the last page."},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["items","nextCursor","limit"],"additionalProperties":false,"description":"The export queue, newest first."},"PublicRenderCanceled":{"type":"object","properties":{"render":{"$ref":"#/components/schemas/PublicRender"},"stoppedImmediately":{"type":"boolean","description":"True when the render had not started and was cancelled outright. False when it is running and will stop within the worker's poll interval, writing `canceled` itself."}},"required":["render","stoppedImmediately"],"additionalProperties":false},"PublicRenderPresetCatalogueEntry":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"description":{"type":"string"},"aspect":{"type":"string"},"width":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"height":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"fps":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"codec":{"type":"string"}},"required":["id","label","description","aspect","width","height","fps","codec"],"additionalProperties":false},"PublicRenderPresetCatalogue":{"type":"object","properties":{"presets":{"type":"array","items":{"$ref":"#/components/schemas/PublicRenderPresetCatalogueEntry"}}},"required":["presets"],"additionalProperties":false,"description":"The export presets this deployment offers. A fixed catalogue in code, so it is not paginated."},"PublicGraphic":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"productionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"kind":{"type":"string"},"source":{"type":"string","description":"`house` for one we drew, `canva` for one Canva autofilled."},"status":{"type":"string"},"name":{"type":"string"},"assetId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"The PNG, as a library asset. Null while `pending`."},"houseTemplateId":{"anyOf":[{"type":"string"},{"type":"null"}]},"fieldValues":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"participantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"recipientId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"1-based, +1 per regeneration — a new row each time, never an overwrite."},"generationId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"error":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","productionId","kind","source","status","name","assetId","houseTemplateId","fieldValues","participantId","recipientId","version","generationId","error","createdAt"],"additionalProperties":false,"description":"One super from a show's Supers library. Regenerated-over versions are hidden unless `history` is set: a strap recoloured four times would otherwise render five of everybody.","examples":[{"id":"8b7a6959-0000-4000-8000-00000000000a","productionId":"3f2b0c1a-0000-4000-8000-000000000002","kind":"super","source":"house","status":"ready","name":"Dan Jackson","assetId":"9c8d7e6f-0000-4000-8000-000000000006","houseTemplateId":"house-bar","fieldValues":{"name":"Dan Jackson","title":"Founder"},"participantId":null,"recipientId":null,"version":1,"generationId":null,"error":null,"createdAt":"2026-08-01T09:15:00.000Z"}]},"PublicGraphicList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PublicGraphic"}},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as `cursor` for the next page. Null on the last page."},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["items","nextCursor","limit"],"additionalProperties":false,"description":"A show's supers, newest first."},"PublicComment":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"threadId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"body":{"type":"string","description":"Empty on a tombstone — a deleted comment keeps its place in the thread."},"mentions":{"type":"array","items":{"type":"string"}},"authorKind":{"type":"string","description":"`user`, `participant` or `share_viewer`."},"authorDisplayName":{"type":"string"},"editedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"deletedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","threadId","body","mentions","authorKind","authorDisplayName","editedAt","deletedAt","createdAt"],"additionalProperties":false},"PublicCommentThread":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"targetKind":{"type":"string"},"targetId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"productionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"sessionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"anchor":{"anyOf":[{},{"type":"null"}],"description":"Where the thread is pinned, or null for the whole thing. `kind` is the coordinate system: `time` (session-clock ms on a recording), `baseline` (a position on an edit's baseline timeline) or `region` (a normalised 0–1 box on a frame)."},"resolvedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"authorKind":{"type":"string"},"authorDisplayName":{"type":"string"},"replyCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Replies only — the opening message is not counted."},"lastActivityAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"comments":{"type":"array","items":{"$ref":"#/components/schemas/PublicComment"}}},"required":["id","targetKind","targetId","productionId","sessionId","anchor","resolvedAt","authorKind","authorDisplayName","replyCount","lastActivityAt","createdAt","comments"],"additionalProperties":false,"description":"A review conversation on one thing, with its whole thread inline. Read-only on this surface: the three author kinds a comment may have all name a *person*, and a credential is not one — see the endpoint's description.","examples":[{"id":"0f1e2d3c-0000-4000-8000-00000000000b","targetKind":"recording","targetId":"979c3d7c-0000-4000-8000-000000000003","productionId":"3f2b0c1a-0000-4000-8000-000000000002","sessionId":"979c3d7c-0000-4000-8000-000000000003","anchor":{"kind":"time","timeMs":184300},"resolvedAt":null,"authorKind":"user","authorDisplayName":"Dan Jackson","replyCount":2,"lastActivityAt":"2026-08-01T11:02:00.000Z","createdAt":"2026-08-01T10:55:00.000Z","comments":[]}]},"PublicCommentThreadList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PublicCommentThread"}},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as `cursor` for the next page. Null on the last page."},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["items","nextCursor","limit"],"additionalProperties":false,"description":"Threads on one target, newest first. Resolved threads are omitted unless `includeResolved`."},"PublicShare":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"targetKind":{"type":"string","description":"`recording`, `render`, `media_asset` or `edit_doc`."},"targetId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"productionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"visibility":{"type":"string"},"permission":{"type":"string","description":"`view` or `comment`."},"allowDownload":{"type":"boolean"},"expiresAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"viewCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"lastViewedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"revokedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"url":{"anyOf":[{"type":"string","format":"uri"},{"type":"null"}],"description":"The link, for a live **public** share. Null for a private one, which has no link of its own: a private share mints one token per named person and stores only its sha256."},"recipientCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Named people on a private share. 0 on a public one."},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","targetKind","targetId","productionId","visibility","permission","allowDownload","expiresAt","viewCount","lastViewedAt","revokedAt","url","recipientCount","createdAt"],"additionalProperties":false,"description":"A share link on one thing. Revoked shares are listed too — 'this was public for a fortnight in March' is a fact somebody will need back.","examples":[{"id":"c3d4e5f6-0000-4000-8000-00000000000c","targetKind":"recording","targetId":"979c3d7c-0000-4000-8000-000000000003","productionId":"3f2b0c1a-0000-4000-8000-000000000002","visibility":"public","permission":"view","allowDownload":false,"expiresAt":null,"viewCount":17,"lastViewedAt":"2026-08-02T22:41:00.000Z","revokedAt":null,"url":"https://app.tablecast.studio/s/sp_…","recipientCount":0,"createdAt":"2026-08-01T09:15:00.000Z"}]},"PublicShareList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PublicShare"}},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as `cursor` for the next page. Null on the last page."},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["items","nextCursor","limit"],"additionalProperties":false,"description":"Every share on one target, newest first."},"PublicChatMessage":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"participantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"displayName":{"type":"string"},"body":{"type":"string"},"clockMs":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Session-clock ms, or null for a message sent before the clock was up."},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","sessionId","participantId","displayName","body","clockMs","createdAt"],"additionalProperties":false,"description":"One message from the room's open channel. The producers channel and private threads are never on this surface — see the endpoint's description.","examples":[{"id":"d4e5f607-0000-4000-8000-00000000000d","sessionId":"979c3d7c-0000-4000-8000-000000000003","participantId":"4d5e6f70-0000-4000-8000-000000000009","displayName":"Dan Jackson","body":"Let's take that intro again.","clockMs":61200,"createdAt":"2026-08-01T09:15:00.000Z"}]},"PublicChatList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PublicChatMessage"}},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as `cursor` for the next page. Null on the last page."},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["items","nextCursor","limit"],"additionalProperties":false,"description":"A session's open chat, newest first — the same keyset order as every other list here, which is the reverse of what a chat window renders. Reverse the page client-side."},"PublicInviteRecipient":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Null on a production roster entry, which covers every episode."},"productionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"email":{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"displayName":{"anyOf":[{"type":"string"},{"type":"null"}]},"status":{"type":"string","description":"`invited` → `opened` → `checked_in` → `in_lobby` → `ready` → `on_air`, or a terminal state."},"statusAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$","description":"When the *current* status was reached."},"sentAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"lastDeliveryError":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Why the last invitation did not go, for the failures that happen before there is a message id."},"openedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"checkedInAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"inLobbyAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"readyAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"onAirAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"noShowAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"revokedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"admittedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"participantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"The seat they took, once they are in the room."},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","sessionId","productionId","email","displayName","status","statusAt","sentAt","lastDeliveryError","openedAt","checkedInAt","inLobbyAt","readyAt","onAirAt","noShowAt","revokedAt","admittedAt","participantId","createdAt"],"additionalProperties":false,"description":"One person on an episode or on a show's roster, and how far through the funnel they have got. **Never their token** — only its sha256 is stored, and the link exists exactly once, in the response that mints it.","examples":[{"id":"e5f60718-0000-4000-8000-00000000000e","sessionId":"979c3d7c-0000-4000-8000-000000000003","productionId":null,"email":"guest@example.com","displayName":"A Guest","status":"on_air","statusAt":"2026-08-01T09:16:40.000Z","sentAt":"2026-07-28T02:05:00.000Z","lastDeliveryError":null,"openedAt":"2026-07-28T08:11:00.000Z","checkedInAt":"2026-08-01T09:15:00.000Z","inLobbyAt":"2026-08-01T09:15:00.000Z","readyAt":"2026-08-01T09:15:00.000Z","onAirAt":"2026-08-01T09:16:40.000Z","noShowAt":null,"revokedAt":null,"admittedAt":null,"participantId":"4d5e6f70-0000-4000-8000-000000000009","createdAt":"2026-07-28T02:04:00.000Z"}]},"PublicInviteRecipientList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PublicInviteRecipient"}},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as `cursor` for the next page. Null on the last page."},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["items","nextCursor","limit"],"additionalProperties":false,"description":"The guest funnel, newest first."},"PublicInviteRecipientCreated":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Null on a production roster entry, which covers every episode."},"productionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"email":{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"displayName":{"anyOf":[{"type":"string"},{"type":"null"}]},"status":{"type":"string","description":"`invited` → `opened` → `checked_in` → `in_lobby` → `ready` → `on_air`, or a terminal state."},"statusAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$","description":"When the *current* status was reached."},"sentAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"lastDeliveryError":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Why the last invitation did not go, for the failures that happen before there is a message id."},"openedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"checkedInAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"inLobbyAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"readyAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"onAirAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"noShowAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"revokedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"admittedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"participantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"The seat they took, once they are in the room."},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"joinUrl":{"type":"string","format":"uri","description":"Returned **once**, here. Only the token's sha256 is stored."}},"required":["id","sessionId","productionId","email","displayName","status","statusAt","sentAt","lastDeliveryError","openedAt","checkedInAt","inLobbyAt","readyAt","onAirAt","noShowAt","revokedAt","admittedAt","participantId","createdAt","joinUrl"],"additionalProperties":false},"PublicInviteRecipientsCreated":{"type":"object","properties":{"recipients":{"type":"array","items":{"$ref":"#/components/schemas/PublicInviteRecipientCreated"}}},"required":["recipients"],"additionalProperties":false},"PublicShareCreated":{"type":"object","properties":{"share":{"$ref":"#/components/schemas/PublicShare"},"created":{"type":"boolean","description":"False when a live public share already covered this target and that one was returned. Public is a toggle, not a mint: a second URL nobody could tell apart from the first is not a second share."}},"required":["share","created"],"additionalProperties":false},"PublicSessionEvent":{"type":"object","properties":{"kind":{"type":"string","enum":["production.state","session.record","session.participant","session.chat","session.coordinator"]},"id":{"type":"string","description":"The source row's id. Stable, so an overlapping replay deduplicates."},"at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"seq":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"`edl_events.seq` on `production.state` — a strict per-session sequence. Null on every other kind."},"clockMs":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Session-clock ms, where the source row carries one."},"summary":{"type":"string","description":"One line of English. What happened, in the show's terms rather than ours."},"data":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"The event's own fields; the shape depends on `kind`."}},"required":["kind","id","at","seq","clockMs","summary","data"],"additionalProperties":false,"description":"One thing that happened in a session."},"PublicSessionEventPage":{"type":"object","properties":{"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"productionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"events":{"type":"array","items":{"$ref":"#/components/schemas/PublicSessionEvent"}},"cursor":{"type":"string","description":"Where this batch left off. Pass it as `since` on the next call — or ignore it and poll the subscription, which remembers it server-side."},"hasMore":{"type":"boolean","description":"At least one source had more rows than this batch could carry. Call again immediately."},"waitedMs":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How long the server held the request open waiting for an event."}},"required":["sessionId","productionId","events","cursor","hasMore","waitedMs"],"additionalProperties":false,"description":"One batch of a session's events, oldest first."},"PublicEventSubscription":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"productionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"kinds":{"type":"array","items":{"type":"string","enum":["production.state","session.record","session.participant","session.chat","session.coordinator"]},"description":"Empty means every kind."},"status":{"type":"string","enum":["active","expired","revoked"]},"cursor":{"type":"string"},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"expiresAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"lastPolledAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"deliveredCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","sessionId","productionId","kinds","status","cursor","createdAt","expiresAt","lastPolledAt","deliveredCount"],"additionalProperties":false,"description":"A durable place in a session's event feed, held server-side so a reconnect costs nothing."},"PublicEventSubscriptionPage":{"type":"object","properties":{"subscription":{"$ref":"#/components/schemas/PublicEventSubscription"},"events":{"type":"array","items":{"$ref":"#/components/schemas/PublicSessionEvent"}},"cursor":{"type":"string"},"hasMore":{"type":"boolean"},"waitedMs":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"final":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Set on the last batch a subscription will ever return — it has expired or been revoked. Says which, and what to do about it. Null while the subscription is live."}},"required":["subscription","events","cursor","hasMore","waitedMs","final"],"additionalProperties":false,"description":"One batch from a subscription, plus the subscription's own state so an agent can see it ending."},"RevokedEventSubscription":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"status":{"type":"string","const":"revoked"},"revokedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","status","revokedAt"],"additionalProperties":false,"description":"The subscription is closed. Polling it returns one final batch saying so."},"DatabaseHealth":{"type":"object","properties":{"configured":{"type":"boolean","description":"Whether a DATABASE_URL is present in this environment"},"reachable":{"type":"boolean","description":"Whether `select 1` succeeded"},"latencyMs":{"description":"Round trip of the connectivity probe","type":"number"},"error":{"description":"Failure class, never the connection string","type":"string"}},"required":["configured","reachable"],"additionalProperties":false,"description":"Result of the database connectivity probe."},"HealthResponse":{"type":"object","properties":{"status":{"type":"string","enum":["ok","degraded"],"description":"`degraded` when a dependency is unreachable"},"service":{"type":"string","const":"tablecast-api"},"version":{"type":"string"},"env":{"type":"string"},"uptimeMs":{"type":"number"},"database":{"$ref":"#/components/schemas/DatabaseHealth"}},"required":["status","service","version","env","uptimeMs","database"],"additionalProperties":false,"description":"Liveness plus dependency status. Always 200 so the check itself is never ambiguous."},"OpenApiDocument":{"type":"object","properties":{"openapi":{"type":"string"},"info":{"type":"object","properties":{"title":{"type":"string"},"version":{"type":"string"}},"required":["title","version"],"additionalProperties":{}}},"required":["openapi","info"],"additionalProperties":{},"description":"This API's OpenAPI 3.1 document."},"OAuthAuthorizationServerMetadata":{"type":"object","properties":{"issuer":{"type":"string"},"authorization_endpoint":{"type":"string"},"token_endpoint":{"type":"string"},"registration_endpoint":{"type":"string"},"revocation_endpoint":{"type":"string"},"response_types_supported":{"type":"array","items":{"type":"string"}},"grant_types_supported":{"type":"array","items":{"type":"string"}},"code_challenge_methods_supported":{"type":"array","items":{"type":"string"}},"scopes_supported":{"type":"array","items":{"type":"string"}}},"required":["issuer","authorization_endpoint","token_endpoint","registration_endpoint","revocation_endpoint","response_types_supported","grant_types_supported","code_challenge_methods_supported","scopes_supported"],"additionalProperties":{},"description":"RFC 8414 authorization-server metadata. MCP clients read this to discover the authorize, token, registration and revocation endpoints."},"OAuthProtectedResourceMetadata":{"type":"object","properties":{"resource":{"type":"string"},"authorization_servers":{"type":"array","items":{"type":"string"}},"scopes_supported":{"type":"array","items":{"type":"string"}},"bearer_methods_supported":{"type":"array","items":{"type":"string"}}},"required":["resource","authorization_servers","scopes_supported","bearer_methods_supported"],"additionalProperties":{},"description":"RFC 9728 protected-resource metadata for `/mcp`. The `WWW-Authenticate` challenge a bare `/mcp` request receives points at this document."},"OAuthClientRegistrationResponse":{"type":"object","properties":{"client_id":{"type":"string"},"client_secret":{"type":"string"},"client_id_issued_at":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"client_name":{"type":"string"},"redirect_uris":{"type":"array","items":{"type":"string"}},"token_endpoint_auth_method":{"type":"string"},"grant_types":{"type":"array","items":{"type":"string"}},"response_types":{"type":"array","items":{"type":"string"}},"scope":{"type":"string"}},"required":["client_id","client_id_issued_at","client_name","redirect_uris","token_endpoint_auth_method","grant_types","response_types","scope"],"additionalProperties":false},"McpAuthorizationRequest":{"type":"object","properties":{"client":{"type":"object","properties":{"clientId":{"type":"string"},"clientName":{"type":"string"},"clientUri":{"anyOf":[{"type":"string"},{"type":"null"}]},"logoUri":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["clientId","clientName","clientUri","logoUri"],"additionalProperties":false},"redirectUri":{"type":"string"},"requestedScopes":{"type":"array","items":{"type":"string"}},"expiresAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["client","redirectUri","requestedScopes","expiresAt"],"additionalProperties":false,"description":"What the consent page renders: who is asking, where the answer goes, and how much they asked for. Read with an ordinary Cognito bearer, because the page is an ordinary signed-in page."},"McpAuthorizationConsentResult":{"type":"object","properties":{"redirectTo":{"type":"string"}},"required":["redirectTo"],"additionalProperties":false},"McpOAuthTokenResponse":{"type":"object","properties":{"access_token":{"type":"string"},"token_type":{"type":"string","const":"Bearer"},"expires_in":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"refresh_token":{"type":"string"},"scope":{"type":"string"}},"required":["access_token","token_type","expires_in","refresh_token","scope"],"additionalProperties":false},"McpPrincipal":{"type":"object","properties":{"kind":{"type":"string","enum":["api_key","oauth_client","mcp_oauth"]},"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"scopes":{"type":"array","items":{"type":"string"}}},"required":["kind","orgId","scopes"],"additionalProperties":false,"description":"Who a credential is, for the MCP server's tool gate. `apps/mcp` holds no database and no secret of its own, so it asks this endpoint which tools the caller may be offered rather than deciding from a header it cannot verify."},"McpOAuthError":{"type":"object","properties":{"error":{"type":"string"},"error_description":{"type":"string"}},"required":["error"],"additionalProperties":false,"description":"RFC 6749 §5.2. Deliberately not an RFC 7807 problem document: OAuth client libraries are written against this shape."},"CompanionOpResult":{"type":"object","properties":{"op":{"type":"string"},"accepted":{"type":"boolean"},"version":{"description":"Production-state version after the op.","type":"number"},"id":{"description":"`record_commands.id`, a comms route id, or an egress job id.","type":"string"},"code":{"description":"Why it was refused: `invariant_violated`, `unknown_target`, …","type":"string"},"atClockMs":{"type":"number"}},"required":["op","accepted"],"additionalProperties":false},"CompanionControlResult":{"type":"object","properties":{"version":{"type":"number","description":"Production-state version after the batch, or 0 when no op touched the document — a record-only or go-live-only batch changes nothing about the production state."},"results":{"type":"array","items":{"$ref":"#/components/schemas/CompanionOpResult"}}},"required":["version","results"],"additionalProperties":false,"description":"One result per op, in the order they were sent. A batch runs to the end: a stale graphic id must not swallow the take next to it, so the response reports both rather than failing the request."},"CompanionState":{"type":"object","properties":{"variables":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"feedbacks":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"boolean"}}},"required":["variables","feedbacks"],"additionalProperties":false,"description":"The tally picture as flat Companion variables and boolean feedbacks. Derived from the live production document on every call — a cache would be a second thing that can be stale about what is on air."},"CompanionPairingCreated":{"type":"object","properties":{"pairingId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"label":{"type":"string"},"sessionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"productionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"keyPrefix":{"type":"string","description":"The public handle, e.g. `tck_9f3a1c7e2b04`. Safe to display."},"expiresAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"pairing":{"type":"string","description":"The `tcpair1.` blob to paste into the module, or render as a QR. **Contains the credential and is returned exactly once** — it is not stored and cannot be read back."}},"required":["pairingId","label","sessionId","productionId","keyPrefix","expiresAt","pairing"],"additionalProperties":false,"description":"Returned once. Nothing here is recoverable afterwards except the prefix."},"CompanionPairing":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"label":{"type":"string"},"sessionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"productionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"actorParticipantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"keyPrefix":{"type":"string"},"keyStatus":{"type":"string"},"expiresAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"revokedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"lastSeenAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"device":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"name":{"type":"string"},"host":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastSeenAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]}},"required":["id","name","host","lastSeenAt"],"additionalProperties":false},{"type":"null"}]}},"required":["id","label","sessionId","productionId","actorParticipantId","keyPrefix","keyStatus","expiresAt","revokedAt","lastSeenAt","createdAt","device"],"additionalProperties":false},"CompanionPairingList":{"type":"object","properties":{"pairings":{"type":"array","items":{"$ref":"#/components/schemas/CompanionPairing"}}},"required":["pairings"],"additionalProperties":false,"description":"Every pairing for this session, revoked ones included — a panel that hid them would leave a producer unable to tell 'I revoked that deck' from 'that deck was never paired'."},"ControlDevice":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"userId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"kind":{"type":"string"},"name":{"type":"string"},"identity":{"type":"object","properties":{"host":{"anyOf":[{"type":"string"},{"type":"null"}]},"port":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]}},"required":["host","port"],"additionalProperties":false},"firmware":{"anyOf":[{"type":"string"},{"type":"null"}]},"capabilities":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"lastSeenAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","orgId","userId","kind","name","identity","firmware","capabilities","lastSeenAt","createdAt","updatedAt"],"additionalProperties":false,"description":"A control surface this organisation has seen. Never a credential — a pairing's key lives in `api_keys`, hashed, and never comes back out."},"ControlDeviceList":{"type":"object","properties":{"devices":{"type":"array","items":{"$ref":"#/components/schemas/ControlDevice"}}},"required":["devices"],"additionalProperties":false},"CompanionTallyTicket":{"type":"object","properties":{"url":{"type":"string"},"ticket":{"type":"string"},"expiresAt":{"type":"string"},"intervalMs":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["url","ticket","expiresAt","intervalMs"],"additionalProperties":false},"CanvaProvenance":{"type":"object","properties":{"designId":{"type":"string"},"version":{"anyOf":[{"type":"string"},{"type":"null"}]},"editUrl":{"anyOf":[{"type":"string"},{"type":"null"}]},"templateId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"templateVersion":{"anyOf":[{"type":"string"},{"type":"null"}]},"participantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"autofillInputs":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}]},"supersedesAssetId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]}},"required":["designId","version","editUrl","templateId","templateVersion","participantId","autofillInputs","supersedesAssetId"],"additionalProperties":false,"description":"Where a media asset came from in Canva."},"AssetKind":{"type":"string","enum":["video","audio","image","stinger","font","caption","document"],"description":"What the switcher will do with the object. Decided by a magic-number sniff of the file's first bytes; a declared kind only has to agree with it. `stinger` is a video that a transition plays over the cut."},"AssetSource":{"type":"string","enum":["upload","canva","render","system"],"description":"Where the asset came from. `canva` carries design provenance (plan §7.3); `render` is something Tablecast produced."},"Asset":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"teamId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"productionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"kind":{"$ref":"#/components/schemas/AssetKind"},"source":{"$ref":"#/components/schemas/AssetSource"},"name":{"type":"string"},"mimeType":{"anyOf":[{"type":"string"},{"type":"null"}]},"bytes":{"anyOf":[{"type":"number"},{"type":"null"}]},"width":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"durationMs":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"hasAlpha":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"proxies":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"object","properties":{"key":{"type":"string"},"width":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"height":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["key"],"additionalProperties":false}},"status":{"type":"string","enum":["processing","ready","failed"]},"createdBy":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"updatedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"canva":{"anyOf":[{"$ref":"#/components/schemas/CanvaProvenance"},{"type":"null"}]},"renderDefaults":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"assetVersion":{"type":"integer","minimum":1,"maximum":9007199254740991}},"required":["id","orgId","teamId","productionId","kind","source","name","mimeType","bytes","width","height","durationMs","hasAlpha","proxies","status","createdBy","createdAt","updatedAt","canva","renderDefaults","assetVersion"],"additionalProperties":false,"description":"One item in the media library."},"AssetUpload":{"type":"object","properties":{"url":{"type":"string"},"method":{"type":"string","const":"PUT"},"headers":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"expiresAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"expiresInSeconds":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"maxBytes":{"type":"number"}},"required":["url","method","headers","expiresAt","expiresInSeconds","maxBytes"],"additionalProperties":false,"description":"A presigned PUT for the asset's object. Single-shot until the multipart upload service lands, so 5 GiB is the ceiling and an interrupted upload restarts."},"CreatedAsset":{"type":"object","properties":{"asset":{"$ref":"#/components/schemas/Asset"},"upload":{"$ref":"#/components/schemas/AssetUpload"},"proxyJobId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"detected":{"type":"object","properties":{"kind":{"$ref":"#/components/schemas/AssetKind"},"mimeType":{"type":"string"},"label":{"type":"string"}},"required":["kind","mimeType","label"],"additionalProperties":false}},"required":["asset","upload","proxyJobId","detected"],"additionalProperties":false,"description":"The created row, plus somewhere to put the bytes."},"AssetList":{"type":"object","properties":{"assets":{"type":"array","items":{"$ref":"#/components/schemas/Asset"}},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["assets","nextCursor","limit"],"additionalProperties":false,"description":"A page of the library, newest first. A production's page includes its team's and its org's shared assets — an org-wide stinger is usable in every show without being copied."},"AssetDownloadLink":{"type":"object","properties":{"url":{"type":"string"},"expiresAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"expiresInSeconds":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"filename":{"type":"string"},"contentType":{"type":"string"},"bytes":{"anyOf":[{"type":"number"},{"type":"null"}]}},"required":["url","expiresAt","expiresInSeconds","filename","contentType","bytes"],"additionalProperties":false,"description":"A presigned GET, valid for fifteen minutes, with the filename signed into it."},"CanvaCapabilities":{"type":"object","properties":{"autofill":{"anyOf":[{"type":"boolean"},{"type":"string","const":"unknown"}]},"autofillReason":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["autofill","autofillReason"],"additionalProperties":false,"description":"What the connected Canva account's plan allows. Autofill needs Canva Enterprise."},"CanvaConnection":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"externalAccountId":{"type":"string"},"displayName":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"status":{"type":"string"},"accessTokenExpiresAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastRefreshedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]},"capabilities":{"$ref":"#/components/schemas/CanvaCapabilities"},"createdAt":{"type":"string"}},"required":["id","externalAccountId","displayName","scopes","status","accessTokenExpiresAt","lastRefreshedAt","lastError","capabilities","createdAt"],"additionalProperties":false,"description":"An organisation's connected Canva account. Never a token, never a secret path."},"CanvaConnectionEnvelope":{"type":"object","properties":{"connection":{"anyOf":[{"$ref":"#/components/schemas/CanvaConnection"},{"type":"null"}]}},"required":["connection"],"additionalProperties":false},"CanvaAuthorizeUrl":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"state":{"type":"string"},"codeVerifier":{"type":"string","minLength":43,"maxLength":128}},"required":["url","state","codeVerifier"],"additionalProperties":false},"CanvaDesign":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"editUrl":{"anyOf":[{"type":"string"},{"type":"null"}]},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}]},"pageCount":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"importedAssetId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"importedUpToDate":{"type":"boolean"}},"required":["id","title","updatedAt","editUrl","thumbnailUrl","pageCount","importedAssetId","importedUpToDate"],"additionalProperties":false,"description":"A design in the connected Canva account."},"CanvaDesignList":{"type":"object","properties":{"designs":{"type":"array","items":{"$ref":"#/components/schemas/CanvaDesign"}},"continuation":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["designs","continuation"],"additionalProperties":false},"CanvaJob":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"kind":{"type":"string"},"status":{"type":"string"},"error":{"anyOf":[{"type":"string"},{"type":"null"}]},"assetId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"createdAt":{"type":"string"},"finishedAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","kind","status","error","assetId","createdAt","finishedAt"],"additionalProperties":false,"description":"One piece of asynchronous Canva work — an export, an autofill or an alpha conversion."},"CanvaImportResult":{"type":"object","properties":{"outcome":{"type":"string","enum":["queued","reused"]},"assetId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"job":{"anyOf":[{"$ref":"#/components/schemas/CanvaJob"},{"type":"null"}]}},"required":["outcome","assetId","job"],"additionalProperties":false},"CanvaJobEnvelope":{"type":"object","properties":{"job":{"$ref":"#/components/schemas/CanvaJob"}},"required":["job"],"additionalProperties":false},"CanvaBrandTemplate":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}]},"registeredId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]}},"required":["id","title","updatedAt","thumbnailUrl","registeredId"],"additionalProperties":false,"description":"A Brand Template in the connected Canva account. Enterprise only."},"CanvaBrandTemplateList":{"type":"object","properties":{"templates":{"type":"array","items":{"$ref":"#/components/schemas/CanvaBrandTemplate"}},"continuation":{"anyOf":[{"type":"string"},{"type":"null"}]},"capabilities":{"$ref":"#/components/schemas/CanvaCapabilities"}},"required":["templates","continuation","capabilities"],"additionalProperties":false},"CanvaDatasetField":{"type":"object","properties":{"name":{"type":"string"},"type":{"type":"string"}},"required":["name","type"],"additionalProperties":false},"CanvaOutputPreset":{"type":"object","properties":{"width":{"type":"integer","minimum":16,"maximum":8192},"height":{"type":"integer","minimum":16,"maximum":8192},"format":{"type":"string","enum":["png","mp4","gif"]},"transparent":{"type":"boolean"}},"required":["width","height","format"],"additionalProperties":false},"CanvaTemplate":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"brandTemplateId":{"type":"string"},"name":{"type":"string"},"fieldMap":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"outputPreset":{"$ref":"#/components/schemas/CanvaOutputPreset"},"dataset":{"type":"array","items":{"$ref":"#/components/schemas/CanvaDatasetField"}},"datasetVersion":{"anyOf":[{"type":"string"},{"type":"null"}]},"autofillAvailable":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"lastSyncedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string"}},"required":["id","brandTemplateId","name","fieldMap","outputPreset","dataset","datasetVersion","autofillAvailable","lastSyncedAt","lastError","createdAt"],"additionalProperties":false,"description":"A Canva Brand Template registered for autofill, and the field map that drives it."},"CanvaTemplateList":{"type":"object","properties":{"templates":{"type":"array","items":{"$ref":"#/components/schemas/CanvaTemplate"}},"capabilities":{"$ref":"#/components/schemas/CanvaCapabilities"}},"required":["templates","capabilities"],"additionalProperties":false},"CanvaTemplateEnvelope":{"type":"object","properties":{"template":{"$ref":"#/components/schemas/CanvaTemplate"}},"required":["template"],"additionalProperties":false},"CreatedCanvaDesign":{"type":"object","properties":{"designId":{"type":"string"},"title":{"type":"string"},"editUrl":{"anyOf":[{"type":"string"},{"type":"null"}]},"seededAssetIds":{"type":"array","items":{"type":"string"}},"seedWarnings":{"type":"array","items":{"type":"string"}}},"required":["designId","title","editUrl","seededAssetIds","seedWarnings"],"additionalProperties":false},"CanvaAssetVersion":{"type":"object","properties":{"assetId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"assetVersion":{"type":"integer","minimum":1,"maximum":9007199254740991},"canvaVersion":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string"},"supersedesAssetId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]}},"required":["assetId","assetVersion","canvaVersion","createdAt","supersedesAssetId"],"additionalProperties":false},"CanvaAssetVersionList":{"type":"object","properties":{"versions":{"type":"array","items":{"$ref":"#/components/schemas/CanvaAssetVersion"}},"latestAssetId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]}},"required":["versions","latestAssetId"],"additionalProperties":false},"AutofillStrapResult":{"type":"object","properties":{"straps":{"type":"array","items":{"type":"object","properties":{"templateId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"outcome":{"type":"string","enum":["cached","queued","unavailable"]},"assetId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"jobId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"reason":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["templateId","outcome","assetId","jobId","reason"],"additionalProperties":false}}},"required":["straps"],"additionalProperties":false},"GraphicAsset":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"productionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"kind":{"type":"string","enum":["super","title","bug"]},"source":{"type":"string","enum":["house","canva"]},"status":{"type":"string","enum":["pending","ready","failed"]},"name":{"type":"string"},"assetId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"houseTemplateId":{"anyOf":[{"type":"string"},{"type":"null"}]},"canvaTemplateId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"fieldValues":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"locale":{"anyOf":[{"type":"string"},{"type":"null"}]},"variants":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}}},"variantAssets":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"locales":{"type":"array","items":{"type":"string"}},"participantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"recipientId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"version":{"type":"integer","minimum":1,"maximum":9007199254740991},"supersedesGraphicId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"generationId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"jobId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"error":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"updatedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","productionId","kind","source","status","name","assetId","houseTemplateId","canvaTemplateId","fieldValues","locale","variants","variantAssets","locales","participantId","recipientId","version","supersedesGraphicId","generationId","jobId","error","createdAt","updatedAt"],"additionalProperties":false,"description":"One super: a person, a template and the show it belongs to, plus whichever rendering of it is current."},"GraphicAssetList":{"type":"object","properties":{"graphics":{"type":"array","items":{"$ref":"#/components/schemas/GraphicAsset"}}},"required":["graphics"],"additionalProperties":false,"description":"A show's supers, newest first. Regenerated-over versions are hidden unless `history` is set."},"GraphicAssetEnvelope":{"type":"object","properties":{"graphic":{"$ref":"#/components/schemas/GraphicAsset"}},"required":["graphic"],"additionalProperties":false,"description":"One super."},"GraphicGenerationProgress":{"type":"object","properties":{"generationId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"total":{"type":"integer","minimum":0,"maximum":9007199254740991},"ready":{"type":"integer","minimum":0,"maximum":9007199254740991},"pending":{"type":"integer","minimum":0,"maximum":9007199254740991},"failed":{"type":"integer","minimum":0,"maximum":9007199254740991},"done":{"type":"boolean"}},"required":["generationId","total","ready","pending","failed","done"],"additionalProperties":false,"description":"How one generate run is going. A count over the run's own rows, not a job of its own — so a person who already had a super comes back in `graphics` but is not part of `total`, because nothing was started for them."},"GenerateGraphicsResult":{"type":"object","properties":{"progress":{"$ref":"#/components/schemas/GraphicGenerationProgress"},"graphics":{"type":"array","items":{"$ref":"#/components/schemas/GraphicAsset"}},"skipped":{"type":"array","items":{"type":"object","properties":{"participantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"recipientId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]}},"required":["participantId","recipientId"],"additionalProperties":false}},"remaining":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["progress","graphics","skipped","remaining"],"additionalProperties":false,"description":"What a generate run produced. House supers come back `ready`; Canva supers come back `pending`."},"HouseSuperTemplate":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"variant":{"type":"string"},"width":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"height":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"previewSvg":{"type":"string"}},"required":["id","name","variant","width","height","previewSvg"],"additionalProperties":false,"description":"A built-in lower-third template, rendered with the org's own colours. Every org has these without connecting anything."},"HouseSuperTemplateList":{"type":"object","properties":{"templates":{"type":"array","items":{"$ref":"#/components/schemas/HouseSuperTemplate"}},"localeSupport":{"type":"object","properties":{"locale":{"anyOf":[{"type":"string"},{"type":"null"}]},"script":{"type":"string"},"shaping":{"type":"string","enum":["simple","joining","complex"]},"support":{"type":"string","enum":["embedded","system","unavailable"]}},"required":["locale","script","shaping","support"],"additionalProperties":false}},"required":["templates","localeSupport"],"additionalProperties":false,"description":"The house templates, previewed with this show's brand kit and in the language asked for."},"GraphicVersionList":{"type":"object","properties":{"versions":{"type":"array","items":{"$ref":"#/components/schemas/GraphicAsset"}}},"required":["versions"],"additionalProperties":false,"description":"Every version of one super, newest first. A regeneration is a new row; old ones are retained because scenes hold ids."},"BrandKitColors":{"type":"object","propertyNames":{"type":"string","pattern":"^[A-Za-z][A-Za-z0-9_]{0,63}$"},"additionalProperties":{"type":"string","pattern":"^(#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})|rgba?\\(\\s*\\d{1,3}(?:\\.\\d+)?\\s*[, ]\\s*\\d{1,3}(?:\\.\\d+)?\\s*[, ]\\s*\\d{1,3}(?:\\.\\d+)?\\s*(?:[,/]\\s*(?:0?\\.\\d+|[01](?:\\.0+)?|\\d{1,3}%)\\s*)?\\))$"},"description":"The palette, by token name. The eight roles with defaults are `primary`, `secondary`, `background`, `text`, `textMuted`, `accent`, `accentText` and `surface`; an org may add its own and reference it from a template as `$myToken`. Hex or `rgb()`/`rgba()` only — a named CSS colour cannot be contrast-checked or given an alpha."},"BrandKitFont":{"type":"object","properties":{"family":{"type":"string","minLength":1,"maxLength":120},"role":{"type":"string","enum":["display","body"]},"assetId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"url":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"weight":{"anyOf":[{"type":"integer","minimum":1,"maximum":1000},{"type":"string","maxLength":20}]},"style":{"type":"string","enum":["normal","italic"]},"source":{"default":"upload","type":"string","enum":["upload","google","system","canva"]},"note":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]}},"required":["family","role","source"],"additionalProperties":false,"description":"One face. `assetId` names a `media_assets` row of kind `font` that this API stores; `url` is for a Google or system face we do not host. `note` says why this is not the family that was asked for — a Canva import sets it when Canva would not license one out."},"LowerThirdTemplate":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9-]*$"},"name":{"type":"string","minLength":1,"maxLength":80},"variant":{"type":"string","enum":["bar","block","stack","minimal"]},"version":{"default":1,"type":"integer","minimum":1,"maximum":9007199254740991},"builtin":{"default":false,"type":"boolean"},"animation":{"default":{"in":"slide","out":"slide","durationMs":400},"type":"object","properties":{"in":{"default":"slide","type":"string","enum":["none","fade","slide","wipe","scale"]},"out":{"default":"slide","type":"string","enum":["none","fade","slide","wipe","scale"]},"durationMs":{"default":400,"type":"integer","minimum":0,"maximum":5000}},"required":["in","out","durationMs"],"additionalProperties":false},"colors":{"default":{},"type":"object","properties":{"plate":{"type":"string","minLength":1,"maxLength":64},"foreground":{"type":"string","minLength":1,"maxLength":64},"muted":{"type":"string","minLength":1,"maxLength":64},"accent":{"type":"string","minLength":1,"maxLength":64},"accentForeground":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false},"layout":{"default":{},"type":"object","properties":{"height":{"type":"number","exclusiveMinimum":0,"maximum":1},"width":{"type":"number","exclusiveMinimum":0,"maximum":1},"paddingX":{"type":"number","minimum":0,"maximum":0.5},"accentWidth":{"type":"number","minimum":0,"maximum":0.5},"primarySize":{"type":"number","exclusiveMinimum":0,"maximum":1},"secondarySize":{"type":"number","exclusiveMinimum":0,"maximum":1},"radius":{"type":"number","minimum":0,"maximum":0.5},"align":{"type":"string","enum":["left","centre","right"]},"uppercaseSecondary":{"type":"boolean"}},"additionalProperties":false}},"required":["id","name","variant","version","builtin","animation","colors","layout"],"additionalProperties":false,"description":"A named lower-third design: one of four layout variants, an animation, and a mapping from the renderer's colour slots onto brand tokens (`$primary`) or literals. `version` is bumped by the server whenever the definition changes, so a session that pinned an earlier kit revision keeps the design it went to air with."},"BrandKitCanvaSync":{"type":"object","properties":{"brandKitName":{"type":"string","maxLength":200},"syncedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"fontNotes":{"default":[],"maxItems":32,"type":"array","items":{"type":"object","properties":{"family":{"type":"string","maxLength":120},"mappedTo":{"type":"string","maxLength":120},"reason":{"type":"string","maxLength":200}},"required":["family","mappedTo","reason"],"additionalProperties":false}}},"required":["fontNotes"],"additionalProperties":false,"description":"What the last Canva import found: which kit, when, and every font family Canva would not export, with what it was mapped to instead."},"BrandKit":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"teamId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"name":{"type":"string"},"revision":{"type":"integer","minimum":1,"maximum":9007199254740991},"colors":{"$ref":"#/components/schemas/BrandKitColors"},"fonts":{"type":"array","items":{"$ref":"#/components/schemas/BrandKitFont"}},"logoAssetId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"bugAssetId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"lowerThirdTemplates":{"type":"array","items":{"$ref":"#/components/schemas/LowerThirdTemplate"}},"canvaBrandKitId":{"anyOf":[{"type":"string"},{"type":"null"}]},"canvaSync":{"anyOf":[{"$ref":"#/components/schemas/BrandKitCanvaSync"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"updatedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","orgId","teamId","name","revision","colors","fonts","logoAssetId","bugAssetId","lowerThirdTemplates","canvaBrandKitId","canvaSync","createdAt","updatedAt"],"additionalProperties":false,"description":"A brand kit: the palette, the faces, the logo and bug, and the lower-third library. `revision` is the optimistic-concurrency token — send it back as `expectedRevision`."},"BrandKitList":{"type":"object","properties":{"brandKits":{"type":"array","items":{"$ref":"#/components/schemas/BrandKit"}}},"required":["brandKits"],"additionalProperties":false,"description":"Every brand kit this organisation can use, newest first."},"BrandFontUpload":{"type":"object","properties":{"brandKit":{"$ref":"#/components/schemas/BrandKit"},"assetId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"upload":{"type":"object","properties":{"url":{"type":"string"},"method":{"type":"string","const":"PUT"},"headers":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"expiresAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"expiresInSeconds":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"maxBytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["url","method","headers","expiresAt","expiresInSeconds","maxBytes"],"additionalProperties":false},"detected":{"type":"object","properties":{"mimeType":{"type":"string"},"label":{"type":"string"}},"required":["mimeType","label"],"additionalProperties":false}},"required":["brandKit","assetId","upload","detected"],"additionalProperties":false,"description":"The updated kit, and a presigned PUT for the font's bytes. The headers must be replayed verbatim: the size and content type are signed, so the URL is a capability for one file of one size."},"BrandTheme":{"type":"object","properties":{"brandKitId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"revision":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"versionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"kit":{"anyOf":[{"type":"object","properties":{"name":{"type":"string"},"colors":{"$ref":"#/components/schemas/BrandKitColors"},"fonts":{"type":"array","items":{"$ref":"#/components/schemas/BrandKitFont"}},"logoAssetId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"bugAssetId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"lowerThirdTemplates":{"type":"array","items":{"$ref":"#/components/schemas/LowerThirdTemplate"}}},"required":["name","colors","fonts","logoAssetId","bugAssetId","lowerThirdTemplates"],"additionalProperties":false},{"type":"null"}]}},"required":["brandKitId","revision","versionId","kit"],"additionalProperties":false,"description":"The brand a session renders against, pinned on the first read so an edit made mid-show lands on the next session rather than restyling the one on air."},"CanvaBrandKitSummary":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name"],"additionalProperties":false,"description":"A brand kit in the connected Canva account."},"CanvaBrandKitList":{"type":"object","properties":{"brandKits":{"type":"array","items":{"$ref":"#/components/schemas/CanvaBrandKitSummary"}}},"required":["brandKits"],"additionalProperties":false,"description":"The Canva brand kits this organisation's connected account can see."},"ImportedBrandKit":{"type":"object","properties":{"brandKit":{"$ref":"#/components/schemas/BrandKit"},"created":{"type":"boolean"},"fontNotes":{"type":"array","items":{"type":"object","properties":{"family":{"type":"string"},"mappedTo":{"type":"string"},"reason":{"type":"string"}},"required":["family","mappedTo","reason"],"additionalProperties":false}}},"required":["brandKit","created","fontNotes"],"additionalProperties":false,"description":"The kit the import wrote, and what it had to substitute."},"AuditEventSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"actorKind":{"type":"string","enum":["user","api_key","device","guest"]},"actorId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"action":{"type":"string"},"resourceType":{"type":"string"},"resourceId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"createdAt":{"type":"string"}},"required":["id","orgId","actorKind","actorId","action","resourceType","resourceId","createdAt"],"additionalProperties":false,"description":"One entry in an organisation's audit log. `before`/`after` are on the detail resource."},"AuditEventList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/AuditEventSummary"}},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["items","nextCursor","limit"],"additionalProperties":false},"AuditEventDetail":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"actorKind":{"type":"string","enum":["user","api_key","device","guest"]},"actorId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"action":{"type":"string"},"resourceType":{"type":"string"},"resourceId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"createdAt":{"type":"string"},"impersonationId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"before":{"anyOf":[{},{"type":"null"}]},"after":{"anyOf":[{},{"type":"null"}]},"ip":{"anyOf":[{"type":"string"},{"type":"null"}]},"userAgent":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","orgId","actorKind","actorId","action","resourceType","resourceId","createdAt","impersonationId","before","after","ip","userAgent"],"additionalProperties":false,"description":"One audit event with the before/after documents that say what actually changed."},"PlanCode":{"type":"string","enum":["free","creator","studio","business","enterprise"],"description":"Tablecast plan tier."},"BillingInterval":{"type":"string","enum":["month","year"],"description":"Billing period. Annual is priced at plan §3's −20%."},"FeatureKey":{"type":"string","enum":["rec_participant_min_included","overage_cents_per_min","max_participants","max_concurrent_sessions","storage_gb","max_resolution","live_destinations","external_ingest","watermark","brand_kit","webmidi","transcription","api_read","switcher","edl","editor","producer_room","ifb","companion","teams","webhooks","mcp","connectors","live_captions","rcv","sso","custom_domain","audit_log","api_full","residency","dedicated_kms","scim","custom_roles","byo_transcription","desktop_helper"],"description":"An entitlement key. Numeric entitlements use -1 for unlimited."},"Entitlement":{"type":"object","properties":{"key":{"$ref":"#/components/schemas/FeatureKey"},"value":{"anyOf":[{"type":"number"},{"type":"boolean"},{"type":"string"}]},"source":{"type":"string","enum":["plan","contract","override","trial"]},"expiresAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]}},"required":["key","value","source","expiresAt"],"additionalProperties":false,"description":"One effective entitlement, with where it came from. Only an override or trial can expire."},"BillingPlan":{"type":"object","properties":{"code":{"$ref":"#/components/schemas/PlanCode"},"name":{"type":"string"},"interval":{"anyOf":[{"$ref":"#/components/schemas/BillingInterval"},{"type":"null"}]},"priceCents":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"includedSeats":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"seatPriceCents":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]}},"required":["code","name","interval","priceCents","includedSeats","seatPriceCents"],"additionalProperties":false},"BillingSubscription":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"status":{"type":"string","enum":["trialing","active","past_due","paused","canceled","incomplete"]},"seats":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"currentPeriodStart":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"currentPeriodEnd":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"cancelAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"trialEndsAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]}},"required":["id","status","seats","currentPeriodStart","currentPeriodEnd","cancelAt","trialEndsAt"],"additionalProperties":false},"BillingUsage":{"type":"object","properties":{"periodStart":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"periodEnd":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"recParticipantMinutes":{"type":"number"},"includedMinutes":{"type":"number"},"remainingMinutes":{"anyOf":[{"type":"number"},{"type":"null"}]},"overageCentsPerMinute":{"type":"number"}},"required":["periodStart","periodEnd","recParticipantMinutes","includedMinutes","remainingMinutes","overageCentsPerMinute"],"additionalProperties":false,"description":"Metered usage for the current period. Reads 0 until the metering pipeline writes usage_records."},"OrgBillingState":{"type":"string","enum":["active","grace","read_only"],"description":"active: nothing wrong. grace: payment failed, within the 7-day grace period. read_only: grace elapsed — mutating requests are refused with 402 until payment succeeds."},"BillingInvoiceItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"number":{"anyOf":[{"type":"string"},{"type":"null"}]},"status":{"type":"string","enum":["draft","open","paid","void","uncollectible"]},"currency":{"type":"string"},"totalCents":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"periodStart":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"periodEnd":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"paidAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"pdfUrl":{"anyOf":[{"type":"string"},{"type":"null"}]},"provider":{"type":"string","enum":["stripe","xero"]},"amountDueCents":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"dueAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"xeroStatus":{"anyOf":[{"type":"string"},{"type":"null"}]},"overdue":{"type":"boolean"}},"required":["id","number","status","currency","totalCents","periodStart","periodEnd","paidAt","pdfUrl","provider","amountDueCents","dueAt","xeroStatus","overdue"],"additionalProperties":false},"BillingProvider":{"type":"string","enum":["stripe","xero"],"description":"Which system bills this organisation. `stripe` is self-serve by card. `xero` is invoiced on terms; Checkout and the Customer Portal do not apply to it, and its invoices are raised from usage and contract lines."},"XeroConnection":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"tenantId":{"anyOf":[{"type":"string"},{"type":"null"}]},"tenantName":{"anyOf":[{"type":"string"},{"type":"null"}]},"scopes":{"type":"array","items":{"type":"string"}},"status":{"type":"string"},"accessTokenExpiresAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"lastRefreshedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","tenantId","tenantName","scopes","status","accessTokenExpiresAt","lastRefreshedAt","lastError","createdAt"],"additionalProperties":false,"description":"An organisation's Xero connection. Never a token, and never the Secrets Manager path the token lives at — that is an internal address."},"XeroAuthMode":{"type":"string","enum":["authorization_code","custom_connection"],"description":"Which kind of Xero app this environment is configured with. `authorization_code` sends the finance contact to Xero's consent screen and can bill into any organisation they administer. `custom_connection` is a machine-to-machine app bound to one organisation at registration: there is no consent screen, so the connect call takes no `code`."},"XeroBillingStatus":{"type":"object","properties":{"available":{"type":"boolean"},"authMode":{"$ref":"#/components/schemas/XeroAuthMode"},"connection":{"anyOf":[{"$ref":"#/components/schemas/XeroConnection"},{"type":"null"}]}},"required":["available","authMode","connection"],"additionalProperties":false},"XeroAuthorizeUrl":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"state":{"type":"string"},"redirectUri":{"type":"string","format":"uri"}},"required":["url","state","redirectUri"],"additionalProperties":false},"XeroConnectResult":{"type":"object","properties":{"connection":{"anyOf":[{"$ref":"#/components/schemas/XeroConnection"},{"type":"null"}]},"tenants":{"type":"array","items":{"type":"object","properties":{"tenantId":{"type":"string"},"tenantName":{"type":"string"}},"required":["tenantId","tenantName"],"additionalProperties":false}}},"required":["connection","tenants"],"additionalProperties":false},"BillingProviderResult":{"type":"object","properties":{"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"billingProvider":{"$ref":"#/components/schemas/BillingProvider"}},"required":["orgId","billingProvider"],"additionalProperties":false},"XeroInvoiceLine":{"type":"object","properties":{"unit":{"anyOf":[{"type":"string"},{"type":"null"}]},"description":{"type":"string"},"quantity":{"type":"number"},"unitAmountCents":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"amountCents":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["unit","description","quantity","unitAmountCents","amountCents"],"additionalProperties":false},"XeroInvoiceRunResult":{"type":"object","properties":{"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"period":{"type":"string"},"outcome":{"type":"string","enum":["created","already_exists","nothing_to_invoice","preview"]},"reference":{"type":"string"},"currency":{"type":"string"},"lines":{"type":"array","items":{"$ref":"#/components/schemas/XeroInvoiceLine"}},"totalCents":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"invoiceId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"xeroInvoiceId":{"anyOf":[{"type":"string"},{"type":"null"}]},"xeroInvoiceNumber":{"anyOf":[{"type":"string"},{"type":"null"}]},"recordsInvoiced":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["orgId","period","outcome","reference","currency","lines","totalCents","invoiceId","xeroInvoiceId","xeroInvoiceNumber","recordsInvoiced"],"additionalProperties":false,"description":"The result of raising (or previewing) one month's invoice. Invoices are created `DRAFT`: a person reviews and sends them from Xero, so no API call can put a document in front of a customer."},"QuotaKey":{"type":"string","enum":["rec_participant_min","storage_gb_month","concurrent_sessions","participants_per_session"],"description":"One of the four per-organisation quotas: two metered allowances and two concurrency ceilings."},"OrgQuota":{"type":"object","properties":{"key":{"$ref":"#/components/schemas/QuotaKey"},"label":{"type":"string","description":"Sentence-case name for a settings screen."},"description":{"type":"string"},"entitlementKey":{"$ref":"#/components/schemas/FeatureKey"},"kind":{"type":"string","enum":["metered","concurrent"],"description":"`metered` is summed over the billing period and refuses with 402 once a hard cap is spent. `concurrent` is counted at this instant and refuses with 429, because it frees up on its own."},"scope":{"type":"string","enum":["org","session"]},"unit":{"type":"string","description":"What the numbers count, e.g. `participant-minutes`."},"limit":{"type":"number"},"used":{"anyOf":[{"type":"number"},{"type":"null"}]},"remaining":{"anyOf":[{"type":"number"},{"type":"null"}]},"percent":{"anyOf":[{"type":"number"},{"type":"null"}]},"unlimited":{"type":"boolean"},"exceeded":{"type":"boolean"},"blocked":{"type":"boolean"},"retryable":{"type":"boolean"}},"required":["key","label","description","entitlementKey","kind","scope","unit","limit","used","remaining","percent","unlimited","exceeded","blocked","retryable"],"additionalProperties":false,"description":"One quota, its ceiling from `org_entitlements` and where the organisation stands against it."},"BillingSummary":{"type":"object","properties":{"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"plan":{"$ref":"#/components/schemas/BillingPlan"},"subscription":{"anyOf":[{"$ref":"#/components/schemas/BillingSubscription"},{"type":"null"}]},"entitlements":{"type":"array","items":{"$ref":"#/components/schemas/Entitlement"}},"usage":{"$ref":"#/components/schemas/BillingUsage"},"selfServeAvailable":{"type":"boolean"},"billingProvider":{"$ref":"#/components/schemas/BillingProvider"},"xero":{"anyOf":[{"$ref":"#/components/schemas/XeroBillingStatus"},{"type":"null"}]},"state":{"$ref":"#/components/schemas/OrgBillingState"},"stateReason":{"anyOf":[{"type":"string","enum":["subscription_past_due","invoice_overdue"]},{"type":"null"}]},"graceEndsAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"invoices":{"type":"array","items":{"$ref":"#/components/schemas/BillingInvoiceItem"}},"quotas":{"type":"array","items":{"$ref":"#/components/schemas/OrgQuota"}}},"required":["orgId","plan","subscription","entitlements","usage","selfServeAvailable","billingProvider","xero","state","stateReason","graceEndsAt","invoices","quotas"],"additionalProperties":false},"CheckoutSession":{"type":"object","properties":{"sessionId":{"type":"string"},"url":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["sessionId","url"],"additionalProperties":false},"WebhookAck":{"type":"object","properties":{"received":{"type":"boolean"},"outcome":{"type":"string","enum":["applied","duplicate","ignored","no_match"]}},"required":["received","outcome"],"additionalProperties":false,"description":"LiveKit only needs a 2xx; the body is for operators reading the access log."},"UsageUnit":{"type":"string","enum":["rec_participant_min","live_participant_min","egress_min","ingest_min","storage_gb_month","render_min","transcription_min","api_calls","turn_gb","translation_char"],"description":"A metered unit. Minutes for the participant/egress/render/transcription units, GB-months for storage, calls for the API, gigabytes for relay traffic, and characters of source text for translation (TAB-595) — the quantity is in the unit's own dimension, never normalised to minutes."},"UnitUsage":{"type":"object","properties":{"unit":{"$ref":"#/components/schemas/UsageUnit"},"used":{"type":"number"},"included":{"anyOf":[{"type":"number"},{"type":"null"}]},"remaining":{"anyOf":[{"type":"number"},{"type":"null"}]},"percent":{"anyOf":[{"type":"number"},{"type":"null"}]},"hardCap":{"type":"boolean"},"exceeded":{"type":"boolean"},"blocked":{"type":"boolean"},"thresholdReached":{"anyOf":[{"type":"number"},{"type":"null"}]},"thresholdsFired":{"type":"array","items":{"type":"number"}}},"required":["unit","used","included","remaining","percent","hardCap","exceeded","blocked","thresholdReached","thresholdsFired"],"additionalProperties":false,"description":"One metered unit's month-to-date position, and where it sits against the plan's allowance."},"OrgUsage":{"type":"object","properties":{"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"periodStart":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"periodEnd":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"generatedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"overageCentsPerMinute":{"type":"number"},"units":{"type":"array","items":{"$ref":"#/components/schemas/UnitUsage"}}},"required":["orgId","periodStart","periodEnd","generatedAt","overageCentsPerMinute","units"],"additionalProperties":false,"description":"Month-to-date usage by unit with the cap and alert state behind the soft-cap banner."},"ChatMessage":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"channel":{"type":"string"},"body":{"type":"string"},"clockMs":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"createdAt":{"type":"string"},"author":{"type":"object","properties":{"participantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"displayName":{"type":"string"},"role":{"anyOf":[{"type":"string","enum":["producer","director","host","guest","camera","viewer","ingest","egress","prompter"]},{"type":"null"}]}},"required":["participantId","displayName","role"],"additionalProperties":false}},"required":["id","sessionId","channel","body","clockMs","createdAt","author"],"additionalProperties":false,"description":"One chat line. `clockMs` is the session clock, which is what ties it to the recording and EDL."},"ChatPage":{"type":"object","properties":{"channel":{"type":"string"},"messages":{"type":"array","items":{"$ref":"#/components/schemas/ChatMessage"}},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["channel","messages","nextCursor","limit"],"additionalProperties":false},"Cue":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"kind":{"type":"string","enum":["text","countdown"]},"target":{"type":"string"},"body":{"type":"string"},"clockMs":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"deadlineClockMs":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"createdAt":{"type":"string"},"from":{"type":"object","properties":{"participantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"displayName":{"type":"string"},"role":{"anyOf":[{"type":"string","enum":["producer","director","host","guest","camera","viewer","ingest","egress","prompter"]},{"type":"null"}]}},"required":["participantId","displayName","role"],"additionalProperties":false}},"required":["id","sessionId","kind","target","body","clockMs","deadlineClockMs","createdAt","from"],"additionalProperties":false,"description":"One silent producer cue. Persisted in the session log with its session-clock timestamp."},"CueList":{"type":"object","properties":{"cues":{"type":"array","items":{"$ref":"#/components/schemas/Cue"}}},"required":["cues"],"additionalProperties":false},"ChatChannel":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"kind":{"type":"string","enum":["planning","show"]},"productionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"sessionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"slug":{"type":"string"},"name":{"type":"string"},"topic":{"anyOf":[{"type":"string"},{"type":"null"}]},"archivedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string"},"lastMessageAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"unreadCount":{"type":"integer","minimum":0,"maximum":9007199254740991},"unreadMentionCount":{"type":"integer","minimum":0,"maximum":9007199254740991},"lastReadAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","kind","productionId","sessionId","slug","name","topic","archivedAt","createdAt","lastMessageAt","unreadCount","unreadMentionCount","lastReadAt"],"additionalProperties":false,"description":"A persisted chat channel. Materialised on first use — there is no create call."},"ChatChannelList":{"type":"object","properties":{"channels":{"type":"array","items":{"$ref":"#/components/schemas/ChatChannel"}}},"required":["channels"],"additionalProperties":false},"ChannelAttachment":{"type":"object","properties":{"assetId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"kind":{"type":"string","enum":["media","graphic"]},"name":{"type":"string","maxLength":200},"bytes":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["assetId","kind"],"additionalProperties":false},"ChannelMessage":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"channelId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"author":{"type":"object","properties":{"userId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"participantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"displayName":{"type":"string"}},"required":["userId","participantId","displayName"],"additionalProperties":false},"body":{"anyOf":[{"type":"string"},{"type":"null"}]},"mentions":{"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"attachments":{"type":"array","items":{"$ref":"#/components/schemas/ChannelAttachment"}},"editedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"deletedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"pinnedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string"},"reactions":{"type":"array","items":{"type":"object","properties":{"emoji":{"type":"string"},"count":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"mine":{"type":"boolean"}},"required":["emoji","count","mine"],"additionalProperties":false}}},"required":["id","channelId","author","body","mentions","attachments","editedAt","deletedAt","pinnedAt","createdAt"],"additionalProperties":false,"description":"One line in a persisted chat channel."},"ChannelMessagePage":{"type":"object","properties":{"channelId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"messages":{"type":"array","items":{"$ref":"#/components/schemas/ChannelMessage"}},"prevCursor":{"anyOf":[{"type":"string"},{"type":"null"}]},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]},"latestCursor":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["channelId","messages","prevCursor","nextCursor","latestCursor"],"additionalProperties":false},"ChannelUnread":{"type":"object","properties":{"channelId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"unreadCount":{"type":"integer","minimum":0,"maximum":9007199254740991},"unreadMentionCount":{"type":"integer","minimum":0,"maximum":9007199254740991},"lastReadAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["channelId","unreadCount","unreadMentionCount","lastReadAt"],"additionalProperties":false},"ChatUnreadSummary":{"type":"object","properties":{"channels":{"type":"array","items":{"$ref":"#/components/schemas/ChannelUnread"}},"total":{"type":"integer","minimum":0,"maximum":9007199254740991},"mentions":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["channels","total","mentions"],"additionalProperties":false,"description":"Unread across every channel in the caller's org, for the badge on the nav."},"ChannelReactionList":{"type":"object","properties":{"messageId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"reactions":{"type":"array","items":{"type":"object","properties":{"emoji":{"type":"string"},"count":{"type":"integer","minimum":0,"maximum":9007199254740991},"mine":{"type":"boolean"}},"required":["emoji","count","mine"],"additionalProperties":false}}},"required":["messageId","reactions"],"additionalProperties":false},"PrompterScriptMarker":{"type":"object","properties":{"id":{"type":"string","description":"Stable marker id. This is what a `prompter.jump` op names, so match on it, not the name."},"name":{"type":"string","description":"What the producer sees in their jump menu."},"wordIndex":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Where the marker falls, in words from the start of the script."},"ratio":{"type":"number","description":"`wordIndex / words` — the 0..1 unit the prompter position model integrates in."}},"required":["id","name","wordIndex","ratio"],"additionalProperties":false},"PrompterScript":{"type":"object","properties":{"docId":{"type":"string","description":"`session:<uuid>:script` — the collab document this was rendered from."},"text":{"type":"string","description":"The words, with block boundaries as newlines. Empty when nobody has written one."},"words":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Word count. The denominator of every ratio in the prompter plane, counted once here so every surface agrees."},"markers":{"type":"array","items":{"$ref":"#/components/schemas/PrompterScriptMarker"}},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The stored document's save counter. 0 when nothing has been saved yet."},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When the doc host last saved. Null for a script nobody has written."}},"required":["docId","text","words","markers","version","updatedAt"],"additionalProperties":false,"description":"A flat read of a session's script, for a client that cannot hold a Y.Doc. Reflects the last debounced save rather than the live document — a client that needs live text opens the collab WebSocket instead, which is why `version` and `updatedAt` are here."},"CollabTicket":{"type":"object","properties":{"docId":{"type":"string","description":"`<scope>:<uuid>:<kind>` — the document this ticket opens."},"scope":{"type":"string","enum":["production","session"]},"kind":{"type":"string","enum":["show_notes","whiteboard","script","timeline"]},"url":{"type":"string","format":"uri","description":"Open this with a WebSocket. The ticket is already on it."},"ticket":{"type":"string","description":"Also supplied separately, for a client that builds its own URL."},"role":{"type":"string","enum":["editor","viewer"],"description":"`editor` may write; `viewer` may read and be seen, and the server refuses its writes."},"expiresAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$","description":"Minutes away. Mint a new one on every dial rather than keeping this."},"subjectId":{"type":"string"},"displayName":{"anyOf":[{"type":"string"},{"type":"null"}]},"avatarUrl":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["docId","scope","kind","url","ticket","role","expiresAt","subjectId","displayName","avatarUrl"],"additionalProperties":false,"description":"Where a collaborative document lives, and the short-lived ticket that opens it."},"RealtimeTicket":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"Open this with a WebSocket. The ticket is already on it as `?ticket=`."},"ticket":{"type":"string","description":"Also supplied separately, for a client that builds its own URL or uses the subprotocol."},"sessionId":{"type":"string","description":"The session this ticket follows. The only one this credential can name."},"productionId":{"type":"string","description":"The production the session hangs off — for the planning-chat fan-out."},"expiresAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$","description":"Minutes away. Re-mint on every connect rather than keeping this."}},"required":["url","ticket","sessionId","productionId","expiresAt"],"additionalProperties":false,"description":"Where a guest's realtime relay lives, and the short-lived ticket that opens it."},"CollabDocumentSummary":{"type":"object","properties":{"docId":{"type":"string"},"scope":{"type":"string","enum":["production","session"]},"kind":{"type":"string","enum":["show_notes","whiteboard","script","timeline"]},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The save counter. Increments on every persisted change."},"bytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Size of the stored document."},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"updatedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["docId","scope","kind","version","bytes","createdAt","updatedAt"],"additionalProperties":false},"CollabDocumentList":{"type":"object","properties":{"documents":{"type":"array","items":{"$ref":"#/components/schemas/CollabDocumentSummary"}}},"required":["documents"],"additionalProperties":false,"description":"The collaborative documents that exist here. A kind that is absent has never been written in — which is not an error, because opening it creates it."},"CommentAuthor":{"type":"object","properties":{"kind":{"type":"string","enum":["user","participant","share_viewer"]},"userId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"participantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"shareViewerId":{"anyOf":[{"type":"string"},{"type":"null"}]},"displayName":{"type":"string"}},"required":["kind","userId","participantId","shareViewerId","displayName"],"additionalProperties":false,"description":"Who wrote it. Exactly one id is set and `kind` says which — a guest and a share viewer have no account, so a client resolves an avatar only for `user` (via `POST /v1/orgs/{orgId}/members/avatars`) and renders initials for the rest."},"Comment":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"threadId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"body":{"type":"string"},"mentions":{"type":"array","items":{"type":"string"}},"attachments":{"type":"array","items":{"type":"object","properties":{"assetId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"name":{"type":"string"},"mimeType":{"type":"string"}},"required":["assetId"],"additionalProperties":false}},"author":{"$ref":"#/components/schemas/CommentAuthor"},"editedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},{"type":"null"}]},"deletedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},{"type":"null"}],"description":"Set on a retracted comment, whose `body` is empty. Deleted comments are returned as tombstones rather than omitted: a conversation with the third of five messages silently removed reads as though the fourth were a non-sequitur."},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"}},"required":["id","threadId","body","mentions","attachments","author","editedAt","deletedAt","createdAt"],"additionalProperties":false},"CommentThread":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"targetKind":{"type":"string","enum":["media_asset","recording","render","graphic_asset","edit_doc"]},"targetId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"productionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"sessionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"anchor":{"anyOf":[{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"time"},"timeMs":{"type":"integer","minimum":0,"maximum":36000000},"durationMs":{"type":"integer","minimum":0,"maximum":36000000}},"required":["kind","timeMs"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"baseline"},"baselineMs":{"type":"integer","minimum":0,"maximum":36000000},"durationMs":{"type":"integer","minimum":0,"maximum":36000000},"trackRef":{"type":"string","maxLength":120}},"required":["kind","baselineMs"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"region"},"x":{"type":"number","minimum":0,"maximum":1},"y":{"type":"number","minimum":0,"maximum":1},"w":{"type":"number","minimum":0,"maximum":1},"h":{"type":"number","minimum":0,"maximum":1},"timeMs":{"type":"integer","minimum":0,"maximum":36000000},"page":{"type":"integer","minimum":1,"maximum":10000}},"required":["kind","x","y","w","h"],"additionalProperties":false}]},{"type":"null"}]},"resolvedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},{"type":"null"}]},"resolvedBy":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"author":{"$ref":"#/components/schemas/CommentAuthor"},"replyCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"lastActivityAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"comments":{"type":"array","items":{"$ref":"#/components/schemas/Comment"}}},"required":["id","targetKind","targetId","productionId","sessionId","anchor","resolvedAt","resolvedBy","author","replyCount","lastActivityAt","createdAt","comments"],"additionalProperties":false},"CommentThreadList":{"type":"object","properties":{"threads":{"type":"array","items":{"$ref":"#/components/schemas/CommentThread"}},"channel":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The AppSync Events channel carrying `comment.*` for this organisation, or null when the caller cannot subscribe (a guest or a share viewer has no Cognito identity). Filter on `targetKind`/`targetId`."}},"required":["threads","channel"],"additionalProperties":false},"LiveSessionsResponse":{"type":"object","properties":{"sessions":{"type":"array","items":{"type":"object","properties":{"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"mediaRegion":{"anyOf":[{"type":"string"},{"type":"null"}]},"livekitRoomName":{"type":"string"}},"required":["sessionId","orgId","mediaRegion","livekitRoomName"],"additionalProperties":false}}},"required":["sessions"],"additionalProperties":false},"BootstrapSession":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"productionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"title":{"type":"string"},"status":{"type":"string","enum":["scheduled","lobby","live","ending","complete","failed","canceled"]},"mediaProvider":{"type":"string","enum":["livekit_cloud","livekit_self_do","livekit_self_aws"]},"mediaRegion":{"anyOf":[{"type":"string"},{"type":"null"}]},"livekitRoomName":{"type":"string","description":"`org_<orgid8>:session_<session id>`"},"livekitRoomSid":{"anyOf":[{"type":"string"},{"type":"null"}]},"sessionClockEpoch":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}],"description":"Wall-clock time at session clock 0; null until the session starts"},"scheduledAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"startedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"recordingPolicy":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"required":["id","orgId","productionId","title","status","mediaProvider","mediaRegion","livekitRoomName","livekitRoomSid","sessionClockEpoch","scheduledAt","startedAt","recordingPolicy"],"additionalProperties":false},"BootstrapLocation":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"name":{"type":"string"},"isRemoteSingle":{"type":"boolean"},"audioSourceParticipantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"monitorParticipantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"mixMinus":{"type":"boolean"}},"required":["id","name","isRemoteSingle","audioSourceParticipantId","monitorParticipantId","mixMinus"],"additionalProperties":false,"description":"A physical room: exactly one audio source and one monitor, enforced by the coordinator."},"BootstrapParticipant":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"displayName":{"type":"string"},"role":{"type":"string","enum":["producer","director","host","guest","camera","viewer","ingest","egress","prompter"]},"livekitIdentity":{"type":"string"},"isHidden":{"type":"boolean"},"locationId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"micHardMuted":{"type":"boolean"},"playsSessionAudio":{"type":"boolean"}},"required":["id","displayName","role","livekitIdentity","isHidden","locationId","micHardMuted","playsSessionAudio"],"additionalProperties":false},"BootstrapProductionState":{"type":"object","properties":{"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Optimistic-concurrency version the coordinator resumes from"},"state":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"updatedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]}},"required":["version","state","updatedAt"],"additionalProperties":false},"BootstrapEdlTail":{"type":"object","properties":{"lastSeq":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Highest `edl_events.seq` written; 0 when the log is empty"},"lastClockMs":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]}},"required":["lastSeq","lastClockMs"],"additionalProperties":false,"description":"Where the append-only EDL got to. A restarted coordinator continues the sequence from here."},"BootstrapQuota":{"type":"object","properties":{"blocked":{"type":"boolean"},"quota":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Which quota refused, e.g. `rec_participant_min`; null when nothing did"},"detail":{"anyOf":[{"type":"string"},{"type":"null"}]},"retryable":{"type":"boolean","description":"True for a concurrency ceiling that frees up on its own; false for a spent allowance"}},"required":["blocked","quota","detail","retryable"],"additionalProperties":false,"description":"TAB-397 — the organisation's quota verdict for this session. The coordinator holds no database credentials, so the guard runs here and its answer travels with the snapshot; a coordinator that reads `blocked: true` refuses to join the room."},"SessionBootstrap":{"type":"object","properties":{"session":{"$ref":"#/components/schemas/BootstrapSession"},"locations":{"type":"array","items":{"$ref":"#/components/schemas/BootstrapLocation"}},"participants":{"type":"array","items":{"$ref":"#/components/schemas/BootstrapParticipant"}},"productionState":{"anyOf":[{"$ref":"#/components/schemas/BootstrapProductionState"},{"type":"null"}]},"edl":{"$ref":"#/components/schemas/BootstrapEdlTail"},"quota":{"description":"Omitted by an API older than TAB-397, which the coordinator reads as allowed.","$ref":"#/components/schemas/BootstrapQuota"},"livekitUrl":{"description":"SFU websocket URL for the session's region. Omitted when the coordinator's own config wins.","type":"string"}},"required":["session","locations","participants","productionState","edl"],"additionalProperties":false,"description":"Everything the session coordinator needs to attach itself to a session at startup. Mirrors `SessionBootstrap` in `@tablecast/shared`, which both sides import."},"RecordCommandStored":{"type":"object","properties":{"commandId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"op":{"type":"string"},"status":{"type":"string"},"ackCount":{"type":"integer","minimum":0,"maximum":9007199254740991},"pendingCount":{"type":"integer","minimum":0,"maximum":9007199254740991},"created":{"type":"boolean","description":"False when this write updated a row that already existed"}},"required":["commandId","sessionId","op","status","ackCount","pendingCount","created"],"additionalProperties":false},"TrackEgressResponse":{"type":"object","properties":{"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"op":{"type":"string","enum":["start","stop"]},"jobs":{"type":"array","items":{"type":"object","properties":{"egressJobId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"trackId":{"type":"string"},"participantId":{"type":"string"},"livekitEgressId":{"anyOf":[{"type":"string"},{"type":"null"}]},"status":{"type":"string","enum":["created","starting","active","ending","ended","failed"]},"error":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["egressJobId","trackId","participantId","livekitEgressId","status","error"],"additionalProperties":false}},"skipped":{"type":"array","items":{"type":"object","properties":{"participantId":{"type":"string"},"reason":{"type":"string","enum":["no-egressable-tracks","already-running","unknown-participant","start-failed"]}},"required":["participantId","reason"],"additionalProperties":false}}},"required":["sessionId","op","jobs","skipped"],"additionalProperties":false},"CoordinatorEventResult":{"type":"object","properties":{"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"event":{"type":"string","enum":["coordinator.started","coordinator.stopped"]},"recorded":{"type":"boolean","description":"False when an identical report had already landed — a retry, not a failure"},"sessionClockEpoch":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}],"description":"Wall time at session clock 0 after this report. Set on the first `coordinator.started`, and never moved."}},"required":["sessionId","event","recorded","sessionClockEpoch"],"additionalProperties":false},"CollabDocumentSnapshot":{"type":"object","properties":{"snapshot":{"type":"string","maxLength":8388608,"description":"`Y.encodeStateAsUpdate(doc)`, base64 — the compacted document."},"stateVector":{"type":"string","maxLength":8388608,"description":"`Y.encodeStateVector(doc)`, base64 — what the snapshot contains."},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Monotonic save counter."},"updatedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"bytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Decoded size of the snapshot."}},"required":["snapshot","stateVector","version","updatedAt","bytes"],"additionalProperties":false},"ReadCollabDocumentResult":{"type":"object","properties":{"docId":{"type":"string"},"scope":{"type":"string","enum":["production","session"]},"kind":{"type":"string","enum":["show_notes","whiteboard","script","timeline"]},"document":{"anyOf":[{"$ref":"#/components/schemas/CollabDocumentSnapshot"},{"type":"null"}],"description":"Null for a document nobody has typed in — which is an empty document, not an error."}},"required":["docId","scope","kind","document"],"additionalProperties":false},"WriteCollabDocumentResult":{"type":"object","properties":{"docId":{"type":"string"},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"updatedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"bytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["docId","version","updatedAt","bytes"],"additionalProperties":false},"OutputPreset":{"type":"object","properties":{"width":{"type":"integer","minimum":160,"maximum":3840},"height":{"type":"integer","minimum":120,"maximum":2160},"fps":{"type":"integer","minimum":1,"maximum":60},"videoBitrateKbps":{"type":"integer","minimum":100,"maximum":51000},"audioBitrateKbps":{"type":"integer","minimum":32,"maximum":320},"latencyMode":{"type":"string","maxLength":32}},"required":["width","height","fps","videoBitrateKbps","audioBitrateKbps"],"additionalProperties":false,"description":"Encoder settings for one output. Validated against the provider's own ceiling on write."},"SrtSettings":{"type":"object","properties":{"mode":{"type":"string","enum":["caller","listener"]},"latencyMs":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"hasPassphrase":{"type":"boolean"},"streamId":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["mode","latencyMs","hasPassphrase","streamId"],"additionalProperties":false},"HlsSettings":{"type":"object","properties":{"segmentSeconds":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"dvrWindowSeconds":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["segmentSeconds","dvrWindowSeconds"],"additionalProperties":false},"EncodingPreset":{"type":"object","properties":{"key":{"type":"string"},"label":{"type":"string"},"width":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"height":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"fps":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"videoBitrateKbps":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"audioBitrateKbps":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"geometry":{"type":"string"}},"required":["key","label","width","height","fps","videoBitrateKbps","audioBitrateKbps","geometry"],"additionalProperties":false},"EncodingPresetList":{"type":"object","properties":{"presets":{"type":"array","items":{"$ref":"#/components/schemas/EncodingPreset"}}},"required":["presets"],"additionalProperties":false},"Destination":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"productionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"provider":{"type":"string","enum":["youtube","linkedin","twitch","facebook","x","kick","custom_rtmp","custom_srt","whep","hls"]},"name":{"type":"string"},"url":{"anyOf":[{"type":"string"},{"type":"null"}]},"streamKeyMasked":{"anyOf":[{"type":"string"},{"type":"null"}]},"hasStreamKey":{"type":"boolean"},"outputPreset":{"$ref":"#/components/schemas/OutputPreset"},"presetKey":{"anyOf":[{"type":"string","enum":["1080p60","1080p30","720p60","720p30","540p30","vertical-1080p30","vertical-720p30"]},{"type":"null"}]},"srt":{"anyOf":[{"$ref":"#/components/schemas/SrtSettings"},{"type":"null"}]},"hls":{"anyOf":[{"$ref":"#/components/schemas/HlsSettings"},{"type":"null"}]},"enabled":{"type":"boolean"},"externalChannelId":{"anyOf":[{"type":"string"},{"type":"null"}]},"oauthIntegrationId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","orgId","productionId","provider","name","url","streamKeyMasked","hasStreamKey","outputPreset","presetKey","srt","hls","enabled","externalChannelId","oauthIntegrationId","createdAt","updatedAt"],"additionalProperties":false},"DestinationList":{"type":"object","properties":{"destinations":{"type":"array","items":{"$ref":"#/components/schemas/Destination"}}},"required":["destinations"],"additionalProperties":false},"DestinationTestResult":{"type":"object","properties":{"outcome":{"type":"string","enum":["ok","unreachable","timeout","tls_error","protocol_error","unsupported"]},"reachable":{"type":"boolean"},"handshake":{"type":"boolean"},"latencyMs":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"host":{"anyOf":[{"type":"string"},{"type":"null"}]},"port":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"detail":{"type":"string"}},"required":["outcome","reachable","handshake","latencyMs","host","port","detail"],"additionalProperties":false,"description":"Result of a TCP connect + RTMP handshake against the destination's ingest. No stream key is sent and none appears here."},"CalendarFeedToken":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"label":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastUsedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}],"description":"Approximate, to the hour. A calendar client polls constantly and an exact value would cost a write per poll."},"revokedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","label","lastUsedAt","revokedAt","createdAt"],"additionalProperties":false,"description":"A calendar subscription. Never carries the token — see `CalendarFeedTokenCreated`."},"CalendarFeedTokenList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/CalendarFeedToken"}}},"required":["items"],"additionalProperties":false},"CalendarFeedTokenCreated":{"type":"object","properties":{"token":{"$ref":"#/components/schemas/CalendarFeedToken"},"url":{"type":"string","description":"The subscription URL, **returned exactly once**. Only its sha256 is stored, so it cannot be shown again — losing it means minting another and revoking this one."}},"required":["token","url"],"additionalProperties":false},"SessionEmbed":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"enabled":{"type":"boolean"},"tokenTtlSeconds":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"tokensValidFrom":{"type":"string"},"label":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","sessionId","enabled","tokenTtlSeconds","tokensValidFrom","label","createdAt","updatedAt"],"additionalProperties":false},"MintedEmbedToken":{"type":"object","properties":{"token":{"type":"string"},"url":{"type":"string"},"expiresAt":{"type":"string"}},"required":["token","url","expiresAt"],"additionalProperties":false,"description":"A short-lived viewer token. Anyone holding it can watch this one session until it expires."},"EmbedTransport":{"type":"object","properties":{"kind":{"type":"string","const":"livekit"},"url":{"type":"string"},"token":{"type":"string"},"identity":{"type":"string"},"expiresAt":{"type":"string"}},"required":["kind","url","token","identity","expiresAt"],"additionalProperties":false},"EmbedSession":{"type":"object","properties":{"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"title":{"type":"string"},"enabled":{"type":"boolean"},"state":{"type":"string","enum":["scheduled","live","ended"]},"pollSeconds":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"transport":{"anyOf":[{"$ref":"#/components/schemas/EmbedTransport"},{"type":"null"}]},"whepEndpoint":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["sessionId","title","enabled","state","pollSeconds","transport","whepEndpoint"],"additionalProperties":false},"PlaybackUrl":{"type":"object","properties":{"playlistUrl":{"type":"string"},"expiresAt":{"type":"string"},"dvrWindowSeconds":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"state":{"type":"string","enum":["pending","ready","ended"]}},"required":["playlistUrl","expiresAt","dvrWindowSeconds","state"],"additionalProperties":false},"Integration":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"provider":{"type":"string"},"externalAccountId":{"type":"string"},"displayName":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"status":{"type":"string"},"accessTokenExpiresAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastRefreshedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string"}},"required":["id","provider","externalAccountId","displayName","scopes","status","accessTokenExpiresAt","lastRefreshedAt","lastError","createdAt"],"additionalProperties":false},"IntegrationList":{"type":"object","properties":{"integrations":{"type":"array","items":{"$ref":"#/components/schemas/Integration"}}},"required":["integrations"],"additionalProperties":false},"AuthorizeUrl":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"state":{"type":"string"}},"required":["url","state"],"additionalProperties":false},"Connection":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"provider":{"type":"string"},"name":{"type":"string"},"externalAccountId":{"anyOf":[{"type":"string"},{"type":"null"}]},"scopes":{"type":"array","items":{"type":"string"}},"status":{"type":"string"},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]},"accessTokenExpiresAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastRefreshedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastHealthCheckAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","provider","name","externalAccountId","scopes","status","lastError","accessTokenExpiresAt","lastRefreshedAt","lastHealthCheckAt","createdAt","updatedAt"],"additionalProperties":false},"ConnectionList":{"type":"object","properties":{"connections":{"type":"array","items":{"$ref":"#/components/schemas/Connection"}}},"required":["connections"],"additionalProperties":false},"ConnectorAuthorizeUrl":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"state":{"type":"string"},"codeVerifier":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["url","state","codeVerifier"],"additionalProperties":false},"ConnectionHealth":{"type":"object","properties":{"status":{"type":"string"},"ok":{"type":"boolean"},"detail":{"anyOf":[{"type":"string"},{"type":"null"}]},"checkedAt":{"type":"string"}},"required":["status","ok","detail","checkedAt"],"additionalProperties":false},"ImpersonationSession":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"adminUserId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"targetUserId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"orgId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"reason":{"type":"string"},"startedAt":{"type":"string"},"expiresAt":{"type":"string"},"endedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"endedBy":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]}},"required":["id","adminUserId","targetUserId","orgId","reason","startedAt","expiresAt","endedAt","endedBy"],"additionalProperties":false,"description":"One window in which a platform admin was permitted to read an organisation's data."},"CurrentImpersonation":{"type":"object","properties":{"session":{"anyOf":[{"$ref":"#/components/schemas/ImpersonationSession"},{"type":"null"}]}},"required":["session"],"additionalProperties":false,"description":"The calling admin's open impersonation session, if there is one. What the UI banner polls."},"ImpersonationList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ImpersonationSession"}}},"required":["items"],"additionalProperties":false,"description":"Every platform-admin session that entered this organisation, newest first."},"RealtimeAuthzDecision":{"type":"object","properties":{"allowed":{"type":"boolean"},"reason":{"type":"string","enum":["org-member","org-own-user-channel","session-participant","session-producer","production-member","job-org-member","unknown-channel","unknown-subject","not-an-org-member","not-a-participant","not-a-production-member","unknown-scope","other-users-channel","publish-not-permitted"]},"channel":{"type":"string"},"scope":{"type":"string"},"userId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"orgId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"roles":{"type":"array","items":{"type":"string"}},"ttlSeconds":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["allowed","reason","channel","scope","userId","orgId","roles","ttlSeconds"],"additionalProperties":false,"description":"Whether a Cognito subject may subscribe to (or publish on) one realtime channel."},"EdlEventRecord":{"type":"object","properties":{"seq":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"Monotonic per session, from 1"},"clockMs":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Session-clock ms — negative before T0, never wall time"},"actor":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"`participants.id`, or null for a system op"},"source":{"type":"string","enum":["ui","midi","companion","osc","api","mcp","system"]},"op":{"type":"array","items":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"description":"RFC 6902 patch array"},"resultingVersion":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"`ProductionState.version` after applying it"},"label":{"type":"string","maxLength":500}},"required":["seq","clockMs","actor","source","op","resultingVersion"],"additionalProperties":false,"description":"One row of the append-only switching EDL."},"AppendEdlResult":{"type":"object","properties":{"appended":{"type":"integer","minimum":0,"maximum":9007199254740991},"duplicates":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Rows whose seq was already present — a retried flush"},"lastSeq":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["appended","duplicates","lastSeq"],"additionalProperties":false},"ReplayEdlResult":{"type":"object","properties":{"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"events":{"type":"array","items":{"$ref":"#/components/schemas/EdlEventRecord"}},"lastSeq":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Highest seq in the whole log, not just this page"},"hasMore":{"type":"boolean","description":"True when the page stopped at `limit` rather than at the end"}},"required":["sessionId","events","lastSeq","hasMore"],"additionalProperties":false},"ProductionStateSnapshotResult":{"type":"object","properties":{"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"snapshot":{"anyOf":[{"type":"object","properties":{"version":{"type":"integer","minimum":0,"maximum":9007199254740991},"state":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"updatedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]}},"required":["version","state","updatedAt"],"additionalProperties":false},{"type":"null"}]},"lastSeq":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["sessionId","snapshot","lastSeq"],"additionalProperties":false,"description":"The stored fold plus the log's high-water mark — both halves of a rehydration, in one read."},"WriteProductionStateResult":{"type":"object","properties":{"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"version":{"type":"integer","minimum":0,"maximum":9007199254740991},"updatedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"unchanged":{"type":"boolean","description":"True when that version was already stored — an idempotent retry"}},"required":["sessionId","version","updatedAt","unchanged"],"additionalProperties":false},"RecordEgressRestartResult":{"type":"object","properties":{"egressJobId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The new job; later webhooks resolve to this row"},"previousEgressJobId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"attempt":{"type":"integer","minimum":1,"maximum":9007199254740991},"runsMoved":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["egressJobId","previousEgressJobId","attempt","runsMoved"],"additionalProperties":false},"RecordDestinationRetryResult":{"type":"object","properties":{"runId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"attempt":{"type":"integer","minimum":1,"maximum":9007199254740991},"discontinuityCount":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"How many seams this leg's output now has"}},"required":["runId","attempt","discontinuityCount"],"additionalProperties":false},"SessionInviteRole":{"type":"string","enum":["host","guest","producer","viewer","camera","prompter"],"description":"The session role an invite hands out. `director`, `ingest` and `egress` are deliberately absent: a director runs the show from an authenticated seat, and ingest/egress are processes. `prompter` is a teleprompter surface — it publishes no media, subscribes to none, and reads the session's script document over the data channel."},"SessionInvite":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"productionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"scope":{"type":"string","enum":["session","production"]},"audience":{"type":"string","enum":["open","named"],"description":"`open` — one secret, N uses, anonymous until somebody types a name. `named` — a row and a token per person in `invite_recipients`. A statement of intent rather than a derivation from 'has recipients': a named invite with nobody added yet is still a named invite, and must not admit a stranger holding the parent token."},"admission":{"type":"string","enum":["auto","waiting_room"]},"role":{"$ref":"#/components/schemas/SessionInviteRole"},"email":{"anyOf":[{"type":"string"},{"type":"null"}]},"displayNameHint":{"anyOf":[{"type":"string"},{"type":"null"}]},"locale":{"anyOf":[{"$ref":"#/components/schemas/LocaleTag"},{"type":"null"}],"description":"TAB-564 — the language this invite's join flow is written in."},"locationId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"singleUse":{"type":"boolean"},"maxUses":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"uses":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"expiresAt":{"type":"string"},"createdBy":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"createdAt":{"type":"string"}},"required":["id","sessionId","productionId","scope","audience","admission","role","email","displayNameHint","locale","locationId","singleUse","maxUses","uses","expiresAt","createdBy","createdAt"],"additionalProperties":false,"description":"An invite. The token is never in this object — only `createSessionInvite` ever returns it."},"CreatedSessionInvite":{"type":"object","properties":{"invite":{"$ref":"#/components/schemas/SessionInvite"},"token":{"type":"string","description":"Returned once, here. Only its SHA-256 is stored."},"joinUrl":{"type":"string","description":"`https://<web>/j/<token>` — the link to send."},"emailSent":{"type":"boolean","description":"Whether the invite was emailed, with a `.ics` carrying this guest's own join link (TAB-105). False when the invite has no address, when the session has no date yet, when the recipient has opted out, or when this environment cannot send email — the join link above is unaffected in every case, so a client can offer to send it another way."}},"required":["invite","token","joinUrl","emailSent"],"additionalProperties":false},"SessionInviteList":{"type":"object","properties":{"invites":{"type":"array","items":{"$ref":"#/components/schemas/SessionInvite"}}},"required":["invites"],"additionalProperties":false},"MintedJoinCode":{"type":"object","properties":{"inviteId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"code":{"type":"string","description":"Returned once, here. Only a scrypt digest of it is stored."},"formattedCode":{"type":"string","description":"`ABCD-EFGH` — the same code, grouped for reading aloud."},"expiresAt":{"type":"string","description":"Always the invite's own expiry: a code cannot outlive it."},"joinUrl":{"type":"string","description":"`https://<web>/j` — where the code is typed. Carries no secret, so it may be printed."}},"required":["inviteId","code","formattedCode","expiresAt","joinUrl"],"additionalProperties":false,"description":"A short join code for an existing invite. Minting again replaces it, so there is only ever one live code per invite."},"IceServer":{"type":"object","properties":{"urls":{"minItems":1,"type":"array","items":{"type":"string"},"description":"`stun:`, `turn:` or `turns:` URLs for one server."},"username":{"type":"string"},"credential":{"description":"Short-lived TURN password. A relay quota — treat it as a credential, do not log it.","type":"string"}},"required":["urls"],"additionalProperties":false},"IceServersResult":{"type":"object","properties":{"participantId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"iceServers":{"type":"array","items":{"$ref":"#/components/schemas/IceServer"}},"iceServersExpiresAt":{"type":"string"},"source":{"type":"string","enum":["cloudflare","static"],"description":"`cloudflare` is a credential minted for this call; `static` is configured, long-lived."}},"required":["participantId","sessionId","iceServers","source"],"additionalProperties":false,"description":"Fresh ICE servers for a participant already in a session. A TURN credential lives an hour and a show does not, so this is how a client that is still connected gets the next one."},"JoinResult":{"type":"object","properties":{"participantId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"role":{"type":"string","enum":["producer","director","host","guest","camera","viewer","ingest","egress","prompter"]},"displayName":{"type":"string"},"userId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Bound when a signed-in user joined; null for an anonymous guest."},"livekitIdentity":{"type":"string","description":"`p_<participantId>` — what other participants see."},"livekitUrl":{"type":"string"},"livekitToken":{"type":"string"},"livekitExpiresAt":{"type":"string"},"iceServers":{"type":"array","items":{"$ref":"#/components/schemas/IceServer"}},"iceServersExpiresAt":{"type":"string"},"guestToken":{"type":"string","description":"Participant-scoped HS256 JWT. Bearer credential — do not log."},"guestTokenExpiresAt":{"type":"string"}},"required":["participantId","sessionId","role","displayName","userId","livekitIdentity","guestToken","guestTokenExpiresAt"],"additionalProperties":false},"JoinPreflight":{"type":"object","properties":{"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionTitle":{"type":"string"},"sessionStatus":{"type":"string"},"scheduledAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"productionName":{"type":"string"},"productionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"recordCuration":{"type":"object","properties":{"hiddenFromTalent":{"type":"array","items":{"type":"string"},"description":"Source ids the record room hides from the talent grid."}},"required":["hiddenFromTalent"],"additionalProperties":false},"activeRoom":{"type":"string","enum":["planning","record","studio"],"description":"Where the cast is now — an admitted guest lands here (`planning` by default)."},"mode":{"type":"string","enum":["studio","record"],"description":"The room this episode is booked for — a live/go resolves here (`studio` or `record`)."},"role":{"$ref":"#/components/schemas/SessionInviteRole"},"displayNameHint":{"anyOf":[{"type":"string"},{"type":"null"}]},"locale":{"anyOf":[{"$ref":"#/components/schemas/LocaleTag"},{"type":"null"}]},"brand":{"anyOf":[{"type":"object","properties":{"name":{"type":"string"},"colors":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"logoAssetId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]}},"required":["name","colors","logoAssetId"],"additionalProperties":false},{"type":"null"}]},"featureFlags":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"boolean"}},"recipient":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"displayName":{"anyOf":[{"type":"string"},{"type":"null"}]},"email":{"type":"string"},"avatarUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Signed and short-lived. Null → render initials."},"status":{"type":"string","enum":["invited","sent","opened","checked_in","in_lobby","ready","on_air","no_show","revoked"]}},"required":["id","displayName","email","avatarUrl","status"],"additionalProperties":false},{"type":"null"}]},"admission":{"type":"string","enum":["auto","waiting_room"],"description":"What **this invitation** says redeeming it does: open the room, or knock on it. Read `admissionState` rather than this to decide what to render — since TAB-618 the *session* can gate an `auto` link as well."},"admissionState":{"type":"string","enum":["auto","waiting","admitted"],"description":"`auto` — nothing to wait for. `waiting` — redeeming this link will not put you in the room until a producer says so, either because the invitation is a `waiting_room` one or because the session's `autoAdmit` is off (TAB-618). `admitted` — one already has. A landing page renders the waiting screen on `waiting`."},"autoAdmit":{"type":"boolean","description":"False when this session holds every guest in a lobby until a producer admits them."}},"required":["sessionId","sessionTitle","sessionStatus","scheduledAt","productionName","productionId","recordCuration","activeRoom","mode","role","displayNameHint","locale","brand","featureFlags","recipient","admission","admissionState","autoAdmit"],"additionalProperties":false,"description":"What the pre-join page needs. Reads only — looking at the page never spends a single-use link, though for a **named** link it does record that it was opened (TAB-514)."},"JoinPending":{"type":"object","properties":{"admission":{"type":"string","enum":["waiting_room","lobby"],"description":"`waiting_room` — a named guest, no seat taken, poll by re-redeeming the token. `lobby` — a seat is held unadmitted, poll `POST /v1/join/media-token` with `guestToken`."},"admissionState":{"type":"string","const":"waiting","description":"Always `waiting` — this shape only ever describes somebody who is not in yet."},"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"recipientId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"participantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"status":{"type":"string","description":"Their funnel status — `checked_in` while a named guest waits."},"displayName":{"anyOf":[{"type":"string"},{"type":"null"}]},"guestToken":{"description":"Participant-scoped HS256 JWT. Bearer credential — do not log. `lobby` door only.","type":"string"},"guestTokenExpiresAt":{"type":"string"},"retryAfterMs":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How long to wait before asking again. The producer's admit is what changes the answer."}},"required":["admission","admissionState","sessionId","recipientId","participantId","status","displayName","retryAfterMs"],"additionalProperties":false},"MediaTokenResult":{"type":"object","properties":{"participantId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"livekitUrl":{"type":"string"},"livekitToken":{"type":"string"},"livekitExpiresAt":{"type":"string"},"iceServers":{"type":"array","items":{"$ref":"#/components/schemas/IceServer"}},"iceServersExpiresAt":{"type":"string"}},"required":["participantId","sessionId","livekitUrl","livekitToken","livekitExpiresAt"],"additionalProperties":false},"InviteScope":{"type":"string","enum":["session","production"],"description":"`session` — one episode. `production` — the show, and whichever episode is open when the holder arrives. Production scope is what makes a recurring co-host one invitation rather than twelve."},"InviteAdmission":{"type":"string","enum":["auto","waiting_room"],"description":"`auto` — redeeming the link puts them in the room. `waiting_room` — redeeming it puts them in front of a producer, who admits them. Defaults to `auto` for both audiences today; the product plan's `open → waiting_room` default arrives with the People panel that can admit."},"InviteRecipientStatus":{"type":"string","enum":["invited","sent","opened","checked_in","in_lobby","ready","on_air","no_show","revoked"],"description":"Where one named person has got to. Forward only — a guest who reloads the landing does not fall back from `ready` to `opened`, and an invite email that goes out late does not un-open anybody. `no_show` is a producer's judgement; `revoked` is terminal."},"InviteRecipient":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"inviteId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"scope":{"$ref":"#/components/schemas/InviteScope"},"sessionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"productionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"email":{"type":"string"},"displayName":{"anyOf":[{"type":"string"},{"type":"null"}]},"locale":{"anyOf":[{"$ref":"#/components/schemas/LocaleTag"},{"type":"null"}],"description":"TAB-564 — the language this person's invitation and landing page are written in, as the producer chose it. Null means nobody chose, which falls through to the invite's locale and then the org's default; an explicit value does not."},"role":{"$ref":"#/components/schemas/SessionInviteRole"},"admission":{"$ref":"#/components/schemas/InviteAdmission"},"status":{"$ref":"#/components/schemas/InviteRecipientStatus"},"statusAt":{"type":"string"},"sentAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"openedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"checkedInAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"inLobbyAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"readyAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"onAirAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"noShowAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"revokedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"admittedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When a *person* let them through a waiting room. Null for an auto admission."},"userId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"The member this address turned out to be. Bound when a signed-in person redeems the link, or supplied by the producer when they pick a colleague from the team list — never guessed from the address, because `users` is self-scoped under RLS and a lookup that could map addresses to accounts would make every address in the org enumerable."},"avatarUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Signed and short-lived. Null when there is none, and the client renders initials."},"participantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"The seat they are sitting in, once they have one."},"lastDeliveryError":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"TAB-520 — why the last invitation to this person did not go: this deployment has no email provider, the address is not one, they turned invitations off, or the provider refused. Null once a message has gone. Somebody sitting on `invited` with a reason here is waiting on a setting, not on the guest."},"createdAt":{"type":"string"}},"required":["id","inviteId","scope","sessionId","productionId","email","displayName","locale","role","admission","status","statusAt","sentAt","openedAt","checkedInAt","inLobbyAt","readyAt","onAirAt","noShowAt","revokedAt","admittedAt","userId","avatarUrl","participantId","lastDeliveryError","createdAt"],"additionalProperties":false,"description":"One named person on an invite. **The token is never on this object** — it is returned once, by create and by resend, and only its SHA-256 is stored."},"RecipientEmailStatus":{"type":"object","properties":{"outcome":{"type":"string","enum":["sent","suppressed","failed"]},"reason":{"anyOf":[{"type":"string"},{"type":"null"}]},"provider":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["outcome","reason","provider"],"additionalProperties":false,"description":"What became of the invitation email. `suppressed` is a decision — no provider configured, an address that is not one, a guest who opted out; `failed` is a supplier that refused. Neither prevents the person from being added: their link is in this same response."},"CreatedInviteRecipient":{"type":"object","properties":{"recipient":{"$ref":"#/components/schemas/InviteRecipient"},"email":{"description":"Null only for somebody who was not emailed at all — a withdrawn invitation, whose link is already a 404.","anyOf":[{"$ref":"#/components/schemas/RecipientEmailStatus"},{"type":"null"}]},"token":{"type":"string","description":"Returned once, here. Only its SHA-256 is stored."},"joinUrl":{"type":"string","description":"`https://<web>/j/<token>` — the link to send. Same path as an open link."},"created":{"type":"boolean","description":"False when the address was already on this invite: the row was refreshed and its token rotated rather than a second one being made. Two live tokens for one person are two ways to be told apart at the door and only one of them is the one the panel is showing."}},"required":["recipient","token","joinUrl","created"],"additionalProperties":false},"InviteRecipientList":{"type":"object","properties":{"recipients":{"type":"array","items":{"$ref":"#/components/schemas/InviteRecipient"}},"counts":{"type":"object","propertyNames":{"type":"string","enum":["invited","sent","opened","checked_in","in_lobby","ready","on_air","no_show","revoked"]},"additionalProperties":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"required":["invited","sent","opened","checked_in","in_lobby","ready","on_air","no_show","revoked"],"description":"How many are at each status — the panel's header, without a second read."}},"required":["recipients","counts"],"additionalProperties":false,"description":"Everybody named on this episode (or on this show's roster), in funnel order."},"CreatedInviteRecipientList":{"type":"object","properties":{"recipients":{"type":"array","items":{"$ref":"#/components/schemas/CreatedInviteRecipient"}}},"required":["recipients"],"additionalProperties":false},"InviteRecipientResult":{"type":"object","properties":{"recipient":{"$ref":"#/components/schemas/InviteRecipient"},"token":{"description":"Present only when this call minted a new link — a resend, or a role change.","anyOf":[{"type":"string"},{"type":"null"}]},"joinUrl":{"anyOf":[{"type":"string"},{"type":"null"}]},"email":{"description":"TAB-520 — what became of the message, on the calls that send one. Null on the calls that do not (an admission, a rename).","anyOf":[{"$ref":"#/components/schemas/RecipientEmailStatus"},{"type":"null"}]}},"required":["recipient"],"additionalProperties":false},"Location":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"name":{"type":"string"},"isRemoteSingle":{"type":"boolean"},"audioSourceParticipantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"monitorParticipantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"mixMinus":{"type":"boolean"},"detectedVia":{"type":"string"},"chirping":{"type":"boolean","description":"Whether this room has an ultrasonic token to announce."},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","sessionId","name","isRemoteSingle","audioSourceParticipantId","monitorParticipantId","mixMinus","detectedVia","chirping","createdAt","updatedAt"],"additionalProperties":false,"description":"A physical room: one microphone in, one speaker out. The ultrasonic token itself is never in this object — a client that is not the room's own monitor has no use for it, and a client that had it could claim to be in a room it has never been near."},"LocationStatus":{"type":"object","properties":{"location":{"$ref":"#/components/schemas/Location"},"participantIds":{"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"noSource":{"type":"boolean","description":"Blocking: this room's audio has no nominated microphone."},"noMonitor":{"type":"boolean"},"sourceMissing":{"type":"boolean","description":"The nominated source has left the room."},"mixMinusOff":{"type":"boolean","description":"People in this room will hear themselves back."},"empty":{"type":"boolean"}},"required":["location","participantIds","noSource","noMonitor","sourceMissing","mixMinusOff","empty"],"additionalProperties":false,"description":"A room and what is wrong with it, in the vocabulary the producer panel and the go-live checklist both draw from."},"LocationStatusList":{"type":"object","properties":{"locations":{"type":"array","items":{"$ref":"#/components/schemas/LocationStatus"}}},"required":["locations"],"additionalProperties":false},"AssignedParticipant":{"type":"object","properties":{"participantId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"locationId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"displayName":{"type":"string"}},"required":["participantId","sessionId","locationId","displayName"],"additionalProperties":false},"LocationChirpToken":{"type":"object","properties":{"locationId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"token":{"type":"string"}},"required":["locationId","token"],"additionalProperties":false},"LocationSuggestion":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"participantId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"locationId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"peerParticipantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"The other half of a double-mic finding."},"method":{"type":"string","enum":["ip_hint","ultrasonic","correlation"]},"confidence":{"type":"number"},"evidence":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"status":{"type":"string"},"expiresAt":{"type":"string"},"createdAt":{"type":"string"}},"required":["id","sessionId","participantId","locationId","peerParticipantId","method","confidence","evidence","status","expiresAt","createdAt"],"additionalProperties":false,"description":"A guess a human has to confirm. Confirming is the only way it becomes an assignment, and the database will not let a row reach `confirmed` without recording who confirmed it."},"LocationSuggestionList":{"type":"object","properties":{"suggestions":{"type":"array","items":{"$ref":"#/components/schemas/LocationSuggestion"}}},"required":["suggestions"],"additionalProperties":false},"ConfirmedSuggestion":{"type":"object","properties":{"suggestion":{"$ref":"#/components/schemas/LocationSuggestion"},"assigned":{"type":"array","items":{"$ref":"#/components/schemas/AssignedParticipant"}}},"required":["suggestion","assigned"],"additionalProperties":false},"TranscriptionCallbackAccepted":{"type":"object","properties":{"jobId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"status":{"type":"string","enum":["succeeded","failed","ignored"],"description":"`ignored` means the job was already terminal — a replayed webhook, accepted and discarded."},"transcriptId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"trackId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"wordCount":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"billedSeconds":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"sessionTranscriptBuilt":{"type":"boolean"}},"required":["jobId","status","transcriptId","trackId","wordCount","billedSeconds","sessionTranscriptBuilt"],"additionalProperties":false,"description":"What the API did with a provider's transcription callback."},"DestinationStats":{"type":"object","properties":{"uptimeMs":{"anyOf":[{"type":"number"},{"type":"null"}]},"bitrateKbps":{"anyOf":[{"type":"number"},{"type":"null"}]},"droppedFrames":{"anyOf":[{"type":"number"},{"type":"null"}]},"livekitRetries":{"anyOf":[{"type":"number"},{"type":"null"}]},"retryAttempt":{"type":"number"},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["uptimeMs","bitrateKbps","droppedFrames","livekitRetries","retryAttempt","updatedAt"],"additionalProperties":false},"DestinationRun":{"type":"object","properties":{"runId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"destinationId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"destinationName":{"type":"string"},"provider":{"type":"string","enum":["youtube","linkedin","twitch","facebook","x","kick","custom_rtmp","custom_srt","whep","hls"]},"egressJobId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"livekitEgressId":{"anyOf":[{"type":"string"},{"type":"null"}]},"status":{"type":"string","enum":["created","starting","active","ending","ended","failed"]},"health":{"type":"string","enum":["pending","connecting","healthy","stopping","stopped","failed"]},"startedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"endedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"error":{"anyOf":[{"type":"string"},{"type":"null"}]},"groupKey":{"anyOf":[{"type":"string"},{"type":"null"}]},"externalBroadcastId":{"anyOf":[{"type":"string"},{"type":"null"}]},"retryAttempt":{"type":"number"},"discontinuityCount":{"type":"number"},"stats":{"$ref":"#/components/schemas/DestinationStats"}},"required":["runId","sessionId","destinationId","destinationName","provider","egressJobId","livekitEgressId","status","health","startedAt","endedAt","error","groupKey","externalBroadcastId","retryAttempt","discontinuityCount","stats"],"additionalProperties":false},"LiveGroup":{"type":"object","properties":{"groupKey":{"type":"string"},"egressJobId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"status":{"type":"string","enum":["created","starting","active","ending","ended","failed"]},"destinationIds":{"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}}},"required":["groupKey","egressJobId","status","destinationIds"],"additionalProperties":false},"LiveState":{"type":"object","properties":{"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"status":{"type":"string","enum":["idle","created","starting","active","ending","ended","failed"]},"runs":{"type":"array","items":{"$ref":"#/components/schemas/DestinationRun"}},"groups":{"type":"array","items":{"$ref":"#/components/schemas/LiveGroup"}}},"required":["sessionId","status","runs","groups"],"additionalProperties":false},"ChecklistItem":{"type":"object","properties":{"key":{"type":"string"},"label":{"type":"string"},"outcome":{"type":"string","enum":["pass","warn","fail"]},"detail":{"type":"string"},"blocking":{"type":"boolean"},"destinationId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]}},"required":["key","label","outcome","detail","blocking"],"additionalProperties":false},"GoLiveChecklist":{"type":"object","properties":{"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"ok":{"type":"boolean"},"items":{"type":"array","items":{"$ref":"#/components/schemas/ChecklistItem"}},"plannedGroups":{"type":"array","items":{"type":"object","properties":{"groupKey":{"type":"string"},"destinationIds":{"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}}},"required":["groupKey","destinationIds"],"additionalProperties":false}}},"required":["sessionId","ok","items","plannedGroups"],"additionalProperties":false},"ControlOpsResult":{"type":"object","properties":{"accepted":{"type":"boolean"},"version":{"type":"integer","minimum":0,"maximum":9007199254740991},"seq":{"type":"integer","minimum":0,"maximum":9007199254740991},"rejection":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"source":{"type":"string"},"retryAfterMs":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["code","message"],"additionalProperties":false}},"required":["accepted","version"],"additionalProperties":false,"description":"What the coordinator did with the ops. A refusal is a 200 with `accepted: false`."},"DestinationSeam":{"type":"object","properties":{"at":{"type":"string"},"reason":{"type":"string"},"scope":{"type":"string","enum":["destination","group"]},"attempt":{"type":"integer","minimum":0,"maximum":9007199254740991},"detail":{"type":"string"}},"required":["at","reason","scope","attempt"],"additionalProperties":false},"DestinationHealthEntry":{"type":"object","properties":{"runId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"destinationId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"destinationName":{"type":"string"},"provider":{"type":"string"},"health":{"type":"string","enum":["pending","connecting","healthy","stopping","stopped","failed"]},"groupKey":{"anyOf":[{"type":"string"},{"type":"null"}]},"uptimeMs":{"anyOf":[{"type":"number"},{"type":"null"}]},"bitrateKbps":{"anyOf":[{"type":"number"},{"type":"null"}]},"droppedFrames":{"anyOf":[{"type":"number"},{"type":"null"}]},"retryAttempt":{"type":"integer","minimum":0,"maximum":9007199254740991},"retriesRemaining":{"type":"integer","minimum":0,"maximum":9007199254740991},"seams":{"type":"array","items":{"$ref":"#/components/schemas/DestinationSeam"}},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastErrorAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"alertedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"playbackKey":{"anyOf":[{"type":"string"},{"type":"null"}]},"egressMinutes":{"type":"number","minimum":0}},"required":["runId","destinationId","destinationName","provider","health","groupKey","uptimeMs","bitrateKbps","droppedFrames","retryAttempt","retriesRemaining","seams","lastError","lastErrorAt","alertedAt","playbackKey","egressMinutes"],"additionalProperties":false},"DestinationHealth":{"type":"object","properties":{"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"at":{"type":"string"},"destinations":{"type":"array","items":{"$ref":"#/components/schemas/DestinationHealthEntry"}}},"required":["sessionId","at","destinations"],"additionalProperties":false,"description":"Every output of a session and what it is doing — including the finished ones."},"Notification":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"orgId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"type":{"type":"string"},"title":{"type":"string"},"body":{"anyOf":[{"type":"string"},{"type":"null"}]},"data":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"readAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string"}},"required":["id","orgId","type","title","body","data","readAt","createdAt"],"additionalProperties":false,"description":"One row of the caller's notification inbox."},"NotificationList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Notification"}},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"unreadCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["items","nextCursor","limit","unreadCount"],"additionalProperties":false},"MarkNotificationsReadResult":{"type":"object","properties":{"marked":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"unreadCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["marked","unreadCount"],"additionalProperties":false},"UnsubscribeResult":{"type":"object","properties":{"unsubscribed":{"type":"boolean","const":true},"category":{"type":"string"}},"required":["unsubscribed","category"],"additionalProperties":false,"description":"The outcome of a one-click unsubscribe."},"OrgRole":{"type":"string","enum":["org_owner","org_admin","org_billing","org_member","org_guest"],"description":"Role of a person within an organisation."},"Org":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"slug":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string"},"ownerUserId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"homeRegion":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","slug","name","kind","ownerUserId","homeRegion","createdAt","updatedAt"],"additionalProperties":false},"OrgSettings":{"type":"object","properties":{"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"defaultBrandKitId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"defaultLocale":{"type":"string","description":"TAB-564 — the house UI language, and the last stop before `en-AU` in the locale chain. A plain string on the read for the reason `UserProfile.locale` is: a value written by a build with a catalogue this one lacks must still be readable."},"customDomain":{"anyOf":[{"type":"string"},{"type":"null"}]},"emailSender":{"anyOf":[{"type":"string"},{"type":"null"}]},"security":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"recordingDefaults":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"privacy":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"billing":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"featureFlags":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"boolean"}},"updatedAt":{"type":"string"}},"required":["orgId","defaultBrandKitId","defaultLocale","customDomain","emailSender","security","recordingDefaults","privacy","billing","featureFlags","updatedAt"],"additionalProperties":false},"Invitation":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"email":{"type":"string"},"role":{"$ref":"#/components/schemas/OrgRole"},"teamIds":{"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"invitedBy":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"expiresAt":{"type":"string"},"acceptedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"acceptedByUserId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"createdAt":{"type":"string"}},"required":["id","orgId","email","role","teamIds","invitedBy","expiresAt","acceptedAt","acceptedByUserId","createdAt"],"additionalProperties":false},"InvitationEmailStatus":{"type":"object","properties":{"outcome":{"type":"string","enum":["sent","suppressed","failed"]},"reason":{"anyOf":[{"type":"string"},{"type":"null"}]},"provider":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["outcome","reason","provider"],"additionalProperties":false,"description":"What became of the invitation email. `suppressed` is a decision the platform made — no provider configured, the recipient opted out — and `failed` is a supplier that refused. Neither prevents the invitation from being created: the token is in this same response."},"CreatedInvitation":{"type":"object","properties":{"invitation":{"$ref":"#/components/schemas/Invitation"},"token":{"type":"string"},"email":{"$ref":"#/components/schemas/InvitationEmailStatus"}},"required":["invitation","token","email"],"additionalProperties":false},"InvitePolicy":{"type":"object","properties":{"emailInvitesAllowed":{"type":"boolean"},"restriction":{"anyOf":[{"type":"string","enum":["sso-enforced"]},{"type":"null"}]},"detail":{"anyOf":[{"type":"string"},{"type":"null"}]},"ssoManagedDomains":{"type":"array","items":{"type":"string"}},"providerNames":{"type":"array","items":{"type":"string"}}},"required":["emailInvitesAllowed","restriction","detail","ssoManagedDomains","providerNames"],"additionalProperties":false},"AcceptedInvitation":{"type":"object","properties":{"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"membershipId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"role":{"$ref":"#/components/schemas/OrgRole"},"teamIds":{"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}}},"required":["orgId","membershipId","role","teamIds"],"additionalProperties":false},"OrgMember":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"userId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"role":{"$ref":"#/components/schemas/OrgRole"},"status":{"type":"string","enum":["invited","active","suspended"]},"joinedVia":{"type":"string"},"seatType":{"type":"string"},"displayName":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","userId","role","status","joinedVia","seatType","displayName","createdAt","updatedAt"],"additionalProperties":false},"OrgMemberList":{"type":"object","properties":{"members":{"type":"array","items":{"$ref":"#/components/schemas/OrgMember"}}},"required":["members"],"additionalProperties":false},"MemberAvatar":{"type":"object","properties":{"userId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"url":{"type":"string","description":"Signed, and short-lived — `DOWNLOAD_URL_TTL_S`, fifteen minutes by default."},"expiresAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"}},"required":["userId","url","expiresAt"],"additionalProperties":false},"MemberAvatarList":{"type":"object","properties":{"avatars":{"type":"array","items":{"$ref":"#/components/schemas/MemberAvatar"}}},"required":["avatars"],"additionalProperties":false},"InvitationList":{"type":"object","properties":{"invitations":{"type":"array","items":{"$ref":"#/components/schemas/Invitation"}},"policy":{"$ref":"#/components/schemas/InvitePolicy"}},"required":["invitations","policy"],"additionalProperties":false},"PlatformUser":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"email":{"type":"string"},"displayName":{"anyOf":[{"type":"string"},{"type":"null"}]},"status":{"type":"string"},"lastSeenAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string"},"orgCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","email","displayName","status","lastSeenAt","createdAt","orgCount"],"additionalProperties":false,"description":"One account, as the platform console lists it."},"PlatformUserList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PlatformUser"}}},"required":["items"],"additionalProperties":false,"description":"Accounts matching the search, newest first."},"PlatformOrg":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"slug":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string"},"plan":{"anyOf":[{"type":"string"},{"type":"null"}]},"memberCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"storageProvider":{"type":"string"},"createdAt":{"type":"string"},"ownerUserId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"required":["id","slug","name","kind","plan","memberCount","storageProvider","createdAt","ownerUserId"],"additionalProperties":false,"description":"One organisation, as the platform console lists it."},"PlatformOrgList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PlatformOrg"}}},"required":["items"],"additionalProperties":false,"description":"Organisations matching the search, newest first."},"PlatformIdentity":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"kind":{"type":"string"},"label":{"type":"string"},"email":{"anyOf":[{"type":"string"},{"type":"null"}]},"emailVerified":{"type":"boolean"},"isPrimary":{"type":"boolean"},"lastLoginAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","kind","label","email","emailVerified","isPrimary","lastLoginAt"],"additionalProperties":false,"description":"One way this person signs in."},"PlatformMembership":{"type":"object","properties":{"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"orgName":{"type":"string"},"orgSlug":{"type":"string"},"orgKind":{"type":"string"},"role":{"type":"string"},"status":{"type":"string"},"joinedVia":{"type":"string"},"joinedAt":{"type":"string"}},"required":["orgId","orgName","orgSlug","orgKind","role","status","joinedVia","joinedAt"],"additionalProperties":false,"description":"One organisation this person belongs to, and how they got there."},"PlatformUserDetail":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"email":{"type":"string"},"displayName":{"anyOf":[{"type":"string"},{"type":"null"}]},"status":{"type":"string"},"lastSeenAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string"},"orgCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"identities":{"type":"array","items":{"$ref":"#/components/schemas/PlatformIdentity"}},"memberships":{"type":"array","items":{"$ref":"#/components/schemas/PlatformMembership"}},"platformRoles":{"type":"array","items":{"type":"string"}}},"required":["id","email","displayName","status","lastSeenAt","createdAt","orgCount","identities","memberships","platformRoles"],"additionalProperties":false,"description":"One account in full: how they sign in, where they belong, and whether they operate the platform."},"PlatformSession":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"adminUserId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"adminName":{"anyOf":[{"type":"string"},{"type":"null"}]},"targetUserId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"targetName":{"anyOf":[{"type":"string"},{"type":"null"}]},"orgId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"orgName":{"anyOf":[{"type":"string"},{"type":"null"}]},"reason":{"type":"string"},"startedAt":{"type":"string"},"expiresAt":{"type":"string"}},"required":["id","adminUserId","adminName","targetUserId","targetName","orgId","orgName","reason","startedAt","expiresAt"],"additionalProperties":false,"description":"One impersonation session that is open right now, with the names behind the ids."},"PlatformSessionList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PlatformSession"}}},"required":["items"],"additionalProperties":false,"description":"Every impersonation session open across the platform, newest first."},"PlatformOrgStorage":{"type":"object","properties":{"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"provider":{"type":"string"},"bucket":{"anyOf":[{"type":"string"},{"type":"null"}]},"previousProvider":{"type":"string"},"previousBucket":{"anyOf":[{"type":"string"},{"type":"null"}]},"unchanged":{"type":"boolean"}},"required":["orgId","provider","bucket","previousProvider","previousBucket","unchanged"],"additionalProperties":false,"description":"Where this organisation's NEXT artefacts will be written. Existing objects are untouched."},"PlatformOrgPlan":{"type":"object","properties":{"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"planCode":{"type":"string"},"interval":{"type":"string"},"seats":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"status":{"type":"string"},"subscriptionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"previousPlanCode":{"anyOf":[{"type":"string"},{"type":"null"}]},"entitlements":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]}}},"required":["orgId","planCode","interval","seats","status","subscriptionId","previousPlanCode","entitlements"],"additionalProperties":false,"description":"An organisation's plan after a platform admin set it, with the entitlements it resolved to."},"EmulationRequest":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"requesterUserId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"requesterName":{"anyOf":[{"type":"string"},{"type":"null"}]},"targetUserId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"targetName":{"anyOf":[{"type":"string"},{"type":"null"}]},"targetPlatformRoles":{"type":"array","items":{"type":"string"}},"orgId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"orgName":{"anyOf":[{"type":"string"},{"type":"null"}]},"reason":{"type":"string"},"status":{"type":"string"},"state":{"type":"string","enum":["pending","approved","denied","expired","consumed"]},"expiresAt":{"type":"string"},"approverUserId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"approverName":{"anyOf":[{"type":"string"},{"type":"null"}]},"decidedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"decisionNote":{"anyOf":[{"type":"string"},{"type":"null"}]},"consumedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"sessionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"createdAt":{"type":"string"}},"required":["id","requesterUserId","requesterName","targetUserId","targetName","targetPlatformRoles","orgId","orgName","reason","status","state","expiresAt","approverUserId","approverName","decidedAt","decisionNote","consumedAt","sessionId","createdAt"],"additionalProperties":false,"description":"One request to emulate a platform operator, and what a second admin did about it."},"EmulationRequestList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/EmulationRequest"}}},"required":["items"],"additionalProperties":false,"description":"Emulation requests, newest first."},"Permission":{"type":"object","properties":{"key":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["key","description"],"additionalProperties":false,"description":"One entry of the permission catalogue a custom role is composed from. `platform:*` keys are never listed: they authorise operating Tablecast itself and are not grantable to an org role."},"Role":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"orgId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"scope":{"type":"string","enum":["platform","organization","team","production","session"]},"key":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"isSystem":{"type":"boolean"},"permissions":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","orgId","scope","key","name","description","isSystem","permissions","createdAt","updatedAt"],"additionalProperties":false},"RoleList":{"type":"object","properties":{"roles":{"type":"array","items":{"$ref":"#/components/schemas/Role"}},"permissions":{"type":"array","items":{"$ref":"#/components/schemas/Permission"}},"customRolesEnabled":{"type":"boolean"}},"required":["roles","permissions","customRolesEnabled"],"additionalProperties":false},"RoleAssignment":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"roleId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"scope":{"type":"string","enum":["platform","organization","team","production","session"]},"scopeId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"principalKind":{"type":"string","enum":["user","api_key","device","guest"]},"principalId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"grantedBy":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"expiresAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string"}},"required":["id","roleId","scope","scopeId","principalKind","principalId","grantedBy","expiresAt","createdAt"],"additionalProperties":false},"RoleAssignmentList":{"type":"object","properties":{"assignments":{"type":"array","items":{"$ref":"#/components/schemas/RoleAssignment"}}},"required":["assignments"],"additionalProperties":false},"RecordingPolicy":{"type":"object","properties":{"resolution":{"description":"`1080p`, `1440p`, `2160p`","type":"string"},"fps":{"type":"integer","minimum":1,"maximum":120},"codec":{"description":"`h264`, `hevc`, `av1`, `prores`","type":"string"},"wav":{"description":"Record a 48 kHz PCM companion alongside Opus","type":"boolean"},"serverBackup":{"description":"Run LiveKit track egress as a recording safety net","type":"boolean"},"rendererBundle":{"description":"Read-only. The renderer bundle that drew this session's program feed, stamped at go-live. A conform reproduces a show against this value.","type":"string","maxLength":64}},"additionalProperties":false,"description":"Capture defaults for a show, plus the renderer that actually drew it."},"ProductionLanguages":{"type":"object","properties":{"source":{"maxItems":10,"type":"array","items":{"type":"string","pattern":"^[A-Za-z]{2,3}(-[A-Za-z]{4})?(-([A-Za-z]{2}|[0-9]{3}))?$","description":"BCP-47 language tag, e.g. `en-NZ`, `mi-NZ`."},"description":"Languages spoken on this show. **Empty means detect anything.** One tag is an assertion — the transcriber is told rather than asked. Two or more restrict language identification to that set, which is what makes a bilingual show's detection accurate. Suggested tags: en-AU, mi-NZ, en-NZ, en-GB, en-US, es-ES."},"targets":{"maxItems":10,"type":"array","items":{"type":"string","pattern":"^[A-Za-z]{2,3}(-[A-Za-z]{4})?(-([A-Za-z]{2}|[0-9]{3}))?$","description":"BCP-47 language tag, e.g. `en-NZ`, `mi-NZ`."},"description":"Languages this show's transcripts are translated into by default (TAB-561)."}},"required":["source","targets"],"additionalProperties":false,"description":"The languages a show is recorded in, and the languages it is translated into."},"Production":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"teamId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"slug":{"type":"string","description":"URL handle, unique within the team"},"name":{"type":"string"},"kind":{"type":"string","enum":["podcast","show","webinar","event","other"]},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"coverAssetId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"brandKitId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"defaultSceneId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"recordingPolicy":{"$ref":"#/components/schemas/RecordingPolicy"},"languages":{"$ref":"#/components/schemas/ProductionLanguages"},"homeRegion":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pins this show's media region, overriding the org default. Null means inherit."},"settings":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"archivedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"updatedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","orgId","teamId","slug","name","kind","description","coverAssetId","brandKitId","defaultSceneId","recordingPolicy","languages","homeRegion","settings","archivedAt","createdAt","updatedAt"],"additionalProperties":false,"description":"A show. Sessions are recordings of it."},"ProductionList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Production"}},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"offset":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["items","limit","offset"],"additionalProperties":false},"ProductionMembership":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"productionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"userId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"role":{"type":"string","enum":["team_lead","producer","director","host","member","viewer"]},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"updatedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","productionId","userId","role","createdAt","updatedAt"],"additionalProperties":false,"description":"A per-show role override. It replaces the user's team role on this production — in both directions."},"ProductionMembershipList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ProductionMembership"}}},"required":["items"],"additionalProperties":false},"SceneLayout":{"type":"object","properties":{"type":{"type":"string","enum":["single","pip","grid","split","custom"]},"slots":{"maxItems":16,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64},"rect":{"type":"object","properties":{"x":{"type":"number","minimum":0,"maximum":1},"y":{"type":"number","minimum":0,"maximum":1},"w":{"type":"number","exclusiveMinimum":0,"maximum":1},"h":{"type":"number","exclusiveMinimum":0,"maximum":1}},"required":["x","y","w","h"],"additionalProperties":false},"mask":{"type":"object","properties":{"kind":{"type":"string","enum":["none","rounded","circle","custom"]},"radius":{"type":"number","minimum":0,"maximum":1},"path":{"type":"string","maxLength":4096}},"required":["kind"],"additionalProperties":false},"sourceId":{"anyOf":[{"type":"string","minLength":1,"maxLength":64},{"type":"null"}]},"zIndex":{"type":"integer","minimum":-1000,"maximum":1000}},"required":["id","rect"],"additionalProperties":false}}},"required":["type","slots"],"additionalProperties":false,"description":"Where the pictures go. Rects are 0..1 of the program frame, so one layout renders at 720p and 4K."},"Scene":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"productionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"name":{"type":"string"},"layout":{"$ref":"#/components/schemas/SceneLayout"},"graphics":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64},"kind":{"type":"string","minLength":1,"maxLength":32}},"required":["id","kind"],"additionalProperties":{}}},"audio":{"type":"object","properties":{"gains":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"number","minimum":-60,"maximum":12}},"muted":{"type":"array","items":{"type":"string"}},"afv":{"type":"array","items":{"type":"string"}}},"additionalProperties":{}},"thumbnailAssetId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"sortOrder":{"type":"integer","minimum":0,"maximum":9007199254740991},"hotkey":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"updatedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","orgId","productionId","name","layout","graphics","audio","thumbnailAssetId","sortOrder","hotkey","createdAt","updatedAt"],"additionalProperties":false},"SceneList":{"type":"object","properties":{"scenes":{"type":"array","items":{"$ref":"#/components/schemas/Scene"}}},"required":["scenes"],"additionalProperties":false},"RecordingTrackCounts":{"type":"object","properties":{"total":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"byStatus":{"type":"object","properties":{"recording":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"uploading":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"verified":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"failed":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"waived":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"deleted":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["recording","uploading","verified","failed","waived","deleted"],"additionalProperties":false}},"required":["total","byStatus"],"additionalProperties":false,"description":"Tracks by upload/verification status. Comms tracks are never counted."},"RecordingProduction":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"teamId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"slug":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string"}},"required":["id","teamId","slug","name","kind"],"additionalProperties":false},"RecordingSummary":{"type":"object","properties":{"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"title":{"type":"string"},"episodeNo":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"status":{"type":"string","enum":["scheduled","lobby","live","ending","complete","failed","canceled"]},"integrityStatus":{"type":"string","enum":["pending","verified","partial","waived","failed"]},"tags":{"type":"array","items":{"type":"string"}},"archivedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"scheduledAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"startedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"endedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"recordedAt":{"type":"string"},"durationMs":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"production":{"$ref":"#/components/schemas/RecordingProduction"},"participantCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"tracks":{"$ref":"#/components/schemas/RecordingTrackCounts"},"renders":{"type":"object","properties":{"total":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"ready":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["total","ready"],"additionalProperties":false},"transcript":{"type":"object","properties":{"status":{"type":"string","enum":["none","queued","running","ready","failed"]},"count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["status","count"],"additionalProperties":false},"totalBytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["sessionId","title","episodeNo","status","integrityStatus","tags","archivedAt","scheduledAt","startedAt","endedAt","recordedAt","durationMs","production","participantCount","tracks","renders","transcript","totalBytes"],"additionalProperties":false},"RecordingList":{"type":"object","properties":{"recordings":{"type":"array","items":{"$ref":"#/components/schemas/RecordingSummary"}},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["recordings","nextCursor","limit"],"additionalProperties":false},"RecordingTrack":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"participantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"participantName":{"anyOf":[{"type":"string"},{"type":"null"}]},"kind":{"type":"string"},"origin":{"type":"string"},"isPrimary":{"type":"boolean"},"codec":{"anyOf":[{"type":"string"},{"type":"null"}]},"container":{"anyOf":[{"type":"string"},{"type":"null"}]},"width":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"fps":{"anyOf":[{"type":"number"},{"type":"null"}]},"sampleRate":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"channels":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"bitrateKbps":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"startClockMs":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"durationMs":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"status":{"type":"string","enum":["recording","uploading","verified","failed","waived","deleted"]},"bytes":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"sha256":{"anyOf":[{"type":"string"},{"type":"null"}]},"droppedFrames":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"clockOffsetMs":{"anyOf":[{"type":"number"},{"type":"null"}]},"driftPpm":{"anyOf":[{"type":"number"},{"type":"null"}]},"alignmentConfidence":{"anyOf":[{"type":"number"},{"type":"null"}]},"proxies":{"type":"object","properties":{"proxy":{"type":"boolean"},"peaks":{"type":"boolean"},"sprites":{"type":"boolean"}},"required":["proxy","peaks","sprites"],"additionalProperties":false},"downloadable":{"type":"boolean"},"waivedReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"primaryReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"upload":{"anyOf":[{"type":"object","properties":{"completedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"abortedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"expectedParts":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"parts":{"type":"object","properties":{"total":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"uploaded":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"verified":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["total","uploaded","verified"],"additionalProperties":false}},"required":["completedAt","abortedAt","expectedParts","parts"],"additionalProperties":false},{"type":"null"}]}},"required":["id","participantId","participantName","kind","origin","isPrimary","codec","container","width","height","fps","sampleRate","channels","bitrateKbps","startClockMs","durationMs","status","bytes","sha256","droppedFrames","clockOffsetMs","driftPpm","alignmentConfidence","proxies","downloadable","waivedReason","primaryReason","upload"],"additionalProperties":false,"description":"One ISO track. `tracks.kind = comms` is excluded by construction and never appears here."},"RecordingRender":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"kind":{"type":"string"},"mode":{"type":"string"},"status":{"type":"string"},"preset":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"bytes":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"durationMs":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"ready":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","kind","mode","status","preset","bytes","durationMs","ready","createdAt","updatedAt"],"additionalProperties":false},"RecordingDetail":{"type":"object","properties":{"session":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"title":{"type":"string"},"episodeNo":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"status":{"type":"string","enum":["scheduled","lobby","live","ending","complete","failed","canceled"]},"integrityStatus":{"type":"string","enum":["pending","verified","partial","waived","failed"]},"tags":{"type":"array","items":{"type":"string"}},"archivedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"deletedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"purgeAfter":{"anyOf":[{"type":"string"},{"type":"null"}]},"scheduledAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"startedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"endedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"recordedAt":{"type":"string"},"durationMs":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"sessionClockEpoch":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string"}},"required":["id","title","episodeNo","status","integrityStatus","tags","archivedAt","deletedAt","purgeAfter","scheduledAt","startedAt","endedAt","recordedAt","durationMs","sessionClockEpoch","createdAt"],"additionalProperties":false},"production":{"$ref":"#/components/schemas/RecordingProduction"},"participants":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"userId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"displayName":{"type":"string"},"role":{"type":"string"},"isHidden":{"type":"boolean"},"locationId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"joinedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"leftAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","userId","displayName","role","isHidden","locationId","joinedAt","leftAt"],"additionalProperties":false}},"tracks":{"type":"array","items":{"$ref":"#/components/schemas/RecordingTrack"}},"renders":{"type":"array","items":{"$ref":"#/components/schemas/RecordingRender"}},"transcripts":{"type":"object","properties":{"status":{"type":"string","enum":["none","queued","running","ready","failed"]},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"trackId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"participantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"language":{"anyOf":[{"type":"string"},{"type":"null"}]},"languageConfidence":{"anyOf":[{"type":"number","minimum":0,"maximum":1},{"type":"null"}],"description":"How sure the provider was about the language it identified. **Null when the job named its own language** — a single-language show identifies nothing, so there is no score, and reporting 1.0 would make a real detection's score meaningless."},"languageSource":{"anyOf":[{"type":"string","enum":["detected","configured","provider","unknown"]},{"type":"null"}],"description":"`detected`: the provider identified it. `configured`: we named it, from the show's single-language setting. `provider`: it disagreed with what we named. `unknown`: nothing reported one."},"origin":{"type":"string","enum":["asr","machine","human"],"description":"How the text was produced. `asr` today; machine translation lands in TAB-561."},"segmentCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"hasWords":{"type":"boolean"},"hasVtt":{"type":"boolean"},"isFinal":{"type":"boolean"},"updatedAt":{"type":"string"}},"required":["id","trackId","participantId","language","languageConfidence","languageSource","origin","segmentCount","hasWords","hasVtt","isFinal","updatedAt"],"additionalProperties":false}}},"required":["status","items"],"additionalProperties":false},"egressJobs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"kind":{"type":"string"},"status":{"type":"string"},"participantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"startedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"endedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"error":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","kind","status","participantId","startedAt","endedAt","error"],"additionalProperties":false}},"totals":{"type":"object","properties":{"tracks":{"$ref":"#/components/schemas/RecordingTrackCounts"},"bytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["tracks","bytes"],"additionalProperties":false}},"required":["session","production","participants","tracks","renders","transcripts","egressJobs","totals"],"additionalProperties":false},"DownloadLink":{"type":"object","properties":{"url":{"type":"string"},"expiresAt":{"type":"string"},"expiresInSeconds":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"filename":{"type":"string"},"contentType":{"type":"string"},"bytes":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"sha256":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["url","expiresAt","expiresInSeconds","filename","contentType","bytes","sha256"],"additionalProperties":false,"description":"A short-lived, signed URL for one stored object. Every issue is written to `audit_events`."},"RenderCaptionList":{"type":"object","properties":{"renderId":{"type":"string"},"items":{"type":"array","items":{"type":"object","properties":{"language":{"type":"string"},"origin":{"type":"string"},"cues":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"burnedIn":{"type":"boolean"},"vtt":{"$ref":"#/components/schemas/DownloadLink"},"srt":{"$ref":"#/components/schemas/DownloadLink"}},"required":["language","origin","cues","burnedIn","vtt","srt"],"additionalProperties":false}}},"required":["renderId","items"],"additionalProperties":false,"description":"The caption sidecars delivered beside one render, one entry per language, each with a signed download URL for both formats."},"RecordingManagementState":{"type":"object","properties":{"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"title":{"type":"string"},"episodeNo":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"tags":{"type":"array","items":{"type":"string"}},"archivedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"deletedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"purgeAfter":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"type":"string"}},"required":["sessionId","title","episodeNo","tags","archivedAt","deletedAt","purgeAfter","updatedAt"],"additionalProperties":false,"description":"What rename/tag/archive/delete/restore return: the recording's management state, nothing else."},"RecordingShare":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"scope":{"type":"string","enum":["all","tracks","renders"]},"itemIds":{"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"passcodeProtected":{"type":"boolean"},"expiresAt":{"type":"string"},"maxViews":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"views":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"lastViewedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"revokedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdBy":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"createdAt":{"type":"string"},"dead":{"type":"boolean"}},"required":["id","sessionId","scope","itemIds","passcodeProtected","expiresAt","maxViews","views","lastViewedAt","revokedAt","createdBy","createdAt","dead"],"additionalProperties":false,"description":"One share link. The token is returned exactly once, by the create call."},"RecordingShareCreated":{"type":"object","properties":{"share":{"$ref":"#/components/schemas/RecordingShare"},"token":{"type":"string"},"url":{"type":"string"}},"required":["share","token","url"],"additionalProperties":false},"RecordingShareList":{"type":"object","properties":{"shares":{"type":"array","items":{"$ref":"#/components/schemas/RecordingShare"}}},"required":["shares"],"additionalProperties":false},"SharedRecordingUnlockResult":{"type":"object","properties":{"accessToken":{"type":"string"},"expiresAt":{"type":"string"},"expiresInSeconds":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["accessToken","expiresAt","expiresInSeconds"],"additionalProperties":false},"SharedRecordingItem":{"type":"object","properties":{"kind":{"type":"string","enum":["track","render"]},"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"label":{"type":"string"},"mediaKind":{"type":"string"},"durationMs":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"bytes":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"width":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"download":{"anyOf":[{"$ref":"#/components/schemas/DownloadLink"},{"type":"null"}]}},"required":["kind","id","label","mediaKind","durationMs","bytes","width","height","download"],"additionalProperties":false},"SharedRecording":{"type":"object","properties":{"title":{"type":"string"},"episodeNo":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"productionName":{"type":"string"},"recordedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"durationMs":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"expiresAt":{"type":"string"},"scope":{"type":"string","enum":["all","tracks","renders"]},"viewsRemaining":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"items":{"type":"array","items":{"$ref":"#/components/schemas/SharedRecordingItem"}}},"required":["title","episodeNo","productionName","recordedAt","durationMs","expiresAt","scope","viewsRemaining","items"],"additionalProperties":false,"description":"The public view of a shared recording: enough to play or download what was shared, and nothing else."},"RenditionSegment":{"type":"object","properties":{"key":{"type":"string"},"startMs":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"durationMs":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"bytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["key","startMs","durationMs","bytes"],"additionalProperties":false,"description":"One CMAF media segment, located in the bucket and timed on the session clock."},"RenditionManifest":{"type":"object","properties":{"initKey":{"type":"string"},"width":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"height":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"codecs":{"type":"string"},"segments":{"type":"array","items":{"$ref":"#/components/schemas/RenditionSegment"}}},"required":["initKey","width","height","codecs","segments"],"additionalProperties":false,"description":"The segment index for one rung. Present once the rendition is `ready`; `{}` before that."},"Rendition":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"trackId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"renderId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"rung":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"status":{"type":"string","enum":["pending","running","ready","failed"]},"baselineStartMs":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"durationMs":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"manifest":{"anyOf":[{"$ref":"#/components/schemas/RenditionManifest"},{"type":"object","properties":{},"additionalProperties":false}]},"peaksKey":{"anyOf":[{"type":"string"},{"type":"null"}]},"spritesKey":{"anyOf":[{"type":"string"},{"type":"null"}]},"bytes":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"error":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","trackId","renderId","rung","status","baselineStartMs","durationMs","manifest","peaksKey","spritesKey","bytes","error"],"additionalProperties":false,"description":"One rung of one recording's CMAF ladder, with its manifest once it is ready."},"RenditionList":{"type":"object","properties":{"renditions":{"type":"array","items":{"$ref":"#/components/schemas/Rendition"}}},"required":["renditions"],"additionalProperties":false,"description":"Every rendition of one recording — both rungs of every ISO, and of the program master."},"SessionCaptionList":{"type":"object","properties":{"sessionId":{"type":"string"},"sourceLanguage":{"anyOf":[{"type":"string"},{"type":"null"}]},"items":{"type":"array","items":{"type":"object","properties":{"language":{"type":"string"},"label":{"type":"string"},"origin":{"type":"string"},"cues":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"isSource":{"type":"boolean"},"vttUrl":{"type":"string"},"playlistUrl":{"type":"string"}},"required":["language","label","origin","cues","isSource","vttUrl","playlistUrl"],"additionalProperties":false}},"hlsMediaTags":{"type":"array","items":{"type":"string"}}},"required":["sessionId","sourceLanguage","items","hlsMediaTags"],"additionalProperties":false,"description":"Every language this recording has captions in, with the URLs a player's caption menu needs."},"PlaybackTicketSegment":{"type":"object","properties":{"key":{"type":"string"},"url":{"type":"string"},"startMs":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"durationMs":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["key","url","startMs","durationMs"],"additionalProperties":false,"description":"One presigned CMAF segment."},"PlaybackTicket":{"type":"object","properties":{"renditionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"rung":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"initUrl":{"type":"string"},"segments":{"type":"array","items":{"$ref":"#/components/schemas/PlaybackTicketSegment"}},"fromIndex":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"nextIndex":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"expiresAt":{"type":"string"}},"required":["renditionId","rung","initUrl","segments","fromIndex","nextIndex","expiresAt"],"additionalProperties":false,"description":"A batch of presigned GETs for one rung, provider-agnostic: an R2 org and an S3 org get the same shape and the player never learns which it is."},"RegenerateRenditionsQueued":{"type":"object","properties":{"queued":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"recordings":{"type":"array","items":{"type":"object","properties":{"trackId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"renderId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]}},"required":["trackId","renderId"],"additionalProperties":false}}},"required":["queued","recordings"],"additionalProperties":false,"description":"What was queued. 202, not 200: the row a rendition ends up in is written by the pipeline, not by this request."},"RenditionAssetLink":{"type":"object","properties":{"renditionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"kind":{"type":"string","enum":["peaks","sprites","sprite-sheet"]},"url":{"type":"string"},"expiresAt":{"type":"string"},"expiresInSeconds":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["renditionId","kind","url","expiresAt","expiresInSeconds"],"additionalProperties":false,"description":"A one-hour presigned GET for one rendition asset — the merged waveform, the sprite index, or one sprite sheet image."},"RecordingTranscriptLanguage":{"type":"object","properties":{"language":{"type":"string"},"origin":{"type":"string","enum":["asr","machine","human"]},"languageSource":{"anyOf":[{"type":"string","enum":["detected","configured","provider","unknown","translated"]},{"type":"null"}]},"isSource":{"type":"boolean"},"translationProvider":{"anyOf":[{"type":"string"},{"type":"null"}]},"transcriptCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"segmentCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"stale":{"type":"boolean"}},"required":["language","origin","languageSource","isSource","translationProvider","transcriptCount","segmentCount","stale"],"additionalProperties":false},"RecordingTranscriptLanguages":{"type":"object","properties":{"sessionId":{"type":"string"},"targetLanguages":{"type":"array","items":{"type":"string"}},"languages":{"type":"array","items":{"$ref":"#/components/schemas/RecordingTranscriptLanguage"}}},"required":["sessionId","targetLanguages","languages"],"additionalProperties":false},"RecordingTranscriptTrack":{"type":"object","properties":{"transcriptId":{"type":"string"},"trackId":{"anyOf":[{"type":"string"},{"type":"null"}]},"participantId":{"anyOf":[{"type":"string"},{"type":"null"}]},"speaker":{"type":"string"},"language":{"anyOf":[{"type":"string"},{"type":"null"}]},"origin":{"type":"string","enum":["asr","machine","human"]},"languageSource":{"anyOf":[{"type":"string","enum":["detected","configured","provider","unknown","translated"]},{"type":"null"}]},"languageConfidence":{"anyOf":[{"type":"number","minimum":0,"maximum":1},{"type":"null"}]},"sourceTranscriptId":{"anyOf":[{"type":"string"},{"type":"null"}]},"translationProvider":{"anyOf":[{"type":"string"},{"type":"null"}]},"stale":{"type":"boolean"},"startClockMs":{"type":"number"},"sessionOffsetMs":{"type":"number"},"segments":{"type":"array","items":{"type":"object","properties":{"startMs":{"type":"number"},"endMs":{"type":"number"},"text":{"type":"string"},"edited":{"type":"boolean"}},"required":["startMs","endMs","text","edited"],"additionalProperties":false}},"wordsUrl":{"anyOf":[{"type":"string"},{"type":"null"}]},"vttUrl":{"anyOf":[{"type":"string"},{"type":"null"}]},"expiresInSeconds":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]}},"required":["transcriptId","trackId","participantId","speaker","language","origin","languageSource","languageConfidence","sourceTranscriptId","translationProvider","stale","startClockMs","sessionOffsetMs","segments","wordsUrl","vttUrl","expiresInSeconds"],"additionalProperties":false},"RecordingTranscript":{"type":"object","properties":{"sessionId":{"type":"string"},"language":{"type":"string"},"origin":{"type":"string","enum":["asr","machine","human"]},"isSource":{"type":"boolean"},"timeBase":{"type":"string","const":"session"},"tracks":{"type":"array","items":{"$ref":"#/components/schemas/RecordingTranscriptTrack"}}},"required":["sessionId","language","origin","isSource","timeBase","tracks"],"additionalProperties":false},"TranslateTranscriptsQueued":{"type":"object","properties":{"sessionId":{"type":"string"},"sourceTranscriptIds":{"type":"array","items":{"type":"string"}},"languages":{"type":"array","items":{"type":"string"}},"force":{"type":"boolean"}},"required":["sessionId","sourceTranscriptIds","languages","force"],"additionalProperties":false},"TranscriptSegmentEdit":{"type":"object","properties":{"id":{"type":"string"},"transcriptId":{"type":"string"},"segmentIndex":{"type":"integer","minimum":-1,"maximum":9007199254740991},"text":{"anyOf":[{"type":"string"},{"type":"null"}]},"machineText":{"anyOf":[{"type":"string"},{"type":"null"}]},"sourceVersion":{"type":"string"},"applies":{"type":"boolean"},"reviewReason":{"anyOf":[{"type":"string","enum":["unmatched-pin","language-unexpected","low-confidence"]},{"type":"null"}]},"reviewState":{"type":"string","enum":["none","open","resolved"]},"updatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","transcriptId","segmentIndex","text","machineText","sourceVersion","applies","reviewReason","reviewState","updatedAt"],"additionalProperties":false},"TranscriptReviewItem":{"type":"object","properties":{"transcriptId":{"type":"string"},"trackId":{"anyOf":[{"type":"string"},{"type":"null"}]},"speaker":{"type":"string"},"language":{"anyOf":[{"type":"string"},{"type":"null"}]},"segmentIndex":{"type":"integer","minimum":-1,"maximum":9007199254740991},"reason":{"type":"string","enum":["unmatched-pin","language-unexpected","low-confidence"]},"state":{"type":"string","enum":["open","resolved"]},"startMs":{"type":"number"},"endMs":{"type":"number"},"text":{"type":"string"},"stored":{"type":"boolean"}},"required":["transcriptId","trackId","speaker","language","segmentIndex","reason","state","startMs","endMs","text","stored"],"additionalProperties":false},"TranscriptReviewSummary":{"type":"object","properties":{"state":{"type":"string","enum":["clear","needs-review"]},"open":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"resolved":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"reasons":{"type":"array","items":{"type":"string","enum":["unmatched-pin","language-unexpected","low-confidence"]}}},"required":["state","open","resolved","reasons"],"additionalProperties":false},"TranscriptReview":{"type":"object","properties":{"sessionId":{"type":"string"},"language":{"type":"string"},"summary":{"$ref":"#/components/schemas/TranscriptReviewSummary"},"items":{"type":"array","items":{"$ref":"#/components/schemas/TranscriptReviewItem"}}},"required":["sessionId","language","summary","items"],"additionalProperties":false},"RenderPresetSummary":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"description":{"type":"string"},"renderKind":{"type":"string"},"aspect":{"type":"string"},"width":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"height":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"fps":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"codec":{"type":"string"},"bitrateKbps":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"captionMode":{"type":"string","enum":["off","burn","sidecar"]},"audioOnly":{"type":"boolean"},"loudness":{"type":"string","enum":["podcast","program"]}},"required":["id","label","description","renderKind","aspect","width","height","fps","codec","captionMode","audioOnly","loudness"],"additionalProperties":false,"description":"One entry in the render-preset catalogue. The `id` is what a render is queued with."},"RenderPresetList":{"type":"object","properties":{"presets":{"type":"array","items":{"$ref":"#/components/schemas/RenderPresetSummary"}}},"required":["presets"],"additionalProperties":false},"RenderParamsView":{"type":"object","properties":{"version":{"default":1,"type":"number"},"engine":{"default":null,"anyOf":[{"type":"string"},{"type":"null"}]},"presetId":{"default":null,"anyOf":[{"type":"string"},{"type":"null"}]},"audio":{"default":null,"anyOf":[{"type":"object","properties":{"mode":{"type":"string"},"reason":{"default":null,"anyOf":[{"type":"string"},{"type":"null"}]},"reference":{"default":null,"anyOf":[{"type":"string"},{"type":"null"}]},"sources":{"default":[],"type":"array","items":{"type":"string"}}},"required":["mode","reason","reference","sources"],"additionalProperties":false},{"type":"null"}]},"cuts":{"default":{"count":0,"sources":[],"fallbackCount":0,"fallbacks":[]},"type":"object","properties":{"count":{"default":0,"type":"number"},"sources":{"default":[],"type":"array","items":{"type":"string"}},"fallbackCount":{"default":0,"type":"number"},"fallbacks":{"default":[],"type":"array","items":{"type":"object","properties":{"atMs":{"type":"number"},"reason":{"type":"string"}},"required":["atMs","reason"],"additionalProperties":false}}},"required":["count","sources","fallbackCount","fallbacks"],"additionalProperties":false},"clip":{"default":null,"anyOf":[{"type":"object","properties":{"aspect":{"type":"string"},"strategy":{"type":"string"},"shots":{"default":0,"type":"number"},"keyframes":{"default":0,"type":"number"},"moved":{"default":false,"type":"boolean"},"reasons":{"default":[],"type":"array","items":{"type":"string"}},"tracks":{"default":[],"type":"array","items":{"type":"string"}}},"required":["aspect","strategy","shots","keyframes","moved","reasons","tracks"],"additionalProperties":false},{"type":"null"}]},"posterKey":{"default":null,"anyOf":[{"type":"string"},{"type":"null"}]},"captions":{"default":[],"type":"array","items":{"type":"object","properties":{"language":{"type":"string"},"origin":{"default":"machine","type":"string"},"cues":{"default":0,"type":"number"},"burnedIn":{"default":false,"type":"boolean"}},"required":["language","origin","cues","burnedIn"],"additionalProperties":false}},"captionCount":{"default":0,"type":"number"},"burnInLanguage":{"default":null,"anyOf":[{"type":"string"},{"type":"null"}]},"error":{"default":null,"anyOf":[{"type":"string"},{"type":"null"}]},"extra":{"default":{},"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]}}},"required":["version","engine","presetId","audio","cuts","clip","posterKey","captions","captionCount","burnInLanguage","error","extra"],"additionalProperties":false,"description":"What the job recorded about itself, normalised. Always present and always well formed: a render queued before the conform recorded anything reads as this object with `audio` null and `cuts.count` 0, never as an error. `version` is the projection's own."},"RenderQueueEntry":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"kind":{"type":"string"},"mode":{"type":"string"},"status":{"type":"string","enum":["queued","running","succeeded","failed","canceled"]},"preset":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"progressPct":{"anyOf":[{"type":"number","minimum":0,"maximum":100},{"type":"null"}]},"progressStage":{"anyOf":[{"type":"string"},{"type":"null"}]},"bytes":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"durationMs":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"sha256":{"anyOf":[{"type":"string"},{"type":"null"}]},"assetId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"attempts":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"cancelPending":{"type":"boolean"},"error":{"anyOf":[{"type":"string"},{"type":"null"}]},"params":{"$ref":"#/components/schemas/RenderParamsView"},"ready":{"type":"boolean"},"createdAt":{"type":"string"},"startedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"finishedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"updatedAt":{"type":"string"}},"required":["id","sessionId","kind","mode","status","preset","progressPct","progressStage","bytes","durationMs","sha256","assetId","attempts","cancelPending","error","params","ready","createdAt","startedAt","finishedAt","updatedAt"],"additionalProperties":false},"RenderQueue":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/RenderQueueEntry"}},"nextBefore":{"anyOf":[{"type":"string"},{"type":"null"}]},"activeCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["items","nextBefore","activeCount"],"additionalProperties":false},"CancelRenderResponse":{"type":"object","properties":{"render":{"$ref":"#/components/schemas/RenderQueueEntry"},"stoppedImmediately":{"type":"boolean"}},"required":["render","stoppedImmediately"],"additionalProperties":false},"RetryRenderResponse":{"type":"object","properties":{"render":{"$ref":"#/components/schemas/RenderQueueEntry"},"resumingAfter":{"type":"array","items":{"type":"string"}}},"required":["render","resumingAfter"],"additionalProperties":false},"ClipQueue":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/RenderQueueEntry"}},"nextBefore":{"anyOf":[{"type":"string"},{"type":"null"}]},"activeCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["items","nextBefore","activeCount"],"additionalProperties":false,"description":"The session's clips, newest first. Each entry is a render row: the same statuses, the same download route, and `params.clip` saying what shape it was cut to and how the window was decided."},"RecordingClaim":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"participantId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"email":{"anyOf":[{"type":"string"},{"type":"null"}]},"expiresAt":{"type":"string"},"claimedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"claimedByUserId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"createdAt":{"type":"string"}},"required":["id","sessionId","participantId","email","expiresAt","claimedAt","claimedByUserId","createdAt"],"additionalProperties":false,"description":"An offer of one participant's recording. The token itself is never in this shape — only its sha256 is stored, and the plaintext exists once, in the response that created it."},"ClaimEmailStatus":{"type":"object","properties":{"outcome":{"type":"string","enum":["sent","suppressed","failed"]},"reason":{"anyOf":[{"type":"string"},{"type":"null"}]},"provider":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["outcome","reason","provider"],"additionalProperties":false,"description":"What became of the claim-link email. `suppressed` is a decision the platform made — no provider configured, the recipient opted out, the address is not an address — and `failed` is a supplier that refused. Neither prevents the claim from being minted: the token is in this same response and can be sent by hand."},"CreatedClaim":{"type":"object","properties":{"claim":{"$ref":"#/components/schemas/RecordingClaim"},"token":{"type":"string"},"email":{"anyOf":[{"$ref":"#/components/schemas/ClaimEmailStatus"},{"type":"null"}]}},"required":["claim","token","email"],"additionalProperties":false},"ClaimList":{"type":"object","properties":{"claims":{"type":"array","items":{"$ref":"#/components/schemas/RecordingClaim"}}},"required":["claims"],"additionalProperties":false},"ClaimPreview":{"type":"object","properties":{"claimId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"participantId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"displayName":{"type":"string"},"sessionTitle":{"type":"string"},"episodeNo":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"productionName":{"type":"string"},"recordedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"expiresAt":{"type":"string"},"trackCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["claimId","orgId","sessionId","participantId","displayName","sessionTitle","episodeNo","productionName","recordedAt","expiresAt","trackCount"],"additionalProperties":false,"description":"What the holder of a claim link is about to accept. Deliberately says nothing about the other participants, the other tracks or the team: a preview is not a foothold."},"RedeemedClaim":{"type":"object","properties":{"claim":{"$ref":"#/components/schemas/RecordingClaim"},"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"participantId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"trackCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"membershipCreated":{"type":"boolean"}},"required":["claim","orgId","sessionId","participantId","trackCount","membershipCreated"],"additionalProperties":false},"ClaimedTrack":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"kind":{"type":"string"},"codec":{"anyOf":[{"type":"string"},{"type":"null"}]},"container":{"anyOf":[{"type":"string"},{"type":"null"}]},"durationMs":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"bytes":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"status":{"type":"string"}},"required":["id","kind","codec","container","durationMs","bytes","status"],"additionalProperties":false},"ClaimedRecording":{"type":"object","properties":{"claimId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"participantId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionTitle":{"type":"string"},"episodeNo":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"productionName":{"type":"string"},"recordedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"claimedAt":{"type":"string"},"tracks":{"type":"array","items":{"$ref":"#/components/schemas/ClaimedTrack"}}},"required":["claimId","sessionId","participantId","sessionTitle","episodeNo","productionName","recordedAt","claimedAt","tracks"],"additionalProperties":false},"ClaimedRecordingList":{"type":"object","properties":{"recordings":{"type":"array","items":{"$ref":"#/components/schemas/ClaimedRecording"}}},"required":["recordings"],"additionalProperties":false,"description":"What was recorded of *you* in this organisation. Not the org library: somebody whose only standing is `org_guest` sees their own tracks and nothing else."},"EditDocument":{"type":"object","properties":{"kind":{"type":"string","const":"tablecast.edit"},"version":{"type":"number","enum":[1,2]},"sessionId":{"type":"string"},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["kind","version","sessionId","revision"],"additionalProperties":{},"description":"The browser editor's document (`@tablecast/editor`'s `EditDoc`). Validated in full on write."},"EditSuggestion":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["filler_word","silence"]},"baseInMs":{"type":"number"},"baseOutMs":{"type":"number"},"state":{"type":"string","enum":["pending","applied","rejected"]},"label":{"type":"string"},"trackId":{"anyOf":[{"type":"string"},{"type":"null"}]},"confidence":{"type":"number"}},"required":["id","kind","baseInMs","baseOutMs","state","label"],"additionalProperties":false,"description":"A proposed removal, in baseline session-clock ms. Produced by a worker; applied by a human."},"EditProject":{"type":"object","properties":{"id":{"type":"string"},"orgId":{"type":"string"},"productionId":{"type":"string"},"sessionId":{"anyOf":[{"type":"string"},{"type":"null"}]},"name":{"type":"string"},"status":{"type":"string"},"ownerUserId":{"anyOf":[{"type":"string"},{"type":"null"}]},"currentVersionId":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","orgId","productionId","sessionId","name","status","ownerUserId","currentVersionId","createdAt","updatedAt"],"additionalProperties":false,"description":"An edit project. Versions hang off it; `currentVersionId` is the one the editor opens."},"EditProjectList":{"type":"object","properties":{"projects":{"type":"array","items":{"$ref":"#/components/schemas/EditProject"}}},"required":["projects"],"additionalProperties":false},"EditVersionSummary":{"type":"object","properties":{"id":{"type":"string"},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"note":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdBy":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string"},"suggestionCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","version","note","createdBy","createdAt","suggestionCount"],"additionalProperties":false,"description":"One entry of the version history. Versions are append-only — a save never overwrites one."},"EditVersionList":{"type":"object","properties":{"versions":{"type":"array","items":{"$ref":"#/components/schemas/EditVersionSummary"}}},"required":["versions"],"additionalProperties":false},"OpenEditProject":{"type":"object","properties":{"project":{"$ref":"#/components/schemas/EditProject"},"versionId":{"type":"string"},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"note":{"anyOf":[{"type":"string"},{"type":"null"}]},"doc":{"$ref":"#/components/schemas/EditDocument"},"suggestions":{"type":"array","items":{"$ref":"#/components/schemas/EditSuggestion"}}},"required":["project","versionId","version","note","doc","suggestions"],"additionalProperties":false,"description":"A project at one version: the document to edit and the machine's current proposals."},"SeededEditProject":{"type":"object","properties":{"projectId":{"type":"string"},"versionId":{"type":"string"},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"events":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"isos":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"doc":{"$ref":"#/components/schemas/EditDocument"}},"required":["projectId","versionId","version","events","isos","doc"],"additionalProperties":false},"SavedEditVersion":{"type":"object","properties":{"projectId":{"type":"string"},"versionId":{"type":"string"},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["projectId","versionId","version"],"additionalProperties":false},"TranscriptTrack":{"type":"object","properties":{"trackId":{"type":"string"},"participantId":{"anyOf":[{"type":"string"},{"type":"null"}]},"speaker":{"type":"string"},"language":{"anyOf":[{"type":"string"},{"type":"null"}]},"languageConfidence":{"anyOf":[{"type":"number","minimum":0,"maximum":1},{"type":"null"}]},"languageSource":{"anyOf":[{"type":"string","enum":["detected","configured","provider","unknown","translated"]},{"type":"null"}]},"origin":{"type":"string","enum":["asr","machine","human"]},"stale":{"type":"boolean"},"languageWithinAllowList":{"type":"boolean"},"startClockMs":{"type":"number"},"sessionOffsetMs":{"type":"number"},"transcriptId":{"type":"string"},"sourceVersion":{"type":"string"},"segments":{"type":"array","items":{"type":"object","properties":{"startMs":{"type":"number"},"endMs":{"type":"number"},"text":{"type":"string"},"edited":{"type":"boolean"},"machineText":{"anyOf":[{"type":"string"},{"type":"null"}]},"review":{"anyOf":[{"type":"string","enum":["unmatched-pin","language-unexpected","low-confidence"]},{"type":"null"}]}},"required":["startMs","endMs","text","edited","machineText","review"],"additionalProperties":false}},"review":{"$ref":"#/components/schemas/TranscriptReviewSummary"},"wordsUrl":{"anyOf":[{"type":"string"},{"type":"null"}]},"wordsExpiresInSeconds":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]}},"required":["trackId","participantId","speaker","language","languageConfidence","languageSource","origin","stale","languageWithinAllowList","startClockMs","sessionOffsetMs","transcriptId","sourceVersion","segments","review","wordsUrl","wordsExpiresInSeconds"],"additionalProperties":false,"description":"One ISO's transcript. The speaker comes from the track — diarization is off everywhere."},"SessionTranscriptLanguage":{"type":"object","properties":{"language":{"type":"string"},"origin":{"type":"string","enum":["asr","machine","human"]},"isSource":{"type":"boolean"},"trackCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"stale":{"type":"boolean"},"review":{"$ref":"#/components/schemas/TranscriptReviewSummary"}},"required":["language","origin","isSource","trackCount","stale","review"],"additionalProperties":false},"SessionTranscript":{"type":"object","properties":{"sessionId":{"type":"string"},"language":{"anyOf":[{"type":"string"},{"type":"null"}]},"languages":{"type":"array","items":{"$ref":"#/components/schemas/SessionTranscriptLanguage"}},"timeBase":{"type":"string","const":"session"},"tracks":{"type":"array","items":{"$ref":"#/components/schemas/TranscriptTrack"}}},"required":["sessionId","language","languages","timeBase","tracks"],"additionalProperties":false},"BundleJob":{"type":"object","properties":{"jobId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"status":{"type":"string","enum":["queued","running","succeeded","failed","canceled"]},"itemCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"estimatedBytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"bytes":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"filename":{"type":"string"},"error":{"anyOf":[{"type":"string"},{"type":"null"}]},"attempts":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"createdAt":{"type":"string"},"startedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"finishedAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["jobId","sessionId","status","itemCount","estimatedBytes","bytes","filename","error","attempts","createdAt","startedAt","finishedAt"],"additionalProperties":false,"description":"A bulk-download job. Poll it until `status` is `succeeded`, then read `download`."},"BundleAccepted":{"type":"object","properties":{"job":{"$ref":"#/components/schemas/BundleJob"},"created":{"type":"boolean"}},"required":["job","created"],"additionalProperties":false},"BundleStatus":{"type":"object","properties":{"job":{"$ref":"#/components/schemas/BundleJob"},"download":{"anyOf":[{"type":"object","properties":{"url":{"type":"string"},"expiresAt":{"type":"string"},"expiresInSeconds":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"filename":{"type":"string"},"bytes":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]}},"required":["url","expiresAt","expiresInSeconds","filename","bytes"],"additionalProperties":false},{"type":"null"}]},"skipped":{"type":"array","items":{"type":"string"}}},"required":["job","download","skipped"],"additionalProperties":false},"OrgStorageTotals":{"type":"object","properties":{"iso":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"proxy":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"master":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"render":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"bundle":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"bytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"gb":{"type":"number"},"objects":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["iso","proxy","master","render","bundle","bytes","gb","objects"],"additionalProperties":false,"description":"Bytes by what they are. Read from the rows that own the objects, not from a bucket listing."},"OrgStorageReport":{"type":"object","properties":{"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"generatedAt":{"type":"string"},"totals":{"$ref":"#/components/schemas/OrgStorageTotals"},"byProduction":{"type":"array","items":{"type":"object","properties":{"productionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"slug":{"type":"string"},"name":{"type":"string"},"teamId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"bytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"sessions":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"tracks":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["productionId","slug","name","teamId","bytes","sessions","tracks"],"additionalProperties":false}},"topSessions":{"type":"array","items":{"type":"object","properties":{"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"title":{"type":"string"},"episodeNo":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"productionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"productionSlug":{"type":"string"},"recordedAt":{"type":"string"},"bytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"tracks":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"archivedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"deletedAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["sessionId","title","episodeNo","productionId","productionSlug","recordedAt","bytes","tracks","archivedAt","deletedAt"],"additionalProperties":false}},"monthlyGrowth":{"type":"array","items":{"type":"object","properties":{"month":{"type":"string"},"bytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"cumulativeBytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"sessions":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["month","bytes","cumulativeBytes","sessions"],"additionalProperties":false}},"retention":{"type":"object","properties":{"expiring":{"type":"object","properties":{"count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"bytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"soonest":{"anyOf":[{"type":"string"},{"type":"null"}]},"tracks":{"type":"array","items":{"type":"object","properties":{"trackId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionTitle":{"type":"string"},"retainUntil":{"type":"string"},"bytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["trackId","sessionId","sessionTitle","retainUntil","bytes"],"additionalProperties":false}}},"required":["count","bytes","soonest","tracks"],"additionalProperties":false},"pendingPurge":{"type":"object","properties":{"count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"bytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"sessions":{"type":"array","items":{"type":"object","properties":{"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"title":{"type":"string"},"deletedAt":{"type":"string"},"purgeAfter":{"anyOf":[{"type":"string"},{"type":"null"}]},"bytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["sessionId","title","deletedAt","purgeAfter","bytes"],"additionalProperties":false}}},"required":["count","bytes","sessions"],"additionalProperties":false}},"required":["expiring","pendingPurge"],"additionalProperties":false},"lastMeasured":{"anyOf":[{"type":"object","properties":{"day":{"type":"string"},"gbMonth":{"type":"number"}},"required":["day","gbMonth"],"additionalProperties":false},{"type":"null"}]}},"required":["orgId","generatedAt","totals","byProduction","topSessions","monthlyGrowth","retention","lastMeasured"],"additionalProperties":false},"SessionReminder":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"offsetMinutes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"channel":{"type":"string","enum":["in_app","email"]},"scheduledFor":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$","description":"Absolute, resolved against `scheduledAt` when planned."},"state":{"type":"string","enum":["pending","sending","sent","skipped","canceled"]},"sentAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"attempts":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Why the last attempt failed or was skipped. Never a token."},"recipient":{"type":"object","properties":{"kind":{"type":"string","enum":["user","invite"]},"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"required":["kind","id"],"additionalProperties":false}},"required":["id","offsetMinutes","channel","scheduledFor","state","sentAt","attempts","lastError","recipient"],"additionalProperties":false,"description":"One reminder owed to one person about one session."},"SessionReminderList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/SessionReminder"}},"skippedPastOffsets":{"type":"array","items":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"description":"Offsets that were already in the past when the session was last saved, so no reminder exists."}},"required":["items","skippedPastOffsets"],"additionalProperties":false},"SessionCalendarLink":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"integrationId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"provider":{"type":"string"},"state":{"type":"string","enum":["pending","synced","failed","canceled"]},"externalHtmlUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Where a producer can open the event. Not a credential."},"lastSyncedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"lastError":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"What the integration health badge quotes. On the link and not the connection: one refused event is not the account being broken."},"attempts":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","integrationId","provider","state","externalHtmlUrl","lastSyncedAt","lastError","attempts"],"additionalProperties":false,"description":"This session's event in one connected calendar account."},"SessionCalendarLinkList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/SessionCalendarLink"}}},"required":["items"],"additionalProperties":false},"RecordCuration":{"type":"object","properties":{"hiddenFromTalent":{"maxItems":256,"type":"array","items":{"type":"string","minLength":1,"maxLength":200},"description":"Source ids the talent grid hides — `p_<participantId>` or `p_<participantId>:<cameraSlug>`."}},"required":["hiddenFromTalent"],"additionalProperties":false,"description":"What the record room hides from the on-camera talent's grid (TAB-628). View-only; never a recording control."},"MoveRoomResult":{"type":"object","properties":{"room":{"type":"string","enum":["planning","record","studio"],"description":"The room the session is now in — persisted as `active_room`."}},"required":["room"],"additionalProperties":false,"description":"The room the session was moved into. Persisted so a late joiner lands there too."},"Session":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"productionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"title":{"type":"string"},"episodeNo":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"status":{"type":"string","enum":["scheduled","lobby","live","ending","complete","failed","canceled"]},"scheduledAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"scheduledDurationMin":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"scheduledTimezone":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"IANA zone the schedule was authored in (`Australia/Sydney`). `scheduledAt` is the instant and is never ambiguous; this is the intent behind it — a show booked for 9am Sydney and then moved across a DST boundary means 9am *Sydney*. Null on a session scheduled before this existed, or through a client that did not say; readers fall back to the org's zone, then the user's, then UTC."},"startedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"endedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"mediaProvider":{"type":"string","enum":["livekit_cloud","livekit_self_do","livekit_self_aws"]},"mediaRegion":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"production.home_region -> organizations.home_region -> deployment default"},"livekitRoomName":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"`org_<orgid8>:session_<session id>`"},"livekitRoomSid":{"anyOf":[{"type":"string"},{"type":"null"}]},"sessionClockEpoch":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}],"description":"Wall-clock time at session-clock 0. Stamped at start; every recorder's PTS hangs off it."},"integrityStatus":{"type":"string","enum":["pending","verified","partial","waived","failed"]},"recordingPolicy":{"$ref":"#/components/schemas/RecordingPolicy"},"mode":{"type":"string","enum":["studio","record"],"description":"The room this episode is booked for — `studio` or `record`."},"activeRoom":{"type":"string","enum":["planning","record","studio"],"description":"The room the cast is in right now. Late joiners land here. Defaults to `planning`."},"autoAdmit":{"type":"boolean","description":"False when every guest waits in the lobby until a producer admits them."},"calendarEventRef":{"anyOf":[{"type":"string"},{"type":"null"}]},"lifecycleNote":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Why a start or end failed. Null on a healthy session. Never contains credentials."},"createdBy":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"updatedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","orgId","productionId","title","episodeNo","status","scheduledAt","scheduledDurationMin","scheduledTimezone","startedAt","endedAt","mediaProvider","mediaRegion","livekitRoomName","livekitRoomSid","sessionClockEpoch","integrityStatus","recordingPolicy","mode","activeRoom","autoAdmit","calendarEventRef","lifecycleNote","createdBy","createdAt","updatedAt"],"additionalProperties":false,"description":"One recording or broadcast of a production."},"SessionList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Session"}},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"offset":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["items","limit","offset"],"additionalProperties":false},"OrgSessionSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"productionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"productionName":{"type":"string"},"title":{"type":"string"},"episodeNo":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"status":{"type":"string","enum":["scheduled","lobby","live","ending","complete","failed","canceled"]},"scheduledAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"scheduledTimezone":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"IANA zone the schedule was authored in; see `Session.scheduledTimezone`."},"startedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"endedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]}},"required":["id","productionId","productionName","title","episodeNo","status","scheduledAt","scheduledTimezone","startedAt","endedAt"],"additionalProperties":false,"description":"One session in the org-wide sessions list, with its production's name."},"OrgSessionList":{"type":"object","properties":{"sessions":{"type":"array","items":{"$ref":"#/components/schemas/OrgSessionSummary"}},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"offset":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["sessions","limit","offset"],"additionalProperties":false},"SessionLifecycleResult":{"type":"object","properties":{"session":{"$ref":"#/components/schemas/Session"},"failure":{"anyOf":[{"type":"object","properties":{"system":{"type":"string","enum":["livekit","coordinator"]},"detail":{"type":"string"}},"required":["system","detail"],"additionalProperties":false},{"type":"null"}],"description":"Set when the media plane refused. The session is then `failed` and `lifecycleNote` says why — the call still returns 200, because a session that failed to start is a fact about the session, not a malfunction of the API."}},"required":["session","failure"],"additionalProperties":false},"EdlEvent":{"type":"object","properties":{"seq":{"type":"integer","minimum":0,"maximum":9007199254740991},"clockMs":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"actor":{"anyOf":[{"type":"string"},{"type":"null"}]},"source":{"type":"string","enum":["ui","midi","companion","osc","api","mcp","system"]},"kind":{"type":"string","enum":["take","preview","transition","graphic","audio","media","talkback","scene","source","location","prompter","other"]},"label":{"type":"string"},"op":{"type":"array","items":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"resultingVersion":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["seq","clockMs","actor","source","kind","label","op","resultingVersion"],"additionalProperties":false,"description":"One row of the switching EDL, with the patch and a human reading of it."},"EdlPage":{"type":"object","properties":{"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"events":{"type":"array","items":{"$ref":"#/components/schemas/EdlEvent"}},"lastSeq":{"type":"integer","minimum":0,"maximum":9007199254740991},"hasMore":{"type":"boolean"}},"required":["sessionId","events","lastSeq","hasMore"],"additionalProperties":false},"RecordControlAccepted":{"type":"object","properties":{"commandId":{"type":"string"},"op":{"type":"string"},"atClockMs":{"type":"number"},"t0ClockMs":{"description":"`start`: the instant every device opens its file at.","type":"number"},"exceptions":{"description":"`start`: the devices rolled without.","type":"array","items":{"type":"string"}},"targets":{"type":"array","items":{"type":"string"}}},"required":["commandId","op","atClockMs","targets"],"additionalProperties":false},"LobbyParticipant":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"displayName":{"type":"string"},"role":{"type":"string","enum":["producer","director","host","guest","camera","viewer","ingest","egress","prompter"]},"userId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Bound when a signed-in person joined; null for an anonymous guest."},"inviteId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Which link they came in on. Null for a member join."},"claimEmail":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"What the guest typed on the pre-join page so their ISO can reach them. Never the invite's."},"livekitIdentity":{"type":"string"},"joinedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}],"description":"When they arrived at the door."},"admittedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}],"description":"Null for everybody on this list — it is what makes them a member of it."},"admittedBy":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","sessionId","displayName","role","userId","inviteId","claimEmail","livekitIdentity","joinedAt","admittedAt","admittedBy","createdAt"],"additionalProperties":false,"description":"Somebody waiting in a session's lobby for a producer to let them in."},"SessionLobby":{"type":"object","properties":{"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"autoAdmit":{"type":"boolean","description":"The session's door. When true this list is empty by construction — nobody is ever held — and a producer looking at an empty lobby they expected people in should check it."},"items":{"type":"array","items":{"$ref":"#/components/schemas/LobbyParticipant"},"description":"Oldest knock first."}},"required":["sessionId","autoAdmit","items"],"additionalProperties":false,"description":"Everybody knocking on one session, and whether the door is even closed."},"SessionParticipant":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"displayName":{"type":"string","description":"The name they joined under — what a guest typed on the pre-join page."},"role":{"type":"string","enum":["producer","director","host","guest","camera","viewer","ingest","egress","prompter"]},"userId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Bound when a signed-in member joined; null for a guest. This is what tells the two apart."},"inviteId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"Which link they came in on. Null for a member join."},"claimEmail":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"What the guest typed so their ISO can reach them later."},"livekitIdentity":{"type":"string"},"isHidden":{"type":"boolean","description":"Producers, egress and ingest. Returned rather than filtered so the caller decides: an attendance list wants the producer, a who-is-on-camera list does not."},"joinedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}],"description":"When they arrived."},"leftAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}],"description":"Set by the presence webhook. Null means still connected — or that they never disconnected cleanly."},"admittedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}],"description":"When a producer let them through. Null on an auto-admit session, where nobody was ever held — which is why membership of this list is not decided by this column."},"admittedBy":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","sessionId","displayName","role","userId","inviteId","claimEmail","livekitIdentity","isHidden","joinedAt","leftAt","admittedAt","admittedBy","createdAt"],"additionalProperties":false,"description":"Somebody who was admitted to a session — the attendance record, guests included."},"SessionParticipants":{"type":"object","properties":{"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"autoAdmit":{"type":"boolean","description":"Whether this session gated its door at all."},"items":{"type":"array","items":{"$ref":"#/components/schemas/SessionParticipant"},"description":"Earliest arrival first."}},"required":["sessionId","autoAdmit","items"],"additionalProperties":false,"description":"Everybody who got into one session."},"AdmitLobbyResult":{"type":"object","properties":{"admitted":{"type":"array","items":{"$ref":"#/components/schemas/LobbyParticipant"},"description":"Everybody who was waiting, now admitted. Empty when nobody was."}},"required":["admitted"],"additionalProperties":false,"description":"The result of opening the door for the whole queue at once."},"AdmitParticipantResult":{"type":"object","properties":{"participant":{"description":"The participant, now with `admittedAt` set.","$ref":"#/components/schemas/LobbyParticipant"}},"required":["participant"],"additionalProperties":false},"ShareTargetKind":{"type":"string","enum":["recording","render","media_asset","edit_doc"],"description":"What the link points at. `recording` is a session (`sessions.id`), `render` an export, `media_asset` a library file, `edit_doc` an editor project."},"ShareVisibility":{"type":"string","enum":["public","private"],"description":"`public` — one link, anybody who holds it, no sign-in. `private` — a row and a token per named person, delivered by email; the parent share has no link of its own."},"SharePermission":{"type":"string","enum":["view","comment"],"description":"A ladder, in order: `comment` grants everything `view` does. A client comparing these should test the ladder, not equality — a third rung would break `=== 'comment'` everywhere."},"ShareRecipientSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"shareId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"email":{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"displayName":{"anyOf":[{"type":"string"},{"type":"null"}]},"locale":{"anyOf":[{"$ref":"#/components/schemas/LocaleTag"},{"type":"null"}],"description":"TAB-564 — the language their share email is written in."},"userId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"The member this address turned out to be, if any."},"lastSentAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"viewCount":{"type":"integer","minimum":0,"maximum":9007199254740991},"lastViewedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"revokedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","shareId","email","displayName","locale","userId","lastSentAt","viewCount","lastViewedAt","revokedAt","createdAt"],"additionalProperties":false,"description":"One named person on a private share. Their token is **never** returned — it exists in plaintext only in the email that carried it, and a resend rotates it."},"Share":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"targetKind":{"$ref":"#/components/schemas/ShareTargetKind"},"targetId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"productionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"visibility":{"$ref":"#/components/schemas/ShareVisibility"},"permission":{"$ref":"#/components/schemas/SharePermission"},"allowDownload":{"type":"boolean"},"expiresAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"viewCount":{"type":"integer","minimum":0,"maximum":9007199254740991},"lastViewedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"revokedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"createdBy":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"updatedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"url":{"anyOf":[{"type":"string","format":"uri"},{"type":"null"}],"description":"The public link, ready to copy — `${PUBLIC_WEB_URL}/s/<token>`. Present on every read of a live **public** share, because a public token is not a secret about a person and a dialog that could only show it once would turn 'copy link' into 'rotate link'. Always null for a private share, whose links belong to its recipients one apiece."},"recipients":{"type":"array","items":{"$ref":"#/components/schemas/ShareRecipientSummary"},"description":"The named people on a private share. Empty for a public one."}},"required":["id","targetKind","targetId","productionId","visibility","permission","allowDownload","expiresAt","viewCount","lastViewedAt","revokedAt","createdBy","createdAt","updatedAt","url","recipients"],"additionalProperties":false,"description":"One share link."},"ShareList":{"type":"object","properties":{"shares":{"type":"array","items":{"$ref":"#/components/schemas/Share"}}},"required":["shares"],"additionalProperties":false},"ShareDeliveryReport":{"type":"array","items":{"type":"object","properties":{"recipientId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"email":{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"outcome":{"type":"string","enum":["sent","suppressed","failed"]},"reason":{"anyOf":[{"type":"string"},{"type":"null"}]},"messageId":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["recipientId","email","outcome","reason","messageId"],"additionalProperties":false},"description":"What happened to each invitation. `suppressed` is a decision — no provider, a bad address, an opt-out — and `failed` is a supplier. Neither fails the request: the share exists either way, and reporting a send that did not happen as a 500 would lose the durable half to save the disposable one."},"ShareCreated":{"type":"object","properties":{"share":{"$ref":"#/components/schemas/Share"},"created":{"type":"boolean","description":"False when a live public share already covered this target."},"delivery":{"$ref":"#/components/schemas/ShareDeliveryReport"}},"required":["share","created","delivery"],"additionalProperties":false},"ShareRecipientsAdded":{"type":"object","properties":{"share":{"$ref":"#/components/schemas/Share"},"delivery":{"$ref":"#/components/schemas/ShareDeliveryReport"}},"required":["share","delivery"],"additionalProperties":false},"ShareRecipientResent":{"type":"object","properties":{"recipient":{"$ref":"#/components/schemas/ShareRecipientSummary"},"delivery":{"$ref":"#/components/schemas/ShareDeliveryReport"}},"required":["recipient","delivery"],"additionalProperties":false,"description":"The person, and what happened to their fresh link. **The previous link is dead** the instant this returns, and answers the same 404 an invented one does."},"SharedTarget":{"type":"object","properties":{"kind":{"$ref":"#/components/schemas/ShareTargetKind"},"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"title":{"type":"string"},"sessionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"The recording the player is keyed on: pass it to the renditions routes."},"productionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"productionName":{"anyOf":[{"type":"string"},{"type":"null"}]},"durationMs":{"anyOf":[{"type":"integer","minimum":0,"maximum":9007199254740991},{"type":"null"}]},"mimeType":{"anyOf":[{"type":"string"},{"type":"null"}]},"bytes":{"anyOf":[{"type":"integer","minimum":0,"maximum":9007199254740991},{"type":"null"}]}},"required":["kind","id","title","sessionId","productionId","productionName","durationMs","mimeType","bytes"],"additionalProperties":false,"description":"Enough about the shared thing to render a share page, and deliberately no more. Every field here is one somebody chose to put behind a link they handed out."},"SharedViewer":{"type":"object","properties":{"displayName":{"anyOf":[{"type":"string"},{"type":"null"}]},"email":{"anyOf":[{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},{"type":"null"}]},"userId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]}},"required":["displayName","email","userId"],"additionalProperties":false,"description":"Who the token says is looking, on a **private** share — what makes the landing say 'Viewing as Dan'. All three are null on a public link, which has no person behind it."},"SharedCapabilities":{"type":"object","properties":{"canView":{"type":"boolean","const":true},"canComment":{"type":"boolean"},"canDownload":{"type":"boolean"}},"required":["canView","canComment","canDownload"],"additionalProperties":false,"description":"What this token may do, resolved for the client so a page does not have to re-derive the permission ladder. The API enforces it regardless of what the page renders."},"ResolvedShare":{"type":"object","properties":{"shareId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"visibility":{"$ref":"#/components/schemas/ShareVisibility"},"permission":{"$ref":"#/components/schemas/SharePermission"},"capabilities":{"$ref":"#/components/schemas/SharedCapabilities"},"expiresAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"target":{"$ref":"#/components/schemas/SharedTarget"},"viewer":{"$ref":"#/components/schemas/SharedViewer"}},"required":["shareId","visibility","permission","capabilities","expiresAt","target","viewer"],"additionalProperties":false,"description":"What `GET /v1/s/{token}` answers. Send the token back as `Authorization: Share <token>` on the renditions, playback-ticket, download and comment routes; it is the credential for all of them."},"TeamRole":{"type":"string","enum":["team_lead","producer","director","host","member","viewer"],"description":"Role of a person within a team."},"Team":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"slug":{"type":"string"},"name":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"visibility":{"type":"string","enum":["open","closed","secret"]},"defaultRoleForOrgMembers":{"anyOf":[{"type":"string","enum":["team_lead","producer","director","host","member","viewer"]},{"type":"null"}]},"archivedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","orgId","slug","name","description","visibility","defaultRoleForOrgMembers","archivedAt","createdAt","updatedAt"],"additionalProperties":false},"TeamSettings":{"type":"object","properties":{"teamId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"brandKitId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"recordingDefaults":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"defaultDestinationIds":{"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"defaultIntegrationIds":{"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"budgetMinutesPerMonth":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"notificationChannels":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"updatedAt":{"type":"string"}},"required":["teamId","orgId","brandKitId","recordingDefaults","defaultDestinationIds","defaultIntegrationIds","budgetMinutesPerMonth","notificationChannels","updatedAt"],"additionalProperties":false},"TeamMember":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"teamId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"userId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"role":{"$ref":"#/components/schemas/TeamRole"},"addedVia":{"type":"string"},"displayName":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string"}},"required":["id","teamId","userId","role","addedVia","displayName","createdAt"],"additionalProperties":false},"TeamList":{"type":"object","properties":{"teams":{"type":"array","items":{"$ref":"#/components/schemas/Team"}}},"required":["teams"],"additionalProperties":false},"TeamWithSettings":{"type":"object","properties":{"team":{"$ref":"#/components/schemas/Team"},"settings":{"$ref":"#/components/schemas/TeamSettings"}},"required":["team","settings"],"additionalProperties":false},"TeamMemberList":{"type":"object","properties":{"members":{"type":"array","items":{"$ref":"#/components/schemas/TeamMember"}}},"required":["members"],"additionalProperties":false},"OrgTeamMembershipList":{"type":"object","properties":{"memberships":{"type":"array","items":{"$ref":"#/components/schemas/TeamMember"}}},"required":["memberships"],"additionalProperties":false},"TelemetryAccepted":{"type":"object","properties":{"accepted":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["accepted"],"additionalProperties":false},"PublishJob":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"productionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"integrationId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"provider":{"anyOf":[{"type":"string"},{"type":"null"}]},"renderId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"title":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"tags":{"type":"array","items":{"type":"string"}},"chapters":{"type":"array","items":{"type":"object","properties":{"startMs":{"type":"integer","minimum":0,"maximum":9007199254740991},"title":{"type":"string","minLength":1,"maxLength":200}},"required":["startMs","title"],"additionalProperties":false}},"visibility":{"type":"string","enum":["public","unlisted","private","scheduled"]},"status":{"type":"string","enum":["draft","scheduled","uploading","processing","published","failed","canceled"]},"scheduledAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"externalId":{"anyOf":[{"type":"string"},{"type":"null"}]},"externalUrl":{"anyOf":[{"type":"string"},{"type":"null"}]},"progressPct":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"error":{"anyOf":[{"type":"string"},{"type":"null"}]},"publishedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"updatedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["id","orgId","productionId","sessionId","integrationId","provider","renderId","title","description","tags","chapters","visibility","status","scheduledAt","externalId","externalUrl","progressPct","error","publishedAt","createdAt","updatedAt"],"additionalProperties":false,"description":"A publish job: the target, the thing being published, and its status through the pipeline."},"TrackSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"participantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"kind":{"type":"string"},"origin":{"type":"string"},"status":{"type":"string","description":"`recording` → `uploading` → `verified`, or `failed`"},"bytes":{"anyOf":[{"type":"number"},{"type":"null"}]},"sha256":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Base64 digest of the whole object, once verified"},"storageKey":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","sessionId","participantId","kind","origin","status","bytes","sha256","storageKey"],"additionalProperties":false},"UploadSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"trackId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"bucket":{"type":"string"},"key":{"type":"string"},"partSizeBytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"expectedParts":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"completedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"abortedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]}},"required":["id","trackId","bucket","key","partSizeBytes","expectedParts","completedAt","abortedAt"],"additionalProperties":false,"description":"The multipart upload backing one track. `storage_upload_id` is deliberately not exposed."},"CreateTrackUploadResponse":{"type":"object","properties":{"track":{"$ref":"#/components/schemas/TrackSummary"},"upload":{"$ref":"#/components/schemas/UploadSummary"}},"required":["track","upload"],"additionalProperties":false},"SignedPart":{"type":"object","properties":{"partNo":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"url":{"type":"string","format":"uri","description":"Presigned PUT URL for this part"},"headers":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"},"description":"Headers that must be sent verbatim, and no others: they are inside the URL's signature, so omitting one and adding one both fail with 403 SignatureDoesNotMatch. Carries `x-amz-checksum-sha256` on an S3-backed org and `content-md5` on an R2-backed one; empty when the part was signed without a digest (the client declared none for it)."}},"required":["partNo","url","headers"],"additionalProperties":false},"SignPartsResponse":{"type":"object","properties":{"uploadId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"bucket":{"type":"string"},"key":{"type":"string"},"partSizeBytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"expiresAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$","description":"15 minutes out; re-sign rather than retry a stale URL"},"parts":{"type":"array","items":{"$ref":"#/components/schemas/SignedPart"}}},"required":["uploadId","bucket","key","partSizeBytes","expiresAt","parts"],"additionalProperties":false},"UploadPart":{"type":"object","properties":{"partNo":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"state":{"type":"string","enum":["pending","uploaded","verified","failed"]},"bytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"etag":{"anyOf":[{"type":"string"},{"type":"null"}]},"sha256":{"anyOf":[{"type":"string"},{"type":"null"}]},"presentInStore":{"type":"boolean","description":"Confirmed by ListParts — the only trustworthy signal"},"uploadedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]}},"required":["partNo","state","bytes","etag","sha256","presentInStore","uploadedAt"],"additionalProperties":false},"UploadStatusResponse":{"type":"object","properties":{"upload":{"$ref":"#/components/schemas/UploadSummary"},"track":{"$ref":"#/components/schemas/TrackSummary"},"parts":{"type":"array","items":{"$ref":"#/components/schemas/UploadPart"}},"uploadedPartNos":{"type":"array","items":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"description":"Parts a resuming client can skip"},"storedBytes":{"type":"number","description":"Sum of the part sizes the store reports"}},"required":["upload","track","parts","uploadedPartNos","storedBytes"],"additionalProperties":false,"description":"The ledger reconciled against the store. This is the resume endpoint."},"RecordPartResponse":{"type":"object","properties":{"part":{"$ref":"#/components/schemas/UploadPart"}},"required":["part"],"additionalProperties":false},"CompleteUploadResponse":{"type":"object","properties":{"track":{"$ref":"#/components/schemas/TrackSummary"},"upload":{"$ref":"#/components/schemas/UploadSummary"},"bytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"storageKey":{"type":"string"}},"required":["track","upload","bytes","storageKey"],"additionalProperties":false},"AbortUploadResponse":{"type":"object","properties":{"upload":{"$ref":"#/components/schemas/UploadSummary"}},"required":["upload"],"additionalProperties":false},"LocalExportResponse":{"type":"object","properties":{"recorded":{"type":"boolean"},"trackId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"required":["recorded","trackId"],"additionalProperties":false},"WebhookEndpoint":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"url":{"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"eventTypes":{"type":"array","items":{"type":"string"}},"preset":{"anyOf":[{"type":"string","enum":["slack","teams","zapier"]},{"type":"null"}]},"enabled":{"type":"boolean"},"disabledReason":{"anyOf":[{"type":"string"},{"type":"null"}]},"consecutiveFailures":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"lastSuccessAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastFailureAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"secretRotatedAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"previousSecretExpiresAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","orgId","url","description","eventTypes","preset","enabled","disabledReason","consecutiveFailures","lastSuccessAt","lastFailureAt","secretRotatedAt","previousSecretExpiresAt","createdAt","updatedAt"],"additionalProperties":false,"description":"A webhook subscription. The signing secret is never part of this shape."},"WebhookEndpointList":{"type":"object","properties":{"webhooks":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEndpoint"}}},"required":["webhooks"],"additionalProperties":false},"WebhookEndpointCreated":{"type":"object","properties":{"webhook":{"$ref":"#/components/schemas/WebhookEndpoint"},"secret":{"type":"string"}},"required":["webhook","secret"],"additionalProperties":false,"description":"The endpoint, plus the signing secret. This is the only response that carries the secret; store it now."},"WebhookSecretRotated":{"type":"object","properties":{"webhook":{"$ref":"#/components/schemas/WebhookEndpoint"},"secret":{"type":"string"},"previousSecretExpiresAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["webhook","secret","previousSecretExpiresAt"],"additionalProperties":false,"description":"The new secret. The previous one keeps signing alongside it for 24 hours so a verifier can be redeployed without dropping an event."},"WebhookDelivery":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"endpointId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"eventId":{"type":"string"},"eventType":{"type":"string"},"status":{"type":"string","enum":["pending","delivered","failed","dead"]},"attempts":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"redriveCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"responseStatus":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"responseBody":{"anyOf":[{"type":"string"},{"type":"null"}]},"error":{"anyOf":[{"type":"string"},{"type":"null"}]},"latencyMs":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"payload":{},"lastAttemptAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"nextAttemptAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"deliveredAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"createdAt":{"type":"string"}},"required":["id","endpointId","eventId","eventType","status","attempts","redriveCount","responseStatus","responseBody","error","latencyMs","payload","lastAttemptAt","nextAttemptAt","deliveredAt","createdAt"],"additionalProperties":false},"WebhookDeliveryList":{"type":"object","properties":{"deliveries":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDelivery"}},"nextBefore":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["deliveries","nextBefore"],"additionalProperties":false},"WebhookTestResult":{"type":"object","properties":{"delivered":{"type":"boolean"},"responseStatus":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"error":{"anyOf":[{"type":"string"},{"type":"null"}]},"latencyMs":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"delivery":{"anyOf":[{"$ref":"#/components/schemas/WebhookDelivery"},{"type":"null"}]}},"required":["delivered","responseStatus","error","latencyMs","delivery"],"additionalProperties":false,"description":"The result of a synchronous `webhook.ping`. A ping ignores the endpoint's event filter and works on a disabled endpoint, because it is the question a customer asks before re-enabling one."},"UserProfilePatch":{"type":"object","properties":{"displayName":{"type":"string","minLength":1,"maxLength":120},"pronouns":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}]},"title":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}]},"bio":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]},"timezone":{"type":"string","minLength":1,"maxLength":64},"locale":{"type":"string","enum":["en-AU","en-NZ","en-GB","en-US","es","pt-BR"],"description":"TAB-564 — the language this person reads the product in."},"links":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"lowerThirdDefault":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"additionalProperties":false},"UserPreferencesPatch":{"type":"object","properties":{"theme":{"type":"string","enum":["system","light","dark"]},"notifications":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"defaultDevices":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"recordingDefaults":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"keymap":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"lastOrgId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"onboarding":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"additionalProperties":false},"SetAvatarBody":{"type":"object","properties":{"name":{"default":"avatar","type":"string","minLength":1,"maxLength":200},"head":{"type":"string","minLength":4,"maxLength":1024,"pattern":"^[A-Za-z0-9+/=_-]+$"},"bytes":{"type":"integer","minimum":1,"maximum":5242880}},"required":["head","bytes"],"description":"The bytes' first 512 bytes, for sniffing, and how many there are in total."},"RegisterPushTokenBody":{"type":"object","properties":{"token":{"type":"string","minLength":1,"maxLength":512,"description":"`ExponentPushToken[…]`, from `expo-notifications`."},"platform":{"type":"string","enum":["ios","android","web"]},"deviceId":{"description":"The handset's own id, as the app persists it. Lets a producer retire one phone.","anyOf":[{"type":"string","minLength":1,"maxLength":200},{"type":"null"}]},"appId":{"description":"The bundle id that registered it, e.g. `studio.tablecast.capture.dev`.","anyOf":[{"type":"string","minLength":1,"maxLength":200},{"type":"null"}]},"environment":{"anyOf":[{"type":"string","enum":["development","production"]},{"type":"null"}]}},"required":["token","platform"]},"CreateIdpBody":{"type":"object","properties":{"type":{"type":"string","enum":["oidc","saml"]},"name":{"type":"string","minLength":2,"maxLength":60},"issuer":{"type":"string","format":"uri"},"clientId":{"type":"string","minLength":1,"maxLength":255},"clientSecret":{"type":"string","minLength":8,"maxLength":512,"description":"Write-only. Stored in Secrets Manager and referenced by path; never returned."},"metadataXml":{"type":"string","minLength":1,"maxLength":1048576},"metadataUrl":{"type":"string","format":"uri"},"scopes":{"maxItems":20,"type":"array","items":{"type":"string","minLength":1,"maxLength":64}},"attributeMap":{"type":"object","propertyNames":{"type":"string","maxLength":64},"additionalProperties":{"type":"string","maxLength":255}},"groupRoleMap":{"maxItems":100,"type":"array","items":{"$ref":"#/components/schemas/GroupRoleMappingInput"}},"jitProvision":{"type":"boolean"},"defaultOrgRole":{"type":"string","enum":["org_admin","org_billing","org_member","org_guest"]},"enabled":{"type":"boolean"}},"required":["type","name"],"additionalProperties":false},"UpdateIdpBody":{"type":"object","properties":{"name":{"type":"string","minLength":2,"maxLength":60},"issuer":{"type":"string","format":"uri"},"clientId":{"type":"string","minLength":1,"maxLength":255},"clientSecret":{"type":"string","minLength":8,"maxLength":512,"description":"Write-only. Stored in Secrets Manager and referenced by path; never returned."},"metadataXml":{"type":"string","minLength":1,"maxLength":1048576},"metadataUrl":{"type":"string","format":"uri"},"scopes":{"maxItems":20,"type":"array","items":{"type":"string","minLength":1,"maxLength":64}},"attributeMap":{"type":"object","propertyNames":{"type":"string","maxLength":64},"additionalProperties":{"type":"string","maxLength":255}},"groupRoleMap":{"maxItems":100,"type":"array","items":{"$ref":"#/components/schemas/GroupRoleMappingInput"}},"jitProvision":{"type":"boolean"},"defaultOrgRole":{"type":"string","enum":["org_admin","org_billing","org_member","org_guest"]},"enabled":{"type":"boolean"}},"additionalProperties":false},"ClaimDomainBody":{"type":"object","properties":{"domain":{"type":"string","minLength":3,"maxLength":253},"identityProviderId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]}},"required":["domain"],"additionalProperties":false},"UpdateDomainBody":{"type":"object","properties":{"captureMode":{"type":"string","enum":["none","suggest","force"]},"identityProviderId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]}},"additionalProperties":false},"TestConnectionBody":{"type":"object","properties":{"claims":{"type":"object","propertyNames":{"type":"string","maxLength":128},"additionalProperties":{}}},"required":["claims"],"additionalProperties":false},"StepUpBody":{"type":"object","properties":{"identityId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"returnTo":{"type":"string","maxLength":2048}},"required":["identityId"],"additionalProperties":false},"BackChannelLogoutBody":{"type":"object","properties":{"logout_token":{"type":"string","minLength":16,"maxLength":8192}},"required":["logout_token"]},"SamlLogoutBody":{"type":"object","properties":{"SAMLRequest":{"type":"string","minLength":16,"maxLength":65536},"RelayState":{"type":"string","maxLength":512}},"required":["SAMLRequest"]},"CreateApiKey":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]},"scopes":{"minItems":1,"maxItems":3,"type":"array","items":{"type":"string","enum":["read","control","admin"],"description":"`admin` includes `control`, which includes `read`."}},"teamId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"expiresAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"rateLimitPerMin":{"anyOf":[{"type":"integer","minimum":1,"maximum":100000},{"type":"null"}]}},"required":["name","scopes"]},"UpdateApiKey":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]},"scopes":{"minItems":1,"maxItems":3,"type":"array","items":{"type":"string","enum":["read","control","admin"],"description":"`admin` includes `control`, which includes `read`."}},"expiresAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"rateLimitPerMin":{"anyOf":[{"type":"integer","minimum":1,"maximum":100000},{"type":"null"}]}}},"RotateApiKey":{"type":"object","properties":{"overlapHours":{"description":"How long the superseded secret keeps working. Default 24. Send 0 to retire it immediately, which is the right choice when the old secret is known to have leaked.","type":"integer","minimum":0,"maximum":168}}},"CreateOAuthClient":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]},"scopes":{"minItems":1,"maxItems":3,"type":"array","items":{"type":"string","enum":["read","control","admin"],"description":"`admin` includes `control`, which includes `read`."}},"expiresAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]}},"required":["name","scopes"]},"CreatePublicSession":{"type":"object","properties":{"productionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"title":{"type":"string","minLength":1,"maxLength":300},"episodeNo":{"anyOf":[{"type":"integer","minimum":0,"maximum":9007199254740991},{"type":"null"}]},"scheduledAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"scheduledDurationMin":{"anyOf":[{"type":"integer","minimum":1,"maximum":1440},{"type":"null"}]}},"required":["productionId","title"]},"CreatePublicInvite":{"type":"object","properties":{"role":{"default":"guest","type":"string","enum":["host","guest","producer","viewer","camera","prompter"]},"email":{"anyOf":[{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},{"type":"null"}]},"displayNameHint":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"singleUse":{"description":"Defaults to true. `true` with a `maxUses` above 1 is a contradiction and is refused.","type":"boolean"},"maxUses":{"description":"Defaults to 1. Above 1 implies `singleUse: false`.","type":"integer","minimum":1,"maximum":100},"expiresInHours":{"type":"integer","minimum":1,"maximum":2160}}},"RecordCommand":{"type":"object","properties":{"op":{"type":"string","enum":["arm","start","stop","marker"]},"atClockMs":{"type":"integer","minimum":0,"maximum":9007199254740991},"scope":{"default":"session","type":"string","enum":["session","location","participants"]},"locationId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"participantIds":{"maxItems":64,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"label":{"description":"For `marker`: what the marker is.","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]}},"required":["op"]},"OAuthTokenRequest":{"type":"object","properties":{"grant_type":{"type":"string","const":"client_credentials"},"client_id":{"type":"string","minLength":1},"client_secret":{"type":"string","minLength":1},"scope":{"type":"string","maxLength":200}},"required":["grant_type"],"description":"RFC 6749 §4.4. Credentials may be sent here or, preferably, as HTTP Basic authentication."},"PublicPlaybackTicketRequest":{"type":"object","properties":{"renditionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"fromIndex":{"default":0,"description":"Index into the rendition's segment list.","type":"integer","minimum":0,"maximum":9007199254740991}},"required":["renditionId"],"description":"Which rendition to play, and where to resume.","examples":[{"renditionId":"5e6f7a8b-0000-4000-8000-000000000005","fromIndex":0}]},"PublicRenditionAssetRequest":{"type":"object","properties":{"renditionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"kind":{"type":"string","enum":["peaks","sprites","sprite-sheet"]},"sheetIndex":{"description":"Required for `sprite-sheet`; ignored otherwise.","type":"integer","minimum":0,"maximum":9007199254740991}},"required":["renditionId","kind"],"description":"Which artefact of a rendition to presign. The request names the *kind*, never a storage key — the server resolves the object from the rendition row.","examples":[{"renditionId":"5e6f7a8b-0000-4000-8000-000000000005","kind":"peaks"}]},"CreatePublicRender":{"type":"object","properties":{"presetId":{"type":"string","minLength":1,"description":"From the catalogue at `GET /v1/public/render-presets`, e.g. `master_16x9`."},"editVersionId":{"description":"Render a specific cut. Omit for the recording as captured.","anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"rangeMs":{"description":"A clip, in session-clock ms. Omit for the whole thing.","anyOf":[{"type":"object","properties":{"startMs":{"type":"integer","minimum":0,"maximum":9007199254740991},"endMs":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["startMs","endMs"]},{"type":"null"}]}},"required":["presetId"],"description":"Queue a server render of a recording. `mode` is not on the body: an API caller gets a **server** render, because `browser` mode reserves a row the calling *page* then fills in and there is no page here.","examples":[{"presetId":"master_16x9"}]},"AddPublicInviteRecipients":{"type":"object","properties":{"recipients":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"object","properties":{"email":{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"displayName":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"role":{"type":"string","enum":["host","guest","producer","viewer","camera","prompter"]}},"required":["email"]}},"role":{"default":"guest","type":"string","enum":["host","guest","producer","viewer","camera","prompter"]},"expiresInHours":{"type":"integer","minimum":1,"maximum":2160}},"required":["recipients"],"description":"People to invite. Each gets their own link and their own branded email; a send that fails does not fail the call, and shows up as `lastDeliveryError` on the row.","examples":[{"recipients":[{"email":"guest@example.com","displayName":"A Guest"}],"role":"guest"}]},"CreatePublicShare":{"type":"object","properties":{"targetKind":{"type":"string","enum":["recording","render","media_asset","edit_doc"]},"targetId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"permission":{"default":"view","type":"string","enum":["view","comment"]},"allowDownload":{"default":false,"description":"'Look at this' and 'here is the file' are different acts. The second is off by default.","type":"boolean"},"expiresAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]}},"required":["targetKind","targetId"],"description":"A **public** share: one link, for anybody who holds it. `visibility` is not on the body — a private share mints one token per named person and emails each of them on the organisation's behalf, which is a first-party act with a delivery report rather than something a credential does on its own.","examples":[{"targetKind":"recording","targetId":"979c3d7c-0000-4000-8000-000000000003","permission":"view"}]},"PollSubscription":{"type":"object","properties":{"limit":{"description":"How many events at most. Default 50.","type":"integer","minimum":1,"maximum":200},"wait":{"description":"Long-poll: hold the request open up to this many ms for an event. Clamped to 10000.","type":"integer","minimum":0,"maximum":60000}},"description":"How many events to take, and how long to wait for one."},"CreateEventSubscription":{"type":"object","properties":{"kinds":{"maxItems":5,"type":"array","items":{"type":"string","enum":["production.state","session.record","session.participant","session.chat","session.coordinator"]}},"since":{"description":"Start from a cursor you already hold, rather than from the beginning of the session. This is how a reconnect loses nothing.","type":"string","minLength":1,"maxLength":2048},"fromNow":{"default":false,"description":"Skip everything that already happened and start at the present moment. Ignored when `since` is given.","type":"boolean"},"ttlSeconds":{"description":"How long the subscription lives. Default 3600s, ceiling 86400s.","type":"integer","minimum":60,"maximum":86400}},"description":"What to watch, and from when."},"OAuthClientRegistrationRequest":{"type":"object","properties":{"redirect_uris":{"minItems":1,"maxItems":10,"type":"array","items":{"type":"string","maxLength":2000}},"client_name":{"type":"string","maxLength":200},"client_uri":{"type":"string","maxLength":2000},"logo_uri":{"type":"string","maxLength":2000},"token_endpoint_auth_method":{"type":"string","maxLength":64},"scope":{"type":"string","maxLength":200},"grant_types":{"maxItems":10,"type":"array","items":{"type":"string","maxLength":64}},"response_types":{"maxItems":10,"type":"array","items":{"type":"string","maxLength":64}},"contacts":{"maxItems":10,"type":"array","items":{"type":"string","maxLength":320}},"tos_uri":{"type":"string","maxLength":2000},"policy_uri":{"type":"string","maxLength":2000},"software_id":{"type":"string","maxLength":200},"software_version":{"type":"string","maxLength":200}},"required":["redirect_uris"],"description":"RFC 7591 §2. Unauthenticated: a registration announces a callback and grants nothing until a signed-in human approves a connection on the consent page."},"McpAuthorizationConsent":{"type":"object","properties":{"ticket":{"type":"string","minLength":1,"maxLength":4000},"decision":{"type":"string","enum":["approve","deny"]},"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"scopes":{"maxItems":2,"type":"array","items":{"type":"string","enum":["read","control"]}}},"required":["ticket","decision"]},"McpOAuthTokenRequest":{"type":"object","properties":{"grant_type":{"type":"string","maxLength":64},"code":{"type":"string","maxLength":500},"redirect_uri":{"type":"string","maxLength":2000},"code_verifier":{"type":"string","maxLength":200},"refresh_token":{"type":"string","maxLength":500},"client_id":{"type":"string","maxLength":200},"client_secret":{"type":"string","maxLength":500},"scope":{"type":"string","maxLength":200},"resource":{"type":"string","maxLength":2000}},"required":["grant_type"],"description":"RFC 6749 §4.1.3 (`authorization_code`, PKCE required) and §6 (`refresh_token`). `application/x-www-form-urlencoded` or JSON."},"OAuthRevocationRequest":{"type":"object","properties":{"token":{"type":"string","minLength":1,"maxLength":500},"token_type_hint":{"type":"string","maxLength":64},"client_id":{"type":"string","maxLength":200},"client_secret":{"type":"string","maxLength":500}},"required":["token"],"description":"RFC 7009 §2.1. Revoking either half of a grant revokes the whole grant."},"CompanionControl":{"type":"object","properties":{"ops":{"minItems":1,"maxItems":16,"type":"array","items":{"oneOf":[{"type":"object","properties":{"op":{"type":"string","const":"scene.take"},"sceneId":{"type":"string","minLength":1,"maxLength":128},"transition":{"type":"object","properties":{"type":{"default":"cut","type":"string","enum":["cut","dissolve","fade","wipe","stinger"]},"durationMs":{"default":0,"type":"integer","minimum":0,"maximum":10000},"assetId":{"anyOf":[{"type":"string","minLength":1,"maxLength":128},{"type":"null"}]}}}},"required":["op","sceneId"]},{"type":"object","properties":{"op":{"type":"string","const":"scene.preview"},"sceneId":{"type":"string","minLength":1,"maxLength":128}},"required":["op","sceneId"]},{"type":"object","properties":{"op":{"type":"string","const":"scene.cut"}},"required":["op"]},{"type":"object","properties":{"op":{"type":"string","const":"scene.assignSlot"},"sceneId":{"type":"string","minLength":1,"maxLength":128},"slotId":{"type":"string","minLength":1,"maxLength":128},"sourceId":{"anyOf":[{"type":"string","minLength":1,"maxLength":128},{"type":"null"}]}},"required":["op","sceneId","slotId","sourceId"]},{"type":"object","properties":{"op":{"type":"string","const":"graphic.set"},"graphicId":{"type":"string","minLength":1,"maxLength":128},"visible":{"default":"toggle","anyOf":[{"type":"boolean"},{"type":"string","const":"toggle"}]}},"required":["op","graphicId"]},{"type":"object","properties":{"op":{"type":"string","const":"media.play"},"sourceId":{"type":"string","minLength":1,"maxLength":128},"positionMs":{"type":"integer","minimum":0,"maximum":9007199254740991},"loop":{"type":"boolean"}},"required":["op","sourceId"]},{"type":"object","properties":{"op":{"type":"string","const":"media.stop"},"sourceId":{"type":"string","minLength":1,"maxLength":128}},"required":["op","sourceId"]},{"type":"object","properties":{"op":{"type":"string","const":"media.pause"},"sourceId":{"type":"string","minLength":1,"maxLength":128}},"required":["op","sourceId"]},{"type":"object","properties":{"op":{"type":"string","const":"media.cue"},"sourceId":{"anyOf":[{"type":"string","minLength":1,"maxLength":128},{"type":"null"}]}},"required":["op","sourceId"]},{"type":"object","properties":{"op":{"type":"string","const":"media.seek"},"sourceId":{"type":"string","minLength":1,"maxLength":128},"positionMs":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["op","sourceId","positionMs"]},{"type":"object","properties":{"op":{"type":"string","const":"graphic.preset"},"presetId":{"type":"string","minLength":1,"maxLength":128}},"required":["op","presetId"]},{"type":"object","properties":{"op":{"type":"string","const":"graphic.countdown"},"graphicId":{"type":"string","minLength":1,"maxLength":128},"action":{"default":"toggle","type":"string","enum":["start","pause","toggle","reset"]},"durationMs":{"type":"integer","minimum":0,"maximum":86400000}},"required":["op","graphicId"]},{"type":"object","properties":{"op":{"type":"string","const":"mixer.gain"},"sourceId":{"type":"string","minLength":1,"maxLength":128},"gainDb":{"type":"number","minimum":-60,"maximum":12}},"required":["op","sourceId","gainDb"]},{"type":"object","properties":{"op":{"type":"string","const":"mixer.mute"},"sourceId":{"type":"string","minLength":1,"maxLength":128},"muted":{"default":"toggle","anyOf":[{"type":"boolean"},{"type":"string","const":"toggle"}]}},"required":["op","sourceId"]},{"type":"object","properties":{"op":{"type":"string","const":"mixer.afv"},"sourceId":{"type":"string","minLength":1,"maxLength":128},"afv":{"default":"toggle","anyOf":[{"type":"boolean"},{"type":"string","const":"toggle"}]}},"required":["op","sourceId"]},{"type":"object","properties":{"op":{"type":"string","const":"talkback.key"},"target":{"type":"object","properties":{"kind":{"type":"string","enum":["participant","location","group","all"]},"id":{"type":"string","minLength":1,"maxLength":128}},"required":["kind"]},"mode":{"default":"ptt","type":"string","enum":["ptt","latch"]},"down":{"default":"toggle","anyOf":[{"type":"boolean"},{"type":"string","const":"toggle"}]}},"required":["op","target"]},{"type":"object","properties":{"op":{"type":"string","const":"record.arm"},"scope":{"default":"session","type":"string","enum":["session","location","participants"]},"locationId":{"anyOf":[{"type":"string","minLength":1,"maxLength":128},{"type":"null"}]},"participantIds":{"maxItems":256,"type":"array","items":{"type":"string","minLength":1,"maxLength":128}},"take":{"type":"string","maxLength":128}},"required":["op"]},{"type":"object","properties":{"op":{"type":"string","const":"record.start"},"scope":{"default":"session","type":"string","enum":["session","location","participants"]},"locationId":{"anyOf":[{"type":"string","minLength":1,"maxLength":128},{"type":"null"}]},"participantIds":{"maxItems":256,"type":"array","items":{"type":"string","minLength":1,"maxLength":128}}},"required":["op"]},{"type":"object","properties":{"op":{"type":"string","const":"record.stop"},"scope":{"default":"session","type":"string","enum":["session","location","participants"]},"locationId":{"anyOf":[{"type":"string","minLength":1,"maxLength":128},{"type":"null"}]},"participantIds":{"maxItems":256,"type":"array","items":{"type":"string","minLength":1,"maxLength":128}}},"required":["op"]},{"type":"object","properties":{"op":{"type":"string","const":"marker"},"label":{"type":"string","maxLength":256},"kind":{"default":"marker","type":"string","enum":["marker","chapter","flag","error"]}},"required":["op"]},{"type":"object","properties":{"op":{"type":"string","const":"live.start"},"destinationIds":{"minItems":1,"maxItems":16,"type":"array","items":{"type":"string","minLength":1,"maxLength":128}}},"required":["op","destinationIds"]},{"type":"object","properties":{"op":{"type":"string","const":"live.stop"}},"required":["op"]}]},"description":"The ops this key press stands for, applied in order."},"atClockMs":{"description":"Session-clock ms of the press. Omitted, the coordinator stamps its own — which is up to a round trip later, and is the difference between a marker that lands on the word and one that lands after it.","type":"number"},"key":{"description":"Surface id (`page/row/column`). Audit only.","type":"string","maxLength":64}},"required":["ops"]},"CreateCompanionPairing":{"type":"object","properties":{"label":{"type":"string","minLength":1,"maxLength":120,"description":"What the producer calls this deck."},"actorParticipantId":{"description":"The participant the surface acts as. Without it the EDL records no actor and talkback keys do not work, because the comms plane asks about a role.","anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"productionId":{"description":"Pin the pairing to the production instead of this session, so it survives the episode.","anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"expiresInHours":{"anyOf":[{"type":"number","exclusiveMinimum":0,"maximum":2160},{"type":"null"}]},"controlDeviceId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]}},"required":["label"]},"RegisterControlDevice":{"type":"object","properties":{"kind":{"type":"string","enum":["companion","midi","osc","stream_deck"],"description":"What sort of surface this is."},"name":{"type":"string","minLength":1,"maxLength":120,"description":"What the producer calls this box."},"host":{"type":"string","minLength":1,"maxLength":253,"description":"Hostname or IP. Not a URL — a scheme or path is a 422."},"port":{"type":"integer","minimum":1,"maximum":65535},"firmware":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}]},"capabilities":{"description":"A free-form feature map the device reports about itself. Bounded at 32 keys / 4 KiB.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"required":["kind","name","host","port"]},"AssetProbe":{"type":"object","properties":{"width":{"type":"integer","minimum":1,"maximum":16384},"height":{"type":"integer","minimum":1,"maximum":16384},"durationMs":{"type":"integer","minimum":0,"maximum":43200000},"hasAlpha":{"type":"boolean"}},"description":"What the client already measured after decoding the file. The post pipeline's ffprobe is the authority and overwrites it; this is what the library shows until then."},"CreateAssetBody":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"head":{"type":"string","minLength":4,"maxLength":1024,"pattern":"^[A-Za-z0-9+/=_-]+$"},"kind":{"type":"string","enum":["video","audio","image","stinger","font","caption","document"]},"bytes":{"type":"integer","minimum":0,"maximum":5368709120},"probe":{"$ref":"#/components/schemas/AssetProbe"}},"required":["name","head","bytes"],"description":"A new library asset. The row and its queued probe job are created immediately; the bytes go straight to object storage through the returned presigned PUT."},"CanvaAuthorizeUrlBody":{"type":"object","properties":{"redirectUri":{"type":"string","format":"uri"}},"required":["redirectUri"]},"CanvaConnectBody":{"type":"object","properties":{"code":{"type":"string","minLength":1,"maxLength":2048},"redirectUri":{"type":"string","format":"uri"},"state":{"type":"string","minLength":1,"maxLength":200},"codeVerifier":{"type":"string","minLength":43,"maxLength":128}},"required":["code","redirectUri","state","codeVerifier"]},"CanvaImportBody":{"type":"object","properties":{"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"teamId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"productionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"name":{"type":"string","minLength":1,"maxLength":200},"format":{"default":"png","type":"string","enum":["png","mp4","gif"]},"transparent":{"default":true,"type":"boolean"},"width":{"type":"integer","minimum":16,"maximum":8192},"height":{"type":"integer","minimum":16,"maximum":8192},"render":{"type":"object","properties":{"animation":{"type":"string","minLength":1,"maxLength":64},"key":{"type":"object","properties":{"mode":{"type":"string","enum":["none","chroma","luma"]},"color":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"similarity":{"type":"number","minimum":0,"maximum":1},"blend":{"type":"number","minimum":0,"maximum":1},"threshold":{"type":"number","minimum":0,"maximum":1}},"required":["mode"]}}},"force":{"default":false,"type":"boolean"}}},"RegisterCanvaTemplateBody":{"type":"object","properties":{"brandTemplateId":{"type":"string","minLength":1,"maxLength":200},"name":{"type":"string","minLength":1,"maxLength":200},"fieldMap":{"type":"object","propertyNames":{"type":"string","minLength":1,"maxLength":200},"additionalProperties":{"type":"string","minLength":1,"maxLength":64}},"preset":{"type":"string","enum":["lower-third","full-frame","vertical"]},"outputPreset":{"$ref":"#/components/schemas/CanvaOutputPresetInput"}},"required":["brandTemplateId","fieldMap"]},"CreateCanvaDesignBody":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":200},"preset":{"default":"full-frame","type":"string","enum":["lower-third","full-frame","vertical"]},"brandKitId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"brandTemplateId":{"type":"string","minLength":1,"maxLength":200}},"required":["title"]},"AutofillStrapBody":{"type":"object","properties":{"participantId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"templateIds":{"maxItems":20,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"force":{"default":false,"type":"boolean"}},"required":["participantId"]},"CreateBrandKitBody":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"teamId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"colors":{"type":"object","propertyNames":{"type":"string","pattern":"^[A-Za-z][A-Za-z0-9_]{0,63}$"},"additionalProperties":{"type":"string","pattern":"^(#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})|rgba?\\(\\s*\\d{1,3}(?:\\.\\d+)?\\s*[, ]\\s*\\d{1,3}(?:\\.\\d+)?\\s*[, ]\\s*\\d{1,3}(?:\\.\\d+)?\\s*(?:[,/]\\s*(?:0?\\.\\d+|[01](?:\\.0+)?|\\d{1,3}%)\\s*)?\\))$"}},"fonts":{"maxItems":8,"type":"array","items":{"type":"object","properties":{"family":{"type":"string","minLength":1,"maxLength":120},"role":{"type":"string","enum":["display","body"]},"assetId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"url":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"weight":{"anyOf":[{"type":"integer","minimum":1,"maximum":1000},{"type":"string","maxLength":20}]},"style":{"type":"string","enum":["normal","italic"]},"source":{"default":"upload","type":"string","enum":["upload","google","system","canva"]},"note":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]}},"required":["family","role"]}},"logoAssetId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"bugAssetId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"lowerThirdTemplates":{"maxItems":32,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9-]*$"},"name":{"type":"string","minLength":1,"maxLength":80},"variant":{"type":"string","enum":["bar","block","stack","minimal"]},"animation":{"default":{"in":"slide","out":"slide","durationMs":400},"type":"object","properties":{"in":{"default":"slide","type":"string","enum":["none","fade","slide","wipe","scale"]},"out":{"default":"slide","type":"string","enum":["none","fade","slide","wipe","scale"]},"durationMs":{"default":400,"type":"integer","minimum":0,"maximum":5000}}},"colors":{"default":{},"type":"object","properties":{"plate":{"type":"string","minLength":1,"maxLength":64},"foreground":{"type":"string","minLength":1,"maxLength":64},"muted":{"type":"string","minLength":1,"maxLength":64},"accent":{"type":"string","minLength":1,"maxLength":64},"accentForeground":{"type":"string","minLength":1,"maxLength":64}}},"layout":{"default":{},"type":"object","properties":{"height":{"type":"number","exclusiveMinimum":0,"maximum":1},"width":{"type":"number","exclusiveMinimum":0,"maximum":1},"paddingX":{"type":"number","minimum":0,"maximum":0.5},"accentWidth":{"type":"number","minimum":0,"maximum":0.5},"primarySize":{"type":"number","exclusiveMinimum":0,"maximum":1},"secondarySize":{"type":"number","exclusiveMinimum":0,"maximum":1},"radius":{"type":"number","minimum":0,"maximum":0.5},"align":{"type":"string","enum":["left","centre","right"]},"uppercaseSecondary":{"type":"boolean"}}}},"required":["id","name","variant"]}}},"required":["name"],"description":"A new brand kit. Everything but the name is optional — an empty kit renders in Tablecast's own palette until somebody fills it in."},"UpdateBrandKitBody":{"type":"object","properties":{"expectedRevision":{"type":"integer","minimum":1,"maximum":9007199254740991},"name":{"type":"string","minLength":1,"maxLength":120},"teamId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"colors":{"type":"object","propertyNames":{"type":"string","pattern":"^[A-Za-z][A-Za-z0-9_]{0,63}$"},"additionalProperties":{"type":"string","pattern":"^(#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})|rgba?\\(\\s*\\d{1,3}(?:\\.\\d+)?\\s*[, ]\\s*\\d{1,3}(?:\\.\\d+)?\\s*[, ]\\s*\\d{1,3}(?:\\.\\d+)?\\s*(?:[,/]\\s*(?:0?\\.\\d+|[01](?:\\.0+)?|\\d{1,3}%)\\s*)?\\))$"}},"fonts":{"maxItems":8,"type":"array","items":{"type":"object","properties":{"family":{"type":"string","minLength":1,"maxLength":120},"role":{"type":"string","enum":["display","body"]},"assetId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"url":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"weight":{"anyOf":[{"type":"integer","minimum":1,"maximum":1000},{"type":"string","maxLength":20}]},"style":{"type":"string","enum":["normal","italic"]},"source":{"default":"upload","type":"string","enum":["upload","google","system","canva"]},"note":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}]}},"required":["family","role"]}},"logoAssetId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"bugAssetId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"lowerThirdTemplates":{"maxItems":32,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9-]*$"},"name":{"type":"string","minLength":1,"maxLength":80},"variant":{"type":"string","enum":["bar","block","stack","minimal"]},"animation":{"default":{"in":"slide","out":"slide","durationMs":400},"type":"object","properties":{"in":{"default":"slide","type":"string","enum":["none","fade","slide","wipe","scale"]},"out":{"default":"slide","type":"string","enum":["none","fade","slide","wipe","scale"]},"durationMs":{"default":400,"type":"integer","minimum":0,"maximum":5000}}},"colors":{"default":{},"type":"object","properties":{"plate":{"type":"string","minLength":1,"maxLength":64},"foreground":{"type":"string","minLength":1,"maxLength":64},"muted":{"type":"string","minLength":1,"maxLength":64},"accent":{"type":"string","minLength":1,"maxLength":64},"accentForeground":{"type":"string","minLength":1,"maxLength":64}}},"layout":{"default":{},"type":"object","properties":{"height":{"type":"number","exclusiveMinimum":0,"maximum":1},"width":{"type":"number","exclusiveMinimum":0,"maximum":1},"paddingX":{"type":"number","minimum":0,"maximum":0.5},"accentWidth":{"type":"number","minimum":0,"maximum":0.5},"primarySize":{"type":"number","exclusiveMinimum":0,"maximum":1},"secondarySize":{"type":"number","exclusiveMinimum":0,"maximum":1},"radius":{"type":"number","minimum":0,"maximum":0.5},"align":{"type":"string","enum":["left","centre","right"]},"uppercaseSecondary":{"type":"boolean"}}}},"required":["id","name","variant"]}}},"required":["expectedRevision"],"description":"A patch. `expectedRevision` is the revision you loaded; a 409 means somebody else saved first and the detail says which revision the kit is on now."},"AddBrandFontBody":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"head":{"type":"string","minLength":4,"maxLength":1024,"pattern":"^[A-Za-z0-9+/=_-]+$"},"bytes":{"type":"integer","minimum":1,"maximum":5242880},"family":{"type":"string","minLength":1,"maxLength":120},"role":{"type":"string","enum":["display","body"]},"weight":{"anyOf":[{"type":"integer","minimum":1,"maximum":1000},{"type":"string","maxLength":20}]},"style":{"type":"string","enum":["normal","italic"]}},"required":["name","head","bytes","family","role"],"description":"A font file to store and attach. The kind is decided by sniffing `head`: woff2, woff, ttf and otf are accepted and nothing else, whatever the filename says. Replacing the face for a role replaces the entry rather than adding a second one."},"ImportCanvaBrandKitBody":{"type":"object","properties":{"canvaBrandKitId":{"type":"string","minLength":1,"maxLength":200},"brandKitId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"teamId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]}},"required":["canvaBrandKitId"],"description":"Import (or re-sync) a Canva brand kit. Colours and fonts are replaced, the org's own tokens and its lower-third templates are left alone — templates reference `$primary`, so they follow the new palette instead of being rewritten."},"XeroAuthorizeUrlBody":{"default":{},"type":"object","properties":{"redirectUri":{"type":"string","format":"uri"}}},"XeroConnectBody":{"type":"object","properties":{"code":{"type":"string","minLength":1},"tenantId":{"type":"string","minLength":1}}},"SetBillingProviderBody":{"type":"object","properties":{"provider":{"type":"string","enum":["stripe","xero"]}},"required":["provider"]},"XeroInvoiceRunBody":{"default":{},"type":"object","properties":{"period":{"type":"string","pattern":"^\\d{4}-(0[1-9]|1[0-2])$"},"dryRun":{"type":"boolean"}}},"CheckoutBody":{"type":"object","properties":{"planCode":{"type":"string","enum":["creator","studio","business"]},"interval":{"default":"month","type":"string","enum":["month","year"]},"seats":{"type":"integer","minimum":1,"maximum":500},"successUrl":{"type":"string","format":"uri"},"cancelUrl":{"type":"string","format":"uri"}},"required":["planCode"]},"PortalBody":{"default":{},"type":"object","properties":{"returnUrl":{"type":"string","format":"uri"}}},"PostChatBody":{"type":"object","properties":{"channel":{"default":"all","type":"string","maxLength":64,"description":"`all`, `producers`, or `private:<participantId>`. Defaults to `all`."},"body":{"type":"string","minLength":1,"maxLength":4000},"clockMs":{"anyOf":[{"type":"integer","minimum":0,"maximum":86400000},{"type":"null"}]}},"required":["body"]},"SendCueBody":{"type":"object","properties":{"cueId":{"description":"Optional client-minted id. Send the same id the data-channel cue carried so a recipient de-duplicates the two deliveries; re-sending the same id returns the existing cue.","anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"kind":{"default":"text","description":"`text` for a note, `countdown` for a wrap/cut clock. `seconds` is required for a countdown.","type":"string","enum":["text","countdown"]},"target":{"description":"`all` (the default) or a participant id. A cue to one person is seen by that person only.","type":"string","maxLength":64},"body":{"type":"string","minLength":1,"maxLength":200},"clockMs":{"anyOf":[{"type":"integer","minimum":0,"maximum":86400000},{"type":"null"}]},"seconds":{"description":"Countdown length. The deadline is `clockMs + seconds * 1000`, in session-clock ms.","anyOf":[{"type":"integer","minimum":1,"maximum":3600},{"type":"null"}]}},"required":["body"]},"PostChannelMessageBody":{"type":"object","properties":{"body":{"type":"string","minLength":1,"maxLength":4000},"mentions":{"description":"User ids or `@handle` tokens. Resolved against active org members; anything that does not resolve to exactly one of them is dropped rather than refused. `@handle`s in the body are picked up too, so a client with no picker can send nothing here.","maxItems":50,"type":"array","items":{"type":"string","minLength":1,"maxLength":64}},"attachments":{"maxItems":10,"type":"array","items":{"$ref":"#/components/schemas/ChannelAttachmentInput"}}},"required":["body"]},"EditChannelMessageBody":{"type":"object","properties":{"body":{"type":"string","minLength":1,"maxLength":4000},"mentions":{"description":"User ids or `@handle` tokens. Resolved against active org members; anything that does not resolve to exactly one of them is dropped rather than refused. `@handle`s in the body are picked up too, so a client with no picker can send nothing here.","maxItems":50,"type":"array","items":{"type":"string","minLength":1,"maxLength":64}}},"required":["body"]},"PinChannelMessageBody":{"type":"object","properties":{"pinned":{"type":"boolean"}},"required":["pinned"]},"MarkChannelReadBody":{"type":"object","properties":{"messageId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"readAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]}}},"CollabTicketBody":{"type":"object","properties":{"scope":{"description":"Which document to open. `session` (the default) is of this show and no other; `production` is the show's durable one — the run sheet that outlives every session. A guest asking for the production's document always gets a read-only ticket: they are in the room, not on the team.","type":"string","enum":["production","session"]}}},"CreateCommentThreadBody":{"type":"object","properties":{"targetKind":{"type":"string","enum":["media_asset","recording","render","graphic_asset","edit_doc"]},"targetId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"anchor":{"anyOf":[{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"time"},"timeMs":{"type":"integer","minimum":0,"maximum":36000000},"durationMs":{"type":"integer","minimum":0,"maximum":36000000}},"required":["kind","timeMs"]},{"type":"object","properties":{"kind":{"type":"string","const":"baseline"},"baselineMs":{"type":"integer","minimum":0,"maximum":36000000},"durationMs":{"type":"integer","minimum":0,"maximum":36000000},"trackRef":{"type":"string","maxLength":120}},"required":["kind","baselineMs"]},{"type":"object","properties":{"kind":{"type":"string","const":"region"},"x":{"type":"number","minimum":0,"maximum":1},"y":{"type":"number","minimum":0,"maximum":1},"w":{"type":"number","minimum":0,"maximum":1},"h":{"type":"number","minimum":0,"maximum":1},"timeMs":{"type":"integer","minimum":0,"maximum":36000000},"page":{"type":"integer","minimum":1,"maximum":10000}},"required":["kind","x","y","w","h"]}]},{"type":"null"}]},"body":{"type":"string","minLength":1,"maxLength":4000,"description":"What the comment says. Trimmed; an empty result is a 422."},"mentions":{"description":"Who is named. Each entry is a `users.id` **or** a bare `@handle` without the `@` — the same field shape the whiteboard writes (TAB-492), resolved by the same server-side resolver (TAB-494). A handle that matches two people resolves to neither, on purpose: a mention delivered to the wrong person is worse than one delivered to nobody.","maxItems":50,"type":"array","items":{"type":"string","minLength":1,"maxLength":200}},"attachments":{"description":"Asset ids and hints. Never URLs — a URL stored on a row has expired by the time it is read back.","maxItems":10,"type":"array","items":{"type":"object","properties":{"assetId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"name":{"type":"string","maxLength":300},"mimeType":{"type":"string","maxLength":120}},"required":["assetId"]}}},"required":["targetKind","targetId","body"]},"CreateCommentBody":{"type":"object","properties":{"body":{"type":"string","minLength":1,"maxLength":4000,"description":"What the comment says. Trimmed; an empty result is a 422."},"mentions":{"description":"Who is named. Each entry is a `users.id` **or** a bare `@handle` without the `@` — the same field shape the whiteboard writes (TAB-492), resolved by the same server-side resolver (TAB-494). A handle that matches two people resolves to neither, on purpose: a mention delivered to the wrong person is worse than one delivered to nobody.","maxItems":50,"type":"array","items":{"type":"string","minLength":1,"maxLength":200}},"attachments":{"description":"Asset ids and hints. Never URLs — a URL stored on a row has expired by the time it is read back.","maxItems":10,"type":"array","items":{"type":"object","properties":{"assetId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"name":{"type":"string","maxLength":300},"mimeType":{"type":"string","maxLength":120}},"required":["assetId"]}}},"required":["body"]},"UpdateCommentBody":{"type":"object","properties":{"body":{"type":"string","minLength":1,"maxLength":4000,"description":"What the comment says. Trimmed; an empty result is a 422."},"mentions":{"description":"Who is named. Each entry is a `users.id` **or** a bare `@handle` without the `@` — the same field shape the whiteboard writes (TAB-492), resolved by the same server-side resolver (TAB-494). A handle that matches two people resolves to neither, on purpose: a mention delivered to the wrong person is worse than one delivered to nobody.","maxItems":50,"type":"array","items":{"type":"string","minLength":1,"maxLength":200}},"attachments":{"description":"Asset ids and hints. Never URLs — a URL stored on a row has expired by the time it is read back.","maxItems":10,"type":"array","items":{"type":"object","properties":{"assetId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"name":{"type":"string","maxLength":300},"mimeType":{"type":"string","maxLength":120}},"required":["assetId"]}}},"required":["body"]},"TrackEgressRequest":{"oneOf":[{"type":"object","properties":{"op":{"type":"string","const":"start"},"atClockMs":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"T₀ in session-clock ms"},"commandId":{"description":"`record_commands.id`, when a record command triggered this","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"participants":{"maxItems":64,"type":"array","items":{"type":"object","properties":{"participantId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"livekitIdentity":{"type":"string","minLength":1},"displayName":{"type":"string","maxLength":200},"tracks":{"maxItems":16,"type":"array","items":{"type":"object","properties":{"sid":{"type":"string","minLength":1,"description":"LiveKit track sid"},"kind":{"type":"string","enum":["camera","camera_alt","screen","mic","scratch_audio","desk_multitrack","program","comms","ingest"]},"media":{"type":"string","enum":["audio","video"]},"name":{"description":"LiveKit track name; carries the `cam:<slug>` camera slug (TAB-636)","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]}},"required":["sid","kind","media"]}}},"required":["participantId","livekitIdentity","tracks"]}}},"required":["op","atClockMs","participants"]},{"type":"object","properties":{"op":{"type":"string","const":"stop"},"atClockMs":{"type":"integer","minimum":0,"maximum":9007199254740991},"reason":{"type":"string","maxLength":200}},"required":["op"]}]},"RecordCommandUpsert":{"type":"object","properties":{"id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"op":{"type":"string","enum":["arm","start","stop","marker"]},"scope":{"type":"string","enum":["session","location","participants"]},"locationId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"participantIds":{"default":[],"maxItems":256,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"atClockMs":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Session-clock ms the command is for — T₀ for a start"},"issuedAtClockMs":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"issuedBy":{"description":"`participants.id` of whoever pressed the button, or null","anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"source":{"type":"string","maxLength":32},"status":{"default":"pending","type":"string","enum":["pending","complete","timeout","aborted"]},"acks":{"default":{},"description":"`participants.id` → its answer. Per-participant timestamps, as the column promises.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"object","properties":{"at":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"ok":{"type":"boolean"},"latencyMs":{"anyOf":[{"type":"integer","minimum":0,"maximum":9007199254740991},{"type":"null"}]},"error":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]}},"required":["at","ok"]}},"pending":{"default":[],"description":"Asked and still silent","maxItems":256,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"label":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}]}},"required":["id","op","scope","atClockMs","issuedAtClockMs","source"]},"CoordinatorEvent":{"type":"object","properties":{"event":{"type":"string","enum":["coordinator.started","coordinator.stopped"]},"coordinatorId":{"type":"string","minLength":1,"maxLength":200,"description":"Stable id of the coordinator process/task"},"at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$","description":"The coordinator's wall clock at the transition"},"reason":{"description":"`session.start`, `session.end`, `sigterm`, …","type":"string","maxLength":200},"uptimeMs":{"description":"How long it held the session; stops only","type":"integer","minimum":0,"maximum":9007199254740991}},"required":["event","coordinatorId","at"],"description":"One coordinator lifecycle transition, as reported by the process it happened in."},"WriteCollabDocumentBody":{"type":"object","properties":{"snapshot":{"type":"string","maxLength":8388608},"stateVector":{"type":"string","maxLength":8388608},"version":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"Must exceed the stored version, or this is a 409."},"updatedBySubjectId":{"description":"`users.id` or `participants.id` of whoever last touched it, when known.","anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]}},"required":["snapshot","stateVector","version"]},"SrtSettingsBody":{"type":"object","properties":{"mode":{"type":"string","enum":["caller","listener"]},"latencyMs":{"type":"integer","minimum":20,"maximum":8000},"passphrase":{"anyOf":[{"type":"string","minLength":10,"maxLength":79},{"type":"null"}]},"streamId":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}]}}},"HlsSettingsBody":{"type":"object","properties":{"segmentSeconds":{"type":"integer","minimum":1,"maximum":10},"dvrWindowSeconds":{"type":"integer","minimum":0,"maximum":14400}}},"CreateDestinationBody":{"type":"object","properties":{"provider":{"type":"string","enum":["youtube","linkedin","twitch","facebook","x","kick","custom_rtmp","custom_srt","whep","hls"]},"name":{"type":"string","minLength":1,"maxLength":120},"url":{"anyOf":[{"type":"string","minLength":1,"maxLength":2048},{"type":"null"}]},"streamKey":{"anyOf":[{"type":"string","minLength":1,"maxLength":512},{"type":"null"}]},"outputPreset":{"type":"object","properties":{"width":{"type":"integer","minimum":160,"maximum":3840},"height":{"type":"integer","minimum":120,"maximum":2160},"fps":{"type":"integer","minimum":1,"maximum":60},"videoBitrateKbps":{"type":"integer","minimum":100,"maximum":51000},"audioBitrateKbps":{"type":"integer","minimum":32,"maximum":320},"latencyMode":{"type":"string","maxLength":32}}},"presetKey":{"anyOf":[{"type":"string","enum":["1080p60","1080p30","720p60","720p30","540p30","vertical-1080p30","vertical-720p30"]},{"type":"null"}]},"srt":{"$ref":"#/components/schemas/SrtSettingsBody"},"hls":{"$ref":"#/components/schemas/HlsSettingsBody"},"enabled":{"type":"boolean"},"externalChannelId":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}]},"oauthIntegrationId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]}},"required":["provider","name"]},"UpdateDestinationBody":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"url":{"anyOf":[{"type":"string","minLength":1,"maxLength":2048},{"type":"null"}]},"streamKey":{"anyOf":[{"type":"string","minLength":1,"maxLength":512},{"type":"null"}]},"outputPreset":{"type":"object","properties":{"width":{"type":"integer","minimum":160,"maximum":3840},"height":{"type":"integer","minimum":120,"maximum":2160},"fps":{"type":"integer","minimum":1,"maximum":60},"videoBitrateKbps":{"type":"integer","minimum":100,"maximum":51000},"audioBitrateKbps":{"type":"integer","minimum":32,"maximum":320},"latencyMode":{"type":"string","maxLength":32}}},"presetKey":{"anyOf":[{"type":"string","enum":["1080p60","1080p30","720p60","720p30","540p30","vertical-1080p30","vertical-720p30"]},{"type":"null"}]},"srt":{"$ref":"#/components/schemas/SrtSettingsBody"},"hls":{"$ref":"#/components/schemas/HlsSettingsBody"},"enabled":{"type":"boolean"},"externalChannelId":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}]},"oauthIntegrationId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]}},"description":"The provider is immutable — delete and re-add to change it."},"CreateCalendarFeedToken":{"type":"object","properties":{"label":{"description":"What to call this subscription in settings — \"my laptop\". Never a credential.","type":"string","minLength":1,"maxLength":120}}},"EmbedSettingsBody":{"type":"object","properties":{"enabled":{"type":"boolean"},"tokenTtlSeconds":{"type":"integer","minimum":60,"maximum":86400},"label":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}]}}},"AuthorizeUrlBody":{"type":"object","properties":{"redirectUri":{"type":"string","format":"uri"}},"required":["redirectUri"]},"ConnectIntegrationBody":{"type":"object","properties":{"code":{"type":"string","minLength":1,"maxLength":2048},"redirectUri":{"type":"string","format":"uri"},"state":{"type":"string","minLength":1,"maxLength":200}},"required":["code","redirectUri","state"]},"ConnectorAuthorizeUrlBody":{"type":"object","properties":{"redirectUri":{"type":"string","format":"uri"}},"required":["redirectUri"]},"ConnectConnectorBody":{"type":"object","properties":{"code":{"type":"string","minLength":1,"maxLength":4096},"redirectUri":{"type":"string","format":"uri"},"state":{"type":"string","minLength":1,"maxLength":200},"codeVerifier":{"type":"string","minLength":1,"maxLength":256}},"required":["code","redirectUri","state"]},"StartImpersonationBody":{"type":"object","properties":{"targetUserId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"reason":{"type":"string","minLength":8,"maxLength":500},"minutes":{"type":"integer","minimum":1,"maximum":60}},"required":["targetUserId","orgId","reason"],"description":"Opens an audited window in which a platform admin may read one organisation's data."},"AppendEdlBody":{"type":"object","properties":{"events":{"maxItems":500,"type":"array","items":{"$ref":"#/components/schemas/EdlEventRecordInput"}},"coordinatorId":{"type":"string","maxLength":200}},"required":["events"],"description":"A batch of accepted ops. Contiguous by `seq`, and idempotent — a replayed batch inserts nothing."},"WriteProductionStateBody":{"type":"object","properties":{"version":{"type":"integer","minimum":0,"maximum":9007199254740991},"state":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"updatedByParticipantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]}},"required":["version","state"]},"RecordEgressRestartBody":{"type":"object","properties":{"previousEgressJobId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"livekitEgressId":{"type":"string","minLength":1,"maxLength":200},"status":{"type":"string","enum":["starting","active"]},"reason":{"type":"string","enum":["egress_failed","egress_ended_early","heartbeat_silent","manual"]},"attempt":{"type":"integer","minimum":1,"maximum":9007199254740991},"startedAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"detail":{"description":"Never a stream URL — those carry live keys","type":"string","maxLength":500}},"required":["previousEgressJobId","livekitEgressId","status","reason","attempt","startedAt"]},"RecordDestinationRetryBody":{"type":"object","properties":{"runId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"destinationId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"attempt":{"type":"integer","minimum":1,"maximum":9007199254740991},"reason":{"type":"string","minLength":1,"maxLength":200},"scope":{"type":"string","enum":["destination","group"],"description":"`destination`: only this leg broke. `group`: its whole composite was restarted."},"detail":{"description":"Never a stream URL — those carry live keys","type":"string","maxLength":500}},"required":["runId","destinationId","attempt","reason","scope"]},"CreateSessionInviteBody":{"type":"object","properties":{"role":{"default":"guest","type":"string","enum":["host","guest","producer","viewer","camera","prompter"]},"email":{"anyOf":[{"type":"string","maxLength":320,"format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},{"type":"null"}]},"displayNameHint":{"anyOf":[{"type":"string","minLength":1,"maxLength":80},{"type":"null"}]},"locale":{"description":"TAB-564 — the language whoever redeems this link reads the join flow in, unless they say otherwise with `?lang=` or have a profile of their own. The only hint available for an *open* invite, which has no named recipient to carry one.","anyOf":[{"type":"string","enum":["en-AU","en-NZ","en-GB","en-US","es","pt-BR"]},{"type":"null"}]},"locationId":{"description":"The physical room this person will be in (plan §8.2).","anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"singleUse":{"description":"Whether one redemption uses the link up. Defaults to true. Sending this as `true` alongside a `maxUses` above 1 is a contradiction and is refused with 422.","type":"boolean"},"maxUses":{"description":"How many times the link may be redeemed. Defaults to 1; above 1 implies `singleUse: false`.","type":"integer","minimum":1,"maximum":100},"expiresInHours":{"default":336,"type":"number","minimum":1,"maximum":4320}}},"ClientCaps":{"type":"object","properties":{"webcodecs":{"type":"boolean"},"opfs":{"type":"boolean"},"midi":{"type":"boolean"},"native":{"type":"boolean"},"ua":{"type":"string","maxLength":400}},"description":"What the joining browser can do; decides the recording path (plan §6)."},"JoinByInviteBody":{"type":"object","properties":{"displayName":{"description":"Falls back to the invite's hint.","type":"string","minLength":1,"maxLength":80},"clientCaps":{"$ref":"#/components/schemas/ClientCaps"},"deviceInfo":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"claimEmail":{"description":"Where this guest wants their ISO sent later (plan §11). Never shown in the room.","anyOf":[{"type":"string","maxLength":320,"format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},{"type":"null"}]}}},"JoinByCodeBody":{"type":"object","properties":{"code":{"type":"string","minLength":8,"maxLength":32,"description":"The short join code, however it was typed. Case and separators do not matter."},"displayName":{"description":"Falls back to the invite's hint.","type":"string","minLength":1,"maxLength":80},"clientCaps":{"$ref":"#/components/schemas/ClientCaps"},"deviceInfo":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"claimEmail":{"description":"Where this guest wants their ISO sent later (plan §11). Never shown in the room.","anyOf":[{"type":"string","maxLength":320,"format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},{"type":"null"}]}},"required":["code"]},"JoinCodePreflightBody":{"type":"object","properties":{"code":{"type":"string","minLength":8,"maxLength":32,"description":"The short join code, however it was typed. Case and separators do not matter."}},"required":["code"]},"JoinAsMemberBody":{"type":"object","properties":{"role":{"default":"host","type":"string","enum":["director","producer","host","viewer","camera","prompter"]},"displayName":{"type":"string","minLength":1,"maxLength":80},"locationId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"clientCaps":{"$ref":"#/components/schemas/ClientCaps"},"deviceInfo":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"required":["displayName"]},"AddInviteRecipientsBody":{"type":"object","properties":{"recipients":{"description":"The people to add. A pasted panel list arrives with duplicates; they are collapsed.","maxItems":50,"type":"array","items":{"type":"object","properties":{"email":{"type":"string","maxLength":320,"format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"displayName":{"anyOf":[{"type":"string","minLength":1,"maxLength":80},{"type":"null"}]},"locale":{"description":"Overrides the call's default locale for this person.","anyOf":[{"type":"string","enum":["en-AU","en-NZ","en-GB","en-US","es","pt-BR"]},{"type":"null"}]},"role":{"description":"Overrides the call's default role for this person.","type":"string","enum":["host","guest","producer","viewer","camera","prompter"]},"userId":{"description":"A colleague, when the producer picked one. Refused with 422 unless they are an active member.","anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]}},"required":["email"]}},"fromRecipientIds":{"description":"Roster entries to copy onto this episode — the one-click 'add the usual co-host'. Session scope only; each id must already be on this show's roster.","maxItems":50,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"role":{"default":"guest","description":"The role for entries that do not name one.","type":"string","enum":["host","guest","producer","viewer","camera","prompter"]},"locale":{"description":"TAB-564 — the language for entries that do not name one. Absent leaves each recipient's locale unset, which resolves to the invite's, then the organisation's default.","anyOf":[{"type":"string","enum":["en-AU","en-NZ","en-GB","en-US","es","pt-BR"]},{"type":"null"}]},"admission":{"type":"string","enum":["auto","waiting_room"]},"locationId":{"description":"The physical room these people will be in (plan §8.2).","anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"expiresInHours":{"type":"number","minimum":1,"maximum":4320}}},"UpdateInviteRecipientBody":{"type":"object","properties":{"displayName":{"anyOf":[{"type":"string","minLength":1,"maxLength":80},{"type":"null"}]},"locale":{"description":"TAB-564 — change the language their next invitation is written in. Explicit null clears it, putting them back on the invite's locale and then the organisation's default.","anyOf":[{"type":"string","enum":["en-AU","en-NZ","en-GB","en-US","es","pt-BR"]},{"type":"null"}]},"role":{"description":"Moves them to the invite for that role and **rotates their token**: the invite carries the role, so a link that quietly started granting something else would be worse than one that stopped working.","type":"string","enum":["host","guest","producer","viewer","camera","prompter"]},"status":{"description":"Mark somebody as not coming. The only status a client may set — every other step is a fact the system observed. Refused for anybody who has already arrived.","type":"string","const":"no_show"}}},"CreateLocationBody":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"mixMinus":{"default":true,"type":"boolean"},"isRemoteSingle":{"default":false,"type":"boolean"},"audioSourceParticipantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"monitorParticipantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"chirp":{"default":false,"description":"Mint an ultrasonic token so this room's monitor can announce itself (TAB-356).","type":"boolean"}},"required":["name"]},"UpdateLocationBody":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"mixMinus":{"type":"boolean"},"audioSourceParticipantId":{"description":"`null` clears the nomination; omitting the field leaves it alone.","anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"monitorParticipantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"chirp":{"type":"boolean"}}},"AssignParticipantBody":{"type":"object","properties":{"participantId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"locationId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}],"description":"`null` takes them out of every room."}},"required":["participantId","locationId"]},"ReportSignalBody":{"type":"object","properties":{"participantId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"fingerprint":{"description":"Salted SHA-256 digests computed on the device. The API never sees an IP or a BSSID.","type":"object","properties":{"ipHash":{"type":"string","minLength":64,"maxLength":64},"bssidHash":{"type":"string","minLength":64,"maxLength":64},"reportedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}}},"chirpToken":{"description":"An ultrasonic token this device heard.","type":"string","maxLength":32}},"required":["participantId"]},"ConfirmSuggestionBody":{"type":"object","properties":{"locationId":{"description":"Required when the suggestion names no room — every double-mic finding.","anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"confirmedByParticipantId":{"description":"The guest confirming for themselves; absent means the acting user confirmed.","anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"includePeer":{"default":false,"type":"boolean"}}},"GoLiveBody":{"type":"object","properties":{"destinationIds":{"minItems":1,"maxItems":20,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}}},"required":["destinationIds"]},"LiveDestinationBody":{"type":"object","properties":{"destinationId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"required":["destinationId"]},"ControlOpsBody":{"type":"object","properties":{"ops":{"minItems":1,"maxItems":32,"type":"array","items":{"type":"object","properties":{"op":{"type":"string","enum":["add","remove","replace","move","copy","test"]},"path":{"type":"string","minLength":1,"maxLength":512},"from":{"type":"string","minLength":1,"maxLength":512},"value":{}},"required":["op","path"]}},"baseVersion":{"type":"integer","minimum":0,"maximum":9007199254740991},"source":{"type":"string","enum":["ui","midi","companion","osc","api","mcp"]},"label":{"type":"string","minLength":1,"maxLength":120},"opId":{"type":"string","minLength":1,"maxLength":64}},"required":["ops","baseVersion"],"description":"A batch of production-state ops from a control surface."},"MarkNotificationsReadBody":{"type":"object","properties":{"ids":{"maxItems":100,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"all":{"type":"boolean"},"orgId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]}}},"CreateOrgBody":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"slug":{"type":"string","minLength":1,"maxLength":60},"kind":{"default":"studio","type":"string","enum":["personal","studio"]},"homeRegion":{"type":"string","minLength":2,"maxLength":32}},"required":["name"]},"UpdateOrgSettingsBody":{"type":"object","properties":{"defaultBrandKitId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"defaultLocale":{"type":"string","enum":["en-AU","en-NZ","en-GB","en-US","es","pt-BR"],"description":"TAB-564 — the house UI language for this organisation."},"customDomain":{"anyOf":[{"type":"string","maxLength":253},{"type":"null"}]},"emailSender":{"anyOf":[{"type":"string","maxLength":320},{"type":"null"}]},"security":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"recordingDefaults":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"privacy":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"billing":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"featureFlags":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"boolean"}}},"additionalProperties":false,"description":"PATCH semantics: absent keys are left alone; the JSONB sections are merged one level deep. Strict: an unrecognised key is a 422, not a silently ignored one (TAB-215)."},"CreateInvitationBody":{"type":"object","properties":{"email":{"type":"string","maxLength":320,"format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"role":{"default":"org_member","type":"string","enum":["org_owner","org_admin","org_billing","org_member","org_guest"]},"teamIds":{"default":[],"maxItems":50,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"expiresInHours":{"type":"integer","minimum":1,"maximum":2160}},"required":["email"]},"ResendInvitationBody":{"type":"object","properties":{"expiresInHours":{"type":"integer","minimum":1,"maximum":2160}},"additionalProperties":false,"description":"Re-issues the invitation with a **new** token and a fresh expiry, and emails it again. The previous link stops working — the plaintext of the original is not stored anywhere, so there is nothing else re-sending could mean."},"UpdateMemberBody":{"type":"object","properties":{"role":{"type":"string","enum":["org_owner","org_admin","org_billing","org_member","org_guest"]}},"required":["role"]},"MemberAvatarsBody":{"type":"object","properties":{"userIds":{"minItems":1,"maxItems":100,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"description":"Whose avatars to resolve. Ids that are not active members here are ignored, not refused."}},"required":["userIds"]},"CreateRoleBody":{"type":"object","properties":{"scope":{"default":"organization","type":"string","enum":["organization","team","production","session"]},"key":{"type":"string","minLength":2,"maxLength":40},"name":{"type":"string","minLength":1,"maxLength":80},"description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"permissions":{"default":[],"maxItems":64,"type":"array","items":{"type":"string","minLength":3,"maxLength":64,"pattern":"^[a-z][a-z0-9_]*:[a-z][a-z0-9_]*$"}}},"required":["key","name"],"additionalProperties":false,"description":"An organisation's own role. `scope` may not be `platform`, and `permissions` may not contain a `platform:*` key — both are refused by the database as well as here."},"UpdateRoleBody":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"permissions":{"maxItems":64,"type":"array","items":{"type":"string","minLength":3,"maxLength":64,"pattern":"^[a-z][a-z0-9_]*:[a-z][a-z0-9_]*$"}}},"additionalProperties":false,"description":"PATCH semantics for the two text fields; `permissions` replaces the whole list rather than merging."},"AssignRoleBody":{"type":"object","properties":{"principalKind":{"default":"user","type":"string","enum":["user","api_key","device","guest"]},"principalId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"scopeId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"expiresAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["principalId"],"additionalProperties":false},"RecordingPolicySettings":{"type":"object","properties":{"resolution":{"description":"`1080p`, `1440p`, `2160p`","type":"string"},"fps":{"type":"integer","minimum":1,"maximum":120},"codec":{"description":"`h264`, `hevc`, `av1`, `prores`","type":"string"},"wav":{"description":"Record a 48 kHz PCM companion alongside Opus","type":"boolean"},"serverBackup":{"description":"Run LiveKit track egress as a recording safety net","type":"boolean"}},"description":"Capture defaults for a show. Inherited by every session created under it."},"ProductionLanguagesSettings":{"type":"object","properties":{"source":{"maxItems":10,"type":"array","items":{"type":"string","pattern":"^[A-Za-z]{2,3}(-[A-Za-z]{4})?(-([A-Za-z]{2}|[0-9]{3}))?$","description":"BCP-47 language tag, e.g. `en-NZ`, `mi-NZ`."},"description":"Languages spoken on this show. **Empty means detect anything.** One tag is an assertion — the transcriber is told rather than asked. Two or more restrict language identification to that set, which is what makes a bilingual show's detection accurate. Suggested tags: en-AU, mi-NZ, en-NZ, en-GB, en-US, es-ES."},"targets":{"maxItems":10,"type":"array","items":{"type":"string","pattern":"^[A-Za-z]{2,3}(-[A-Za-z]{4})?(-([A-Za-z]{2}|[0-9]{3}))?$","description":"BCP-47 language tag, e.g. `en-NZ`, `mi-NZ`."},"description":"Languages this show's transcripts are translated into by default (TAB-561)."}},"description":"The languages a show is recorded in, and the languages it is translated into."},"CreateProduction":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"slug":{"description":"Derived from the name when omitted. Lowercase letters, digits and single hyphens.","type":"string","minLength":1,"maxLength":64},"kind":{"type":"string","enum":["podcast","show","webinar","event","other"]},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]},"coverAssetId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"brandKitId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"homeRegion":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}]},"recordingPolicy":{"$ref":"#/components/schemas/RecordingPolicySettings"},"languages":{"description":"Omitted, the show is seeded with the organisation's default locale as its single source language. Send `{ \"source\": [] }` for unrestricted language identification.","$ref":"#/components/schemas/ProductionLanguagesSettings"},"settings":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"required":["name"]},"UpdateProduction":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"slug":{"type":"string","minLength":1,"maxLength":64},"kind":{"type":"string","enum":["podcast","show","webinar","event","other"]},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]},"coverAssetId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"brandKitId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"defaultSceneId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"homeRegion":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}]},"recordingPolicy":{"$ref":"#/components/schemas/RecordingPolicySettings"},"languages":{"description":"Replaces the document wholesale; a partial patch of an allow-list is ambiguous.","$ref":"#/components/schemas/ProductionLanguagesSettings"},"settings":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}}},"UpsertProductionMember":{"type":"object","properties":{"role":{"type":"string","enum":["team_lead","producer","director","host","member","viewer"]}},"required":["role"]},"CreateSceneBody":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"layout":{"$ref":"#/components/schemas/SceneLayoutInput"},"graphics":{"maxItems":32,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64},"kind":{"type":"string","minLength":1,"maxLength":32}},"required":["id","kind"],"additionalProperties":{}}},"audio":{"type":"object","properties":{"gains":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"number","minimum":-60,"maximum":12}},"muted":{"type":"array","items":{"type":"string"}},"afv":{"type":"array","items":{"type":"string"}}},"additionalProperties":{}},"hotkey":{"anyOf":[{"type":"string","minLength":1,"maxLength":16},{"type":"null"}]},"thumbnailAssetId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]}},"required":["name","layout"]},"UpdateSceneBody":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"layout":{"$ref":"#/components/schemas/SceneLayoutInput"},"graphics":{"maxItems":32,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64},"kind":{"type":"string","minLength":1,"maxLength":32}},"required":["id","kind"],"additionalProperties":{}}},"audio":{"type":"object","properties":{"gains":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"number","minimum":-60,"maximum":12}},"muted":{"type":"array","items":{"type":"string"}},"afv":{"type":"array","items":{"type":"string"}}},"additionalProperties":{}},"hotkey":{"anyOf":[{"type":"string","minLength":1,"maxLength":16},{"type":"null"}]},"thumbnailAssetId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]}}},"ReorderScenesBody":{"type":"object","properties":{"sceneIds":{"minItems":1,"maxItems":200,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}}},"required":["sceneIds"]},"RecordingPatch":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":200},"episodeNo":{"anyOf":[{"type":"integer","minimum":0,"maximum":9007199254740991},{"type":"null"}]},"tags":{"maxItems":24,"type":"array","items":{"type":"string","minLength":1,"maxLength":48}},"archived":{"type":"boolean"},"deleted":{"type":"boolean","const":false}},"description":"A partial update. Every field is independent; an empty body is refused rather than silently doing nothing."},"RecordingShareCreate":{"type":"object","properties":{"scope":{"default":"all","type":"string","enum":["all","tracks","renders"]},"itemIds":{"default":[],"maxItems":100,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"expiresInHours":{"type":"integer","minimum":1,"maximum":8760},"expiresAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"maxViews":{"anyOf":[{"type":"integer","minimum":1,"maximum":9007199254740991},{"type":"null"}]},"passcode":{"anyOf":[{"type":"string","minLength":4,"maxLength":128},{"type":"null"}]}}},"SharedRecordingUnlock":{"type":"object","properties":{"passcode":{"type":"string","minLength":1,"maxLength":128}},"required":["passcode"]},"PlaybackTicketRequest":{"type":"object","properties":{"renditionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"fromIndex":{"default":0,"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["renditionId"],"description":"Which rung to play, and where in it to start."},"RegenerateRenditionsRequest":{"type":"object","properties":{"trackId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"renderId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"description":"Which master to re-encode. Neither field means every recording in the session."},"RenditionAssetRequest":{"type":"object","properties":{"renditionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"kind":{"type":"string","enum":["peaks","sprites","sprite-sheet"]},"sheetIndex":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["renditionId","kind"],"description":"Which rendition asset to presign. Never a storage key: the server resolves it from the rendition row and, for a sprite sheet, the same deterministic key the pipeline wrote it with — a caller names *what*, never *where*."},"TranslateTranscriptsBody":{"type":"object","properties":{"languages":{"maxItems":10,"type":"array","items":{"type":"string","minLength":2,"maxLength":35}},"trackId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"force":{"type":"boolean"}}},"SegmentEditBody":{"type":"object","properties":{"text":{"type":"string","minLength":1,"maxLength":4000}},"required":["text"]},"ResolveReviewBody":{"type":"object","properties":{"reason":{"type":"string","enum":["unmatched-pin","language-unexpected","low-confidence"]}},"required":["reason"]},"ClipCropKeyframe":{"type":"object","properties":{"atMs":{"type":"number","minimum":0},"cx":{"type":"number","minimum":0,"maximum":1},"cy":{"type":"number","minimum":0,"maximum":1},"w":{"type":"number","minimum":0,"maximum":1}},"required":["atMs","cx","cy"]},"ClaimTokenBody":{"type":"object","properties":{"token":{"type":"string","minLength":34,"maxLength":200}},"required":["token"],"additionalProperties":false},"CreateClaimBody":{"type":"object","properties":{"participantId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"email":{"anyOf":[{"type":"string","maxLength":320,"format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},{"type":"null"}]},"expiresInDays":{"type":"integer","minimum":1,"maximum":30}},"required":["participantId"],"additionalProperties":false},"SeedEditProjectBody":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"note":{"type":"string","maxLength":500}},"description":"Seeds a new project from the session's EDL, ISOs and proxies."},"SaveEditVersionBody":{"type":"object","properties":{"doc":{"type":"object","properties":{"kind":{"type":"string","const":"tablecast.edit"},"version":{"type":"number","enum":[1,2]},"sessionId":{"type":"string"},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["kind","version","sessionId","revision"],"additionalProperties":{}},"note":{"type":"string","maxLength":500},"expectedVersion":{"type":"integer","minimum":0,"maximum":9007199254740991},"clear":{"type":"array","items":{"type":"string","enum":["program","supers","switchTrack"]}}},"required":["doc"]},"BundleCreate":{"type":"object","properties":{"all":{"type":"boolean"},"trackIds":{"maxItems":500,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"includeRenders":{"default":false,"type":"boolean"},"includeTranscripts":{"default":false,"type":"boolean"}}},"MoveRoom":{"type":"object","properties":{"room":{"type":"string","enum":["planning","record","studio"],"description":"Where to move the whole cast: the planning room, the record room, or the studio."}},"required":["room"],"description":"Move the whole cast into a room (TAB-633). A navigation broadcast — it changes no lifecycle."},"CreateSession":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":300},"episodeNo":{"anyOf":[{"type":"integer","minimum":0,"maximum":9007199254740991},{"type":"null"}]},"scheduledAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"scheduledDurationMin":{"anyOf":[{"type":"integer","minimum":1,"maximum":1440},{"type":"null"}]},"scheduledTimezone":{"anyOf":[{"type":"string","minLength":1,"maxLength":64,"description":"IANA zone name, e.g. `Australia/Sydney`."},{"type":"null"}]},"reminderOffsetsMinutes":{"maxItems":10,"type":"array","items":{"type":"integer","minimum":1,"maximum":43200},"description":"Minutes before `scheduledAt`. Defaults to 1440, 60, 10. Duplicates are collapsed and offsets already in the past when the session is saved are reported rather than created — scheduling a show an hour from now genuinely has no 24-hour reminder."},"recordingPolicy":{"description":"Defaults to the production's policy.","$ref":"#/components/schemas/RecordingPolicySettings"},"mode":{"description":"Studio or Record room. Defaults to `record`.","type":"string","enum":["studio","record"]},"calendarEventRef":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"mediaRegion":{"description":"Overrides the derived region. The derivation is the normal path.","anyOf":[{"type":"string","maxLength":64},{"type":"null"}]}},"required":["title"]},"UpdateSession":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":300},"episodeNo":{"anyOf":[{"type":"integer","minimum":0,"maximum":9007199254740991},{"type":"null"}]},"scheduledAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"scheduledDurationMin":{"anyOf":[{"type":"integer","minimum":1,"maximum":1440},{"type":"null"}]},"scheduledTimezone":{"anyOf":[{"type":"string","minLength":1,"maxLength":64,"description":"IANA zone name, e.g. `Australia/Sydney`."},{"type":"null"}]},"reminderOffsetsMinutes":{"maxItems":10,"type":"array","items":{"type":"integer","minimum":1,"maximum":43200},"description":"Minutes before `scheduledAt`. Defaults to 1440, 60, 10. Duplicates are collapsed and offsets already in the past when the session is saved are reported rather than created — scheduling a show an hour from now genuinely has no 24-hour reminder."},"recordingPolicy":{"$ref":"#/components/schemas/RecordingPolicySettings"},"calendarEventRef":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"autoAdmit":{"description":"Off holds every guest in the lobby until a producer admits them. Turning it back on admits everybody currently waiting.","type":"boolean"}},"description":"Schedulable fields only. Status, media region, the room and the clock epoch belong to the lifecycle endpoints — a PATCH that moved them could desynchronise the row from the live room.\n\nMoving `scheduledAt` **moves the pending reminders with it** and marks every connected calendar's event for re-sync. Reminders that have already gone out are left alone: a show moved later must not send a second 24-hour reminder about a time the first one already gave."},"IntegrityWaiver":{"type":"object","properties":{"reason":{"type":"string","minLength":3,"maxLength":500,"description":"Why the show is ending with unverified tracks."}},"required":["reason"]},"EndSession":{"anyOf":[{"type":"object","properties":{"reason":{"description":"Recorded with the coordinator stop; defaults to `session.end`.","type":"string","maxLength":200},"waiveIntegrity":{"description":"Deprecated in favour of `waiver`, which carries the reason. Ends the show despite unverified tracks; attributed to the caller and audited with no reason recorded.","type":"boolean"},"waiver":{"description":"Ends the show despite unverified tracks (TAB-128). The reason is required, written to the session's lifecycle note and to an `audit_events` row alongside the tracks that were outstanding at the moment it was given.","$ref":"#/components/schemas/IntegrityWaiver"}}},{"type":"null"}]},"CancelSession":{"anyOf":[{"type":"object","properties":{"reason":{"type":"string","maxLength":200}}},{"type":"null"}]},"RecordControl":{"type":"object","properties":{"op":{"type":"string","enum":["arm","start","stop","marker","rearm"]},"scope":{"default":"session","type":"string","enum":["session","location","participants"]},"locationId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"participantIds":{"maxItems":64,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"atMs":{"description":"Absolute T₀ in session-clock ms. Prefer `leadMs`.","type":"integer","minimum":0,"maximum":9007199254740991},"leadMs":{"description":"`start`: T₀ is now + this. Default 2 s.","type":"integer","minimum":0,"maximum":60000},"allowUnacked":{"description":"`start`: the devices the producer is explicitly rolling without. A listed exception set, not a force flag — the EDL records which camera was left behind.","maxItems":64,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"label":{"description":"`marker`: what the marker is.","type":"string","maxLength":200},"reason":{"type":"string","maxLength":200},"take":{"description":"`arm`: take label the resulting tracks are grouped under.","type":"string","maxLength":128},"source":{"default":"ui","description":"Which control surface pressed it. Lands on the EDL event.","type":"string","enum":["ui","midi","companion","osc","mcp","api"]}},"required":["op"]},"ShareRecipientDraft":{"type":"object","properties":{"email":{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"displayName":{"anyOf":[{"type":"string","minLength":1,"maxLength":120},{"type":"null"}]},"locale":{"description":"TAB-564 — the language this person's share email is written in. A share recipient is very often not a member, so there is no profile locale to read and the sharer's own language would be the wrong answer. Absent falls through to the organisation's default.","anyOf":[{"type":"string","enum":["en-AU","en-NZ","en-GB","en-US","es","pt-BR"]},{"type":"null"}]}},"required":["email"],"description":"One person on a private share. Re-adding an address that is already on the share **rotates** that person's token and re-sends rather than creating a second row."},"CreateShareBody":{"type":"object","properties":{"targetKind":{"type":"string","enum":["recording","render","media_asset","edit_doc"]},"targetId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"visibility":{"type":"string","enum":["public","private"]},"permission":{"default":"view","type":"string","enum":["view","comment"]},"allowDownload":{"default":false,"description":"Whether the viewer may pull the original file down as well as watch the proxy.","type":"boolean"},"expiresAt":{"description":"When the link stops working. Absent or null is no expiry.","anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"recipients":{"description":"Private shares only. Each person gets a token of their own and a branded email with their link. Fifty is generous enough that nobody meets it by accident and mean enough that a paste of an address book is refused before it becomes fifty emails.","maxItems":50,"type":"array","items":{"$ref":"#/components/schemas/ShareRecipientDraft"}}},"required":["targetKind","targetId","visibility"],"description":"Making a link. A public share is a **toggle**: creating a second one while a live public share exists on the same target returns the existing one rather than a second URL nobody can tell apart from the first."},"UpdateShareBody":{"type":"object","properties":{"permission":{"type":"string","enum":["view","comment"]},"allowDownload":{"type":"boolean"},"expiresAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]}},"description":"Changing a live link. Not `visibility` and not `targetKind`/`targetId`: both would change what an already-circulated URL means, which is a revoke and a new share rather than an edit."},"AddShareRecipientsBody":{"type":"object","properties":{"recipients":{"minItems":1,"maxItems":50,"type":"array","items":{"$ref":"#/components/schemas/ShareRecipientDraft"}}},"required":["recipients"],"description":"Adding people to a private share."},"CreateTeamBody":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"slug":{"type":"string","minLength":1,"maxLength":60},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]},"visibility":{"default":"closed","type":"string","enum":["open","closed","secret"]},"defaultRoleForOrgMembers":{"anyOf":[{"type":"string","enum":["team_lead","producer","director","host","member","viewer"]},{"type":"null"}]}},"required":["name"]},"UpdateTeamBody":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"slug":{"type":"string","minLength":1,"maxLength":60},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]},"visibility":{"type":"string","enum":["open","closed","secret"]},"defaultRoleForOrgMembers":{"anyOf":[{"type":"string","enum":["team_lead","producer","director","host","member","viewer"]},{"type":"null"}]},"archived":{"type":"boolean"}}},"UpdateTeamSettingsBody":{"type":"object","properties":{"brandKitId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"recordingDefaults":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"defaultDestinationIds":{"maxItems":50,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"defaultIntegrationIds":{"maxItems":50,"type":"array","items":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"}},"budgetMinutesPerMonth":{"anyOf":[{"type":"integer","minimum":0,"maximum":9007199254740991},{"type":"null"}]},"notificationChannels":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"additionalProperties":false},"AddTeamMemberBody":{"type":"object","properties":{"userId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"role":{"default":"member","type":"string","enum":["team_lead","producer","director","host","member","viewer"]}},"required":["userId"]},"UpdateTeamMemberRoleBody":{"type":"object","properties":{"role":{"type":"string","enum":["team_lead","producer","director","host","member","viewer"]}},"required":["role"]},"TelemetryBatch":{"type":"object","properties":{"samples":{"minItems":1,"maxItems":50,"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"error"},"at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"message":{"type":"string","minLength":1,"maxLength":500},"source":{"type":"string","enum":["error","unhandledrejection","console"]},"stackDepth":{"type":"integer","minimum":0,"maximum":1000},"orgId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"sessionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"participantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"tabId":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}]},"route":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}]},"browser":{"anyOf":[{"type":"string","enum":["chrome","safari","firefox","edge","other"]},{"type":"null"}]},"release":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}]}},"required":["kind","at","message","source"]},{"type":"object","properties":{"kind":{"type":"string","const":"vital"},"at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"name":{"type":"string","enum":["LCP","CLS","INP","FCP","TTFB","FID"]},"value":{"type":"number","minimum":0,"maximum":3600000},"orgId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"sessionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"participantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"tabId":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}]},"route":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}]},"browser":{"anyOf":[{"type":"string","enum":["chrome","safari","firefox","edge","other"]},{"type":"null"}]},"release":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}]}},"required":["kind","at","name","value"]},{"type":"object","properties":{"kind":{"type":"string","const":"webrtc"},"at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"direction":{"type":"string","enum":["inbound","outbound"]},"media":{"type":"string","enum":["audio","video"]},"packetLossPercent":{"type":"number","minimum":0,"maximum":100},"roundTripMs":{"type":"number","minimum":0,"maximum":60000},"bitrateKbps":{"type":"number","minimum":0,"maximum":1000000},"jitterMs":{"type":"number","minimum":0,"maximum":60000},"orgId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"sessionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"participantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"tabId":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}]},"route":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}]},"browser":{"anyOf":[{"type":"string","enum":["chrome","safari","firefox","edge","other"]},{"type":"null"}]},"release":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}]}},"required":["kind","at","direction","media"]},{"type":"object","properties":{"kind":{"type":"string","const":"recorder"},"at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"droppedFrames":{"type":"integer","minimum":0,"maximum":9007199254740991},"queuedBytes":{"type":"integer","minimum":0,"maximum":9007199254740991},"orgId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"sessionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"participantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"tabId":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}]},"route":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}]},"browser":{"anyOf":[{"type":"string","enum":["chrome","safari","firefox","edge","other"]},{"type":"null"}]},"release":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}]}},"required":["kind","at"]},{"type":"object","properties":{"kind":{"type":"string","const":"renderer"},"at":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"renderMode":{"type":"string","enum":["live","egress","timeline"]},"droppedFrames":{"type":"integer","minimum":0,"maximum":1000000},"compositeMeanMs":{"type":"number","minimum":0,"maximum":60000},"compositeP95Ms":{"type":"number","minimum":0,"maximum":60000},"budgetRatio":{"type":"number","minimum":0,"maximum":100},"longTasks":{"type":"integer","minimum":0,"maximum":100000},"fps":{"type":"number","minimum":0,"maximum":240},"orgId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"sessionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"participantId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"tabId":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}]},"route":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}]},"browser":{"anyOf":[{"type":"string","enum":["chrome","safari","firefox","edge","other"]},{"type":"null"}]},"release":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}]}},"required":["kind","at","renderMode"]}]}}},"required":["samples"],"description":"A batch of browser telemetry samples. Ids and numbers only — never names, addresses or URLs."},"CreatePublishJobBody":{"type":"object","properties":{"productionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"integrationId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"provider":{"type":"string","minLength":1,"maxLength":64},"sessionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"renderId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"thumbnailAssetId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"captionsTrackId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"title":{"type":"string","minLength":1,"maxLength":300},"description":{"type":"string","maxLength":5000},"tags":{"maxItems":50,"type":"array","items":{"type":"string","minLength":1,"maxLength":100}},"chapters":{"maxItems":500,"type":"array","items":{"type":"object","properties":{"startMs":{"type":"integer","minimum":0,"maximum":9007199254740991},"title":{"type":"string","minLength":1,"maxLength":200}},"required":["startMs","title"]}},"visibility":{"type":"string","enum":["public","unlisted","private","scheduled"]},"scheduledAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"platformOptions":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"required":["productionId","title"],"description":"Create a publish job for a production's render/clip against a connected integration or a provider."},"CreateTrackUploadBody":{"type":"object","properties":{"kind":{"type":"string","enum":["camera","camera_alt","screen","mic","scratch_audio","desk_multitrack","program","comms","ingest"]},"origin":{"type":"string","enum":["browser_webcodecs","browser_mediarecorder","ios_native","android_native","desk_upload","server_egress","ingest"]},"participantId":{"description":"Ignored for a participant principal, which is already scoped","anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"isPrimary":{"description":"`false` for a server-side safety-net copy","type":"boolean"},"codec":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}]},"container":{"description":"Drives the object key's extension","anyOf":[{"type":"string","maxLength":64},{"type":"null"}]},"extension":{"type":"string","pattern":"^[a-z0-9]{1,8}$"},"contentType":{"type":"string","maxLength":255},"width":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},{"type":"null"}]},"height":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},{"type":"null"}]},"fps":{"anyOf":[{"type":"number","exclusiveMinimum":0},{"type":"null"}]},"sampleRate":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},{"type":"null"}]},"channels":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":64},{"type":"null"}]},"bitrateKbps":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},{"type":"null"}]},"startClockMs":{"description":"Session-clock ms at the first frame","anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]},"partSizeBytes":{"description":"Clamped up to S3's 5 MiB floor","type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"expectedParts":{"type":"integer","minimum":1,"maximum":10000}},"required":["kind","origin"],"description":"Everything known about a track before the first byte is uploaded."},"SignPartsBody":{"type":"object","properties":{"parts":{"minItems":1,"maxItems":100,"type":"array","items":{"anyOf":[{"type":"integer","minimum":1,"maximum":10000},{"type":"object","properties":{"partNo":{"type":"integer","minimum":1,"maximum":10000},"sha256":{"type":"string","pattern":"^([0-9a-fA-F]{64}|[A-Za-z0-9+/]{43}=)$"},"md5":{"type":"string","pattern":"^([0-9a-fA-F]{32}|[A-Za-z0-9+/]{22}==)$"},"bytes":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["partNo"]}]},"description":"Part numbers, or part numbers with the digests and length the client will send. Declare both `sha256` and `md5` when you have the bytes: the server signs whichever one the org's store enforces and returns it in `headers`."}},"required":["parts"]},"RecordPartBody":{"type":"object","properties":{"etag":{"type":"string","minLength":1,"description":"The ETag the store returned for this part"},"sha256":{"type":"string","pattern":"^([0-9a-fA-F]{64}|[A-Za-z0-9+/]{43}=)$"},"bytes":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["etag","bytes"]},"UploadSyncSidecar":{"type":"object","properties":{"firstFrameClockMs":{"description":"Session-clock ms of the frame that opened the file — measured, not the T₀ that was asked for","type":"number"},"t0ClockMs":{"description":"T₀ as commanded, so the frame gate's quantisation is visible","type":"number"},"syncMarks":{"maxItems":4096,"type":"array","items":{"type":"object","properties":{"clockMs":{"type":"number","description":"Session-clock ms"},"ptsUs":{"type":"number","minimum":0,"description":"Presentation time from the start of the recording, µs"},"label":{"type":"string","maxLength":120}},"required":["clockMs","ptsUs"]}},"clockOffsetMs":{"type":"number"},"driftPpm":{"type":"number"},"driftSamples":{"maxItems":1024,"type":"array","items":{"type":"object","properties":{"clockMs":{"type":"number"},"driftPpm":{"type":"number"},"offsetMs":{"type":"number"}},"required":["clockMs","driftPpm"]}},"durationMs":{"type":"number","minimum":0}}},"CompleteUploadBody":{"type":"object","properties":{"manifestSha256":{"type":"string","pattern":"^([0-9a-fA-F]{64}|[A-Za-z0-9+/]{43}=)$","description":"Digest of the whole object, from the recorder's manifest"},"totalBytes":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"sync":{"$ref":"#/components/schemas/UploadSyncSidecar"}},"required":["manifestSha256","totalBytes"]},"AbortUploadBody":{"type":"object","properties":{"reason":{"type":"string","maxLength":500}}},"LocalExportBody":{"type":"object","properties":{"target":{"type":"string","enum":["file-system-access","download"],"description":"Which browser path wrote the file. `download` buffered it in memory first."},"totalBytes":{"type":"integer","minimum":0,"maximum":9007199254740991},"files":{"maxItems":256,"type":"array","items":{"type":"object","properties":{"fileName":{"type":"string","maxLength":255},"bytes":{"type":"integer","minimum":0,"maximum":9007199254740991},"segmentIndex":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["fileName","bytes","segmentIndex"]},"description":"One entry per segment saved. Names only — no paths: where a guest put the file is theirs."},"reason":{"description":"Why the upload could not be used, in the guest's words","type":"string","maxLength":500}},"required":["target","totalBytes","files"]},"CreateWebhookBody":{"type":"object","properties":{"url":{"type":"string","minLength":1,"maxLength":2048},"description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"eventTypes":{"maxItems":50,"type":"array","items":{"type":"string","minLength":1,"maxLength":120,"pattern":"^(\\*|[a-z][a-z0-9_]*(\\.[a-z][a-z0-9_]*)*(\\.\\*)?)$"}},"preset":{"anyOf":[{"type":"string","enum":["slack","teams","zapier"]},{"type":"null"}]},"enabled":{"type":"boolean"}},"required":["url"],"description":"Creates an endpoint and mints its signing secret. Known event types: recording.track.verified, transcription.job.completed, transcription.job.failed, transcription.language.unexpected, transcript.translation.ready, transcript.translation.failed, media.job.completed, render.completed, session.started, session.ended, recording.ready, publish.started, publish.completed, publish.failed, webhook.ping."},"UpdateWebhookBody":{"type":"object","properties":{"url":{"type":"string","minLength":1,"maxLength":2048},"description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"eventTypes":{"maxItems":50,"type":"array","items":{"type":"string","minLength":1,"maxLength":120,"pattern":"^(\\*|[a-z][a-z0-9_]*(\\.[a-z][a-z0-9_]*)*(\\.\\*)?)$"}},"preset":{"anyOf":[{"type":"string","enum":["slack","teams","zapier"]},{"type":"null"}]},"enabled":{"type":"boolean"}},"description":"Re-enabling clears the failure counter — a human turning an endpoint back on is asserting they fixed it."}}},"paths":{"/v1/me":{"get":{"operationId":"getMe","summary":"The signed-in user, their logins and their organisations","tags":["account"],"description":"User-scoped: the read runs with `app.user_id` set and no `app.org_id`, so Row-Level Security — not handler code — is what limits it to this person's rows. On a first-ever call the account, its profile, preferences and personal organisation are created first; see `POST /v1/auth/bootstrap`.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The signed-in user.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Me"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"That email already belongs to another account and cannot be auto-linked.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/auth/bootstrap":{"post":{"operationId":"bootstrapAccount","summary":"Exchange a Cognito access token for a Tablecast account","tags":["account"],"description":"Called by a client immediately after sign-in. Verifies the access token against the user pool,\nthen finds or creates the person behind it: `users`, `user_identities`, `user_profiles`,\n`user_preferences`, a personal `organizations` row with its `org_settings`, and an owner\n`org_memberships` row — all in one transaction, so a failure leaves no half-made account.\n\nIdempotent: the second and every later call finds the identity by (provider, subject) and creates\nnothing. `created` says which happened.\n\nA second login whose *verified* email matches an existing account is attached to that account\nrather than creating a duplicate person (plan §11). An unverified email that collides is a 409:\nlinking then needs the explicit confirmation flow.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The account behind this token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BootstrapResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"That email already belongs to another account and cannot be auto-linked.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/me/profile":{"patch":{"operationId":"updateMyProfile","summary":"Update the signed-in user's profile","tags":["account"],"description":"Partial update, and **strict**: an unrecognised key is a 422 rather than a 200 that changed nothing (TAB-215). `lowerThirdDefault` is what a name super falls back to when this person appears in a production without an explicit override.  Send `x-tablecast-org-id` and a changed `displayName` also lands on the rows the lobby is rendering from, in that organisation — but only on rows whose name is still exactly what the profile used to say. A name a producer typed for somebody on air is theirs, not the account's.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserProfilePatch"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The updated user.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Me"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/me/preferences":{"patch":{"operationId":"updateMyPreferences","summary":"Update the signed-in user's preferences","tags":["account"],"description":"Partial update. Setting `lastOrgId` is how the org switcher persists a choice: it is validated against this user's active memberships, so it can never be pointed at an org they are not in.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserPreferencesPatch"}}}},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The updated user.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Me"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/me/avatar":{"put":{"operationId":"setMyAvatar","summary":"Attach an avatar image to the signed-in user's profile","tags":["account"],"description":"One call: creates the `media_assets` row, points the profile at it, and answers with a\npresigned **PUT** for the bytes. Shaped like `POST /v1/brand-kits/{id}/fonts`, and for the\nsame reason — an upload that needs three round trips has three places to be abandoned\nhalf-done.\n\n**`x-tablecast-org-id` is required even though the profile is not tenant-scoped.** An asset\nhas to live in an organisation's bucket, and the caller says which one they are acting in;\nthe profile records it (`avatarOrgId`) so the picture can be signed later without hunting\nfor it across tenants. Any active membership is enough — this is your own face, not the\nstudio's stinger.\n\nThe `kind` is decided by sniffing `head`, never a filename. **SVG is refused**: it sniffs as\nan image and it is a document that can carry script, and this URL ends up in an `<img>` on\nour own origin. Cropping to a square is the client's business; the server stores what it is\ngiven.\n\nReplacing an avatar leaves the previous asset in the library rather than deleting it — a\ndelete would have to be a soft delete anyway, and one that raced a page still rendering the\nold URL would look like a bug.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetAvatarBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Where to PUT the image, and the profile now pointing at it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AvatarUpload"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such organisation, or not one you are a member of.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Not an image, or an image kind an avatar cannot be.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No object-storage credentials are configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"delete":{"operationId":"clearMyAvatar","summary":"Go back to initials","tags":["account"],"description":"Clears `avatarAssetId` and `avatarOrgId`. The asset itself stays in the organisation's\nlibrary: it is a file somebody uploaded, and removing a picture from a profile is not the\nsame request as deleting it from the studio's media.\n\nUser-scoped — no `x-tablecast-org-id` — because forgetting your own avatar needs no tenant.\nIdempotent: clearing one you do not have is a 204.","security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Cleared."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/me/push-tokens":{"post":{"operationId":"registerPushToken","summary":"Register this device for push notifications","tags":["account"],"description":"Idempotent, and safe to call on every launch — which the app should, because the OS can\nrotate a push token at any time and saying what you hold is the only way to find out.\n\nThe token is the unique key, not `(user, device)`: a phone handed to a colleague, or an app\nsigned out of and into a different account, re-registers the *same* token as somebody else,\nand the row moves to them. Two live rows would keep sending the new owner's mentions to the\nold one.\n\nA token previously retired — Expo reported `DeviceNotRegistered` for it — is revived here: a\nreinstall makes a dead address live again, and refusing to un-retire it would leave a phone\nthat plainly works permanently unreachable.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterPushTokenBody"}}}},"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The registered device.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushToken"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Not an Expo push token.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"get":{"operationId":"listPushTokens","summary":"The devices registered to the signed-in user","tags":["account"],"description":"Live devices only — a retired one is kept for its history and is not a delivery target. What a settings screen lists so somebody can sign a lost phone out.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The devices.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushTokenList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/me/push-tokens/{token}":{"delete":{"operationId":"forgetPushToken","summary":"Stop pushing to one device","tags":["account"],"description":"The sign-out path, and the lost-phone path. Retires the row rather than deleting it, so a\nreinstall on the same handset is one row with a history rather than two and a guess.\n\nRow-Level Security confines this to the caller's own devices, so somebody else's token and\na token that never existed are the same 204 — which is the right answer to both.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":512},"in":"path","name":"token","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Retired, or was not there."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/auth/sso/discover":{"get":{"operationId":"discoverSso","summary":"Where an email address should sign in","tags":["account"],"description":"Called by the login form as the address is typed. `force` means send them straight to the provider; `suggest` means offer it beside the password field; `none` means carry on.\n\nAnswered without authentication, so it is deliberately thin. An unclaimed domain, an unverified one, one whose capture is off, one with no provider attached and one whose provider is disabled all answer `none` — five situations collapsed into one, because each of them would otherwise be a fact about an organisation the caller has not identified themselves to. It never says whether an account exists, and it never names the organisation.","parameters":[{"schema":{"type":"string","minLength":3,"maxLength":320},"in":"query","name":"email","required":true}],"responses":{"200":{"description":"Where to sign in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SsoDiscovery"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/auth/sso/step-up":{"post":{"operationId":"completeSsoStepUp","summary":"Bind this seat to the login just proved, and return where to go","tags":["account"],"description":"The far end of the 403 an SSO organisation answers when it is reached with the wrong login. The caller has been through the organisation's identity provider and now holds the identity it minted; this binds their membership to it and hands back the deep link they were originally following.\n\n`returnTo` is sanitised to a **same-origin absolute path**. Anything else — a scheme, a host, a protocol-relative `//host`, a backslash — becomes `/`, because a caller-supplied redirect target is an open-redirect primitive and this one arrives at the end of an authentication flow, which is exactly where a phishing page wants to be.\n\nThe identity must already belong to this account and must have come from this organisation's provider; anything else is a 403.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StepUpBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The seat is bound.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StepUpResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/auth/sso/logout/oidc":{"post":{"operationId":"oidcBackChannelLogout","summary":"OpenID Connect back-channel logout","tags":["account"],"description":"Called by the identity provider, not by a client. The token is the authentication: it is verified against the provider's JWKS with an RS256/ES256 allowlist, its `aud` must contain our client id, it must carry the back-channel-logout `events` member and must **not** carry a `nonce` (those two together are what stop an `id_token` — which the user's own browser receives on every login — being replayed here), its `iat` must be within five minutes, and its `jti` must not have been seen. Replay protection is a unique index, not a read, because two receivers can take the same POST in the same millisecond.\n\nRevokes the `user_sessions` rows created from that identity — narrowed to one session when the token carries a `sid`. **Sessions from the same person's other identities are untouched**, which is what keeps a corporate logout from closing their personal-account tab.\n\nAnswers 200 with a count. Per the specification a receiver responds 200 to a valid token whatever it found to revoke; an invalid one is a 400 and is recorded.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BackChannelLogoutBody"}}}},"responses":{"200":{"description":"What was revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LogoutResult"}}}},"400":{"description":"The logout token did not verify, or has already been applied.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/auth/sso/logout/saml":{"post":{"operationId":"samlSingleLogout","summary":"SAML single logout (HTTP-POST binding)","tags":["account"],"description":"Called by the identity provider, not by a client. The signature is the authentication.\n\nThe `<LogoutRequest>` is refused outright if it declares a `DOCTYPE` or an entity, matched to a provider by its `<saml:Issuer>` against `entity_id`, and then **checked against that provider's `signing_certificates`** before anything is revoked. Only RSA and ECDSA with SHA-256 or better are accepted; SHA-1 and every HMAC algorithm are refused, because the key we hold for an identity provider is a public certificate and an HMAC keyed with a public value is a signature anybody can make.\n\nEvery fact acted on — the replay key, the subject, the session index — is read from the element the signature actually covered, never from a second parse of the request body. That is what makes signature wrapping pointless rather than merely difficult.\n\nThe `ID` attribute plays the part `jti` plays for OIDC: it is the replay key, enforced by the same unique index. `IssueInstant` must be within five minutes. Revocation is narrowed to the `<SessionIndex>` when one is present, and never reaches the same person's other identities.\n\nThis is the **HTTP-POST** binding: `SAMLRequest` is base64 (not deflated) and carries an enveloped `<ds:Signature>`. For the HTTP-Redirect binding, which most identity providers use for logout, see the `GET` form of this path.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SamlLogoutBody"}}}},"responses":{"200":{"description":"What was revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LogoutResult"}}}},"400":{"description":"The LogoutRequest was malformed, stale, unsigned, or already applied.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The signature did not verify against the provider's certificates.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"get":{"operationId":"samlSingleLogoutRedirect","summary":"SAML single logout (HTTP-Redirect binding)","tags":["account"],"description":"Called by the identity provider, not by a client. The signature is the authentication.\n\nThe `<LogoutRequest>` is refused outright if it declares a `DOCTYPE` or an entity, matched to a provider by its `<saml:Issuer>` against `entity_id`, and then **checked against that provider's `signing_certificates`** before anything is revoked. Only RSA and ECDSA with SHA-256 or better are accepted; SHA-1 and every HMAC algorithm are refused, because the key we hold for an identity provider is a public certificate and an HMAC keyed with a public value is a signature anybody can make.\n\nEvery fact acted on — the replay key, the subject, the session index — is read from the element the signature actually covered, never from a second parse of the request body. That is what makes signature wrapping pointless rather than merely difficult.\n\nThe `ID` attribute plays the part `jti` plays for OIDC: it is the replay key, enforced by the same unique index. `IssueInstant` must be within five minutes. Revocation is narrowed to the `<SessionIndex>` when one is present, and never reaches the same person's other identities.\n\nThis is the **HTTP-Redirect** binding, which is what nearly every identity provider uses for logout and what Tablecast's own metadata parser prefers. `SAMLRequest` is DEFLATE-compressed then base64'd, and the signature is over the query string — `SAMLRequest`, then `RelayState` if present, then `SigAlg`, each percent-encoded exactly as sent. Those octets are taken from the request's own URL and never rebuilt from parsed parameters: percent-encoding is not canonical, so a re-encoded string verifies against whichever provider you tested with and fails against the next one.\n\nInflation is bounded at 64 KiB; a few hundred base64 bytes can otherwise become gigabytes.","parameters":[{"schema":{"type":"string","minLength":16,"maxLength":65536},"in":"query","name":"SAMLRequest","required":true},{"schema":{"type":"string","maxLength":512},"in":"query","name":"RelayState","required":false},{"schema":{"type":"string","maxLength":256},"in":"query","name":"SigAlg","required":false},{"schema":{"type":"string","maxLength":4096},"in":"query","name":"Signature","required":false}],"responses":{"200":{"description":"What was revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LogoutResult"}}}},"400":{"description":"The LogoutRequest was malformed, stale, unsigned, or already applied.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The signature did not verify against the provider's certificates.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/healthz":{"get":{"operationId":"getHealth","summary":"Liveness and dependency status","tags":["system"],"description":"Always returns 200 when the process is serving; the body says whether dependencies are healthy. A non-200 from this route means the function or the API Gateway integration is broken, which is exactly the distinction a load balancer or an on-call page needs.","security":[],"responses":{"200":{"description":"Service is serving. Check `status` and `database` for dependency health.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}}}}},"/openapi.json":{"get":{"operationId":"getOpenApiDocument","summary":"This API's OpenAPI 3.1 document","tags":["system"],"description":"Generated from the Zod schemas at build time. `packages/api-client` is generated from this document.","security":[],"responses":{"200":{"description":"The OpenAPI 3.1 document.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenApiDocument"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/teams/{teamId}/productions":{"post":{"operationId":"createProduction","summary":"Create a production under a team","tags":["productions"],"description":"The slug is derived from the name when omitted and is unique within the team, so two teams in one org can both run a show called `news`. A clash is a 409, never a silent rename. 402 (TAB-59) once the organisation's payment has failed and its 7-day grace period has elapsed — see `GET /v1/orgs/{orgId}/billing`'s `state`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProduction"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"teamId","required":true,"description":"Owning team"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The created production.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Production"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"402":{"description":"The organisation is read-only until its payment method is updated.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such team, or not visible to this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"That slug is already taken in this team.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"get":{"operationId":"listTeamProductions","summary":"List a team's productions","tags":["productions"],"parameters":[{"schema":{"type":"string"},"in":"query","name":"includeArchived","required":false,"description":"Archived shows are hidden by default; their sessions stay reachable either way."},{"schema":{"default":100,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false},{"schema":{"default":0,"type":"integer","minimum":0,"maximum":9007199254740991},"in":"query","name":"offset","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"teamId","required":true,"description":"Owning team"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The team's productions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductionList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such organisation, or the caller is not a member.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/productions/{id}":{"get":{"operationId":"getProduction","summary":"Fetch one production","tags":["productions"],"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Production id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The production.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Production"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No production with that id, or not visible to this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"patch":{"operationId":"updateProduction","summary":"Update a production","tags":["productions"],"description":"Absent fields are left alone; an explicit `null` clears a nullable field.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProduction"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Production id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The updated production.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Production"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"That slug is already taken in this team.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/productions/{id}/archive":{"post":{"operationId":"archiveProduction","summary":"Archive a production","tags":["productions"],"description":"Hides the show from the team's list. Its sessions stay reachable: recordings of a retired show must not disappear with it.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Production id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The archived production.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Production"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/productions/{id}/unarchive":{"post":{"operationId":"unarchiveProduction","summary":"Restore an archived production","tags":["productions"],"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Production id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The restored production.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Production"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/productions/{id}/members":{"get":{"operationId":"listProductionMembers","summary":"List per-show role overrides","tags":["productions"],"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Production id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The production's memberships.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductionMembershipList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/productions/{id}/members/{userId}":{"put":{"operationId":"upsertProductionMember","summary":"Give a user a role on this production","tags":["productions"],"description":"Idempotent, and an override rather than an addition: the role here replaces the user's team role on this show, so it can demote as well as promote.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertProductionMember"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Production id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"userId","required":true,"description":"User id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The membership.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductionMembership"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"delete":{"operationId":"removeProductionMember","summary":"Remove a per-show role override","tags":["productions"],"description":"The user keeps whatever their team role gives them.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Production id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"userId","required":true,"description":"User id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Removed.","content":{"application/json":{"schema":{"description":"Removed."}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such production, or that user has no override on it.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/productions/{productionId}/scenes":{"get":{"operationId":"listScenes","summary":"List a production's saved scenes","tags":["scenes"],"description":"In `sortOrder`, which is the order the director's scene strip shows them in. `id` is the **template** id — `ProductionState.scenes[].sceneId` points at it, while `pgm`/`pvw` hold session-scoped instance ids.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"productionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The production's scenes, in strip order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SceneList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such production, or not visible to this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"post":{"operationId":"createScene","summary":"Save a scene","tags":["scenes"],"description":"The layout is validated against the same schema the reducer enforces, plus the three checks it cannot make on its own: no duplicate slot ids, no rect that leaves the frame, no custom mask without a path. Each of those is a bug that would otherwise be discovered on air. The scene lands at the end of the strip; use the reorder endpoint to move it.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSceneBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"productionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The saved scene.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Scene"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Managing scenes needs producer or above on the show's team.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such production, or not visible to this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"That hotkey is taken, or the production is at its scene limit.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"The layout is not renderable — the detail says why.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/productions/{productionId}/scenes/order":{"put":{"operationId":"reorderScenes","summary":"Set the order of a production's scene strip","tags":["scenes"],"description":"Takes the **whole** list. A partial reorder would have to guess what happens to a scene another producer added a moment ago; the full list makes the caller state what it believes exists, so a stale client gets a 409 rather than silently reordering around a scene it has never seen. `sortOrder` is rewritten dense and 0-based.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReorderScenesBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"productionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The scenes in their new order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SceneList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Managing scenes needs producer or above on the show's team.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such production, or not visible to this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The list does not match the production's scenes; reload and try again.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/scenes/{sceneId}":{"get":{"operationId":"getScene","summary":"One saved scene","tags":["scenes"],"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sceneId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The scene.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Scene"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such scene, or not visible to this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"patch":{"operationId":"updateScene","summary":"Edit a saved scene","tags":["scenes"],"description":"Partial: only the fields present are written. Sending `layout` replaces it whole — a scene's slot list is small and a merge would make 'remove a slot' inexpressible.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSceneBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sceneId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The updated scene.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Scene"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Managing scenes needs producer or above on the show's team.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such scene, or not visible to this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"That hotkey is bound to another scene in this production.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"The layout is not renderable — the detail says why.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"delete":{"operationId":"deleteScene","summary":"Delete a saved scene","tags":["scenes"],"description":"Hard delete — a scene is configuration, not content, and a soft-deleted one would keep its hotkey and its place in the strip. Sessions already running are unaffected: they hold their own instance of the layout in `ProductionState`, which is the point of the two ids.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sceneId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Deleted."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Managing scenes needs producer or above on the show's team.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such scene, or not visible to this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/productions/{id}/sessions":{"post":{"operationId":"createSession","summary":"Schedule a session under a production","tags":["sessions"],"description":"The media region is resolved now, not at start: `production.home_region` -> `organizations.home_region` -> the deployment default. Pinning it at create time means a later edit to the production cannot silently move a scheduled show's media.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSession"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Production id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The scheduled session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Session"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such production in this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"get":{"operationId":"listProductionSessions","summary":"List a production's sessions","tags":["sessions"],"description":"Filterable by status (repeatable) and by scheduled date range.","parameters":[{"schema":{"anyOf":[{"type":"string","enum":["scheduled","lobby","live","ending","complete","failed","canceled"]},{"type":"array","items":{"type":"string","enum":["scheduled","lobby","live","ending","complete","failed","canceled"]}}]},"in":"query","name":"status","required":false,"description":"Repeatable. `?status=live&status=ending`"},{"schema":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"in":"query","name":"scheduledFrom","required":false},{"schema":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"in":"query","name":"scheduledTo","required":false},{"schema":{"default":100,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false},{"schema":{"default":0,"type":"integer","minimum":0,"maximum":9007199254740991},"in":"query","name":"offset","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Production id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The production's sessions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such organisation, or the caller is not a member.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/sessions":{"get":{"operationId":"listOrgSessions","summary":"List the organisation's sessions","tags":["sessions"],"description":"Every non-deleted session across the productions the caller can reach — an org owner or admin sees the whole org, everybody else sees the productions their team or per-show membership covers. Newest scheduled first, sessions with no date last. Filter by status (repeatable) and page with `limit`/`offset`. Each row carries its production's name and links to that session. Distinct from `GET /v1/orgs/{orgId}/recordings`, which lists only sessions that produced media.","parameters":[{"schema":{"anyOf":[{"type":"string","enum":["scheduled","lobby","live","ending","complete","failed","canceled"]},{"type":"array","items":{"type":"string","enum":["scheduled","lobby","live","ending","complete","failed","canceled"]}}]},"in":"query","name":"status","required":false,"description":"Repeatable. `?status=live&status=ending`"},{"schema":{"default":100,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false},{"schema":{"default":0,"type":"integer","minimum":0,"maximum":9007199254740991},"in":"query","name":"offset","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The page of sessions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgSessionList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Bad status filter or page bounds.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{id}":{"get":{"operationId":"getSession","summary":"Fetch one session","tags":["sessions"],"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Session id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Session"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"patch":{"operationId":"updateSession","summary":"Edit a session's schedule","tags":["sessions"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSession"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Session id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The updated session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Session"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{id}/reminders":{"get":{"operationId":"listSessionReminders","summary":"The reminders owed about this session","tags":["sessions"],"description":"The reminder *ledger*, which is the system's record of what has been sent and what is still owed — not a view of EventBridge Scheduler. `state` moves `pending -> sending -> sent`; a row stuck in `sending` is one whose delivery died mid-flight and is what an operator looks for. `skipped` means the reminder was deliberately not sent (the show was cancelled, or it came due long after the show had started) with `lastError` saying which.\n\nThis is also the debugging surface for scheduling: every planned reminder is here whether or not a one-off schedule was created for it.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Session id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"This session's reminders.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionReminderList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{id}/calendar-links":{"get":{"operationId":"listSessionCalendarLinks","summary":"This session's event in each connected calendar","tags":["sessions"],"description":"One entry per connected calendar account (TAB-101/TAB-103). `state` is `pending` while a push is owed, `synced` once the provider holds the current version, `failed` with `lastError` when a push was refused.\n\n`lastError` lives on the *link*, not on the connection: one refused event is not the account being broken, and marking the integration failed for it would tell a producer to reconnect something with nothing wrong with it. A genuine grant failure still marks the integration `expired`, which is where that advice belongs.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Session id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"This session's calendar events.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionCalendarLinkList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{id}/open-lobby":{"post":{"operationId":"openSessionLobby","summary":"Open the lobby (scheduled -> lobby)","tags":["sessions"],"description":"Green-room state. No room and no clock yet — those belong to `start`.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Session id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The session, now in the lobby.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionLifecycleResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The session is not in a status the lobby can be opened from.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{id}/start":{"post":{"operationId":"startSession","summary":"Start the show (scheduled|lobby -> live)","tags":["sessions"],"description":"Creates the LiveKit room, hands the session to the coordinator, then stamps `startedAt` and `sessionClockEpoch` — the anchor every recorder's PTS is measured from.  If either call fails the session is left in `failed` with `lifecycleNote` saying why, and the response is still 200 with `failure` set. That is deliberate: a director who pressed Start needs to know whether a room exists, and a 500 tells them nothing.  Refuses with 402 in three cases. 402 (TAB-59) once the organisation's payment has failed and its 7-day grace period has elapsed — see `GET /v1/orgs/{orgId}/billing`'s `state`. And 402 (TAB-58 / TAB-397) when the organisation is on a plan with no overage and has spent a metered allowance for the period — recording minutes or storage; a plan that *does* sell overage is never blocked here — the usage is billed at the overage rate and the client shows the soft-cap banner from `GET /v1/orgs/{orgId}/usage`.  Refuses with **429** (TAB-397) when the organisation already has as many sessions live as its `max_concurrent_sessions` entitlement allows. That is not a payment problem and must not be shown as one: ending any live session makes the identical request succeed, which is why it is 429 and not 402. `GET /v1/orgs/{orgId}/billing` lists every quota and where the org stands.  Ending or cancelling a session that is already live is never blocked by any of these.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Session id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The session, live — or failed, with the reason.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionLifecycleResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"402":{"description":"The organisation is read-only until its payment method is updated, or it is on a plan with no overage and has used a metered allowance (recording minutes, storage) for the period. Existing recordings are unaffected and stay downloadable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The session cannot be started from its current status.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"The organisation already has as many sessions live as its plan allows. End one, or upgrade, and retry — nothing about the request itself is wrong.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{id}/end":{"post":{"operationId":"endSession","summary":"End the show (live -> ending -> complete)","tags":["sessions"],"description":"Stops the coordinator first — it flushes the final production-state snapshot over the room's data channel — and only then deletes the room.  Blocked with a 409 while the integrity gate reports unverified tracks (plan §6): the coordinator, not the database, is the authority on whether a recorder is still holding bytes. The 409 carries `unverified` — the tracks, and the people, holding the show open — so a client can name them without a second call.  `waiver: {reason}` ends it anyway. That is recorded three times over: `integrity_status` becomes `waived`, `integrity_waived_by` names the caller, and an `audit_events` row keeps the reason together with what was outstanding when it was given.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EndSession"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Session id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The session, complete — or failed, with the reason.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionLifecycleResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Not endable from this status, or the integrity gate refused. A gate refusal carries `unverified`: the tracks, and the people, holding the show open.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{id}/record":{"post":{"operationId":"controlSessionRecord","summary":"Arm, roll, stop, mark or re-arm the rig","tags":["sessions"],"description":"Drives the coordinator's record issuer: `arm` warms every targeted device and opens an acknowledgement window, `start` names T₀, `stop` finalises, `marker` drops a sync mark, and `rearm` re-arms a named subset — the producer's one-click retry on a camera that failed.  **`start` is gated on acknowledgements.** It answers 409 while any armed device is unaccounted for, and the body's `missing` names them — by display name, because the producer's next action is to look across the room at that person. `allowUnacked` rolls anyway and records which cameras were left behind; there is deliberately no `force` spelling, for the same reason the end-session gate takes a waiver with a reason.  The coordinator is the issuer because it is the only process in the room: it holds the clock every device converts T₀ through, and it is where the acknowledgements arrive. So this endpoint, a Companion key and a MIDI pad are the same command through the same gate, distinguishable afterwards only by `source` on the EDL event.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordControl"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Session id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"202":{"description":"The accepted command. Not a report that every camera has rolled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordControlAccepted"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The rig is not ready. An `arm_incomplete` refusal carries `missing`: the devices that have not acknowledged, and why.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No coordinator is running this session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{id}/curation":{"get":{"operationId":"getSessionCuration","summary":"What the record room hides from the talent grid","tags":["sessions"],"description":"The producer's curation for this session's record room (TAB-628): the source ids the on-camera talent's grid must not render. What a member's record room reads on mount, before it applies live `session.curation` events on top. View-only — this endpoint reports what is hidden, it is not a recording control.\n\nAuthorised as every other producer mutation on a session is: producer-or-above on the production's team.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Session id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The source ids hidden from the talent grid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordCuration"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"put":{"operationId":"putSessionCuration","summary":"Replace what the record room hides from the talent grid","tags":["sessions"],"description":"Replaces the curation wholesale (TAB-628): the body's `hiddenFromTalent` becomes the set of source ids the on-camera talent's grid does not render, and the saved set is returned. The list is normalised — trimmed, de-duplicated — so the column holds a clean set however the request was shaped.\n\n**View-only.** Hiding an angle changes what talent *see*; it never stops the angle recording, never unsubscribes it, and never changes the producer's own grid. After the commit the API publishes `session.curation` on `/sessions/{id}/state` — teed to guests as well as members — so every talent screen reconciles without a poll.\n\nProducer-authorised: producer-or-above on the production's team, the same gate every session mutation takes. A guest cannot reach it, and the event is not client-publishable, so a guest can neither PUT it nor forge it on the wire.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordCurationInput"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Session id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The saved curation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordCuration"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{id}/move-room":{"post":{"operationId":"moveSessionRoom","summary":"Move the whole cast into a room","tags":["sessions"],"description":"Moves everybody on the session into the planning room, the record room, or the studio (TAB-633). The three rooms are views of one LiveKit room, so nobody re-joins and everybody keeps their existing credentials.\n\nPersists `active_room` (so a guest who joins later lands there too) and publishes `session.room` on `/sessions/{id}/state` — teed to guests as well as members — so everyone already in a room follows. **It changes no lifecycle:** `active_room` is orthogonal to `status`.\n\nProducer-authorised: producer-or-above on the production's team, the same gate every session mutation takes. A guest cannot reach it, and the event is not client-publishable.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MoveRoom"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Session id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The room the session was moved into.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MoveRoomResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{id}/cancel":{"post":{"operationId":"cancelSession","summary":"Cancel a session that never started (scheduled|lobby -> canceled)","tags":["sessions"],"description":"Terminal. A show that goes ahead after all is a new session, not this one revived.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelSession"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Session id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The canceled session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionLifecycleResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"A session that has gone live is ended, not canceled.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{id}/lobby":{"get":{"operationId":"listSessionLobby","summary":"Everybody waiting to be let into this session","tags":["sessions"],"description":"The participants of this session with no `admittedAt` — the ones standing at the door because `autoAdmit` is off (TAB-618). Oldest knock first.\n\nRevoked participants are excluded: somebody who was removed from the show is not somebody waiting to be let in, and an Admit button that would re-seat them is the one confusion this list must not create.\n\n`autoAdmit` comes back with the list because an empty lobby has two very different readings — nobody has arrived, or the door was never closed — and a producer looking at an empty panel they expected people in needs to be able to tell which.\n\nLive without polling: `session.lobby` on `/sessions/{id}/state` carries every knock and every admission.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Session id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Everybody knocking, and whether the door is closed at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionLobby"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{id}/participants":{"get":{"operationId":"listSessionParticipants","summary":"Everybody who was admitted to this session","tags":["sessions"],"description":"The attendance record: every participant of this session who is in, guests included — the people a producer sees in the planning room, and still sees here afterwards.\n\nMembership is decided the way every token mint decides it (`isParticipantAdmitted`), not by `admittedAt` alone. A join through an open door does stamp the column, but a row held under a gate that was later opened may still carry null — flipping `autoAdmit` back to true releases the queue whether or not the explicit sweep ran — and those people are in the room. A list keyed off the column alone would leave them out of the record of a session they attended.\n\nRevoked participants are excluded — somebody removed from the show did not attend it. Somebody who has left is **not**: `leftAt` is reported rather than filtered, because this is a record of who was there, not of who is there now.\n\n`isHidden` (producers, egress, ingest) is flagged rather than filtered, so a caller can show an attendance list or an on-camera list from the same read.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Session id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Everybody who got in, earliest arrival first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionParticipants"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{id}/lobby/admit-all":{"post":{"operationId":"admitSessionLobby","summary":"Let everybody currently waiting into the room","tags":["sessions"],"description":"Admits every participant of this session with no `admittedAt`, in one transaction, each one audited. The producer's \"admit all\" button.\n\n**It does not open the door.** `autoAdmit` is left exactly as it was, so the next guest to arrive is still held — which is the difference between this and `PATCH /v1/sessions/{id}` with `autoAdmit: true`, and the reason both exist. A producer clearing a queue at the top of the show has not decided to stop screening people.\n\nIdempotent and safe on an empty lobby: `admitted` comes back `[]` and nothing is written.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Session id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Everybody who was waiting, now admitted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdmitLobbyResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{id}/participants/{participantId}/admit":{"post":{"operationId":"admitSessionParticipant","summary":"Let somebody out of the lobby and into the room","tags":["sessions"],"description":"Stamps `admittedAt` and `admittedBy`, which is what makes the guest's next `POST /v1/join/media-token` answer 200 with the room instead of 202 with a `retryAfterMs`. Their waiting screen turns over within a poll interval, or immediately on the `session.lobby` event.\n\n**Idempotent.** Admitting twice is one admission and answers 200 with the same `admittedAt` — a producer double-clicking has not opened the door twice, and re-stamping would rewrite when it opened.\n\nThis is the open-link counterpart of `POST /v1/invite-recipients/{id}/admit`, which admits a *named* guest through their invitation's waiting room before any seat exists. A named guest on a gated session meets both, in that order.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Session id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"participantId","required":true,"description":"Participant id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The participant, now admitted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdmitParticipantResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such session, or no such participant in it.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"That guest was removed from the session. Revocation is one-way; invite them again rather than admitting them back.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{id}/participants/{participantId}/remove":{"post":{"operationId":"removeSessionParticipant","summary":"Remove somebody from the room (producer moderation)","tags":["sessions"],"description":"A producer ejects a participant from a live session. Two things happen, in this order: `participants.revoked_at` is set — a one-way door, the same `requireParticipant` reads as gone, so their next `POST /v1/join/media-token`, collab ticket and realtime-relay refresh are all refused (the removal *sticks*, a still-open tab cannot rejoin) — and then, best-effort after the commit, LiveKit drops them from the room (the removal is *immediate*). Same authorisation as opening the lobby: a team member who may mutate the session. Removing yourself is refused. Idempotent-ish: a participant already removed answers **404**, the same as one who was never here, so a removed id is not a fact to probe for.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Session id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"participantId","required":true,"description":"Participant id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Removed."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such session, or no such live participant in it.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"You cannot remove yourself from the room.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{id}/edl":{"get":{"operationId":"listSessionEdl","summary":"Read a session's switching EDL","tags":["sessions"],"description":"One page of `edl_events` in `seq` order, each with the RFC 6902 patch and a derived `kind` and `label` so it is readable without knowing that `replace /pgm` is a TAKE. `hasMore` is stated rather than inferred — a log whose length is a multiple of the page size is exactly where the 'a full page means more' heuristic is wrong. Filter by `actor` or `source` here; filter by `kind` client-side, since it is a function of the patch rather than a column.","parameters":[{"schema":{"type":"integer","minimum":0,"maximum":9007199254740991},"in":"query","name":"afterSeq","required":false},{"schema":{"type":"integer","minimum":1,"maximum":2000},"in":"query","name":"limit","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"actor","required":false},{"schema":{"type":"string","enum":["ui","midi","companion","osc","api","mcp","system"]},"in":"query","name":"source","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Session id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"One page of the log, ascending by seq.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EdlPage"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No session with that id, or not visible to this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/companion-pairings":{"post":{"operationId":"createCompanionPairing","summary":"Pair a Bitfocus Companion / Stream Deck with this session","tags":["sessions"],"description":"Mints a scoped, short-lived credential (an `api_keys` row — only its scrypt digest is stored) and the pairing that says what it controls and which participant it acts as. The response carries the `tcpair1.` blob the producer pastes into the module or scans as a QR; **it is returned exactly once and cannot be read back**.\n\nSession-scoped pairings expire (24 h by default) because a Stream Deck taped to a flight case travels between clients. Passing `productionId` pins the pairing to the production instead, so a weekly show keeps one configured deck across episodes.\n\n`actorParticipantId` is not optional in practice: without it a Companion take records no actor in the EDL, and talkback keys do nothing, because the comms plane decides who may key a leg by asking about a participant's role.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCompanionPairing"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The pairing, and its credential — once.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanionPairingCreated"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such session in this organisation.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"This deployment has no public API URL configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"get":{"operationId":"listCompanionPairings","summary":"The Companion surfaces paired with this session","tags":["sessions"],"description":"What the producer's devices panel renders: the label, the key's public handle, when it expires, when the deck was last seen, and the `control_devices` row it announced itself as. Revoked pairings are included — a panel that hid them would leave a producer unable to tell 'I revoked that deck' from 'that deck was never paired', which is exactly the question somebody asks when a key stops working mid-show.\n\nNever carries a credential. Only the digest is stored, so there is nothing to return.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Pairings, newest first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanionPairingList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/companion-pairings/{pairingId}":{"delete":{"operationId":"revokeCompanionPairing","summary":"Revoke a Companion pairing","tags":["sessions"],"description":"Revokes the pairing **and** the key behind it, in one transaction. The key is what actually stops the next request: `api_keys.status` is read on the request path with no cache in front of it, so a revoked deck is refused on its next key press rather than when some cache expires (TAB-242).\n\nIdempotent — revoking a revoked pairing is a 204, which is the answer somebody double-clicking in a hurry wants.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"pairingId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Revoked."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/control-devices":{"post":{"operationId":"registerControlDevice","summary":"Announce a control surface, or update the one already at that address","tags":["sessions"],"description":"Called by the web app when it opens a link to a Bitfocus Companion — `CompanionClient`'s registrar port, which until now had nothing to call.\n\n**Idempotent on `(kind, host, port)`.** `register()` runs on every connect, so a deck that reconnects four times in a bad ten minutes must leave one row rather than four; migration 0033 adds the unique index that makes this one statement instead of a read-then-write that would race a reconnect against a retry. `name`, `firmware` and `capabilities` follow the box — renaming a deck in Companion updates the row rather than forking it — but the row keeps the user who first announced it, so a second producer's connect does not move its lifetime onto them.\n\n`host` must be a hostname or an IP, not a URL: a scheme or a path is a 422 rather than something stripped, because stripping is how two spellings of one box quietly become one wrong one. Nothing here is ever a credential — a pairing's key is an `api_keys` row, hashed, and `identity` holds an address and nothing else.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterControlDevice"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The device row, created or updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ControlDevice"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"get":{"operationId":"listControlDevices","summary":"Every control surface this organisation has seen","tags":["sessions"],"description":"Most recently seen first, never-seen last. This is what lets the devices panel say which box is on the other end of a pairing rather than showing a bare host string — the panel already left-joins `control_devices` and, until this endpoint existed, always found null.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The organisation's control surfaces.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ControlDeviceList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/control-devices/{deviceId}/seen":{"post":{"operationId":"touchControlDevice","summary":"Bump a control surface's last-seen time","tags":["sessions"],"description":"`CompanionDeviceRegistrar.touch` — called on every button event the link carries, and therefore **coalesced to one write a minute per device**. A Stream Deck run hard is several presses a second, and a write per press would put a row lock in the path of a take for a column whose only reader is a panel that says '4 s ago'. The coalescing is a `where` clause rather than a read first, so two concurrent presses cannot both decide to write.\n\nAnswers 204 whether or not this call was the one that wrote: 'your bump landed inside the minute' is not news to a caller that fires and forgets. A device id belonging to another organisation is a 404.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"deviceId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Noted."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such control device in this organisation.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/internal/sessions/{id}/bootstrap":{"get":{"operationId":"getSessionBootstrap","summary":"Everything the session coordinator needs to start","tags":["internal"],"description":"Called by the session coordinator when the API starts a task for a session. Returns the session row, its locations and participants, the last production-state snapshot and the EDL tail — the coordinator holds no database credentials, so this payload is the entire world it wakes up into (plan §5). Not part of the public API surface — service-token authenticated.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Session id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"Owning org of the session. Scopes the read to that tenant."}],"security":[{"serviceToken":[]}],"responses":{"200":{"description":"The session bootstrap payload.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionBootstrap"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No session with that id, or not visible to this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"The session id or the org header is not a UUID.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/internal/sessions/live":{"get":{"operationId":"listLiveSessions","summary":"Every session a coordinator should currently own","tags":["internal"],"description":"TAB-660. The coordinator's active-session set is in memory and does not survive a restart: after a redeploy or a crash the database still says `live` but no coordinator holds the session, and the next record/control call 404s (surfaced as a 503). The coordinator calls this on boot and re-`start`s each row, which re-fetches the per-session bootstrap and rejoins the room.  Cross-org on purpose — one coordinator serves every tenant — so it takes no org header and is answered off the RLS-bypassing admin pool. Service-token authenticated like the rest of this surface. Only sessions with a room name are returned: one `live` without a pinned room has nothing to rejoin.","security":[{"serviceToken":[]}],"responses":{"200":{"description":"The sessions that should be running.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LiveSessionsResponse"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/internal/sessions/{id}/track-egress":{"post":{"operationId":"controlTrackEgress","summary":"Start or stop the per-participant recording safety net","tags":["internal"],"description":"Called by the session coordinator when it issues `record.start{at}` and again on `record.stop`. The coordinator is the trigger because it is the only process in the room — it can see which participants are publishing, which of their tracks are comms, and what LiveKit's track sids are, none of which is in Postgres. The work happens here because the coordinator holds no database credentials (plan §5), and because a safety net that dies with the coordinator is not a safety net. Comms tracks are filtered against an allow-list on the way in and can never be egressed. Idempotent: a participant who already has a live egress is skipped, so a `record.start` re-sent on reconnect does not buy a second copy of every track. Not part of the public API surface — service-token authenticated.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackEgressRequest"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Session id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"Owning org of the session. Scopes the read to that tenant."}],"security":[{"serviceToken":[]}],"responses":{"200":{"description":"What is running, and who was skipped.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackEgressResponse"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No session with that id, or not visible to this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The session has no LiveKit room yet.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"LiveKit or object storage is not configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/internal/sessions/{id}/coordinator-events":{"post":{"operationId":"recordCoordinatorEvent","summary":"Record a coordinator lifecycle transition","tags":["internal"],"description":"Posted by the session coordinator when it takes a session (`coordinator.started`, once it has bootstrapped, joined the room and armed its timers) and when it lets one go (`coordinator.stopped`, with the reason and how long it held it). **Best-effort on the caller's side**: the coordinator logs a failure here and carries on, because a lifecycle report must never be the reason a show goes off air. That is what let this route go unimplemented for four waves — every report 404'd and was swallowed. **Idempotent** on `(session, coordinator, event, at)`: the report is at-least-once and the coordinator does not deduplicate, so a retry answers `recorded: false` rather than writing a second handover. A `coordinator.started` is also when the show's clock is claimed: the response carries the session's `sessionClockEpoch`, set from this report's `at` if the row had none. It is set once and never moved — T₀ is the anchor every recorder's PTS is measured from (TAB-583). It does **not** move `sessions.status`. Whether a session is live is the control plane's decision; this says whether a process is actually in the room, which is a different fact. Not part of the public API surface — service-token authenticated.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CoordinatorEvent"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Session id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"Owning org of the session. Scopes the read to that tenant."}],"security":[{"serviceToken":[]}],"responses":{"200":{"description":"What was recorded, and the session clock it implies.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CoordinatorEventResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No session with that id, or not visible to this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/internal/sessions/{id}/record-commands/{commandId}":{"put":{"operationId":"putRecordCommand","summary":"Record one arm/start/stop/marker command and its acknowledgements","tags":["internal"],"description":"Called by the session coordinator's record issuer: once when a command goes on the wire, and again as each device answers, so `record_commands.acks` converges on the ack bag the issuer holds. The coordinator is the writer because only a process in the room can see the acks, and the row lives here because the coordinator holds no database credentials (plan §5). Idempotent on the coordinator-chosen `commandId`, which is what makes a retry after a transient failure a no-op rather than a second row for the same take. The whole row is written rather than a delta: a partial update would let two reports interleave into an ack bag neither of them saw. Not part of the public API surface — service-token authenticated.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordCommandUpsert"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Session id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"commandId","required":true,"description":"`record_commands.id`, chosen by the issuing coordinator"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"Owning org of the session. Scopes the read to that tenant."}],"security":[{"serviceToken":[]}],"responses":{"200":{"description":"The stored command.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordCommandStored"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No session with that id, or not visible to this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The command id belongs to a different session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/internal/authz/realtime":{"get":{"operationId":"authorizeRealtimeChannel","summary":"May this Cognito subject use this realtime channel?","tags":["internal"],"description":"Called by the `realtime-authz` Lambda that backs the AppSync Events channel namespace handlers. Answers for one subject and one channel path. Always answers 200 with an explicit allow or deny — a deny is a decision, not an error. Not part of the public API surface — service-token authenticated.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":255},"in":"query","name":"sub","required":true},{"schema":{"type":"string","minLength":2,"maxLength":512},"in":"query","name":"channel","required":true},{"schema":{"default":"subscribe","type":"string","enum":["subscribe","publish"]},"in":"query","name":"operation","required":false}],"security":[{"serviceToken":[]}],"responses":{"200":{"description":"The authorization decision.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RealtimeAuthzDecision"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"The `sub` or `channel` query parameter is missing or malformed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/internal/sessions/{id}/edl":{"post":{"operationId":"appendSessionEdl","summary":"Append a batch of accepted ops to the switching EDL","tags":["internal"],"description":"The coordinator batches every accepted production-state op and flushes them here (≤ 200 ms or 50 events, and always on session.end). One multi-row insert inside one transaction, so a batch lands whole or not at all. **Idempotent**: a replayed batch inserts nothing and is reported as `duplicates`, which is what lets the coordinator flush at-least-once instead of putting a database round trip in the path of a TAKE. **Gap-refusing**: a batch that starts beyond `lastSeq + 1` is a 409 with `code: \"edl_gap\"`, because a hole in the log replays into a state that was never on air. Not part of the public API surface — service-token authenticated.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppendEdlBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Session id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"Owning org of the session. Scopes the read to that tenant."}],"security":[{"serviceToken":[]}],"responses":{"200":{"description":"What was written.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppendEdlResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No session with that id, or not visible to this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The batch would leave a gap in the log (`code: \"edl_gap\"`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"The batch is internally inconsistent or too large.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"get":{"operationId":"replaySessionEdl","summary":"Read the switching EDL in seq order","tags":["internal"],"description":"How a restarted coordinator recovers: load the snapshot, then walk the tail from `snapshot.version` and refold it (TAB-392/TAB-187). Paged, with `hasMore` stated rather than inferred — a log whose length is a multiple of the page size is exactly where the 'I got a full page, so there is probably more' heuristic is wrong. Not part of the public API surface — service-token authenticated.","parameters":[{"schema":{"default":0,"type":"integer","minimum":0,"maximum":9007199254740991},"in":"query","name":"afterSeq","required":false,"description":"Exclusive lower bound"},{"schema":{"default":1000,"type":"integer","minimum":1,"maximum":5000},"in":"query","name":"limit","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Session id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"Owning org of the session. Scopes the read to that tenant."}],"security":[{"serviceToken":[]}],"responses":{"200":{"description":"One page of the log, ascending by seq.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplayEdlResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No session with that id, or not visible to this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/internal/sessions/{id}/production-state":{"get":{"operationId":"getSessionProductionState","summary":"The last persisted production-state snapshot","tags":["internal"],"description":"The stored fold and the log's high-water mark together, so a rehydration is two calls (this, then the tail) rather than three. Answers `snapshot: null` for a session that has never been snapshotted — a green room, or a show whose first 30 seconds had no ops. Not part of the public API surface — service-token authenticated.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Session id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"Owning org of the session. Scopes the read to that tenant."}],"security":[{"serviceToken":[]}],"responses":{"200":{"description":"The snapshot, or null.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductionStateSnapshotResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No session with that id, or not visible to this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"put":{"operationId":"putSessionProductionState","summary":"Persist a production-state snapshot","tags":["internal"],"description":"Written on an interval and on significant ops, so a rehydration replays seconds of EDL rather than hours of it. Versions move **forward only**: a write from behind the stored version is a 409 with `code: \"stale_version\"`, which is the whole of the optimistic concurrency here — the dangerous writer is not a fast coordinator but a partitioned one that came back. An equal version is idempotent (`unchanged: true`), because a retry after a timeout must not look like a split brain. Not part of the public API surface — service-token authenticated.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WriteProductionStateBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Session id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"Owning org of the session. Scopes the read to that tenant."}],"security":[{"serviceToken":[]}],"responses":{"200":{"description":"The version now stored.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WriteProductionStateResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No session with that id, or not visible to this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"A newer snapshot is already stored (`code: \"stale_version\"`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/internal/sessions/{id}/egress-runs":{"post":{"operationId":"recordEgressRestart","summary":"Record a restarted egress as a new job linked to the one it replaces","tags":["internal"],"description":"The coordinator restarts an egress that LiveKit reported failed, or whose template stopped heartbeating (TAB-184/TAB-424). The restart produces a *new* LiveKit egress writing a new file, so it becomes a new `egress_jobs` row pointing back at its predecessor, and every destination run records the seam in `discontinuities` before moving onto it. Overwriting the old row instead would erase the one fact the editor needs. Capped: the sixth restart of one program feed is a 409, which is the coordinator's signal to stop trying and raise a producer-visible alert. Not part of the public API surface — service-token authenticated.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordEgressRestartBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Session id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"Owning org of the session. Scopes the read to that tenant."}],"security":[{"serviceToken":[]}],"responses":{"201":{"description":"The new job.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordEgressRestartResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such session or egress job in this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The restart cap for this program feed is exhausted.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/internal/sessions/{id}/destination-retries":{"post":{"operationId":"recordDestinationRetry","summary":"Record one destination's leg being re-attached","tags":["internal"],"description":"The narrow sibling of `egress-runs` (TAB-379). A composite restart replaces the whole encode and produces a new `egress_jobs` row; a *destination* re-attach leaves the encode alone and only puts a gap in one leg's output. Reporting the second through the first would tell an editor the program feed has a seam it does not have. `retry_attempt` on the run is its own budget, deliberately separate from the composite's: a YouTube ingest refusing connections must not spend the budget keeping the show on Twitch. Not part of the public API surface — service-token authenticated.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordDestinationRetryBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Session id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"Owning org of the session. Scopes the read to that tenant."}],"security":[{"serviceToken":[]}],"responses":{"201":{"description":"The leg, with its seam count.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordDestinationRetryResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such run in this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"This leg's retry budget is exhausted.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/internal/collab/{docId}":{"get":{"operationId":"getCollabDocument","summary":"Load a collaborative document's stored snapshot","tags":["internal"],"description":"What the coordinator reads once, when the first person opens a document. `document: null` is the **normal** answer for one nobody has typed in: there is no create-document call in this feature, so an absent row is an empty document rather than a 404. The 404 comes from the *parent* — the production or session the doc id names — which is checked inside the tenant transaction, so a document belonging to another tenant and one that does not exist are indistinguishable from outside. The snapshot is the compacted document (`Y.encodeStateAsUpdate`), base64, not a log of updates: a run sheet edited across six shows would otherwise open by replaying every keystroke anybody ever made. Not part of the public API surface — service-token authenticated.","parameters":[{"schema":{"type":"string","minLength":3,"maxLength":128,"pattern":"^[a-z_]+:[0-9a-f-]{36}:[a-z_]+$"},"in":"path","name":"docId","required":true,"description":"The document id, `<scope>:<uuid>:<kind>`."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"Owning org of the session. Scopes the read to that tenant."}],"security":[{"serviceToken":[]}],"responses":{"200":{"description":"The stored document, or null.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReadCollabDocumentResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such production or session, or not visible to this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"The doc id is not one this contract defines.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"put":{"operationId":"putCollabDocument","summary":"Persist a collaborative document's compacted snapshot","tags":["internal"],"description":"The coordinator's debounced save — two seconds after the last edit, or fifteen after the first, whichever comes first, so a document somebody is typing into continuously still gets written down without a database round trip on the path of a keystroke. **Forward only**: a write at or below the stored version is a 409 with `code: \"stale_collab_version\"`. That is not the same instruction as its neighbour's `stale_version`. A stale production-state fold must be discarded; a stale Yjs snapshot is merely behind, so the coordinator re-reads, merges — a CRDT converges — and writes again at the higher version, and nothing either writer did is lost. **This is also where a comment becomes a notification** (TAB-494). The snapshot is the whole document, so the API can decode it and see every comment there has ever been — which makes \"which are new\" a pure function of (this snapshot, the `comments_seen` watermark on the row), and the watermark advances in the *same* `UPDATE` as the content it describes. A retried PUT, a rolled-back transaction and the losing side of a version race all recompute the identical empty answer, so nobody is told twice. See `packages/contexts/collab/src/comments.ts` for the full argument. Not part of the public API surface — service-token authenticated.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WriteCollabDocumentBody"}}}},"parameters":[{"schema":{"type":"string","minLength":3,"maxLength":128,"pattern":"^[a-z_]+:[0-9a-f-]{36}:[a-z_]+$"},"in":"path","name":"docId","required":true,"description":"The document id, `<scope>:<uuid>:<kind>`."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"Owning org of the session. Scopes the read to that tenant."}],"security":[{"serviceToken":[]}],"responses":{"200":{"description":"What was written.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WriteCollabDocumentResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such production or session, or not visible to this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"A later version is already stored (`code: \"stale_collab_version\"`).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Malformed, or a snapshot larger than the storage ceiling.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/internal/email/events":{"post":{"operationId":"receiveEmailDeliveryEvent","summary":"SES delivery-event receiver","tags":["internal"],"description":"Receives SES delivery events (send, delivery, bounce, complaint, reject) over SNS and records them against the recipient. Authenticated by SNS's own message signature and by the topic ARN, because SES and SNS have no credential to present. Idempotent: SNS delivers at least once, and a repeat is recognised by (message id, event type, recipient). Not part of the public API surface.","responses":{"200":{"description":"Acknowledged.","content":{"application/json":{"schema":{"type":"object","properties":{"outcome":{"type":"string"},"recorded":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["outcome","recorded"],"additionalProperties":false,"description":"Acknowledgement of an SES delivery-event notification."}}}},"401":{"description":"The SNS signature did not verify.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Delivery events are not configured in this environment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/invites":{"post":{"operationId":"createSessionInvite","summary":"Create a join link for a session","tags":["invites"],"description":"The invite carries the role, and the joiner cannot change it — that is what makes an anonymous join safe. Only the SHA-256 of the token is stored; the plaintext and the `joinUrl` are returned once, here.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSessionInviteBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"The organisation the caller is acting as."}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The invite and its token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedSessionInvite"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such session in this organisation.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The session is complete, canceled or failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Unknown role, or a location that is not in this session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"get":{"operationId":"listSessionInvites","summary":"List a session's invites","tags":["invites"],"description":"Spent and expired invites are included — `uses`/`maxUses`/`expiresAt` say which.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"The organisation the caller is acting as."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The invites.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionInviteList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/invites/{inviteId}/code":{"post":{"operationId":"createSessionInviteCode","summary":"Mint a short join code for an existing invite","tags":["invites"],"description":"The same invite, in a form somebody can type: eight Crockford base32 characters for the devices where a 43-character token is the reason nobody joins — an in-room iPad, or a phone whose holder was read the code aloud. Role, location, uses and expiry all keep living on the invite, so a code grants nothing a link could not, and deleting the invite deletes the code. Minting again **replaces** the previous code. Returned once, here.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"inviteId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"The organisation the caller is acting as."}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The code and where to type it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MintedJoinCode"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such invite on this session in this organisation.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The session is complete, canceled or failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"410":{"description":"The invite has expired or has been used up.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/invites/{inviteId}":{"delete":{"operationId":"revokeSessionInvite","summary":"Revoke a join link","tags":["invites"],"description":"The row is deleted, so a revoked link and an invented one are both 404. Guests who already joined keep their participant rows.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"inviteId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"The organisation the caller is acting as."}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Revoked."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/invite-recipients":{"post":{"operationId":"addSessionInviteRecipients","summary":"Invite named people to an episode","tags":["invites"],"description":"One person, a pasted list, or one-click copies of the show's roster — all three are this call. Each person gets a **token of their own**, returned once, here, so a named guest is distinguishable at the door from every other holder of the same invite. The role lives on the invite (one invite per role, made on first use), which is what keeps an invite's grant a property of the row rather than of anything the joiner sends.\n\nRe-adding an address that is already on the same invite **refreshes** that row and rotates its token rather than creating a second one, and does not rewind their funnel.\n\n**Each person is emailed their link** (TAB-520), and `email` on each entry says what became of that message. A send that goes moves them to `sent`; one that is suppressed or refused leaves them `invited` with the reason on `lastDeliveryError`, and the link is still in this response for a producer who wants to send it themselves.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddInviteRecipientsBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"The organisation the caller is acting as."}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The people, and their links.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedInviteRecipientList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such session in this organisation, or no such roster entry.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The session is complete, canceled or failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Nobody named, an unknown role, or a user who is not a member.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"get":{"operationId":"listSessionInviteRecipients","summary":"The guest funnel for an episode","tags":["invites"],"description":"Everybody named on this episode, in funnel order — `invited` first, `on_air` last, the two terminal states after. That order is the enum's own, so a client that renders the list as it arrives gets 'still to arrive' above 'in the room' for free.\n\nSeed the People panel from this on mount and keep it live with the `session.invite.recipient` event on `/sessions/{id}/state`; a dropped event is survivable because a re-read gives the same answer.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"The organisation the caller is acting as."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The people and where they have got to.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteRecipientList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/productions/{productionId}/invite-recipients":{"post":{"operationId":"addProductionInviteRecipients","summary":"Add people to a show's roster","tags":["invites"],"description":"A production-scoped invitation covers every episode and the planning room, which is what makes a recurring co-host one invitation rather than twelve. Their link resolves to whichever episode is open when they tap it: one that is live or in its lobby, else the soonest scheduled one, else the one that was due an hour ago and is running late.\n\n**They are emailed** (TAB-520), the same as an episode's guests — the invitation names the show rather than a date, because a roster entry has no one time.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddInviteRecipientsBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"productionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"The organisation the caller is acting as."}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The people, and their links.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedInviteRecipientList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such production in this organisation.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"get":{"operationId":"listProductionInviteRecipients","summary":"A show's roster","tags":["invites"],"description":"The people who travel with this show. `POST /v1/sessions/{sessionId}/invite-recipients` with `fromRecipientIds` is the one-click 'add the usual co-host to Thursday'.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"productionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"The organisation the caller is acting as."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The roster.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteRecipientList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/invite-recipients/{recipientId}":{"patch":{"operationId":"updateInviteRecipient","summary":"Rename, re-role, or mark somebody a no-show","tags":["invites"],"description":"`status` accepts only `no_show`, and only for somebody who has not arrived: every other step of the funnel is a fact the system observed, and a click that could contradict the tile next to it is a panel nobody trusts.\n\nChanging `role` **moves the person to that role's invite and rotates their token**, so `token`/`joinUrl` come back on that call and the previous link stops working. The invite carries the role; editing it in place would silently re-grant everybody else on it.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateInviteRecipientBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"recipientId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"The organisation the caller is acting as."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The person, and a new link if the role moved.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteRecipientResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"They have already arrived, so they are not a no-show.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"410":{"description":"That invitation has been withdrawn.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"delete":{"operationId":"revokeInviteRecipient","summary":"Withdraw an invitation, and remove the person if they are already in","tags":["invites"],"description":"Four things, in the order of how quickly each bites: the token is rotated to a value nobody holds (the link is a 404 immediately, indistinguishable from an invented one); `participants.revoked_at` is set, which refuses the next LiveKit media-token refresh (ten minutes) and the next collab ticket (five); they are dropped from the media room now, best effort; and the panel is told.\n\nThe row is kept, marked `revoked` — 'invited, opened, never arrived, withdrawn' is a thing a producer will want to read next season. What this cannot do is close a collaborative document socket that is already open: the doc host checks its ticket once, at the handshake, so five minutes is the true worst case there.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"recipientId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"The organisation the caller is acting as."}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Withdrawn."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/invite-recipients/{recipientId}/resend":{"post":{"operationId":"resendInviteRecipient","summary":"Issue this person a fresh link","tags":["invites"],"description":"**Rotates the token.** The previous link stops working the instant this returns, and answers the same 404 an invented link does — a link that went to the wrong address is not made safe by sending a second one alongside it.\n\nThe funnel is not rewound: a guest who already opened the first link is still `opened`, because the producer's action was about delivery, not about un-happening what the guest did.\n\n**The message is sent from here** (TAB-520), and `email` says what became of it. The copy says it replaces the earlier invitation, because the link in that one is now dead.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"recipientId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"The organisation the caller is acting as."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The person, and their new link.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteRecipientResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"That invitation has been withdrawn; add the person again instead.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/invite-recipients/{recipientId}/admit":{"post":{"operationId":"admitInviteRecipient","summary":"Let a knocking guest through the waiting room","tags":["invites"],"description":"For an invitation whose `admission` is `waiting_room`. The guest's landing is polling `POST /v1/join/{token}`, which has been answering 202 with no credentials; after this it answers 200 with the room. Idempotent — admitting twice is one admission.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"recipientId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"The organisation the caller is acting as."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The person, now admitted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteRecipientResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"410":{"description":"That invitation has been withdrawn.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/locations":{"get":{"operationId":"listSessionLocations","summary":"List a session's rooms and what is wrong with them","tags":["locations"],"description":"Each room comes with its occupants and its faults. `noSource` is the blocking one: a room with no nominated microphone is either silent or arriving from several hard-muted phones at once, and neither is something to find out on air.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"The organisation the caller is acting as."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The rooms.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LocationStatusList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such session in this organisation.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"post":{"operationId":"createSessionLocation","summary":"Create a room","tags":["locations"],"description":"One microphone in, one speaker out. `chirp` mints the ultrasonic token this room's monitor announces itself with; the token is returned from its own endpoint, never here.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateLocationBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"The organisation the caller is acting as."}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The room.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Location"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such session in this organisation.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The session has ended, or that name is taken.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"A participant that is not in this session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/locations/{locationId}":{"patch":{"operationId":"updateSessionLocation","summary":"Rename a room, or change its source, monitor or mix-minus","tags":["locations"],"description":"Nominating a participant who is in another room is a 409 — a room's microphone must be in that room, and the database says so too. Nominating somebody with no room yet moves them into this one, which is what picking from the room's own list means.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateLocationBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"locationId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"The organisation the caller is acting as."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The room.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Location"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such room on this session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"That name is taken, or the participant is in another room.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"delete":{"operationId":"deleteSessionLocation","summary":"Delete a room","tags":["locations"],"description":"Everybody in it becomes unassigned rather than orphaned — and stops being excluded from everybody else's monitor feed, which is a change to what people hear, not only a row disappearing.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"locationId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"The organisation the caller is acting as."}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Deleted."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/locations/{locationId}/chirp":{"get":{"operationId":"getSessionLocationChirpToken","summary":"The ultrasonic token this room's monitor should chirp","tags":["locations"],"description":"Its own endpoint, and producers only, because holding this token is what lets a device claim to *be* this room. A client that has it can make every phone within earshot offer to join here — which is exactly the point when it is the monitor speaker, and exactly the problem when it is not.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"locationId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"The organisation the caller is acting as."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LocationChirpToken"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such room, or it is not set to chirp.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/locations/assign":{"post":{"operationId":"assignParticipantLocation","summary":"Put a participant in a room, or take them out of every room","tags":["locations"],"description":"Always explicit. The move succeeds even when the person is the room's nominated microphone: the room they left is unsourced afterwards, which the panel says loudly, because refusing a drag that was probably correct is worse than a warning.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssignParticipantBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"The organisation the caller is acting as."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The participant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssignedParticipant"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such participant or room on this session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/locations/suggestions":{"get":{"operationId":"listLocationSuggestions","summary":"Live room suggestions awaiting somebody's yes","tags":["locations"],"description":"Expired suggestions are filtered rather than swept: one that timed out is not wrong, it is old, and a job to delete rows nobody reads would be work for its own sake.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"The organisation the caller is acting as."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The suggestions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LocationSuggestionList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/locations/signals":{"post":{"operationId":"reportLocationSignals","summary":"Report what a device can tell us about where it is","tags":["locations"],"description":"Salted digests of the network, and any ultrasonic token the microphone heard. The API never receives an IP or a BSSID — the device hashes them with the session id, so the same office is unlinkable across shows. Returns suggestions, never an assignment.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportSignalBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"The organisation the caller is acting as."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Any suggestions this evidence produced.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LocationSuggestionList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such participant on this session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/locations/suggestions/{suggestionId}/confirm":{"post":{"operationId":"confirmLocationSuggestion","summary":"Say yes to a suggestion, which is the only way one becomes an assignment","tags":["locations"],"description":"The confirmer is written to `audit_events`, and the database will not accept a confirmed suggestion without one. A double-mic finding names no room, so `locationId` is required for those — the producer's action there is 'make a room out of these two'.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmSuggestionBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"suggestionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"The organisation the caller is acting as."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"What was confirmed and who moved.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmedSuggestion"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"That suggestion has already been decided.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"The suggestion names no room and none was given.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/locations/suggestions/{suggestionId}":{"delete":{"operationId":"dismissLocationSuggestion","summary":"Say no to a suggestion","tags":["locations"],"description":"Kept, not deleted: 'the system suggested this and a human said no' is a fact.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"suggestionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"The organisation the caller is acting as."}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Dismissed."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"That suggestion has already been decided.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/join/{token}":{"get":{"operationId":"getJoinPreflight","summary":"What the pre-join page needs, without joining","tags":["join"],"description":"Public. Returns the show's title and branding so a guest knows what they are about to walk into. Creates nothing and spends nothing — opening the page must not consume a single-use link.","parameters":[{"schema":{"type":"string","minLength":34,"maxLength":200},"in":"path","name":"token","required":true}],"responses":{"200":{"description":"The pre-join summary.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JoinPreflight"}}}},"404":{"description":"No such link.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"410":{"description":"The link has expired or has been used up.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"post":{"operationId":"joinByInvite","summary":"Redeem a join link","tags":["join"],"description":"Public — no bearer required. Creates a `participants` row (anonymous unless the caller is signed in, in which case `user_id` is bound), spends one use of the invite and returns a LiveKit token for the room plus a participant-scoped guest token for this API. Refused with 429 when the room already holds as many people as the organisation's `max_participants` entitlement allows; the link is not spent, so the same link works once somebody leaves.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JoinByInviteBody"}}}},"parameters":[{"schema":{"type":"string","minLength":34,"maxLength":200},"in":"path","name":"token","required":true}],"security":[{},{"bearerAuth":[]}],"responses":{"200":{"description":"The participant and its credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JoinResult"}}}},"202":{"description":"Checked in, and waiting for a producer to open the door.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JoinPending"}}}},"401":{"description":"The bearer token supplied is not valid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such link.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The session is not accepting joins.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"410":{"description":"The link has expired or has been used up.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"The session is full for this plan. Not a payment error: a seat frees up when somebody leaves, and the invite has not been spent.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/join/code/preflight":{"post":{"operationId":"getJoinCodePreflight","summary":"What the pre-join page needs, from a short code","tags":["join"],"description":"Public. The code's equivalent of `GET /v1/join/{token}`: the show's title and branding, and the role the code will land in — which is how a phone knows to show the camera-only screen rather than the full device check. Creates nothing and spends nothing. `POST` rather than `GET` because a short code must not appear in a URL, where every proxy between the phone and us would log it.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JoinCodePreflightBody"}}}},"responses":{"200":{"description":"The pre-join summary.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JoinPreflight"}}}},"404":{"description":"No such code.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"410":{"description":"The code, or the invite behind it, has expired or been used up.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Too many code attempts from this address.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/join/code":{"post":{"operationId":"joinByCode","summary":"Redeem a short join code","tags":["join"],"description":"Public — no bearer required. Resolves the code to the invite it presents and redeems that invite, so this returns exactly what `POST /v1/join/{token}` returns and grants exactly what the link would have granted: the role and the location come from the invite, never from this body. Rate-limited to 5 attempts a minute per address, and refused with the same 429 when the room is already at the plan's `max_participants`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JoinByCodeBody"}}}},"security":[{},{"bearerAuth":[]}],"responses":{"200":{"description":"The participant and its credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JoinResult"}}}},"401":{"description":"The bearer token supplied is not valid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such code.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The session is not accepting joins.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"410":{"description":"The code, or the invite behind it, has expired or been used up.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Too many code attempts from this address, or the session is already full for this plan.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/join":{"post":{"operationId":"joinSessionAsMember","summary":"Join a session as a member of the organisation","tags":["join"],"description":"The host/producer path: no invite, `user_id` always bound. Re-joining updates the existing participant rather than adding a second one, so a reload does not duplicate somebody in the roster.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JoinAsMemberBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true,"description":"The organisation the caller is acting as."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The participant and its credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JoinResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such session in this organisation.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The session is not accepting joins.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"The session is full for this plan. Re-joining an existing seat is never refused, so this only ever means somebody else has to leave first.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/join/media-token":{"post":{"operationId":"refreshMediaToken","summary":"Re-mint the LiveKit token for the current participant","tags":["join"],"description":"A LiveKit token lives ten minutes; a show does not. This is how a guest — who has no account to re-authenticate with — gets the next one, using the participant-scoped token they were given at join. The invite is not consulted and no use is spent.\n\n**This is also the lobby poll** (TAB-618). A guest held by a session whose `autoAdmit` is off holds a `guestToken` and no media token; calling this answers **202** with `JoinPending` and a `retryAfterMs` for as long as they are waiting, and 200 with the room the first time it is called after a producer admits them. One endpoint and one loop, so a client never has to decide which of two calls it is making.","security":[{"guestToken":[]}],"responses":{"200":{"description":"A fresh media token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaTokenResult"}}}},"202":{"description":"Still in the lobby. No media token, and a `retryAfterMs`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JoinPending"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"That participant is no longer in the session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The session is not accepting joins.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"LiveKit is not configured for this environment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/join/ice-servers":{"post":{"operationId":"refreshIceServers","summary":"Re-mint the TURN credentials for the current participant","tags":["join"],"description":"A Cloudflare TURN credential lives an hour; a show does not. A client that hits an ICE failure mid-show, or that is approaching `iceServersExpiresAt`, calls this and restarts ICE with what comes back. The invite is not consulted and no use is spent.\n\nUnlike the join responses — which omit `iceServers` and admit the guest anyway, because most networks need no relay — this one answers 503 when the environment has neither a Cloudflare TURN key nor a configured static list. The caller asked specifically, so the honest answer is the reason rather than an empty list they cannot act on.","security":[{"guestToken":[]}],"responses":{"200":{"description":"Fresh ICE servers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IceServersResult"}}}},"202":{"description":"Still in the lobby. No relay credential yet.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JoinPending"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"That participant is no longer in the session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The session is not accepting joins.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"TURN is not configured for this environment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/internal/transcription/callback/{jobId}":{"post":{"operationId":"transcriptionCallback","summary":"Receive a transcription provider's result","tags":["internal"],"description":"Called by the transcription provider (Deepgram by default) when a batch job finishes. Authenticated by a signed, job-bound, expiring token on the URL rather than by the service-token header, because a third party cannot present one. Idempotent: a replayed delivery for a job that already succeeded is accepted and discarded. Not part of the public API surface.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"org","required":true,"description":"Owning organisation of the job. Covered by the token's signature."},{"schema":{"type":"string","minLength":16,"maxLength":512},"in":"query","name":"token","required":true,"description":"Signed, job-bound, expiring callback token minted when the job was submitted."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"jobId","required":true,"description":"The `transcription_jobs` row this callback belongs to."}],"responses":{"200":{"description":"The callback was accepted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranscriptionCallbackAccepted"}}}},"401":{"description":"The callback token is missing, wrong or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such job in that organisation.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"The job id, org or token is not the right shape.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs":{"post":{"operationId":"createOrg","summary":"Create an organisation","tags":["orgs"],"description":"Creates the organisation, its settings row and an `org_owner` membership for the caller, in one transaction. The org id is minted by the API rather than the database because the only RLS policy that permits the insert checks `id = current_setting('app.org_id')`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrgBody"}}}},"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The new organisation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Org"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The slug is already taken.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}":{"get":{"operationId":"getOrg","summary":"Read an organisation","tags":["orgs"],"description":"404 for a caller who is not an active member — org ids are not enumerable.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The organisation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Org"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/settings":{"get":{"operationId":"getOrgSettings","summary":"Read organisation settings","tags":["orgs"],"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The settings.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgSettings"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"patch":{"operationId":"updateOrgSettings","summary":"Update organisation settings","tags":["orgs"],"description":"Absent keys are left alone; the JSONB sections are merged one level deep.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateOrgSettingsBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The updated settings.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgSettings"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/invitations":{"post":{"operationId":"createInvitation","summary":"Invite somebody to the organisation","tags":["orgs"],"description":"Emails the link (TAB-454) and returns the plaintext token once, here, so an administrator can still send it by hand when the environment has no mail provider. Only the SHA-256 is stored, so this response is the only place the token ever exists. `teamIds` are joined on acceptance. An organisation that enforces SSO, or that force-captures the address's domain, refuses with a 409 that says why.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateInvitationBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The invitation, its single-use token, and what became of the email.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedInvitation"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"This organisation's SSO configuration means email invitations do nothing.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Unknown role, or a team that is not in this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"get":{"operationId":"listInvitations","summary":"List pending invitations","tags":["orgs"],"description":"Carries the organisation's `policy` beside the rows (TAB-454) so a People page can explain why the invite form is absent instead of rendering one whose every submission is a 409.","parameters":[{"schema":{"type":"string"},"in":"query","name":"includeAccepted","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The invitations.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvitationList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/invitations/{invitationId}/resend":{"post":{"operationId":"resendInvitation","summary":"Re-issue an invitation and email it again","tags":["orgs"],"description":"There is no re-send of the *original* link, and there cannot be: only its SHA-256 was stored. This mints a **new** token on the same row with a fresh expiry — so the previous link stops working, which is what somebody re-sending because \"they never got it\" wants if the reason is that it went to the wrong inbox. The email, role and team ids are not editable; revoke and re-invite to change those.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResendInvitationBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"invitationId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The re-issued invitation, its new token, and what became of the email.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedInvitation"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Already accepted, or this org no longer accepts email invitations.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/invitations/{invitationId}":{"delete":{"operationId":"revokeInvitation","summary":"Revoke an invitation","tags":["orgs"],"description":"The row is deleted, so a revoked token and an invented one are both 404.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"invitationId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Revoked."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Already accepted.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/invitations/{token}/accept":{"post":{"operationId":"acceptInvitation","summary":"Redeem an invitation","tags":["orgs"],"description":"Creates an active membership with `joined_via = invite`, plus a team membership per `team_ids`. The org id is carried in the token itself, so redemption — which happens before the caller belongs to the org — still runs under normal tenant RLS.","parameters":[{"schema":{"type":"string","minLength":34,"maxLength":200},"in":"path","name":"token","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The membership that was created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcceptedInvitation"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such invitation.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Already an active member.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"410":{"description":"The invitation has expired or was already used.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/members":{"get":{"operationId":"listOrgMembers","summary":"List organisation members","tags":["orgs"],"description":"`users.primary_email` is not included: `users_self` restricts that table to the caller's own row. Display names come from `user_profiles`, which co-members may read.","parameters":[{"schema":{"type":"string","enum":["invited","active","suspended"]},"in":"query","name":"status","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The roster.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgMemberList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/members/avatars":{"post":{"operationId":"resolveMemberAvatars","summary":"Signed avatar URLs for a set of members, in one call","tags":["orgs"],"description":"A comment thread with twenty authors, or a lobby with twelve faces, needs twenty or twelve\nsigned URLs. This is the one call that mints them — the alternative is a request per face,\nwhich is a request per face on every reload, because the URLs expire.\n\n**A POST that reads.** The ids are the input and there can be a hundred of them, which is\nmore than belongs in a query string, and a URL carrying a list of user ids is a URL that\nends up in an access log. Nothing is created.\n\nIds that are not active members of this organisation are simply absent from the answer, as\nare members with no avatar, one whose asset has been deleted, and — when the deployment has\nno object-storage credentials — all of them. A client renders initials for anything absent,\nwhich is the same thing it already does for a guest with no account at all.\n\nAvatars belonging to another organisation are resolved too: the profile row records where\nits own picture lives, and the row a co-member may read is the capability. See\n`lib/avatars.ts`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberAvatarsBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The avatars that resolved.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberAvatarList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/members/{membershipId}":{"patch":{"operationId":"updateOrgMemberRole","summary":"Change a member's org role","tags":["orgs"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMemberBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"membershipId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The updated membership.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgMember"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"That change would leave the org without an active owner.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"delete":{"operationId":"removeOrgMember","summary":"Remove a member from the organisation","tags":["orgs"],"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"membershipId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Removed."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"That would leave the org without an active owner.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/members/{membershipId}/suspend":{"post":{"operationId":"suspendOrgMember","summary":"Suspend a membership","tags":["orgs"],"description":"Offboarding suspends the membership, never the person (plan §11). The `org_memberships_suspend` trigger then deletes the user's team memberships in this org and revokes their sessions bound to it.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"membershipId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The suspended membership.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgMember"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Already suspended, or the last active owner.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/members/{membershipId}/reinstate":{"post":{"operationId":"reinstateOrgMember","summary":"Reinstate a suspended membership","tags":["orgs"],"description":"Team memberships are not restored — suspension deleted them.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"membershipId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The reinstated membership.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgMember"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Already active.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/roles":{"get":{"operationId":"listRoles","summary":"List the roles this organisation can resolve against","tags":["orgs"],"description":"The platform catalogue plus this organisation's own, with the permission checklist a role is composed from. Another organisation's custom roles are absent — that is `roles_read_global` answering, not this handler. `platform:*` keys are never listed: they authorise operating Tablecast itself and are refused by the database if a role names one.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The roles and the catalogue.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoleList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"post":{"operationId":"createRole","summary":"Define a custom role","tags":["orgs"],"description":"The role belongs to this organisation and replaces the platform role of the same `(scope, key)` for it — the enterprise is *redefining* the role, not extending it, which is what makes taking a permission away expressible. `platform` scope and `platform:*` keys are refused here and again in the database (migration 0021).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRoleBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The new role.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Role"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"402":{"description":"Custom roles are an enterprise feature.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Built-in roles cannot be edited, and `platform:*` is never grantable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"This organisation already defines a role with that scope and key.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/roles/{roleId}":{"get":{"operationId":"getRole","summary":"Read one role","tags":["orgs"],"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"roleId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The role.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Role"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"patch":{"operationId":"updateRole","summary":"Rename a custom role or change what it may do","tags":["orgs"],"description":"`permissions` replaces the whole list rather than merging: a merge-only primitive would make a custom role a ratchet. A built-in role is 403 — the acceptance criterion, and also what `roles_write_tenant` would enforce anyway for a platform row.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateRoleBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"roleId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The updated role.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Role"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"402":{"description":"Custom roles are an enterprise feature.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Built-in roles cannot be edited, and `platform:*` is never grantable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"delete":{"operationId":"deleteRole","summary":"Delete a custom role","tags":["orgs"],"description":"Its grants go with it (`role_assignments` cascades from `roles`), and the audit row records how many, so an administrator can see who lost reach. A built-in role is 403.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"roleId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Deleted."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"402":{"description":"Custom roles are an enterprise feature.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Built-in roles cannot be edited, and `platform:*` is never grantable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/roles/{roleId}/assignments":{"get":{"operationId":"listRoleAssignments","summary":"List who holds a role","tags":["orgs"],"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"roleId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The grants.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoleAssignmentList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"post":{"operationId":"assignRole","summary":"Grant a role to a principal","tags":["orgs"],"description":"Additive, never subtractive — the resolver takes the union of every live grant. `scopeId` names the team, production or session the grant is confined to and is resolved against this tenant before the row is written, so another organisation's team id is a 404 rather than a grant against something invisible.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssignRoleBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"roleId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The grant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoleAssignment"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"402":{"description":"Custom roles are an enterprise feature.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Built-in roles cannot be edited, and `platform:*` is never grantable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"That principal already holds this role in this scope.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/roles/{roleId}/assignments/{assignmentId}":{"delete":{"operationId":"revokeRoleAssignment","summary":"Revoke a grant","tags":["orgs"],"description":"No entitlement gate: an organisation whose plan has lapsed must still be able to take a grant back. Only creating one needs the feature.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"roleId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"assignmentId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Revoked."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/sso/providers":{"get":{"operationId":"listIdentityProviders","summary":"List this organisation's identity providers","tags":["orgs"],"description":"Administrators only. The response never carries a client secret — `hasClientSecret` says whether one is stored, and the value lives in Secrets Manager under a per-provider path.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The providers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdentityProviderList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"post":{"operationId":"createIdentityProvider","summary":"Add an OIDC or SAML identity provider","tags":["orgs"],"description":"The row is written first and the matching Cognito identity provider second, in one transaction: a pool failure rolls the row back, so an organisation is never left claiming a federation that does not exist.\n\nFor SAML, paste the descriptor as `metadataXml`. It is parsed and validated before anything is saved — a document with no `entityID`, no `SingleSignOnService`, no signing certificate or an `http:` endpoint is a 422 and writes nothing. A `DOCTYPE` is refused outright rather than handled.\n\n`clientSecret` is write-only. It goes to Secrets Manager and what is stored on the row is the path.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateIdpBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The provider.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdentityProvider"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"402":{"description":"Single sign-on is not included in this organisation's plan.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Cognito or the secret store could not be reached.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/sso/providers/{idpId}":{"get":{"operationId":"getIdentityProvider","summary":"Read one identity provider","tags":["orgs"],"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"idpId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The provider.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdentityProvider"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"patch":{"operationId":"updateIdentityProvider","summary":"Change a provider's configuration or its group mapping","tags":["orgs"],"description":"`groupRoleMap` replaces the whole list rather than merging, so removing a mapping is expressible. Changes apply at each member's **next login** — the map is read on every sign-in and the grants it names are reconciled then, which is why there is no backfill.\n\nA provider cannot change between OIDC and SAML: delete it and add the other kind. The memberships bound to it have to be re-established either way.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateIdpBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"idpId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The updated provider.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdentityProvider"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"402":{"description":"Single sign-on is not included in this organisation's plan.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Cognito or the secret store could not be reached.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"delete":{"operationId":"deleteIdentityProvider","summary":"Remove an identity provider","tags":["orgs"],"description":"Removes the Cognito identity provider and **suspends the memberships bound to it** — those people's only way in has just been deleted, and a membership that cannot be exercised and cannot be seen to be broken is worse than one marked suspended. Domains that routed here stop capturing. The response says how many of each, so an administrator can see the cost.\n\nNo entitlement gate: an organisation whose plan has lapsed must still be able to take its federation down.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"idpId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"What the removal cost.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteIdpResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Cognito or the secret store could not be reached.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/sso/providers/{idpId}/test":{"post":{"operationId":"testIdentityProvider","summary":"Report what a real sign-in through this provider produced","tags":["orgs"],"description":"The browser performs the login against the hosted UI in a popup and posts the claims it received here; this endpoint says what they *mean* — which groups were seen, which of them the mapping recognises, and the org role and role keys a real login would grant.\n\nThe API deliberately does not perform the login itself. Doing so would mean a second, server-side, half-implemented OIDC client beside the one the browser already has, and a federated credential handled in a place it does not need to be.\n\nNothing is stored but the claim names and the mapping outcome — no token, no secret.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestConnectionBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"idpId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"What the claims mean here.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestConnectionResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"402":{"description":"Single sign-on is not included in this organisation's plan.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Cognito or the secret store could not be reached.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/sso/domains":{"get":{"operationId":"listOrgDomains","summary":"List the email domains this organisation has claimed","tags":["orgs"],"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The domains.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgDomainList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"post":{"operationId":"claimOrgDomain","summary":"Claim an email domain","tags":["orgs"],"description":"The domain starts unverified with capture off, and the response carries the exact TXT record to publish. A domain can only ever be claimed once across all of Tablecast — first claim wins — so a second organisation claiming it is a 409 that says a claim exists and deliberately does not say whose.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClaimDomainBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The claim, and the record to publish.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgDomain"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"402":{"description":"Single sign-on is not included in this organisation's plan.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Cognito or the secret store could not be reached.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/sso/domains/{domainId}/verify":{"post":{"operationId":"verifyOrgDomain","summary":"Check DNS for the verification record","tags":["orgs"],"description":"Idempotent and re-runnable, and the same code path the daily sweep uses so the two cannot disagree. A previously verified domain that now fails **loses** its verification and its capture mode: DNS no longer says this organisation controls it, and continuing to route that domain's logins on the strength of a check that passed last week is the failure a takeover would exploit.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"domainId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"What DNS said.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainVerification"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"402":{"description":"Single sign-on is not included in this organisation's plan.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Cognito or the secret store could not be reached.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/sso/domains/{domainId}":{"patch":{"operationId":"updateOrgDomain","summary":"Set a domain's capture mode or the provider it routes to","tags":["orgs"],"description":"`none` ignores the domain at login, `suggest` offers the provider beside the password form, `force` sends the user straight to it. Both non-`none` modes require the domain to be **verified** and a provider to be attached: capture on an unverified domain would let anybody who can type a domain name redirect that domain's logins at their own IdP.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDomainBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"domainId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The updated domain.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgDomain"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"402":{"description":"Single sign-on is not included in this organisation's plan.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Cognito or the secret store could not be reached.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"delete":{"operationId":"releaseOrgDomain","summary":"Release a claimed domain","tags":["orgs"],"description":"No entitlement gate: giving a domain back must work on any plan.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"domainId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Released."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/teams":{"post":{"operationId":"createTeam","summary":"Create a team","tags":["teams"],"description":"Creates the team, its `team_settings` defaults and a `team_lead` membership for the caller.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTeamBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The team and its settings.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamWithSettings"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The slug is already used in this organisation.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"get":{"operationId":"listTeams","summary":"List the organisation's teams","tags":["teams"],"description":"Archived teams are hidden unless `includeArchived=true`.","parameters":[{"schema":{"type":"string"},"in":"query","name":"includeArchived","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The teams.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/teams/{teamId}":{"get":{"operationId":"getTeam","summary":"Read a team","tags":["teams"],"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"teamId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The team.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Team"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"patch":{"operationId":"updateTeam","summary":"Update a team","tags":["teams"],"description":"`archived: true` archives it, `false` restores it.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTeamBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"teamId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The updated team.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Team"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"delete":{"operationId":"archiveTeam","summary":"Archive a team","tags":["teams"],"description":"Archives rather than deletes: productions, sessions and recordings reference a team. Archived teams disappear from `GET /v1/orgs/{orgId}/teams`.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"teamId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The archived team.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Team"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Already archived.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/teams/{teamId}/settings":{"get":{"operationId":"getTeamSettings","summary":"Read team settings","tags":["teams"],"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"teamId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The settings.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamSettings"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"patch":{"operationId":"updateTeamSettings","summary":"Update team settings","tags":["teams"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTeamSettingsBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"teamId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The updated settings.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamSettings"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/teams/memberships":{"get":{"operationId":"listOrgTeamMemberships","summary":"List every team membership in the organisation","tags":["teams"],"description":"One row per person per team, across every team including archived ones — what the People surface needs to show a Teams column without a request per team.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The organisation's team memberships.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgTeamMembershipList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/teams/{teamId}/members":{"get":{"operationId":"listTeamMembers","summary":"List a team's members","tags":["teams"],"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"teamId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The roster.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamMemberList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"post":{"operationId":"addTeamMember","summary":"Add somebody to a team","tags":["teams"],"description":"The `enforce_team_member_in_org` trigger rejects a user who is not an *active* member of the same org; that rejection is surfaced as a 409 rather than a 500.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddTeamMemberBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"teamId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The team membership.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamMember"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The user is not an active member of this organisation.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Unknown team role.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/teams/{teamId}/members/{userId}":{"patch":{"operationId":"updateTeamMemberRole","summary":"Change somebody's role on a team","tags":["teams"],"description":"PATCH rather than PUT: the API gateway's CORS policy allows GET/POST/PATCH/DELETE, and every other role change in this API (`PATCH /v1/orgs/{orgId}/members/{membershipId}`) is a PATCH. Demoting the only `team_lead` is refused for the same reason removing them is.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTeamMemberRoleBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"teamId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"userId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The updated team membership.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamMember"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such team, or that person is not on it.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"A team must keep at least one lead.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Unknown team role.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"delete":{"operationId":"removeTeamMember","summary":"Remove somebody from a team","tags":["teams"],"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"teamId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"userId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Removed."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"A team must keep at least one lead.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{id}/tracks":{"post":{"operationId":"createTrackUpload","summary":"Start a track and its multipart upload","tags":["uploads"],"description":"Creates the `tracks` row (status `recording`) and initiates a multipart upload at `org/<orgId>/sessions/<sessionId>/tracks/<trackId>.<ext>`, on the org's own bucket (R2 or S3, per `organizations.storage_provider`). A participant principal may only create tracks in its own session, for itself.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTrackUploadBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true,"description":"Session id"}],"security":[{"guestToken":[]},{"bearerAuth":[]},{"serviceToken":[]}],"responses":{"201":{"description":"The track and its upload.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTrackUploadResponse"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No database, or the org's object store is not configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/uploads/{uploadId}/parts/sign":{"post":{"operationId":"signUploadParts","summary":"Mint presigned URLs for a batch of parts","tags":["uploads"],"description":"At most 100 parts per call, each URL valid for 15 minutes. Send each part's returned `headers` verbatim and send nothing else: they are inside the URL's SigV4 signature, so omitting one and adding one both fail with 403 SignatureDoesNotMatch. Which header you get depends on the org's object store, so declare both digests and send back whatever it asks for: `x-amz-checksum-sha256` on an S3-backed org, and `content-md5` on a Cloudflare R2 one, which answers 501 to `x-amz-checksum-sha256` but does enforce a signed `Content-MD5` (400 BadDigest on bytes that do not match it). `headers` is empty for a part you declared no digest for — the case a client hits when it mints URLs ahead of the bytes. Declare `sha256` in every case: `complete` verifies it against the ledger whatever the store did or did not check at upload time.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignPartsBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"uploadId","required":true}],"security":[{"guestToken":[]},{"bearerAuth":[]},{"serviceToken":[]}],"responses":{"200":{"description":"Presigned part URLs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignPartsResponse"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The upload is already complete or was aborted.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No database, or the org's object store is not configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/uploads/{uploadId}/parts/{partNo}":{"post":{"operationId":"recordUploadPart","summary":"Record a part the client has uploaded","tags":["uploads"],"description":"Records the ETag, digest and length the client got back from the store. This is a claim, not a proof: `complete` reconciles every part against `ListParts` before it will finish the upload. It exists so a client that reconnects does not have to re-derive its progress, and so the per-participant uploaded meter has a source.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordPartBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"uploadId","required":true},{"schema":{"type":"integer","minimum":1,"maximum":10000},"in":"path","name":"partNo","required":true}],"security":[{"guestToken":[]},{"bearerAuth":[]},{"serviceToken":[]}],"responses":{"200":{"description":"The recorded part.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordPartResponse"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The upload is already complete or was aborted.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No database, or the org's object store is not configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/uploads/{uploadId}":{"get":{"operationId":"getUploadStatus","summary":"The upload's parts, reconciled against the store","tags":["uploads"],"description":"The resume endpoint. Every part is checked against `ListParts`: one the store has that the ledger lost is adopted, and one the ledger believes in that the store cannot confirm is put back to `pending`. `uploadedPartNos` is what a resuming client may skip.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"uploadId","required":true}],"security":[{"guestToken":[]},{"bearerAuth":[]},{"serviceToken":[]}],"responses":{"200":{"description":"The reconciled upload.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadStatusResponse"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No database, or the org's object store is not configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/uploads/{uploadId}/complete":{"post":{"operationId":"completeUpload","summary":"Complete the upload after server-side verification","tags":["uploads"],"description":"Refuses unless every expected part is present in both the ledger and the store with matching ETags and digests, the parts add up to `totalBytes`, and the assembled object reports that length. Only then does the track become `verified` — the state the coordinator's end-session gate reads. A refusal is a 409 and marks the track `failed` with the reason; it is not retryable without re-uploading. A completed upload completes again idempotently.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompleteUploadBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"uploadId","required":true}],"security":[{"guestToken":[]},{"bearerAuth":[]},{"serviceToken":[]}],"responses":{"200":{"description":"The verified track.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompleteUploadResponse"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Verification failed, or the upload was aborted. The track is now `failed`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No database, or the org's object store is not configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/uploads/{uploadId}/abort":{"post":{"operationId":"abortUpload","summary":"Abandon the upload","tags":["uploads"],"description":"Aborts the multipart upload at the store and marks the track `failed`, so the producer can waive it or promote the safety-net copy. Idempotent, and a store that has already forgotten the upload counts as success.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AbortUploadBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"uploadId","required":true}],"security":[{"guestToken":[]},{"bearerAuth":[]},{"serviceToken":[]}],"responses":{"200":{"description":"The aborted upload.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AbortUploadResponse"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The upload is already complete.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No database, or the org's object store is not configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/uploads/{uploadId}/local-export":{"post":{"operationId":"recordLocalExport","summary":"Record that the participant saved this track to their own disk","tags":["uploads"],"description":"The escape hatch's receipt (TAB-106). A participant on a network that blocks the object store can save their staged segments straight to disk; this writes the `audit_events` row that lets the producer's integrity gate tell 'this track is missing' from 'this track exists, on the guest's laptop'. It changes no state: the upload may still be in flight, and the export is a second copy rather than a replacement. Safe to call more than once — each call is one audit row.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LocalExportBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"uploadId","required":true}],"security":[{"guestToken":[]},{"bearerAuth":[]},{"serviceToken":[]}],"responses":{"200":{"description":"The export was recorded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LocalExportResponse"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No database, or the org's object store is not configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/productions/{productionId}/destinations":{"post":{"operationId":"createDestination","summary":"Add a stream destination","tags":["destinations"],"description":"The stream key is encrypted with the KMS envelope into `stream_key_ciphertext` and is never returned by this or any other endpoint. `outputPreset` is validated against the provider's own ceiling (YouTube 1080p60, Twitch 6000 kbps, `custom_rtmp` up to 4K60).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDestinationBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"productionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The destination, with the key masked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Destination"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The plan's destination allowance is already used.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"The preset exceeds what this provider accepts.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"KMS is not configured in this environment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"get":{"operationId":"listDestinations","summary":"List a production's stream destinations","tags":["destinations"],"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"productionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The destinations, keys masked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DestinationList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/destinations/{destinationId}":{"get":{"operationId":"getDestination","summary":"Read a stream destination","tags":["destinations"],"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"destinationId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The destination.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Destination"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"patch":{"operationId":"updateDestination","summary":"Update a stream destination","tags":["destinations"],"description":"`streamKey` present rotates the key, `null` clears it, absent leaves it alone. The provider is immutable — changing it would reinterpret the URL and key against different limits.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDestinationBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"destinationId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The updated destination.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Destination"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"KMS is not configured in this environment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"delete":{"operationId":"deleteDestination","summary":"Remove a stream destination","tags":["destinations"],"description":"A hard delete: `session_destination_runs` cascade with it, so history for past shows goes too. Set `enabled: false` to keep a destination out of the next go-live without losing the record.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"destinationId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Removed."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/destinations/{destinationId}/test":{"post":{"operationId":"testDestination","summary":"Probe a destination's ingest endpoint","tags":["destinations"],"description":"Opens a TCP (or TLS) connection and completes an RTMP handshake. **No stream key is sent** — the handshake carries no application message — so this is safe to run against a live ingest and can never leak a key. `unreachable` is a network problem, `protocol_error` is the wrong port, `ok` means the endpoint is real and anything still broken is the key or the account.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"destinationId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"What the probe found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DestinationTestResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"This destination has no probeable ingest URL.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/destination-presets":{"get":{"operationId":"listEncodingPresets","summary":"The encoding presets a destination can be given","tags":["destinations"],"description":"The catalogue behind `presetKey` (TAB-248). `geometry` is what decides which destinations share an encode: two destinations with the same geometry ride one render whatever they are pushed to, and a different one costs a second browser compositing the show. Every entry sits inside every provider's ingest ceiling, so choosing from this list can never produce a 422.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The catalogue.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EncodingPresetList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/integrations":{"get":{"operationId":"listIntegrations","summary":"List the channels this org has connected","tags":["destinations"],"description":"Returns the channel, its scopes and when its access token expires — never a token, and never the Secrets Manager path the token lives at. `status` is `expired` when the last refresh failed, with `lastError` saying why; that is the pair the go-live checklist reads.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The connected channels.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntegrationList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/integrations/{provider}/authorize-url":{"post":{"operationId":"getIntegrationAuthorizeUrl","summary":"Start connecting a channel","tags":["destinations"],"description":"Returns the provider's consent URL and the `state` to hand back on the callback. `redirectUri` must be one this environment is configured for — an open redirect on an OAuth callback is an account takeover, so an unlisted one is a 422 rather than a warning.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorizeUrlBody"}}}},"parameters":[{"schema":{"type":"string","enum":["youtube","twitch","google_calendar","microsoft_calendar"]},"in":"path","name":"provider","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Where to send the producer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorizeUrl"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"That redirect URI is not allowed here.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"This provider is not configured in this environment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/integrations/{provider}/connect":{"post":{"operationId":"connectIntegration","summary":"Finish connecting a channel","tags":["destinations"],"description":"Exchanges the authorization code server-side, stores the tokens in Secrets Manager under `/tablecast/<env>/orgs/<org_id>/integrations/<provider>/<id>`, and records the channel. Reconnecting the same channel updates the connection it already has rather than adding a second — two identical entries leave a producer unable to tell which one is live.\n\nA provider that issues no refresh token is a **409**, not a success: that connection would die within the hour and the producer would find out mid-show.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectIntegrationBody"}}}},"parameters":[{"schema":{"type":"string","enum":["youtube","twitch","google_calendar","microsoft_calendar"]},"in":"path","name":"provider","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The connected channel.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Integration"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The provider refused, or issued no refresh token.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"This provider, or the secret store, is not configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/integrations/{integrationId}":{"delete":{"operationId":"disconnectIntegration","summary":"Disconnect a channel","tags":["destinations"],"description":"Deletes the connection and its stored tokens. Destinations that pointed at it **survive** with a null integration — deleting somebody's destinations because they revoked a token would be a surprising amount of collateral for a reversible act. Go-live then refuses those destinations by name until the account is reconnected.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"integrationId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Disconnected."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/connectors":{"get":{"operationId":"listConnectors","summary":"List the accounts this org has connected","tags":["connectors"],"description":"Returns each connection's provider, account name, scopes, status and health — never a token, and never the Secrets Manager path the token lives at. `status` is `error` when the last refresh or health check failed, with `lastError` saying why.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The connected accounts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectionList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/connectors/{provider}/authorize-url":{"post":{"operationId":"getConnectorAuthorizeUrl","summary":"Start connecting an account","tags":["connectors"],"description":"Returns the provider's consent URL, the `state` to hand back on the callback, and a PKCE `codeVerifier` when the connector uses PKCE. `redirectUri` must be one this environment is configured for — an open redirect on an OAuth callback is an account takeover.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectorAuthorizeUrlBody"}}}},"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64},"in":"path","name":"provider","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Where to send the producer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectorAuthorizeUrl"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"That redirect URI is not allowed here.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"This connector is not configured in this environment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/connectors/{provider}/connect":{"post":{"operationId":"connectConnector","summary":"Finish connecting an account","tags":["connectors"],"description":"Exchanges the authorization code server-side, stores the tokens in Secrets Manager under `/tablecast/<env>/orgs/<org_id>/connectors/<provider>/<id>`, and records the account. Reconnecting the same account updates the connection it already has. A provider that issues no refresh token is a **409**, not a success: that connection would die at expiry.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectConnectorBody"}}}},"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":64},"in":"path","name":"provider","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The connected account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Connection"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The provider refused, or issued no refresh token.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"This connector, or the secret store, is not configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/connectors/{connectionId}/health":{"post":{"operationId":"checkConnectorHealth","summary":"Check (and refresh) a connection","tags":["connectors"],"description":"Refreshes the access token if it is near expiry, then probes the provider, and records the outcome on the connection. Returns the resulting status. A refresh or probe failure marks the connection `error` with the reason — the same state the badge shows.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"connectionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The connection's health.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectionHealth"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"This connector is not configured in this environment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/connectors/{connectionId}":{"delete":{"operationId":"disconnectConnector","summary":"Disconnect an account","tags":["connectors"],"description":"Deletes the connection and its stored tokens.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"connectionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Disconnected."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/state/ops":{"post":{"operationId":"applyControlOps","summary":"Apply production-state ops from a control surface","tags":["live"],"description":"Take, preview, supers, faders — the ops a Stream Deck, an OSC console, an MCP tool or a script sends. Tagged in the EDL with the `source` given here, attributed to the caller's participant row in this session, and rate-limited per source (30 ops/s for a controller). A refusal is a 200 with `accepted: false` and a typed `rejection.code`; only an unauthorised caller, an unknown session or an unreachable coordinator is an error status. Unauthorised attempts are written to `audit_events` as `session.control.denied`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ControlOpsBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"What the coordinator did with the ops.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ControlOpsResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The caller has a role on this production that cannot drive a session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such session, or not visible to this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No coordinator is running this session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/live/start":{"post":{"operationId":"startLive","summary":"Go live to one or more destinations","tags":["live"],"description":"Creates a `session_destination_runs` row per destination in `starting`, assembles each RTMP URL server-side from the destination's base URL and its decrypted stream key, and hands them to the session coordinator. The assembled URLs exist only in memory for the duration of the call; nothing logs or stores them. Progress after this point arrives by LiveKit webhook.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GoLiveBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"202":{"description":"The runs, all `starting`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LiveState"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such session, or a destination that is not visible to this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Already live, a disabled destination, or over the plan's allowance.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"The coordinator or KMS is unreachable; nothing was started.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/live/destinations":{"post":{"operationId":"addLiveDestination","summary":"Add a destination to a show that is already live","tags":["live"],"description":"When a composite is already encoding this destination's geometry over its transport, the destination joins it with LiveKit's `UpdateStream` and **nothing else on that encode is disturbed**. When nothing on air matches, a second composite is started — the show gains a render, which is what a 9:16 vertical cut costs. `applied` on the response says which happened, so a producer is never left guessing whether their other streams took a hit.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LiveDestinationBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"202":{"description":"The live state after the destination was added.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LiveState"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such session, or a destination that is not visible to this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Not live, already streaming there, or over the plan's allowance.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"The coordinator is unreachable; the run stays `starting`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/live/destinations/{destinationId}":{"delete":{"operationId":"removeLiveDestination","summary":"Stop streaming to one destination","tags":["live"],"description":"Stops **only that leg**. With siblings on the same encode, LiveKit's `UpdateStream` drops one URL and the others do not notice; alone on its encode, the composite stops with it. No other composite is touched either way. The run is left `ending` until LiveKit confirms — a row that claimed `ended` before the encoder let go is how a producer comes to believe they are off air when they are not.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"destinationId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The live state after the destination was removed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LiveState"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"This session is not streaming to that destination.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"The coordinator is unreachable; the run stays `ending`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/live/stop":{"post":{"operationId":"stopLive","summary":"Stop streaming","tags":["live"],"description":"Moves every running egress to `ending` and asks the coordinator to stop it. Returns 200 with `status: idle` when nothing was running — stopping a stopped stream is not an error, because the caller's intent is already satisfied.","requestBody":{"required":true,"content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"reason":{"type":"string","maxLength":200}}},{"type":"null"}]}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The runs after the stop request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LiveState"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"The coordinator is unreachable; the runs stay `ending`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/live":{"get":{"operationId":"getLiveState","summary":"Destination runs and their health","tags":["live"],"description":"`status` is the stored `live_object_status`; `health` is the derived word — `connecting`, `healthy`, `failed` — that a status light should show. Both are returned so a script can bind to the enum without reverse-engineering the display vocabulary.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The live state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LiveState"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/live/checklist":{"get":{"operationId":"getGoLiveChecklist","summary":"Pre-flight before going live","tags":["live"],"description":"A list of reasons rather than a verdict: every item carries an outcome, a sentence a human can act on, and whether it *blocks*. An item blocks only when going live anyway would be worse than not going live — a destination with no ingest URL cannot be pushed to, so starting the encode to find out costs the show its first thirty seconds. Everything else warns, because a checklist that blocks on things a producer knows are fine is one people learn to click through.\n\nNothing here calls a provider: every check is answerable from rows we already hold, so the panel can poll it. `POST .../live/start` runs the same list and refuses on the same blockers.","parameters":[{"schema":{"type":"string"},"in":"query","name":"destinationIds","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"What is and is not ready.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GoLiveChecklist"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/live/health":{"get":{"operationId":"getDestinationHealth","summary":"Every output's state, bitrate, uptime and restart history","tags":["live"],"description":"The read behind the destinations panel (TAB-245). Lists **every** output of the session, finished ones included — \"what happened to the Twitch leg?\" is asked after a show at least as often as during it. `uptimeMs` is computed server-side from the run's own timestamps rather than left to a browser to subtract from `startedAt`, because a laptop that is ninety seconds fast produces a readout a producer stops believing. Failures also arrive on the `session.state` realtime channel as `session.destination`, which is what makes them visible within three seconds without polling this at that rate.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"What every output is doing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DestinationHealth"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/embed":{"put":{"operationId":"setSessionEmbed","summary":"Turn the program embed on or off","tags":["live"],"description":"Switching the embed **off** moves `tokensValidFrom` forward, which kills every viewer token ever issued — the page polls `GET /v1/embed/{token}` every two seconds and tears its connection down on the refusal. Switching it back **on** moves it forward too: turning an embed off and on again is a new audience, and the links a producer cut do not start working again.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmbedSettingsBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The embed's settings.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionEmbed"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The session is canceled; it has nothing to embed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/embed/tokens":{"post":{"operationId":"mintEmbedToken","summary":"Mint a viewer token for the program embed","tags":["live"],"description":"**Returns the credential once.** Anyone holding the token can watch this one session until it expires; it is scoped to this session by a signed claim, so it cannot be moved to another show even inside the same org. Short-lived by default because a token in an `<iframe src>` ends up in a partner's page source, a CDN log and somebody's bookmarks.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The token and the URL to embed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MintedEmbedToken"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The embed is switched off for this session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"EMBED_JWT_SECRET is not configured in this environment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/live/destinations/{destinationId}/playback":{"get":{"operationId":"getPlaybackUrl","summary":"The HLS playback URL for one destination","tags":["live"],"description":"Returns a manifest URL carrying a signed playback token (TAB-241). The manifest itself is served by `GET /v1/hls/{runId}/index.m3u8`, trimmed to this destination's DVR window and with every segment URI replaced by a presigned storage URL — so the media never passes through Tablecast. `state: pending` while LiveKit has yet to close the first segment, which is the ordinary state for the first few seconds of a show.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"destinationId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Where to point a player.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaybackUrl"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such destination, or it has never published HLS in this session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"EMBED_JWT_SECRET is not configured in this environment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/embed/{token}":{"get":{"operationId":"getEmbedSession","summary":"What a viewer token buys, and whether it still buys it","tags":["embed"],"description":"Called by the embed page on load and every `pollSeconds` after. Returns a **fresh** subscribe-only LiveKit token each time. A producer disabling the embed moves the row's `tokensValidFrom` forward, so every token minted before that instant stops verifying and this endpoint answers 403 — which is what bounds the cut to one poll interval. Unauthenticated by design: the token in the path is the credential.","parameters":[{"schema":{"type":"string","minLength":16,"maxLength":512},"in":"path","name":"token","required":true}],"security":[],"responses":{"200":{"description":"The session and how to reach it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmbedSession"}}}},"403":{"description":"The token is invalid, expired, or was issued before the embed was reset.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Embeds are not configured in this environment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/embed/{token}/whep":{"post":{"operationId":"embedWhep","summary":"WHEP SDP exchange for the program (gateway-dependent)","tags":["embed"],"description":"**Not available on a stock deployment.** LiveKit's Egress has no WHEP output — it writes files, segments and RTMP/SRT streams — so a WHEP endpoint has to be a gateway in front of it, configured as `EMBED_WHEP_BASE_URL`. Without one this answers 501 and names the supported sub-second path: the `livekit` transport from `GET /v1/embed/:token`, which the embed player uses and which has fewer hops than a WHEP proxy would.","parameters":[{"schema":{"type":"string","minLength":16,"maxLength":512},"in":"path","name":"token","required":true}],"security":[],"responses":{"403":{"description":"The token is invalid, expired, or the embed is switched off.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"501":{"description":"No WHEP gateway is configured; use the `livekit` transport.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Embeds are not configured in this environment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/hls/{runId}/index.m3u8":{"get":{"operationId":"getHlsPlaylist","summary":"The HLS manifest for one destination run","tags":["embed"],"description":"Serves LiveKit's playlist, trimmed to the destination's DVR window and with every segment URI replaced by a presigned storage URL. The window is measured in **media time** — summed `EXTINF` durations from the live edge — and `EXT-X-MEDIA-SEQUENCE`, `EXT-X-DISCONTINUITY-SEQUENCE` and `EXT-X-PROGRAM-DATE-TIME` are all recomputed from the source rather than stamped from a clock. Content type `application/vnd.apple.mpegurl`.","parameters":[{"schema":{"type":"string","minLength":16,"maxLength":4096},"in":"query","name":"token","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"runId","required":true}],"security":[],"responses":{"403":{"description":"The playback token is invalid or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such run, or its playlist has not been written yet.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Object storage or embed signing is not configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/calendar/{token}.ics":{"get":{"operationId":"getCalendarFeed","summary":"An iCalendar subscription of one person's upcoming sessions","tags":["calendar"],"description":"Paste this URL into Google Calendar, Outlook or Apple Calendar as a subscribed calendar. It is fetched by *their* servers on their own schedule, so it carries no credential but the URL itself — mint one with `POST /v1/calendar/feed-tokens` and revoke it with `DELETE`.\n\nEvery timestamp is a UTC instant (RFC 5545 §3.3.5 form 2). That is deliberate and not a shortcut: a `TZID` local time requires a `VTIMEZONE` transcribing that zone's daylight rules, and a subtly wrong one produces a file that opens fine and puts the meeting in the wrong hour. Clients convert a UTC instant into the viewer's own zone, which is what a guest in another country wants; the producer's zone travels as `X-WR-TIMEZONE` and in each event's description.\n\nEach event carries a `VALARM` 15 minutes before. Cancelled sessions appear with `STATUS:CANCELLED` rather than disappearing — a feed is a statement of everything, and a dropped event would linger in the subscriber's calendar for ever.","parameters":[{"schema":{"type":"string","minLength":20,"maxLength":200},"in":"path","name":"token","required":true,"description":"The opaque feed token. The URL is the credential."}],"security":[],"responses":{"404":{"description":"No such feed. A revoked token and an invented one answer alike.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/calendar/feed-tokens":{"get":{"operationId":"listCalendarFeedTokens","summary":"This person's calendar subscriptions","tags":["calendar"],"description":"Live subscriptions only. Never returns a token or a URL: only the sha256 is stored, so the URL genuinely cannot be shown twice. `lastUsedAt` is approximate to the hour — a calendar client polls constantly, and an exact value would be a write per poll per subscriber for a field nobody reads twice.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The subscriptions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalendarFeedTokenList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"post":{"operationId":"createCalendarFeedToken","summary":"Mint a calendar subscription URL","tags":["calendar"],"description":"**The URL comes back exactly once.** Only its sha256 is stored — the same shape a session invite's token has — so nothing, including this endpoint on a second call, can produce it again. Losing it means minting another and revoking this one, which is two clicks.\n\nDeliberately not an HMAC-signed URL, even though that would need no row: an HMAC token cannot be revoked without rotating a secret every other outstanding token also depends on, and revoking one person's subscription must not break everybody's.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCalendarFeedToken"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The subscription, and its URL — once.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalendarFeedTokenCreated"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/calendar/feed-tokens/{tokenId}":{"delete":{"operationId":"revokeCalendarFeedToken","summary":"Stop a calendar subscription working","tags":["calendar"],"description":"The feed URL answers 404 from the next poll onwards. Recorded as `revokedAt` rather than deleted, so a support conversation can still answer \"when did this stop working, and who turned it off\".\n\nA token that is not yours, and one that is already revoked, are the same 404: probing ids must not reveal anything about somebody else's subscriptions.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"tokenId","required":true,"description":"Feed token id"},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Revoked."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/internal/livekit/webhook":{"post":{"operationId":"receiveLiveKitWebhook","summary":"LiveKit egress webhook receiver","tags":["internal"],"description":"Verifies the `Authorization` JWT against the LiveKit API secret, then applies `egress_started` / `egress_updated` / `egress_ended` to `egress_jobs` and `session_destination_runs`. Idempotent: replays are recognised by event id and out-of-order events are dropped rather than moving a finished run backwards. The raw event is stored with every stream URL redacted — LiveKit echoes the ingest URL with the stream key on the end.","responses":{"200":{"description":"Acknowledged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookAck"}}}},"401":{"description":"The signature did not verify.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"LiveKit credentials or the database are not configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/recordings":{"get":{"operationId":"listRecordings","summary":"List the organisation's recordings","tags":["recordings"],"description":"One item per session, with its track, render and transcript status rolled up. Only productions the caller can reach are included — an org owner or admin sees the whole org, everybody else sees the productions their team or per-show membership covers. Sorted newest first by started, else scheduled, else created date; page with `cursor`.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"productionId","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"teamId","required":false},{"schema":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"in":"query","name":"from","required":false},{"schema":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"in":"query","name":"to","required":false},{"schema":{"anyOf":[{"type":"string","enum":["scheduled","lobby","live","ending","complete","failed","canceled"]},{"type":"array","items":{"type":"string","enum":["scheduled","lobby","live","ending","complete","failed","canceled"]}}]},"in":"query","name":"status","required":false},{"schema":{"type":"string","minLength":1,"maxLength":200},"in":"query","name":"q","required":false},{"schema":{"default":"false","type":"string","enum":["true","false"]},"in":"query","name":"archived","required":false},{"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":48},{"maxItems":24,"type":"array","items":{"type":"string","minLength":1,"maxLength":48}}]},"in":"query","name":"tags","required":false},{"schema":{"default":25,"type":"integer","minimum":1,"maximum":100},"in":"query","name":"limit","required":false},{"schema":{"type":"string","minLength":1,"maxLength":512},"in":"query","name":"cursor","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The page of recordings.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordingList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Bad filter or cursor.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/recordings/{sessionId}":{"get":{"operationId":"getRecording","summary":"Read one recording","tags":["recordings"],"description":"The session, its production and participants, every ISO track with its integrity state, the renders, a transcript summary and the server-side egress jobs that acted as the safety net. `tracks.kind = comms` is excluded by construction.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The recording.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordingDetail"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"patch":{"operationId":"updateRecording","summary":"Rename, retag, archive or restore a recording","tags":["recordings"],"description":"Needs `recordings:manage` on the production. `archived: true` takes the recording out of the default list and leaves every download working; `deleted: false` restores a soft-deleted recording, and is `gone` once its 30-day window has closed. Tags are lower-cased and de-duplicated. Every change writes an `audit_events` row.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordingPatch"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The recording's management state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordingManagementState"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"410":{"description":"The restore window has closed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"delete":{"operationId":"deleteRecording","summary":"Soft-delete a recording","tags":["recordings"],"description":"Hides the recording everywhere and schedules its purge 30 days out; `PATCH` with `{\"deleted\": false}` restores it inside that window. Refused with a 409 when the organisation's `recordingDefaults.legalHold` is set — a hold is not something a producer can start a countdown against. Downloads stop working immediately, not at purge time.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The recording's management state, now deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordingManagementState"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The organisation is under legal hold.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/tracks/{trackId}/download":{"get":{"operationId":"downloadTrack","summary":"Get a signed download URL for one ISO track","tags":["recordings"],"description":"Returns a presigned GET on the organisation's bucket (R2 or S3), valid for 15 minutes, with the filename `<production>-<episode>-<participant>-<kind>.<ext>` signed into it. Every call writes a `recordings.download` row to `audit_events`. Comms tracks and tracks with nothing uploaded yet are `not-found`.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"trackId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The signed URL.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DownloadLink"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No object-storage credentials are configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/renders/{renderId}/download":{"get":{"operationId":"downloadRender","summary":"Get a signed download URL for a render","tags":["recordings"],"description":"The same contract as the track download, for a finished `renders` row — the program master, a re-switch, a clip or a multicam bundle. A render that has not succeeded yet is `not-found`. `artifact=poster` (TAB-534) signs the still the job took off the finished render instead of the render itself; a render with no poster answers `not-found` for the poster and keeps serving its media.","parameters":[{"schema":{"default":"media","type":"string","enum":["media","poster","captions"]},"in":"query","name":"artifact","required":false},{"schema":{"type":"string","minLength":2,"maxLength":35},"in":"query","name":"language","required":false},{"schema":{"default":"vtt","type":"string","enum":["vtt","srt"]},"in":"query","name":"format","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"renderId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The signed URL.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DownloadLink"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No object-storage credentials are configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/recordings/{sessionId}/captions":{"get":{"operationId":"listRecordingCaptions","summary":"The caption languages a recording has, for a player's menu","tags":["recordings"],"description":"One entry per language the session has transcripts in — the spoken original and every translation (TAB-561) — each with the URL of the whole WebVTT file and of the segmented HLS rendition. `hlsMediaTags` is the same list as `#EXT-X-MEDIA:TYPE=SUBTITLES` lines, for a master playlist.\n\nCues are derived from the transcripts on request, so a corrected word reaches the next viewer; they are on the **session clock**, which is the clock the proxy ladder is on. Takes a member bearer, a guest token for the same session, or a share token — the same three the renditions routes take, because this is the menu beside that player.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]},{"shareToken":[]}],"responses":{"200":{"description":"The recording's caption languages.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionCaptionList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"A member bearer with no `x-tablecast-org-id`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/recordings/{sessionId}/captions/{language}":{"get":{"operationId":"getRecordingCaptionTrack","summary":"One language's captions, as WebVTT or as an HLS subtitle rendition","tags":["recordings"],"description":"`format=vtt` (default) is the whole track. `format=m3u8` is the segmented rendition's media playlist, and `format=segment&index=<n>` is one of its parts — the shape an HLS player wants, where a three-hour file is not one a player will fetch to show one line. Segments repeat a cue that straddles a boundary, whole, so a viewer who joins mid-show sees the sentence that is still on screen.\n\nThe body is `text/vtt` or `application/vnd.apple.mpegurl`; there is no 200 schema for that reason.","parameters":[{"schema":{"default":"vtt","type":"string","enum":["vtt","m3u8","segment"]},"in":"query","name":"format","required":false},{"schema":{"type":"integer","minimum":0,"maximum":9007199254740991},"in":"query","name":"index","required":false},{"schema":{"type":"integer","minimum":1,"maximum":60},"in":"query","name":"targetSeconds","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","minLength":2,"maxLength":35},"in":"path","name":"language","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]},{"shareToken":[]}],"responses":{"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such recording, or no captions in that language.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/renders/{renderId}/captions":{"get":{"operationId":"listRenderCaptions","summary":"The caption sidecars delivered with a render, signed","tags":["recordings"],"description":"One entry per language the conform wrote beside this render, each with a signed URL for the WebVTT and the SubRip file. The same objects `GET /v1/renders/{id}/download?artifact=captions&language=<tag>` signs one at a time; this is the menu a downloads dialog or a share page needs in a single round trip.\n\nA render taken before TAB-563 — or one whose session had no transcripts — answers 200 with an empty list, not 404: the render is fine, it simply has no sidecars, and a 404 would be a statement about the render.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"renderId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The render's caption files.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenderCaptionList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No object-storage credentials are configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/recordings/{sessionId}/shares":{"post":{"operationId":"createRecordingShare","summary":"Create an expiring share link","tags":["recordings"],"description":"Needs `recordings:manage`. Returns the token **once** — only its sha256 is stored, so a lost link is revoked and re-created rather than read back. `scope` chooses what the link exposes, and `itemIds` narrows `tracks`/`renders` to specific items (each verified to belong to this recording). A `passcode` is stored as a scrypt digest and never returned. Comms tracks can never be shared.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordingShareCreate"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The share, and its token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordingShareCreated"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"get":{"operationId":"listRecordingShares","summary":"List a recording's share links","tags":["recordings"],"description":"Newest first, revoked and expired ones included — a producer needs to see that a link was revoked, not just that it is missing. The tokens are not here and cannot be.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The share links.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordingShareList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/recordings/{sessionId}/shares/{shareId}":{"delete":{"operationId":"revokeRecordingShare","summary":"Revoke a share link","tags":["recordings"],"description":"Takes effect immediately: the public route answers 410 from the next request on. The row is kept rather than deleted so the audit trail and the view count survive. Revoking an already-revoked link is a no-op.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"shareId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Revoked."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/shared/{token}":{"get":{"operationId":"getSharedRecording","summary":"Read a shared recording (public)","tags":["recordings"],"description":"The one unauthenticated read in this API. Answers 404 for a token that never existed, 410 for one that has expired, been revoked or run out of views, and 401 for a passcode-protected link with no unlock token — get one from `POST /v1/shared/{token}/unlock` and send it as `Authorization: Bearer`. The payload is built from scratch rather than filtered from the private detail view, and carries presigned downloads for the shared scope only. Rate-limited per client address.","parameters":[{"schema":{"type":"string","minLength":40,"maxLength":200},"in":"path","name":"token","required":true}],"security":[],"responses":{"200":{"description":"The shared recording.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SharedRecording"}}}},"401":{"description":"This link needs its passcode.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"410":{"description":"The link has expired, been revoked, or run out of views.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Rate limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/shared/{token}/unlock":{"post":{"operationId":"unlockSharedRecording","summary":"Exchange a share passcode for a short-lived access token (public)","tags":["recordings"],"description":"Verifies the passcode with scrypt in constant time and returns a token good for 15 minutes, to be sent as `Authorization: Bearer` on `GET /v1/shared/{token}`. A wrong passcode is a flat 401 with no hint. Rate-limited harder than the read, because each attempt costs a key derivation. A link with no passcode is 409: there is nothing to unlock, and pretending otherwise would tell a stranger which links are protected.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SharedRecordingUnlock"}}}},"parameters":[{"schema":{"type":"string","minLength":40,"maxLength":200},"in":"path","name":"token","required":true}],"security":[],"responses":{"200":{"description":"The unlock token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SharedRecordingUnlockResult"}}}},"401":{"description":"Wrong passcode.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"That link has no passcode.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"410":{"description":"Gone","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Rate limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/recordings/{sessionId}/renditions":{"get":{"operationId":"listRecordingRenditions","summary":"List the CMAF proxy renditions of one recording","tags":["recordings"],"description":"Both rungs of every ISO and of the program master, with the row's status and — once it is `ready` — the segment index the editor's player appends. `manifest` is `{}` until then, and a `failed` row carries the encoder's stderr in `error`. Every `startMs` in a manifest is **session-clock ms, absolute**, which is what lets four cameras share one ruler with no sync step. Takes a member bearer, a guest token for the same session, or a share token (TAB-542) — a recording-scoped share sees the whole ladder, a render-scoped one sees only that render's rungs.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]},{"shareToken":[]}],"responses":{"200":{"description":"Every rendition of the recording.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenditionList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"A member bearer with no `x-tablecast-org-id`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/recordings/{sessionId}/playback-ticket":{"post":{"operationId":"issuePlaybackTicket","summary":"Presign a batch of CMAF segments for playback","tags":["recordings"],"description":"Up to 200 presigned segment GETs from `manifest.segments` starting at `fromIndex`, plus the presigned init segment, all valid for an hour. `nextIndex` is what to ask for next, or null when the batch reached the end of the manifest. The URLs are signed through the organisation's own storage target, so an R2 org and an S3 org get the same shape and the player never learns which it is. A rendition that is not `ready` is `conflict`: a ticket for a manifest that does not exist yet is a player that 403s on every segment.\n\nTakes a member bearer, a guest token for the same session, or a share token (TAB-542) — which is how the public share page plays a recording with no account at all.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaybackTicketRequest"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]},{"shareToken":[]}],"responses":{"200":{"description":"The ticket.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaybackTicket"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such recording, or no such rendition on it.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"That rendition has not finished encoding.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No object-storage credentials are configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/recordings/{sessionId}/master.m3u8":{"get":{"operationId":"getRecordingMasterPlaylist","summary":"The HLS master playlist for one recording, or one render of it","tags":["recordings"],"description":"One `#EXT-X-STREAM-INF` per ready rung of the chosen source — with the **measured** peak and average bit rate from the rendition's own manifest, its resolution and its RFC 6381 codecs — and one `#EXT-X-MEDIA:TYPE=SUBTITLES` per caption language (TAB-563), so a caption menu and a rung ladder come out of one document instead of two routes that can disagree.\n\nA master describes **one presentation**: name a `trackId` or a `renderId` (a conform or a clip is a render of its own), or name neither and get the program master if the session has one, else its first track. A render-scoped share token gets that render and nothing else — naming another source is a 404, not a 403.\n\nEach variant carries `X-TABLECAST-RENDITION`, the `recording_renditions` row id, which is what a player passes to `POST .../playback-ticket`. **This route signs nothing**: it costs no presigned URLs at all, so adding it does not spend any of the ticket budget.\n\nBody is `application/vnd.apple.mpegurl`; there is no 200 schema for that reason.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"trackId","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"renderId","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]},{"shareToken":[]}],"responses":{"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such recording, or no such source on it.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Nothing on that source has finished encoding yet.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/recordings/{sessionId}/renditions/{renditionId}/media.m3u8":{"get":{"operationId":"getRenditionMediaPlaylist","summary":"One rung's media playlist — the same window a playback ticket signs","tags":["recordings"],"description":"What a `#EXT-X-STREAM-INF` on the master points at: `#EXT-X-MAP` for the CMAF init segment and up to 200 presigned segments from `fromIndex`, all valid for an hour. It is **the playback ticket in playlist form** — the same batch, the same signer, the same budget — so a player that follows the master pays what a player that posts for a ticket pays, not twice. Within a warm container the two share one signature cache, so asking for both windows costs one set of signatures rather than two.\n\nThe window slides: `X-Tablecast-Next-Index` names the `fromIndex` of the next one, or is absent when this window reached the end (`#EXT-X-ENDLIST`). `#EXT-X-PLAYLIST-TYPE:VOD` is written only when the window *is* the whole recording — claiming VOD over a window makes a player treat segment 200 as the end of the show.\n\nBody is `application/vnd.apple.mpegurl`; there is no 200 schema for that reason.","parameters":[{"schema":{"default":0,"type":"integer","minimum":0,"maximum":9007199254740991},"in":"query","name":"fromIndex","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"renditionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]},{"shareToken":[]}],"responses":{"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such recording, or no such rendition on it.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"That rendition has not finished encoding.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No object-storage credentials are configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/recordings/{sessionId}/rendition-assets":{"post":{"operationId":"issueRenditionAssetUrl","summary":"Presign a rendition's waveform or thumbnail sprite asset","tags":["recordings"],"description":"A one-hour presigned GET for one of the three artefacts a rendition's `peaksKey`/`spritesKey` name: `peaks` (the merged waveform JSON), `sprites` (the sprite index, which names the sheets) or `sprite-sheet` (one sheet image, naming its `sheetIndex`). The request names which asset, never a storage key — the server resolves the object from the rendition row. A rendition this caller may not see, or one with no such asset yet, is `not-found`. Takes a member bearer, a guest token for the same session, or a share token (TAB-542) — a recording-scoped share sees the whole ladder, a render-scoped one sees only that render's rungs, exactly as `GET .../renditions` does.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenditionAssetRequest"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]},{"shareToken":[]}],"responses":{"200":{"description":"The signed URL.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenditionAssetLink"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such recording, no such rendition on it, or that asset does not exist yet.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No object-storage credentials are configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/recordings/{sessionId}/renditions/regenerate":{"post":{"operationId":"regenerateRecordingRenditions","summary":"Re-encode a recording's CMAF proxy ladder","tags":["recordings"],"description":"Announces the master to the proxy pipeline again, exactly as the upload-complete route does when it first arrives — so a regenerate is the same fact a second time, not a second kind of fact. Name a `trackId` or a `renderId`, or neither to re-encode every recording in the session. Needs `recordings:manage`: watching a proxy is reading, spending an encode is managing. 202, because the rendition rows are written by the pipeline and not by this request; segment keys are deterministic, so a re-run overwrites rather than duplicates.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegenerateRenditionsRequest"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"202":{"description":"What was queued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegenerateRenditionsQueued"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such recording, or no such master on it.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Nothing in this recording has a finished master yet.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"This API has no event bus configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/recordings/{sessionId}/claims":{"post":{"operationId":"createRecordingClaim","summary":"Mint a claim link for one account-less participant","tags":["recordings"],"description":"Returns the token **once**; only its sha256 is stored, so a lost link is re-minted rather than looked up, and re-minting replaces the previous one. The session must have ended and the participant must not already be linked to an account. A guest token may only name its own participant; a member bearer needs `recordings:manage`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateClaimBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]}],"responses":{"201":{"description":"The claim and its single-use token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedClaim"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"A guest may only claim their own recording.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The session has not finished, or that participant already has an account.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"get":{"operationId":"listRecordingClaims","summary":"List the claim offers on one recording","tags":["recordings"],"description":"Never the token, and never its digest. A guest token sees only its own participant's claim; a member bearer needs `recordings:manage` and sees them all.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]}],"responses":{"200":{"description":"The claims.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClaimList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/claims/preview":{"post":{"operationId":"previewRecordingClaim","summary":"What a claim link is offering, before accepting it","tags":["recordings"],"description":"The show, the seat and how many tracks — deliberately nothing about the other participants or the rest of the library. A link that was real and is no longer usable is 410, so somebody who was told to expect a recording learns that it expired rather than that it never existed.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClaimTokenBody"}}}},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"What this link offers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClaimPreview"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"That claim link is not valid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"410":{"description":"That claim link has expired or has already been used.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/claims/redeem":{"post":{"operationId":"redeemRecordingClaim","summary":"Link this account to the recording the token names","tags":["recordings"],"description":"Moves `user_id` onto the participant, gives the caller an `org_guest` membership of the owning organisation (`joined_via = 'claim'`) and writes `recordings.claim.redeem` to that organisation's audit log. Single-use: the second call on the same token is 410, and the test is in the UPDATE's own WHERE clause so two browsers racing on one link cannot both win.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClaimTokenBody"}}}},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"What was claimed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RedeemedClaim"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"That claim link is not valid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"That participant is already linked to another account.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"410":{"description":"That claim link has expired or has already been used.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/claims":{"get":{"operationId":"listClaimedRecordings","summary":"What was recorded of you in this organisation","tags":["recordings"],"description":"The claimant's own recordings view. Not the org library: somebody whose only standing is the `org_guest` membership a claim gave them sees their own tracks and nothing else. Each track is downloadable through `GET /v1/tracks/{trackId}/download`.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The claimed recordings.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClaimedRecordingList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/edit-projects":{"get":{"operationId":"listEditProjects","summary":"List the edits made from a session","tags":["edits"],"description":"Newest first. An edit is a derivative of the recording and shares its permissions.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The session's edit projects.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EditProjectList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such session, or not visible to this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"post":{"operationId":"seedEditProject","summary":"Seed an edit from a session","tags":["edits"],"description":"Replays the session's EDL into a `TimelineDoc`, lays the editable program over it, and attaches every ISO with the proxies the post pipeline announced. Returns the seeded document so the editor can open without a second round trip. A session with no EDL is still seeded — a show nobody switched is one program segment.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SeedEditProjectBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The seeded project and its first version.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SeededEditProject"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such session, or not visible to this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/transcript":{"get":{"operationId":"getSessionTranscript","summary":"The session's per-track transcripts, for the editor","tags":["edits"],"description":"One entry per ISO with a transcript: the sentence-level segments inline, and a short-lived presigned URL for the word-level file the browser fetches directly. The speaker comes from the **track**, never from diarization — we record one ISO per person, so it is known exactly. `wordsUrl` is null where no words file has been written yet. TAB-561: `languages` lists every language this session has a transcript in, and `?language=` swaps which one `tracks` carries — absent means the spoken original. Segment times are **session-clock ms** (`timeBase: \"session\"`), not the provider's track-relative clock: `sessionOffsetMs` is what was added, and is what to add to the words file's track seconds to reach the same timeline.","parameters":[{"schema":{"type":"string","minLength":2,"maxLength":35},"in":"query","name":"language","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The session's transcripts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionTranscript"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such session, or not visible to this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/edit-projects/{projectId}":{"get":{"operationId":"openEditProject","summary":"Open an edit project at its current version","tags":["edits"],"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"projectId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The project, its document and the current suggestions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenEditProject"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/edit-projects/{projectId}/versions":{"get":{"operationId":"listEditVersions","summary":"An edit project's version history","tags":["edits"],"description":"Newest first. Append-only: a save adds a version, it never replaces one.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"projectId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The version history.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EditVersionList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"post":{"operationId":"saveEditVersion","summary":"Save the edit as a new version","tags":["edits"],"description":"Appends a version and moves the project's pointer to it. Pass `expectedVersion` to be refused with a 409 when another tab has saved in the meantime.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveEditVersionBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"projectId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The version that was written.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SavedEditVersion"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Somebody else saved first; reload before saving.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"The document is not a valid `EditDoc`, or the save would empty a list it did not declare in `clear` (TAB-597).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/edit-projects/{projectId}/versions/{version}":{"get":{"operationId":"openEditVersion","summary":"Open one version of an edit project","tags":["edits"],"description":"What a producer reaches for when they want the cut they had before lunch.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"projectId","required":true},{"schema":{"type":"integer","minimum":1,"maximum":9007199254740991},"in":"path","name":"version","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The project at that version.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenEditProject"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such project, or no such version of it.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/render-presets":{"get":{"operationId":"listRenderPresets","summary":"List the render presets","tags":["renders"],"description":"The catalogue a render is queued against: a 16:9 master, a 9:16 clip, an audio-only cut and a caption sidecar. A constant rather than a per-org table, so two renders called `16:9 master` in two organisations are the same thing.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The catalogue.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenderPresetList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/renders":{"get":{"operationId":"listRenders","summary":"List the organisation's export queue","tags":["renders"],"description":"Queued and running renders by default; `filter=all` includes finished ones. Each entry carries the durable copy of its progress, so a page opened after the last realtime event went out still shows a bar. Live updates ride the `/jobs/{jobId}` channel.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"sessionId","required":false},{"schema":{"default":"active","type":"string","enum":["active","all"]},"in":"query","name":"filter","required":false},{"schema":{"type":"integer","minimum":1,"maximum":100},"in":"query","name":"limit","required":false},{"schema":{"type":"string"},"in":"query","name":"before","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The queue.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenderQueue"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/renders":{"post":{"operationId":"createRender","summary":"Queue a render of a session","tags":["renders"],"description":"Opens a `renders` row in `queued`. `mode=server` hands it to a worker; `mode=browser` is the in-page clip export, where this call reserves the row and the page fills it in. Needs `recordings:manage` on the production and the `editor` entitlement. An organisation may have ten renders outstanding at once.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"presetId":{"type":"string","enum":["master_16x9","clip_9x16","clip_1x1","audio_only","captions_vtt"]},"mode":{"default":"server","type":"string","enum":["browser","server"]},"editVersionId":{"anyOf":[{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},{"type":"null"}]},"rangeMs":{"type":"object","properties":{"startMs":{"type":"integer","minimum":0,"maximum":9007199254740991},"endMs":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["startMs","endMs"]},"overrides":{"type":"object","properties":{"width":{"type":"integer","minimum":2,"maximum":7680},"height":{"type":"integer","minimum":2,"maximum":4320},"fps":{"type":"integer","minimum":1,"maximum":120},"bitrateKbps":{"type":"integer","minimum":64,"maximum":200000},"captionMode":{"type":"string","enum":["off","burn","sidecar"]},"loudness":{"type":"string","enum":["podcast","program"]}}},"burnIn":{"type":"object","properties":{"language":{"type":"string","minLength":2,"maxLength":35}},"required":["language"]}},"required":["presetId"]}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The queued render.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenderQueueEntry"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"402":{"description":"The organisation's plan does not include the editor.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Too many renders are already outstanding.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/renders/{renderId}":{"get":{"operationId":"getRender","summary":"Get one render","tags":["renders"],"description":"The queue entry, including `params` — the read-only projection of what the job recorded about itself (TAB-586): which audio the conform used and why, how many cuts it laid and which sources they came from, every place it could not do what the document asked, and the reason a reaped render was given up on. A render queued before any of that was recorded answers 200 with `params.audio` null and `params.cuts.count` 0 — never an error.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"renderId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The render.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenderQueueEntry"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/renders/{renderId}/cancel":{"post":{"operationId":"cancelRender","summary":"Cancel a queued or running render","tags":["renders"],"description":"Records the request. A render that has not started is cancelled outright; one that is running stops within the worker's poll interval and writes `canceled` itself — so `stoppedImmediately` tells the two apart rather than leaving a caller to infer it. Idempotent: cancelling twice is not an error.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"renderId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The render, and whether it stopped immediately.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelRenderResponse"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The render has already finished.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/renders/{renderId}/retry":{"post":{"operationId":"retryRender","summary":"Retry a failed or cancelled render","tags":["renders"],"description":"Puts the **same** row back to `queued` — same id, same preset, same key — so the worker finds the steps its previous attempt completed and skips them. `resumingAfter` lists those steps, so a producer deciding whether to retry a twenty-minute conform can see that it will resume rather than start again.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"renderId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The re-queued render.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RetryRenderResponse"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Only a failed or cancelled render can be retried.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/clips":{"post":{"operationId":"createClip","summary":"Cut a vertical, square or 16:9 clip from an edit","tags":["renders"],"description":"Queues a `clip` render of `[inMs, outMs)` on the **edited** clock — a range of the finished program, not of the session. `aspect` picks the delivery shape and `strategy` decides where the crop window sits: `speaker-follow` reads the switch track (exact: it says which camera is on air and where in the frame it sits) and the transcript (exact per ISO, because Tablecast records one ISO per person) and then applies a centre-weighted, smoothed pan that snaps at every cut; `centre` never moves; `manual` follows `cropKeyframes`. Captions are burned in by default, styled for the aspect's social safe area. A shorthand for `POST /v1/sessions/{id}/renders` with the clip preset — same row, same queue, same download route, same `editor` entitlement.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"editVersionId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"inMs":{"type":"integer","minimum":0,"maximum":9007199254740991},"outMs":{"type":"integer","minimum":0,"maximum":9007199254740991},"aspect":{"default":"9:16","type":"string","enum":["9:16","1:1","16:9"]},"strategy":{"default":"speaker-follow","type":"string","enum":["speaker-follow","centre","manual"]},"cropKeyframes":{"maxItems":500,"type":"array","items":{"$ref":"#/components/schemas/ClipCropKeyframe"}},"name":{"type":"string","minLength":1,"maxLength":120},"captionMode":{"type":"string","enum":["off","burn","sidecar"]},"burnIn":{"type":"object","properties":{"language":{"type":"string","minLength":2,"maxLength":35}},"required":["language"]}},"required":["editVersionId","inMs","outMs"]}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The queued clip.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenderQueueEntry"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"402":{"description":"The organisation's plan does not include the editor.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Too many renders are already outstanding.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"get":{"operationId":"listClips","summary":"List a session's clips","tags":["renders"],"description":"Every `clip` render of this session, newest first — queued, running and finished. `params.clip` carries the aspect, the strategy, how many shots the window followed and whether it actually moved; `params.posterKey` is the still taken off the finished clip. The bytes come from the same `GET /v1/renders/{id}/download` a master does.","parameters":[{"schema":{"default":"all","type":"string","enum":["active","all"]},"in":"query","name":"filter","required":false},{"schema":{"type":"integer","minimum":1,"maximum":100},"in":"query","name":"limit","required":false},{"schema":{"type":"string"},"in":"query","name":"before","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The session's clips.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClipQueue"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/recordings/{sessionId}/transcripts":{"get":{"operationId":"listRecordingTranscripts","summary":"What languages this recording has a transcript in","tags":["recordings"],"description":"A catalogue, not the words: one entry per language, with how the text was produced (`asr` for the spoken original, `machine` for a translation, `human` once a person has taken one over), how many tracks carry it, and whether a translation is behind the source it was made from. `targetLanguages` is what the production asks for, so a client can tell a language that is missing from one that was never wanted.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The languages this recording has.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordingTranscriptLanguages"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such recording, or not visible to this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/recordings/{sessionId}/transcripts/{language}":{"get":{"operationId":"getRecordingTranscript","summary":"One language's transcripts for a recording","tags":["recordings"],"description":"Every ISO that has a transcript in this language, with the sentence-level segments inline and short-lived presigned URLs for the word-level JSON and the WebVTT sidecar. The language is matched case-insensitively; a translated transcript's **segment** timings are the source's exactly, while the word timings inside `wordsUrl` are interpolated within each segment — see the field's description.\n\nSegment times are **session-clock ms** (`timeBase: \"session\"`); `sessionOffsetMs` is what was added, and is what to add to the words file's track seconds.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","minLength":2,"maxLength":35},"in":"path","name":"language","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The transcripts in that language.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordingTranscript"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such recording, or no transcript in that language.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/recordings/{sessionId}/transcripts/translate":{"post":{"operationId":"translateRecordingTranscripts","summary":"Translate this recording's transcripts into its target languages","tags":["recordings"],"description":"Announces the recording's source transcripts to the translation worker, which produces one derived transcript per target language. 202, because the work is thousands of engine calls and does not fit in a request. Idempotent: a target whose translation is already current for this version of the source is left exactly where it is unless `force` is set, and a transcript a person has taken over is never overwritten at all. Needs `recordings:manage` — a viewer may read the words; spending a translation is managing the recording.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranslateTranscriptsBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"202":{"description":"What was announced.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranslateTranscriptsQueued"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such recording, or no such track on it.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"This recording has no finished transcript to translate yet.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"This API has no event bus configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/recordings/{sessionId}/transcripts/{language}/review":{"get":{"operationId":"getRecordingTranscriptReview","summary":"What still needs a human in this language","tags":["recordings"],"description":"Three kinds of flag, from three places. `language-unexpected` is TAB-560's allow-list answer — the same condition `transcription.language.unexpected` fires on. `low-confidence` is the provider's own per-segment score, where it gives one. `unmatched-pin` is a producer's correction whose speech has since changed, which is the only one of the three that is stored rather than re-derived on every read: the other two stop being true by themselves when the transcript is fixed, and this one cannot be recomputed once the source has moved on. Resolving any of them writes a dismissal.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","minLength":2,"maxLength":35},"in":"path","name":"language","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The review list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranscriptReview"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such recording, or no transcript in that language.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/recordings/{sessionId}/transcript-edits/{transcriptId}/{segmentIndex}":{"put":{"operationId":"putTranscriptSegmentEdit","summary":"Correct one transcript segment","tags":["recordings"],"description":"Stores the producer's words for one segment, pinned to the fingerprint of the speech under them. The pin survives a re-transcription or a re-translation by being re-seated against the new segments (same index and same source text, or the same source text at exactly one other index); a pin with nowhere to land is kept and flagged `unmatched-pin` rather than dropped, because it is a person's work. Needs `recordings:manage`: reading the words is `recordings:read`, changing what the show says is managing the recording.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SegmentEditBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"transcriptId","required":true},{"schema":{"type":"integer","minimum":-1,"maximum":9007199254740991},"in":"path","name":"segmentIndex","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The stored correction.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranscriptSegmentEdit"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such recording, transcript or segment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"delete":{"operationId":"deleteTranscriptSegmentEdit","summary":"Revert one transcript segment to the machine's words","tags":["recordings"],"description":"Removes the pin. 404 when there was none, so a client can tell 'I reverted it' from 'somebody else already had'.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"transcriptId","required":true},{"schema":{"type":"integer","minimum":-1,"maximum":9007199254740991},"in":"path","name":"segmentIndex","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Reverted."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such recording, transcript, or no correction on that segment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/recordings/{sessionId}/transcript-edits/{transcriptId}/{segmentIndex}/resolve":{"post":{"operationId":"resolveTranscriptSegmentReview","summary":"Mark one review flag as looked at","tags":["recordings"],"description":"For `unmatched-pin` this keeps the producer's words and stops asking about them. For a derived reason it writes a dismissal, which the review reading folds over so the segment stops being offered — keyed to the segment rather than to the transcript version, because somebody who has approved one sentence should not be asked again when an unrelated one is re-recognised. `-1` as the segment index addresses the transcript itself, which is where `language-unexpected` lives.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResolveReviewBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"transcriptId","required":true},{"schema":{"type":"integer","minimum":-1,"maximum":9007199254740991},"in":"path","name":"segmentIndex","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The mark that was written.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranscriptSegmentEdit"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such recording, transcript or segment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/recordings/{sessionId}/bundle":{"post":{"operationId":"requestRecordingBundle","summary":"Queue a bulk download of a recording as one zip","tags":["recordings"],"description":"Resolves the selection now and queues a `deliver` job; the zip is built by a worker and collected from `GET /v1/bundles/{jobId}`. The archive carries `manifest.json` (session and production metadata, each file's SHA-256 as recorded at upload, and its session-clock start offset) and a `README.txt`. Comms tracks are never included. A selection over 50 GB is refused here rather than half-built. An identical request that is still running returns the job already in flight (`created: false`) instead of starting a second one.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BundleCreate"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"202":{"description":"The queued job.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BundleAccepted"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"That recording has been deleted.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Nothing selected, or over the 50 GB ceiling.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/bundles/{jobId}":{"get":{"operationId":"getRecordingBundle","summary":"Read a bundle job, and download it once it is ready","tags":["recordings"],"description":"Poll until `status` is `succeeded`; `download` is null until then and carries a 15-minute presigned URL after. `skipped` names any file the worker had to leave out because its object had gone — a bundle short one ISO is worth more than no bundle, but it is never silent about it. Issuing the URL writes a `recordings.download` audit row, exactly as the single-file downloads do.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"jobId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The job, and the download when it is ready.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BundleStatus"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No object-storage credentials are configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/storage":{"get":{"operationId":"getOrgStorage","summary":"Storage usage and retention for an organisation","tags":["recordings"],"description":"Bytes by kind (recorded ISOs, post's proxies, program masters, other renders, bulk-download bundles), by production, the largest recordings, and month-by-month growth — all read from the rows that own the objects rather than from a bucket listing, so it is an account of what the platform believes it stores. The retention view lists healthy tracks whose `retain_until` has come due and soft-deleted recordings awaiting purge; the two are kept apart because they mean different things. Needs an owner, admin or billing role.","parameters":[{"schema":{"default":10,"type":"integer","minimum":1,"maximum":100},"in":"query","name":"topSessions","required":false},{"schema":{"default":12,"type":"integer","minimum":1,"maximum":60},"in":"query","name":"months","required":false},{"schema":{"default":50,"type":"integer","minimum":1,"maximum":500},"in":"query","name":"retentionLimit","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The storage report.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgStorageReport"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/chat":{"get":{"operationId":"listChatMessages","summary":"Chat history for a session","tags":["chat"],"description":"One page of a channel, oldest message first. A client loads history with no cursor on join *and* on reconnect — there is no incremental `since` parameter, because a client that dropped cannot know which message it missed.\n\n`channel` is `all` (default), `producers` or `private:<participantId>`. `producers` is the cue channel and is limited to the producer, director and host roles; a guest asking for it by name gets a 403 and no row count. A private channel returns both directions of the conversation between the caller and that participant.\n\nAccepts a member access token with `x-tablecast-org-id`, or a participant-scoped guest token whose own claims pin the org and the session.","parameters":[{"schema":{"type":"string","maxLength":64},"in":"query","name":"channel","required":false,"description":"`all`, `producers`, or `private:<participantId>`. Defaults to `all`."},{"schema":{"type":"string","maxLength":512},"in":"query","name":"cursor","required":false,"description":"From a previous page's `nextCursor`; walks backwards in time."},{"schema":{"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]}],"responses":{"200":{"description":"One page of chat, oldest first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatPage"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Not a participant in this session, or not allowed on that channel.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such session in this organisation.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"An unrecognised channel, or a cursor this endpoint did not issue.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"post":{"operationId":"postChatMessage","summary":"Send a chat message","tags":["chat"],"description":"Persists the message with its session-clock timestamp, then publishes it to the session's realtime channel. The message is stored before it is published, so a 201 means it will be in the next history load even if the realtime tier is unavailable.\n\n`clockMs` is the session clock, not wall time: only the client knows its own offset-corrected clock, so it supplies the value and the server bounds it. Omit it before the coordinator's clock is up.\n\nRate limited per participant. Bodies are trimmed and capped; an empty message is a 422.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostChatBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]}],"responses":{"201":{"description":"The stored message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatMessage"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Not a participant, or not allowed to post on that channel.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such session, or a private channel addressed to a non-participant.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Empty or over-long body, an unrecognised channel, or a bad `clockMs`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Too many messages from this participant.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/chat/{messageId}":{"delete":{"operationId":"deleteChatMessage","summary":"Delete a chat message","tags":["chat"],"description":"The author may retract their own message; a producer, director or host may remove anyone's. The row is deleted and a `chat.deleted` event is published, so a client that already rendered the message drops it rather than waiting for a reload.\n\nA message on the producers channel is a 404 — not a 403 — to a caller who may not read that channel: refusing differently would tell a guest the cue channel exists.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"messageId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]}],"responses":{"204":{"description":"Deleted."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Somebody else's message, and the caller is not a moderator.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/chat/cues":{"post":{"operationId":"sendSessionCue","summary":"Send a silent cue","tags":["chat"],"description":"A producer, director or host puts a line on somebody's screen without speaking over them: a note, a wrap countdown, a `you're live` indicator.\n\n`target` is `all` or a participant id. A cue to one person is delivered to that person and readable afterwards by them and by the producers — nobody else is told it exists.\n\nA `countdown` needs `seconds`; the response carries `deadlineClockMs`, an **absolute** session-clock instant. Clients count down to that rather than from a duration, so two screens cannot drift apart and a cue that arrives late still shows the right number.\n\nPersisted first, published second: a 201 means the cue is in the session log whether or not the realtime tier was reachable.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendCueBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]}],"responses":{"201":{"description":"The stored cue.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Cue"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Not a participant, or not a producer, director or host.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such session, or a cue addressed to somebody who is not in it.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Empty or over-long body, an unknown kind, or a countdown with no `seconds`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"get":{"operationId":"listSessionCues","summary":"The cue log for a session","tags":["chat"],"description":"Newest first. A producer, director or host sees every cue in the session — that is the record. Anybody else sees only the cues addressed to them and to everybody, and is given no signal that the others exist.\n\n`deadlineClockMs` is null on every row here: the log records *when a cue was sent*, and the countdown it started is over by the time anyone reads it back.","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":500},"in":"query","name":"limit","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]}],"responses":{"200":{"description":"Cues, newest first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CueList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Not a participant in this session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such session in this organisation.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/productions/{productionId}/chat/channels":{"get":{"operationId":"listProductionChatChannels","summary":"Chat channels for a production","tags":["chat"],"description":"The planning channels of a show — the conversation that runs for weeks before anybody joins a room.\n\nThere is no create call. `general` is materialised by this read, so a production that has never been talked in still returns one channel rather than an empty state.\n\nEach row carries the caller's `unreadCount` and `unreadMentionCount`. A member who has never opened the channel has read nothing, so every message in it counts — which is the right answer for somebody joining a show mid-season.\n\nAccepts a member access token with `x-tablecast-org-id`, or a guest token, in which case the production must be the one that guest's session belongs to.","parameters":[{"schema":{"type":"string","maxLength":64},"in":"query","name":"slug","required":false,"description":"Materialise and return just this one channel."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"productionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]}],"responses":{"200":{"description":"The channels, `general` first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatChannelList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"A role on this production that does not reach its chat.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such production in this organisation.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"A slug that is not a slug.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/chat/channels":{"get":{"operationId":"listSessionChatChannels","summary":"Chat channels for a session","tags":["chat"],"description":"The show-day channels of one session. Distinct from `GET /v1/sessions/{id}/chat`, which is the room's own chat: that one is stamped with the session clock and carries the producer cues, this one is the persisted conversation with mentions and unread.\n\nA guest may list the channels of the session their token names, and no other.","parameters":[{"schema":{"type":"string","maxLength":64},"in":"query","name":"slug","required":false,"description":"Materialise and return just this one channel."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]}],"responses":{"200":{"description":"The channels, `general` first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatChannelList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such session in this organisation.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/chat/channels/{channelId}/messages":{"get":{"operationId":"listChannelMessages","summary":"History for a chat channel","tags":["chat"],"description":"One page, **always oldest first** whichever direction it was read in, so a client appends rather than sorts.\n\n`before` walks backwards through the archive (the scrollback) and `after` catches a client up from a cursor it already holds. There is no `since` in wall time: a client that dropped cannot know which message it missed, and a timestamp it invents would skip one written in the same millisecond as the last it saw. The cursors are keyed on `(created_at, id)` and compared row-wise, so a page boundary between two messages written in the same millisecond cannot lose one.\n\n`pinned=true` returns the pin board instead — the handful of messages a producer put at the top of the channel.\n\nA deleted message keeps its place in history with a null `body` and a `deletedAt`, because a read cursor pointing past it still has to make sense.","parameters":[{"schema":{"type":"string","maxLength":512},"in":"query","name":"before","required":false,"description":"A `prevCursor` from an earlier page. Walks backwards through the archive."},{"schema":{"type":"string","maxLength":512},"in":"query","name":"after","required":false,"description":"A `nextCursor` from an earlier page. Catches a client up. Not with `before`."},{"schema":{"type":"integer","minimum":1,"maximum":100},"in":"query","name":"limit","required":false},{"schema":{"type":"string","enum":["true","false"]},"in":"query","name":"pinned","required":false,"description":"`true` returns only pinned messages — the pin board rather than the scrollback."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"channelId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]}],"responses":{"200":{"description":"One page, oldest first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelMessagePage"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such channel in this organisation.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Both `before` and `after`, or a cursor this endpoint did not issue.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"post":{"operationId":"postChannelMessage","summary":"Post to a chat channel","tags":["chat"],"description":"Persists the message and every mention notification it causes in **one transaction**, then publishes to the channel's realtime path and sends push. A 201 means the message and its notifications are stored, whether or not either fan-out landed.\n\n`mentions` accepts user ids or `@handle` tokens, and `@handle`s in the body are picked up as well. Both are resolved against **active org members**: a token that resolves to nobody, or ambiguously to two people with the same first name, is dropped rather than refused — a mention is not a way to address somebody in another tenant, and a typo should not cost you the message.\n\nA mention is written once per (message, recipient), forever, so an edit that re-names somebody already told does not buzz their phone again.\n\nPosting advances the author's own read cursor in the same transaction, which is what stops your second tab lighting up because you typed.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostChannelMessageBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"channelId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]}],"responses":{"201":{"description":"The stored message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelMessage"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"No role on this production, or the channel is archived.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Empty or over-long body, too many attachments, or an attachment with no asset.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/chat/messages/{messageId}":{"patch":{"operationId":"editChannelMessage","summary":"Edit a chat message","tags":["chat"],"description":"The author's alone, moderator or not. A producer may *remove* somebody's line; rewriting it and leaving their name on it is a different thing entirely.\n\nOnly people the edit **newly** names are notified. Fixing a typo does not ping the room again.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EditChannelMessageBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"messageId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]}],"responses":{"200":{"description":"The edited message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelMessage"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Somebody else's message.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"delete":{"operationId":"deleteChannelMessage","summary":"Retract a chat message","tags":["chat"],"description":"The author may retract their own; a producer may remove anyone's.\n\nSoft, and deliberately so: the row keeps its place and its timestamps and loses its body, because a read cursor pointing past it and a mention that already became a notification both still have to make sense. Idempotent — deleting twice is a 204.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"messageId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]}],"responses":{"204":{"description":"Retracted."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Somebody else's message, and the caller is not a producer here.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/chat/messages/{messageId}/pin":{"put":{"operationId":"setChannelMessagePinned","summary":"Pin or unpin a message","tags":["chat"],"description":"A producer role on the production. A pin is a claim about the channel rather than about one line, which is why it is a moderator act even for your own message. Read the board back with `GET /v1/chat/channels/{id}/messages?pinned=true`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PinChannelMessageBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"messageId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]}],"responses":{"200":{"description":"The message, pinned or not.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelMessage"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Pinning needs a producer role here.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/chat/messages/{messageId}/reactions/{emoji}":{"put":{"operationId":"addChannelMessageReaction","summary":"React to a message","tags":["chat"],"description":"Idempotent: reacting twice with the same emoji is one row and one count.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"messageId","required":true},{"schema":{"type":"string","minLength":1,"maxLength":64},"in":"path","name":"emoji","required":true,"description":"URL-encoded. One emoji."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]}],"responses":{"200":{"description":"Every reaction on the message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelReactionList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"A reaction is one emoji, not a sentence.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"delete":{"operationId":"removeChannelMessageReaction","summary":"Take a reaction back","tags":["chat"],"description":"Idempotent: removing one that is not there is a 200 with the same counts.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"messageId","required":true},{"schema":{"type":"string","minLength":1,"maxLength":64},"in":"path","name":"emoji","required":true,"description":"URL-encoded. One emoji."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]}],"responses":{"200":{"description":"Every reaction on the message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelReactionList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/chat/channels/{channelId}/read":{"post":{"operationId":"markChatChannelRead","summary":"Move this member's read cursor","tags":["chat"],"description":"**Members only.** A read cursor is a fact about a person with a `users.id`; a guest token names a participant, which is a seat in one session and not somebody an unread count belongs to.\n\nForward-only. A cursor that could go backwards would let a stale tab unread a conversation for every other tab; \"mark unread\" is a different feature that would need its own column rather than this one running in reverse. Idempotent, so three tabs replaying it converge.\n\nPublishes `channel.read` on the caller's own `org.user` channel, so the other tab's badge agrees without a poll.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkChannelReadBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"channelId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"What is left unread.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelUnread"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/chat/unread":{"get":{"operationId":"getChatUnread","summary":"Unread chat across the org","tags":["chat"],"description":"The badge on the nav, in one read. Only channels with something in them are listed — a channel absent from `channels` is fully read.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Unread totals.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatUnreadSummary"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"The caller is not a member of the named organisation.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/productions/{productionId}/collab/{kind}/ticket":{"post":{"operationId":"createProductionCollabTicket","summary":"Open the production's show notes or whiteboard","tags":["collab"],"description":"Answers a `wss://` URL and a short-lived ticket for the production's collaborative document — the durable one, which outlives every session of the show.\n\nThere is no create-document call and there does not need to be: the document's id is a function of the production and the kind, the room is made on first open, and a document nobody has typed in is an empty document rather than a 404.\n\n**Role.** Anyone with a role on the production may read it; anyone who may drive the show (`session:control` — team lead, producer, director) may write. A host reads the run sheet, which is what a run sheet is for.\n\nAsk for a new ticket on every dial rather than keeping one. That is also what makes removal work: somebody taken off the production fails *here*, where access is checked.\n\n**503 when this deployment has no collaborative-document host configured.** Unlike the Companion tally feed there is no slower path to fall back to, so a surface should say collaboration is unavailable rather than retry.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"productionId","required":true},{"schema":{"type":"string","enum":["show_notes","whiteboard","script","timeline"]},"in":"path","name":"kind","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Where the document lives, and the ticket to open it with.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollabTicket"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such production, or not one this caller can see.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No collaborative-document host is configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/productions/{productionId}/collab":{"get":{"operationId":"listProductionCollabDocuments","summary":"The production's collaborative documents","tags":["collab"],"description":"What exists, how big it is and when it last changed. A kind that is absent from the list has never been written in — which is not an error: opening it creates it.\n\nFor a surface that wants to say \"show notes (updated 10 minutes ago)\" without opening a socket to find out.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"productionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The documents that exist.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollabDocumentList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/collab/{kind}/ticket":{"post":{"operationId":"createSessionCollabTicket","summary":"Open this show's whiteboard, show notes or script","tags":["collab"],"description":"The session-scoped twin of the production route, and the one a **guest** can call: a participant token names its own org and session, so a guest cannot address another show.\n\n`scope: \"production\"` in the body opens the *show's* durable document instead — the run sheet a guest usually wants to read. A guest always gets a read-only ticket for that one: they are in the room, not on the team.\n\n**Role.** A member is resolved against the production exactly as on the production route. A guest may edit the session's whiteboard — a green room where only the producer can draw is a worse product — and only read its show notes, unless their session role is producer or director.\n\nA **prompter** always gets a read-only ticket, for every kind and every scope: it is a display, and its link is printed and taped to a stand.\n\n**503 when this deployment has no collaborative-document host configured.**","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollabTicketBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","enum":["show_notes","whiteboard","script","timeline"]},"in":"path","name":"kind","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]}],"responses":{"200":{"description":"Where the document lives, and the ticket to open it with.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollabTicket"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such session, or not one this caller can see.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No collaborative-document host is configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/realtime/ticket":{"post":{"operationId":"createSessionRealtimeTicket","summary":"Open this show's realtime relay","tags":["collab"],"description":"Answers a `wss://` URL and a short-lived ticket for the coordinator's guest realtime relay, over which this session's live chat and state are pushed.\n\nThis is how a **guest** follows a live show: a guest cannot authenticate to AppSync Events, so the relay carries the same `session.chat` / `session.state` events (and the planning-chat slice of `production.state`) that a member receives on AppSync. A member may call it too, but a member has no reason to — it follows those channels on AppSync unchanged.\n\nA participant token names its own org and session, so a guest cannot address another show. The participant row is read on every call, so a guest removed from the session fails here rather than keeping a working ticket for six hours.\n\nAsk for a new ticket on every connect rather than keeping one: the relay socket lives for hours and the ticket for five minutes.\n\n**503 when this deployment has no collaborative-document host configured** — the relay rides the same coordinator, so the same configuration gap applies.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]}],"responses":{"200":{"description":"Where the relay lives, and the ticket to open it with.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RealtimeTicket"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such session, or not one this caller can see.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No collaborative-document host is configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/collab":{"get":{"operationId":"listSessionCollabDocuments","summary":"This show's collaborative documents","tags":["collab"],"description":"What exists for this session, how big it is and when it last changed. A kind that is absent has never been written in.\n\nOpen to a guest as well as a member, because a guest surface wants to say whether there is anything on the whiteboard before it opens a socket.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]}],"responses":{"200":{"description":"The documents that exist.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollabDocumentList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/prompter/script":{"get":{"operationId":"readSessionPrompterScript","summary":"Read this show's script as plain text","tags":["collab"],"description":"A flat read of the session's `script` collaborative document: the words, the word count and the named jump points, for a prompter that cannot hold a Yjs document.\n\nOpen to any participant in the room and to any member with `recordings:read`. Read-only — the script is edited through the collab WebSocket.\n\nReflects the doc host's last debounced save, so `version` and `updatedAt` are part of the answer. A session nobody has written a script for is **200 with empty text**, not 404: a missing row is an empty document everywhere in this feature.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]}],"responses":{"200":{"description":"The script as words, with its markers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PrompterScript"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such session, or not one this caller can see.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/comment-threads":{"get":{"operationId":"listCommentThreads","summary":"Comment threads on one thing","tags":["comments"],"description":"Every thread on one target, oldest first, each with its whole conversation — so a review panel and the markers on a player's scrub bar come from one call.\n\n`targetKind` is `media_asset`, `recording`, `render`, `graphic_asset` or `edit_doc`. A `recording`'s id is its **session** id, which is why the recording routes are spelled `/v1/recordings/{sessionId}`.\n\nResolved threads are omitted unless `includeResolved` is true: a review panel is a list of things still to do. Deleted comments come back as tombstones — `deletedAt` set and `body` empty — rather than being dropped, because a conversation with a hole in it reads as a different conversation.\n\n`channel` is the realtime channel these threads' events arrive on, or null for a caller who cannot subscribe (a guest, a share viewer). Filter the events on `targetKind` and `targetId`; the channel is per organisation, not per target.\n\nAccepts a member access token with `x-tablecast-org-id`, a participant-scoped guest token, or a share-link token — the last of which may only name the one target its link is for.","parameters":[{"schema":{"type":"string","enum":["media_asset","recording","render","graphic_asset","edit_doc"]},"in":"query","name":"targetKind","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"targetId","required":true},{"schema":{"type":"string"},"in":"query","name":"includeResolved","required":false,"description":"Resolved threads are hidden by default: a review panel is a list of things still to do."},{"schema":{"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]},{"shareToken":[]}],"responses":{"200":{"description":"The threads on that target.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentThreadList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Not a member of that organisation.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such target in this organisation, or not one this credential may reach.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"A member access token with no `x-tablecast-org-id`, or an unknown target kind.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"post":{"operationId":"createCommentThread","summary":"Start a comment thread","tags":["comments"],"description":"Opens a thread on one target and writes its first message, in one transaction — the opening message is an ordinary comment row, so editing and deleting the first thing said is the same code path as the second.\n\n`anchor` says where on the target the thread is pinned, and its `kind` is the coordinate system: `time` (a position in the source media — session-clock ms on a recording, an offset into the file on a render), `baseline` (a position on an edit's baseline timeline, which survives a trim earlier in the cut) or `region` (a normalised 0–1 box on a frame). Omit it, or send null, for a thread about the whole thing.\n\nThe production and session a thread belongs to are **derived from the target**, never supplied: a client that could name them could name different ones, and they are what the notification's deep link is built from.\n\n`mentions` notifies the people named. Opening a thread notifies nobody by itself — a note on your own render is not an event anybody asked to hear about.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCommentThreadBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]},{"shareToken":[]}],"responses":{"201":{"description":"The thread and its opening message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentThread"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"A view-only share link, or not a member of that organisation.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such target in this organisation.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"An empty body, an anchor that is not one of the three kinds, or a bad target.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/comment-threads/{threadId}/comments":{"post":{"operationId":"replyToCommentThread","summary":"Reply on a thread","tags":["comments"],"description":"Adds a message to an existing thread and notifies everybody who has already written in it — plus anybody named in `mentions`, who gets the mention rather than the reply because it is the more specific thing to say. Never yourself, whichever way you were caught by it.\n\nThe thread is re-checked against the caller's credential, not trusted from the id in the path: a share link for one render must not be able to reply to a thread on a different recording by naming it.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCommentBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"threadId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]},{"shareToken":[]}],"responses":{"201":{"description":"The stored reply.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Comment"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"A view-only share link.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such thread this credential may reach.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"An empty body.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/comments/{commentId}":{"patch":{"operationId":"updateComment","summary":"Edit your own comment","tags":["comments"],"description":"**Only** your own, moderator or not. Deleting somebody's comment is moderation; rewriting it is impersonation, and no role in this product should be able to put different words under another person's name.\n\n`editedAt` is set, so a client can say so. Editing notifies nobody: `dedupe_key` would suppress a second notification anyway, and “somebody rephrased the thing you already read” is not worth a buzz on a phone.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCommentBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"commentId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]},{"shareToken":[]}],"responses":{"200":{"description":"The edited comment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Comment"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Somebody else's comment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such comment this credential may reach, or it was deleted.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"An empty body.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"delete":{"operationId":"deleteComment","summary":"Retract a comment","tags":["comments"],"description":"The author may retract their own; an organisation owner or admin may retract anyone's. The row is kept as a tombstone with its body emptied, because a conversation with the third of five messages silently removed reads as though the fourth were a non-sequitur.\n\nDeleting a thread's **opening** message deletes the thread: the replies have no subject without it. The published event is `comment.thread.deleted` in that case and `comment.deleted` otherwise, so a panel does not have to work out which happened.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"commentId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]},{"shareToken":[]}],"responses":{"204":{"description":"Retracted."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Somebody else's comment, and the caller is not a moderator.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such comment this credential may reach.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"A member access token with no `x-tablecast-org-id`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/comment-threads/{threadId}/resolve":{"post":{"operationId":"resolveCommentThread","summary":"Resolve a thread","tags":["comments"],"description":"Anybody who can reach the target may resolve — including a guest and a share viewer, which is the point: the person asked to review the render is the person who says the note is dealt with. `resolvedBy` records a user id when there is one and stays null when there is not.\n\nIdempotent. Resolving a resolved thread returns it unchanged, because a double-click and a retry are the same request twice and neither should be a 409.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"threadId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]},{"shareToken":[]}],"responses":{"200":{"description":"The resolved thread.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentThread"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"A view-only share link.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such thread this credential may reach.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"A member access token with no `x-tablecast-org-id`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"delete":{"operationId":"reopenCommentThread","summary":"Reopen a thread","tags":["comments"],"description":"The other half of resolve, and idempotent in the same way. `DELETE` on the same sub-resource rather than a `resolved: false` body, so that the two states are one URL and a client cannot express a third.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"threadId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"guestToken":[]},{"shareToken":[]}],"responses":{"200":{"description":"The reopened thread.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentThread"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"A view-only share link.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such thread this credential may reach.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"A member access token with no `x-tablecast-org-id`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/billing":{"get":{"operationId":"getOrgBilling","summary":"Plan, subscription, entitlements and usage","tags":["billing"],"description":"The effective entitlements are plan ⊕ contract ⊕ manual overrides, each entry tagged with where it came from. Numeric entitlements use `-1` for unlimited. An organisation with no subscription reads as the Free plan rather than as an error — every gap in billing resolves downward. Materialises `org_entitlements` on first read so a brand-new organisation does not have to wait for a webhook to have entitlements. `state` (TAB-59) is the dunning write-gate: `active` | `grace` | `read_only`, derived from subscription status and how long the oldest unresolved invoice has been failing — a 7-day grace period, after which mutating requests are refused with 402 until payment succeeds. `graceEndsAt` is set only in `grace`/`read_only`. `quotas` (TAB-397) lists the four caps the plan imposes — recording minutes, storage, concurrent sessions and participants per session — with where the organisation stands against each; `retryable` says whether a breach is answered with 429 (a ceiling that frees up on its own) or 402 (an allowance that has to be bought).","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The organisation's billing state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingSummary"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such organisation, or the caller is not a member.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/usage":{"get":{"operationId":"getOrgUsage","summary":"Month-to-date usage by unit, with cap and alert state","tags":["billing"],"description":"Every metered unit's draw for the current period, summed from `usage_records`, next to the plan's allowance. Separate from `GET /v1/orgs/{orgId}/billing` because it answers a different question at a different rate: billing is a settings page read once, usage is a banner polled while a show is being planned.\n\n**The banner contract.** Render on the `rec_participant_min` entry. `thresholdReached` null means no banner. `80` is advisory — tell them what is left and where to upgrade, and do not block anything. `100` with `hardCap: false` is a soft cap: further minutes are billed at `overageCentsPerMinute`, so the banner states the rate rather than a warning. `100` with `hardCap: true` — equivalently `blocked: true` — is the only state that changes what the UI *permits*: disable Start with the reason, and say plainly that existing recordings are unaffected and still downloadable. Never hide a download control on any of these.\n\n`thresholdsFired` is what has already been recorded in `usage_alerts` this period, so a client can tell a first crossing from a re-read.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Month-to-date usage.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgUsage"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such organisation, or the caller is not a member.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/billing/checkout":{"post":{"operationId":"createBillingCheckout","summary":"Open a Stripe Checkout session","tags":["billing"],"description":"Creates the organisation's Stripe customer if it has none (recorded in `billing_customers` before the session is opened, so a customer that exists in Stripe is never one we have forgotten), then returns a Checkout URL for the chosen plan. Subscription mode, Stripe Tax on, `org_id` in the metadata of both the session and the subscription so the webhook never has to guess. Nothing is written to `subscriptions` here — that happens when Stripe tells us the payment succeeded.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The Checkout session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutSession"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"This organisation is invoiced through Xero; Checkout does not apply.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Unknown plan, or a plan that is not self-serve.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Stripe is not configured for this environment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/billing/portal":{"post":{"operationId":"createBillingPortal","summary":"Open a Stripe Customer Portal session","tags":["billing"],"description":"Where plan §12's promises are actually kept — self-serve downgrade, payment method, invoices. 404 for an organisation that has never had a Stripe customer: there is nothing to manage yet.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The portal session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutSession"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No organisation, or no billing account for it yet.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"This organisation is invoiced through Xero; the portal does not apply.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Stripe is not configured for this environment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/billing/xero":{"get":{"operationId":"getOrgXeroConnection","summary":"This organisation's Xero connection","tags":["billing"],"description":"`available` is false when the environment has no Xero credentials — hide the connect button rather than offering one that 503s. `connection.status` is `expired` when the last token refresh failed, with `lastError` saying why; that pair is what tells a finance contact to reconnect *before* the month-end invoice run finds out.\n\nNever returns a token, and never the Secrets Manager path the token lives at.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The Xero connection, if any.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/XeroBillingStatus"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"delete":{"operationId":"disconnectOrgXero","summary":"Forget this organisation's Xero connection","tags":["billing"],"description":"The row goes and the secret goes with it. Invoices already raised are untouched — they are a record of what was billed, and deleting them because a token was revoked would be a surprising amount of collateral for a reversible act. The organisation stays on the `xero` rail; nothing can raise a new invoice until it reconnects.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Disconnected."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/billing/xero/authorize-url":{"post":{"operationId":"getXeroAuthorizeUrl","summary":"Start connecting a Xero organisation","tags":["billing"],"description":"Returns Xero's consent URL and the `state` to hand back on the connect call. The SPA round-trips the state itself: the callback is completed by an API call from the page that started it, not by a browser navigation this API ever sees, so there is no server session to hang it on.\n\n**The redirect URI is the one this environment is configured with, always.** A caller-supplied one is accepted only when it matches exactly — an open redirect on an OAuth callback is a way to have Xero hand somebody else's authorization code to an attacker's page.\n\nA `custom_connection` environment has no consent screen and answers **503** here; read `authMode` from the connection status and call `/xero/connect` with an empty body instead.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/XeroAuthorizeUrlBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Where to send the finance contact.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/XeroAuthorizeUrl"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"That redirect URI is not the one this environment uses.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Xero is not configured for this environment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/billing/xero/connect":{"post":{"operationId":"connectOrgXero","summary":"Finish connecting a Xero organisation","tags":["billing"],"description":"Exchanges the authorization code server-side, stores the tokens in Secrets Manager under `/tablecast/<env>/orgs/<org_id>/xero/<id>`, and records which Xero organisation to bill into. Reconnecting **updates** the connection this organisation already has rather than adding a second — two would leave an invoice run unable to say whose books to write to.\n\nWhen the consent covered more than one Xero organisation and the request named none, the response carries `tenants` and no `connection`: choosing for the customer could post their invoices into the wrong set of books. Call again with `tenantId`.\n\nA consent that issues no refresh token is a **409**, not a success: without `offline_access` the connection dies in thirty minutes and the failure surfaces at month end.\n\nOn a `custom_connection` environment there is no code to exchange: post an **empty body** and the client-credentials grant runs instead, discovering the one Xero organisation the app is bound to. Sending a `code` there is a 422 — a caller holding one has confused the two flows, and the invoices would land in whichever books the custom connection happens to be bound to. Such a connection has no refresh token *by design*: it re-mints from this deployment's client credentials, so nothing expires at month end.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/XeroConnectBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The connection, or the organisations to choose between.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/XeroConnectResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Xero refused, or issued no refresh token.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Xero, or the secret store, is not configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/billing/provider":{"put":{"operationId":"setOrgBillingProvider","summary":"Move an organisation between the Stripe and Xero rails","tags":["billing"],"description":"Idempotent, and it **does not migrate money**: an organisation moved to `xero` keeps whatever Stripe subscription it had. The flag decides which rail future reads and writes use; cancelling a subscription is a separate, deliberate act, because doing it implicitly here would mean a mis-click stopped somebody's billing.\n\n`org_owner`/`org_admin` only — narrower than the rest of this group. `org_billing` exists so a finance contact can run billing, not so they can change which company bills them.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetBillingProviderBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The organisation's billing rail.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingProviderResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/billing/xero/invoices":{"post":{"operationId":"runOrgXeroInvoice","summary":"Raise (or preview) this organisation's invoice for a month","tags":["billing"],"description":"Prices a whole month from `usage_records` — the *same* aggregation the nightly Stripe meter sync reads — plus the contract's committed minutes, and raises a **DRAFT** invoice in Xero. A person reviews and sends it from Xero; nothing here can put a document in front of a customer.\n\n**Idempotent in three layers**: an `invoices` row for this org and month is returned as `already_exists`; failing that the run searches Xero for the reference `Tablecast usage YYYY-MM` and adopts an invoice that already carries it; failing that the unique index on the Xero invoice id settles a race. Re-running is safe.\n\n`dryRun` returns the priced lines without touching Xero and without marking any usage invoiced — which is also how the whole pricing pipeline is tested without a Xero fake.\n\n**Usage of a unit the contract's rate card does not price is a 409, not a zero.** An invoice that silently omits billable minutes is indistinguishable from a correct one.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/XeroInvoiceRunBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"What was raised, adopted or previewed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/XeroInvoiceRunResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No organisation, or it has not connected Xero.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Not on the Xero rail, or usage this contract's rate card does not price.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"That is not a YYYY-MM period.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Xero rate-limited the run; retry after the interval it named.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Xero is not configured, or could not be reached.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/internal/stripe/webhook":{"post":{"operationId":"receiveStripeWebhook","summary":"Stripe webhook receiver","tags":["internal"],"description":"Authenticated by the `Stripe-Signature` HMAC over the raw body, not by a bearer or service token — see the group's module comment. Idempotent on the Stripe event id via `stripe_events`, in the same transaction as the change the event causes, so a duplicate writes nothing and a failure is genuinely retried. Handles `checkout.session.completed`, `customer.subscription.created|updated|deleted` and `invoice.paid|payment_failed`, then re-materialises the organisation's entitlements. A duplicate, an unhandled type or an event with no organisation still answers 200: any error answer puts Stripe into a retry loop that cannot succeed.","parameters":[{"schema":{"type":"string"},"in":"header","name":"stripe-signature","required":false}],"security":[],"responses":{"200":{"description":"Acknowledged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookAck"}}}},"400":{"description":"The body is not JSON.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"401":{"description":"The Stripe-Signature header is missing or did not verify.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Stripe is not configured for this environment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/assets":{"post":{"operationId":"createOrgAsset","summary":"Add an asset to the organisation's shared library","tags":["assets"],"description":"Org-wide assets are usable by every team and every show — a house stinger, the corporate bug — so writing one needs `org_owner` or `org_admin`. The `kind` is decided by sniffing the bytes in `head`, never from a filename or a declared content type; a declared `kind` only has to agree with them.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAssetBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The created asset, and somewhere to put its bytes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedAsset"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Unrecognised file, or a kind the bytes contradict.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No object-storage credentials are configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"get":{"operationId":"listOrgAssets","summary":"List the organisation's shared library","tags":["assets"],"description":"Only org-wide assets — a team's and a show's own media are listed by their own routes. Filter with `?kind=`, search names with `?q=`, page with `cursor`.","parameters":[{"schema":{"anyOf":[{"type":"string","enum":["video","audio","image","stinger","font","caption","document"]},{"type":"array","items":{"type":"string","enum":["video","audio","image","stinger","font","caption","document"]}}]},"in":"query","name":"kind","required":false},{"schema":{"type":"string","minLength":1,"maxLength":200},"in":"query","name":"q","required":false},{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false},{"schema":{"type":"string","minLength":1,"maxLength":512},"in":"query","name":"cursor","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The page of assets.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Bad filter or cursor.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/teams/{teamId}/assets":{"post":{"operationId":"createTeamAsset","summary":"Add an asset to a team's library","tags":["assets"],"description":"Producer or above on that team, with the usual org owner/admin escape hatch. Visible to every production the team owns.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAssetBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"teamId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The created asset, and somewhere to put its bytes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedAsset"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Unrecognised file, or a kind the bytes contradict.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No object-storage credentials are configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"get":{"operationId":"listTeamAssets","summary":"List a team's library","tags":["assets"],"description":"The team's own assets and the organisation's shared ones, newest first.","parameters":[{"schema":{"anyOf":[{"type":"string","enum":["video","audio","image","stinger","font","caption","document"]},{"type":"array","items":{"type":"string","enum":["video","audio","image","stinger","font","caption","document"]}}]},"in":"query","name":"kind","required":false},{"schema":{"type":"string","minLength":1,"maxLength":200},"in":"query","name":"q","required":false},{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false},{"schema":{"type":"string","minLength":1,"maxLength":512},"in":"query","name":"cursor","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"teamId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The page of assets.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Bad filter or cursor.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/productions/{id}/assets":{"post":{"operationId":"createProductionAsset","summary":"Add an asset to one production's library","tags":["assets"],"description":"The show's own media: VTs, bumpers, its stinger. Needs the `production:manage` permission on that production.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAssetBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The created asset, and somewhere to put its bytes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedAsset"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Unrecognised file, or a kind the bytes contradict.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No object-storage credentials are configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"get":{"operationId":"listProductionAssets","summary":"List one production's library","tags":["assets"],"description":"The show's own assets, its team's, and the organisation's shared ones — everything this production can put on air, in one page.","parameters":[{"schema":{"anyOf":[{"type":"string","enum":["video","audio","image","stinger","font","caption","document"]},{"type":"array","items":{"type":"string","enum":["video","audio","image","stinger","font","caption","document"]}}]},"in":"query","name":"kind","required":false},{"schema":{"type":"string","minLength":1,"maxLength":200},"in":"query","name":"q","required":false},{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false},{"schema":{"type":"string","minLength":1,"maxLength":512},"in":"query","name":"cursor","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The page of assets.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Bad filter or cursor.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/assets/{assetId}":{"get":{"operationId":"getAsset","summary":"Read one asset","tags":["assets"],"description":"The row, with whatever the probe has filled in and the proxy job's state.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"assetId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The asset.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Asset"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"delete":{"operationId":"deleteAsset","summary":"Delete an asset","tags":["assets"],"description":"Soft-deletes the row and cancels its outstanding proxy job; the object itself goes with the storage lifecycle sweep, so a delete five minutes before air is recoverable. **409** while anything still names the asset — a production cover, a scene thumbnail, a super, a brand kit, or a live show's state — and the detail says which. Needs the same role creating at that scope needs.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"assetId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Deleted."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Something still uses this asset; the detail lists what.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/assets/{assetId}/download":{"get":{"operationId":"downloadAsset","summary":"Get a signed download URL for one asset","tags":["assets"],"description":"A presigned GET on the organisation's bucket (R2 or S3), valid for fifteen minutes, with a filename derived from the asset's name signed into it so it cannot be rewritten. Every call writes an `assets.download` row to `audit_events`; the URL itself is never logged.\n\nTakes a member bearer or a **share token** (TAB-542) naming this asset — the second only when that share was created with `allowDownload`, because 'look at this' and 'here is the file' are different acts and the second is off by default.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"assetId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":false}],"security":[{"bearerAuth":[]},{"shareToken":[]}],"responses":{"200":{"description":"The signed URL.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetDownloadLink"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"That share link does not include downloads.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"A member bearer with no `x-tablecast-org-id`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No object-storage credentials are configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/join/assets":{"post":{"operationId":"createGuestWhiteboardAsset","summary":"A guest adds an asset to their session's whiteboard","tags":["assets"],"description":"The guest limb of `POST /v1/productions/{id}/assets`. The production is the one the guest's session belongs to (from the token, never a path id), and the row is written as the system actor because a guest is not a `users` row. The presigned PUT comes back in the same shape the member route answers.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAssetBody"}}}},"security":[{"guestToken":[]}],"responses":{"201":{"description":"The created asset, and somewhere to put its bytes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedAsset"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Unrecognised file, or a kind the bytes contradict.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No object-storage credentials are configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/join/assets/{assetId}/download":{"post":{"operationId":"guestDownloadAsset","summary":"A guest gets a signed URL for an asset on their whiteboard","tags":["assets"],"description":"The guest limb of `GET /v1/assets/{assetId}/download`. The asset must belong to the production of the session the guest's token names; anything else answers **404**, not 403, so a guest cannot probe which asset ids exist outside their own show.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"assetId","required":true}],"security":[{"guestToken":[]}],"responses":{"200":{"description":"The signed URL.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssetDownloadLink"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No object-storage credentials are configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/canva/connection":{"get":{"operationId":"getCanvaConnection","summary":"The organisation's connected Canva account","tags":["assets"],"description":"`connection` is null when nothing is connected — an ordinary state, not a 404, because the settings page asks this question before there is anything to find.\n\n`capabilities.autofill` is what the UI reads to decide whether Brand Templates and pre-rendered name straps are offered at all: they need Canva **Enterprise**, and a producer should learn that here rather than from a lower third that never appears.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The connection, or null.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CanvaConnectionEnvelope"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/canva/authorize-url":{"post":{"operationId":"getCanvaAuthorizeUrl","summary":"Start connecting a Canva account","tags":["assets"],"description":"Returns Canva's consent URL, the `state` to hand back on the callback, and the **PKCE code verifier**. Canva requires PKCE (S256) on top of the client secret.\n\nKeep `codeVerifier` in `sessionStorage` for the length of the flow and send it to `connect`. It is not a credential for anything except the one authorization code Canva is about to issue, and binding that code to this browser is the entire job it does.\n\n`redirectUri` must be one this environment is configured for — an open redirect on an OAuth callback is an account takeover, so an unlisted one is a 422.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CanvaAuthorizeUrlBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Where to send the producer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CanvaAuthorizeUrl"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"That redirect URI is not allowed here.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Canva is not configured in this environment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/canva/connect":{"post":{"operationId":"connectCanva","summary":"Finish connecting a Canva account","tags":["assets"],"description":"Exchanges the authorization code server-side, stores the tokens in Secrets Manager under `/tablecast/<env>/orgs/<org_id>/integrations/canva/<id>`, records the Canva *team* (a Connect integration is authorised for a team, not a person) and probes whether the plan includes Autofill.\n\nReconnecting the same team updates the connection it already has. A Canva that issues no refresh token is a **409**, not a success: that connection would die within the hour.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CanvaConnectBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The connected Canva account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CanvaConnection"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Canva refused, or issued no refresh token.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Canva, or the secret store, is not configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/canva/connection/{integrationId}":{"delete":{"operationId":"disconnectCanva","summary":"Disconnect the Canva account","tags":["assets"],"description":"Deletes the connection and its stored tokens. **Assets already imported survive** — they are files in the library that scenes may be pointing at, and deleting a show's graphics because somebody revoked a design-tool token would be an extraordinary amount of collateral. Registered Brand Templates survive too and simply cannot run until an account is reconnected.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"integrationId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Disconnected."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/canva/designs":{"get":{"operationId":"listCanvaDesigns","summary":"Designs in the connected Canva account","tags":["assets"],"description":"The picker. Each design carries `importedAssetId` and `importedUpToDate` so the list can say *imported* beside one rather than offering an import that will turn out to be a no-op (TAB-411).","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":200},"in":"query","name":"q","required":false},{"schema":{"type":"string","minLength":1,"maxLength":2048},"in":"query","name":"cursor","required":false},{"schema":{"type":"integer","minimum":1,"maximum":100},"in":"query","name":"limit","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"A page of designs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CanvaDesignList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No Canva account is connected.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"post":{"operationId":"createCanvaDesign","summary":"Create a new Canva design seeded with the brand kit","tags":["assets"],"description":"TAB-416. Creates a design at one of three presets — `lower-third` (1920×360), `full-frame` (1920×1080) or `vertical` (1080×1920) — uploads the brand kit's logo and bug to Canva as assets, and hands back the edit URL. The producer designs there and imports on return.\n\nA lower third is deliberately **not** a 1080p sheet with a transparent middle: its own size means Canva's guides land where the strap goes and an export is the size the renderer draws.\n\nA brand kit asset Canva will not take is a `seedWarnings` entry, not a failure — the design is still created, because losing a whole design over one logo would be worse than starting from a blank one.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCanvaDesignBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The new design.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedCanvaDesign"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No Canva account, or no such brand kit.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/canva/designs/{designId}/import":{"post":{"operationId":"importCanvaDesign","summary":"Import a Canva design into the media library","tags":["assets"],"description":"Queues an export on a worker and answers straight away — a Canva export is an async job on their side, and a request that waited for it would hold a Lambda for as long as their queue takes. Poll the returned job.\n\n**Re-importing an unchanged design is a no-op** (`outcome: \"reused\"`): a producer clicking twice, or a freshness poll running every minute, must not fill the library with identical PNGs or spend a Canva export quota per click. `force: true` overrides.\n\nAn import of a design that *has* changed writes a **new version** chained to the old one rather than overwriting it, because scenes hold asset ids and rewriting an object under a live show's feet would change a take with nothing in the UI having moved.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CanvaImportBody"}}}},"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":200},"in":"path","name":"designId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"202":{"description":"Queued, or already imported.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CanvaImportResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No Canva account, or no such design.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/canva/jobs/{jobId}":{"get":{"operationId":"getCanvaJob","summary":"How an export, autofill or key conversion is going","tags":["assets"],"description":"What the library's *exporting…* pill reads. `error` carries Canva's own reason when one failed, which is safe to show and never contains a credential (TAB-411's second acceptance criterion).","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"jobId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The job.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CanvaJobEnvelope"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/canva/assets/{assetId}/versions":{"get":{"operationId":"listCanvaAssetVersions","summary":"Every version of a Canva-sourced asset","tags":["assets"],"description":"TAB-415. A version is a **new row**, never an overwrite: scenes and live production state hold asset ids, and re-exporting into the same storage key would change what a scene draws while a show is on air. Old versions are retained; `latestAssetId` is what a reference resolves to.\n\nAn asset that did not come from Canva has no versions, and answers with an empty list rather than an error.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"assetId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The version chain, newest first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CanvaAssetVersionList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/canva/brand-templates":{"get":{"operationId":"listCanvaBrandTemplates","summary":"Brand Templates in the connected Canva account","tags":["assets"],"description":"**Canva Enterprise only.** When the plan does not include them the answer is a 200 with an empty list and `capabilities.autofill: false` carrying the reason — not a 403 — because *why* the list is empty is the thing the page has to say, and an error status would make the UI render a failure rather than an explanation.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":200},"in":"query","name":"q","required":false},{"schema":{"type":"string","minLength":1,"maxLength":2048},"in":"query","name":"cursor","required":false},{"schema":{"type":"integer","minimum":1,"maximum":100},"in":"query","name":"limit","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The templates, and what the plan allows.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CanvaBrandTemplateList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No Canva account is connected.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/canva/templates":{"get":{"operationId":"listRegisteredCanvaTemplates","summary":"Brand Templates registered for autofill","tags":["assets"],"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The registrations.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CanvaTemplateList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"post":{"operationId":"registerCanvaTemplate","summary":"Register a Brand Template and map its autofill fields","tags":["assets"],"description":"Reads the template's dataset from Canva, checks the field map against it, and stores both. A Canva field the template does not have is a 422 naming it; so is a Tablecast value that does not exist, and so is a type mismatch either way — an avatar into a text field renders a URL on air and a name into an image field renders nothing, and both are silent at autofill time.\n\n**A non-Enterprise plan still registers.** The customer may upgrade, and deleting their work to punish their plan would be rude; `autofillAvailable: false` is what every surface that would run it reads instead (TAB-412).\n\nRegistering the same template again updates the registration it has, rather than leaving a producer unable to tell which field map is the one that runs.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterCanvaTemplateBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The registration.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CanvaTemplateEnvelope"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No Canva account, or no such template.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"A field the template does not have, or a value we cannot fill it with.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/canva/templates/{templateId}/refresh":{"post":{"operationId":"refreshCanvaTemplate","summary":"Re-read a registered template's fields from Canva","tags":["assets"],"description":"Updates the cached dataset and `lastSyncedAt` (TAB-412's third acceptance criterion). `lastSyncedAt` moves on **failure too**, with `lastError` saying why — a repeatedly failing template that looked untouched since the day it was registered would be the harder problem to diagnose.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"templateId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The refreshed registration.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CanvaTemplateEnvelope"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/canva/templates/{templateId}":{"delete":{"operationId":"deleteCanvaTemplate","summary":"Unregister a Brand Template","tags":["assets"],"description":"The straps it has already rendered **survive**: they are media assets a scene may be pointing at, and unregistering a template is a decision about the future.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"templateId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Unregistered."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/canva/autofill":{"post":{"operationId":"autofillCanvaStraps","summary":"Pre-render a participant's lower thirds","tags":["assets"],"description":"TAB-413. Called on lobby join and again on a profile edit. For each registered template it either finds a cached strap (`cached` — costs nothing) or queues an autofill job (`queued`).\n\n**The cache key is the participant, the template *version* and the resolved inputs.** Leaving the version out would serve last week's design after a designer edited the template; leaving the inputs out would keep showing the old title after a lobby edit — which is exactly what this endpoint is called again to fix.\n\nA template that cannot run answers `unavailable` with a reason rather than an error, so the caller can fall back to the native lower third and say why.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutofillStrapBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"202":{"description":"What will happen to each template.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutofillStrapResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No Canva account is connected.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/productions/{id}/graphics":{"get":{"operationId":"listProductionGraphics","summary":"A show's supers","tags":["graphics"],"description":"The Supers library: one card per person, newest first.\n\n**Regenerated-over versions are hidden by default.** A regeneration is a new row rather than an overwrite — scenes hold asset ids, and rewriting bytes under a live show would change a take with nothing in the UI having moved — so a show whose straps have been recoloured four times would otherwise render five of everybody. Pass `history=true` for the version drawer.\n\nA `pending` super is a Canva autofill that has not come back yet; its card shows the person's name and a spinner. A `failed` one carries the reason in `error`.","parameters":[{"schema":{"type":"string","enum":["super","title","bug"]},"in":"query","name":"kind","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"generationId","required":false},{"schema":{"type":"string"},"in":"query","name":"history","required":false},{"schema":{"type":"integer","minimum":1,"maximum":500},"in":"query","name":"limit","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The show's supers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GraphicAssetList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such production, or not visible to this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"post":{"operationId":"createProductionGraphic","summary":"Make one super","tags":["graphics"],"description":"One person, one template, one PNG.\n\nA **house** super comes back `201` and `ready`: it is rendered in this request, because it is arithmetic rather than a third party's queue, and an org that has connected nothing should not wait five minutes for a picture we can draw now.\n\nA **canva** super comes back `201` and `pending` with a `jobId`. Poll the list, or the generation, until it is `ready`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"kind":{"default":"super","type":"string","enum":["super","title","bug"]},"source":{"type":"string","enum":["house","canva"]},"houseTemplateId":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9-]*$"},"canvaTemplateId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"person":{"type":"object","properties":{"participantId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"recipientId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"name":{"type":"string","minLength":1,"maxLength":200},"title":{"type":"string","maxLength":200},"handle":{"type":"string","maxLength":200}}},"fieldValues":{"type":"object","propertyNames":{"type":"string","minLength":1,"maxLength":64},"additionalProperties":{"type":"string","maxLength":200}},"locale":{"type":"string","minLength":2,"maxLength":35,"pattern":"^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$"},"variants":{"type":"object","propertyNames":{"type":"string","minLength":2,"maxLength":35,"pattern":"^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$"},"additionalProperties":{"type":"object","propertyNames":{"type":"string","minLength":1,"maxLength":64},"additionalProperties":{"type":"string","maxLength":200}}},"name":{"type":"string","minLength":1,"maxLength":200}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The super.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GraphicAssetEnvelope"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Managing a show's supers needs producer or above on its team.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such production, or no such template.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"This organisation's Canva plan cannot autofill.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"This API has no object-storage credentials.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/productions/{id}/graphics/templates":{"get":{"operationId":"listHouseSuperTemplates","summary":"The built-in lower-third templates, previewed in this show's brand","tags":["graphics"],"description":"Four house templates every organisation has without connecting anything, each returned as an **SVG preview drawn with this show's brand kit** — so the picker shows a producer their own colours rather than ours, and does so without spending a render.\n\nThe preview is not the asset. Generating is what produces the PNG a scene draws.","parameters":[{"schema":{"type":"string","minLength":2,"maxLength":35,"pattern":"^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$"},"in":"query","name":"locale","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The house templates.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HouseSuperTemplateList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/productions/{id}/graphics/generate":{"post":{"operationId":"generateProductionGraphics","summary":"Generate supers for a set of people — or for the whole roster","tags":["graphics"],"description":"The one-click button. Pick a template, and every person on the show's roster gets a strap with their name and title on it.\n\n**Omitting `people` means the whole production roster** — resolved on the server, so the answer cannot be whichever page of a list the browser had scrolled to. Revoked invites are excluded; somebody whose link was retired is not somebody a strap should be made for.\n\n**Running it twice fills the gaps rather than duplicating the cards that worked.** `regenerateExisting: true` overrides, and then each existing super gets a *new version* chained to the old one — never an overwrite, because a scene may be pointing at the old asset id right now.\n\nHouse supers come back `ready` in this response. Canva supers come back `pending`; poll the generation for progress. A person who could not be resolved is listed in `skipped` rather than failing the batch: a roster read a second ago can name a guest who has just been removed, and eleven of twelve straps is not a reason to produce none.\n\n**At most twelve house supers are drawn per call**, because they are rasterised inside the request and this Lambda has twenty seconds. `remaining` says how many are left; calling again continues, and is safe for the same reason a second run is safe at all. Canva supers are not capped — queueing a job is a row, not a render.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"kind":{"default":"super","type":"string","enum":["super","title","bug"]},"source":{"type":"string","enum":["house","canva"]},"houseTemplateId":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9-]*$"},"canvaTemplateId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"people":{"maxItems":200,"type":"array","items":{"type":"object","properties":{"participantId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"recipientId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"name":{"type":"string","minLength":1,"maxLength":200},"title":{"type":"string","maxLength":200},"handle":{"type":"string","maxLength":200}}}},"locale":{"type":"string","minLength":2,"maxLength":35,"pattern":"^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$"},"regenerateExisting":{"default":false,"type":"boolean"}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"202":{"description":"What the run produced.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateGraphicsResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Managing a show's supers needs producer or above on its team.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such production, or no such template.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"This organisation's Canva plan cannot autofill.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/productions/{id}/graphics/generations/{generationId}":{"get":{"operationId":"getGraphicGeneration","summary":"How a generate run is going","tags":["graphics"],"description":"A count over the run's own rows rather than a job of its own — a batch has no properties beyond the supers in it, and a second table would be a second thing to keep in step. `done` is true when nothing is still rendering; a failure counts as finished, and the reason is on the super's `error`.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"generationId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The run's progress.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GraphicGenerationProgress"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/productions/{id}/graphics/{graphicId}/versions":{"get":{"operationId":"listGraphicVersions","summary":"Every version of one super","tags":["graphics"],"description":"Newest first. A regeneration writes a **new row** chained to the old one rather than overwriting it, because scenes and live production state hold asset ids and re-rendering into the same storage key would change what a scene draws while a show is on air. Old versions are retained, which is what makes this a query rather than an audit-log excavation.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"graphicId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The version chain.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GraphicVersionList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/productions/{id}/graphics/{graphicId}/regenerate":{"post":{"operationId":"regenerateProductionGraphic","summary":"Make a new version of one super","tags":["graphics"],"description":"Redraws the strap and writes a **new version** chained to the current one. The old row and its asset are untouched: a scene may be pointing at that asset id right now, and a producer who regenerates wrongly must be able to see what it used to be.\n\nThis is also how a super moves between looks — and between *sources*. Naming a different template regenerates against it, and a producer who connects Canva mid-season can regenerate a house super as a Canva one. That is a new version of the same super, not a new super, which is why the version chain here is not the same chain as a Canva design's export chain.\n\n`fieldValues` are **merged** over the previous ones, so correcting a title needs one field rather than a full re-send.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"houseTemplateId":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9-]*$"},"canvaTemplateId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"fieldValues":{"type":"object","propertyNames":{"type":"string","minLength":1,"maxLength":64},"additionalProperties":{"type":"string","maxLength":200}},"locale":{"type":"string","minLength":2,"maxLength":35,"pattern":"^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$"},"variants":{"type":"object","propertyNames":{"type":"string","minLength":2,"maxLength":35,"pattern":"^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$"},"additionalProperties":{"type":"object","propertyNames":{"type":"string","minLength":1,"maxLength":64},"additionalProperties":{"type":"string","maxLength":200}}}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"graphicId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The new version.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GraphicAssetEnvelope"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Managing a show's supers needs producer or above on its team.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"This organisation's Canva plan cannot autofill.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/productions/{id}/graphics/{graphicId}":{"delete":{"operationId":"deleteProductionGraphic","summary":"Remove a super from the library","tags":["graphics"],"description":"Soft delete. **The rendered PNG survives**: it is a media asset a scene may be pointing at, and removing a card from the library is a decision about the library. Deleting the picture too would make 'tidy up the supers panel' a way to break a live show.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"graphicId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Removed."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Managing a show's supers needs producer or above on its team.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/brand-kits":{"get":{"operationId":"listBrandKits","summary":"List the organisation's brand kits","tags":["brand-kits"],"description":"Every kit the organisation owns, newest first — the org-wide ones and each team's. Any active member may read them: a producer who cannot see the kit cannot build a super with it.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The organisation's brand kits.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandKitList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"post":{"operationId":"createBrandKit","summary":"Create a brand kit","tags":["brand-kits"],"description":"Needs `org_owner` or `org_admin`: a brand kit is the organisation's identity on air, not one show's preference. Everything but the name is optional — an empty kit renders in Tablecast's own palette until somebody fills it in.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBrandKitBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The created brand kit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandKit"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"A colour, a font or an asset id the kit cannot hold.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/brand-kits/{brandKitId}":{"get":{"operationId":"getBrandKit","summary":"Read one brand kit","tags":["brand-kits"],"description":"The whole document, including its `revision` — send that back to save.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"brandKitId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The brand kit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandKit"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"patch":{"operationId":"updateBrandKit","summary":"Save a brand kit","tags":["brand-kits"],"description":"One update of one row, conditional on `expectedRevision`. A **409** means somebody else saved first and the detail carries the revision the kit is on now, so the editor can say who rather than that something went wrong. Every save snapshots the kit, so a session already on air keeps the look it started with.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateBrandKitBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"brandKitId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The saved brand kit, at its new revision.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandKit"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The kit changed since you loaded it.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"A colour, a font or an asset id the kit cannot hold.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"delete":{"operationId":"deleteBrandKit","summary":"Delete a brand kit","tags":["brand-kits"],"description":"**409** while a production still names it, and the detail lists which — a kit is configuration rather than content, so this is a real delete and the 409 is what stops it happening to one that is in use. A session that had pinned a snapshot of it falls back to its production's current kit rather than losing its graphics.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"brandKitId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Deleted."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"A production still uses this brand kit.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/brand-kits/{brandKitId}/fonts":{"post":{"operationId":"addBrandKitFont","summary":"Upload a font and attach it to a brand kit","tags":["brand-kits"],"description":"The kind is decided by sniffing the bytes in `head`: **woff2, woff, ttf and otf are accepted and nothing else**, whatever the filename says, and the file must be under 5 MB. Answers with the updated kit and a presigned PUT for the bytes — replay its headers verbatim, because the size and content type are signed into the URL.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddBrandFontBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"brandKitId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The updated kit, and somewhere to put the font.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandFontUpload"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Not a font, or over the 5 MB cap.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No object-storage credentials are configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/sessions/{sessionId}/brand-theme":{"post":{"operationId":"resolveSessionBrandTheme","summary":"Resolve (and pin) the brand a session renders against","tags":["brand-kits"],"description":"Idempotent: the first call pins the current revision of the production's brand kit onto the session and every later call returns that same snapshot, so an edit made mid-show lands on the next session rather than restyling the one on air. `kit` is null when the production names no brand kit — the renderer falls back to Tablecast's own palette.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The pinned brand.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandTheme"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/canva/brand-kits":{"get":{"operationId":"listCanvaBrandKits","summary":"List the brand kits in the connected Canva account","tags":["brand-kits"],"description":"**503** when no Canva account is connected for this deployment — a missing integration is a configuration state, not a fault. The Canva Connect authorisation itself is a separate surface; this only reads what it has already connected.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The Canva brand kits available to import.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CanvaBrandKitList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Canva is not connected.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/brand-kits/canva-import":{"post":{"operationId":"importCanvaBrandKit","summary":"Import or re-sync a Canva brand kit","tags":["brand-kits"],"description":"One action: colours, fonts and the primary logo. A family Canva will not license out is mapped to the nearest Google font and the entry carries a note the editor shows — importing a family with no file would render in Arial and never explain why. A re-sync updates the roles Canva defines, keeps tokens the organisation added itself, and does not touch lower-third templates: they reference `$primary`, so they follow the new palette rather than being rewritten.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportCanvaBrandKitBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The imported kit, and what had to be substituted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportedBrandKit"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Nothing importable in that Canva kit.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Canva is not connected.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/audit":{"get":{"operationId":"listAuditEvents","summary":"The organisation's audit log","tags":["audit"],"description":"Newest first, keyset-paginated on `(createdAt, id)` — not offset, because events land continuously and an offset page 2 would re-show rows page 1 already had. `before`/`after` are omitted here and returned by the detail resource: a settings change carries two whole JSON documents, and a hundred of them is a payload measured in megabytes for a screen that renders one line each. Needs `org:manage` — `org_owner`/`org_admin` by default, or any custom org role that carries it. A caller who is not an active member gets 404, not 403.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"actor","required":false},{"schema":{"type":"string","enum":["user","api_key","device","guest"]},"in":"query","name":"actorKind","required":false},{"schema":{"type":"string","minLength":1,"maxLength":120},"in":"query","name":"action","required":false},{"schema":{"type":"string","minLength":1,"maxLength":120},"in":"query","name":"resourceType","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"resourceId","required":false},{"schema":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"in":"query","name":"from","required":false},{"schema":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"in":"query","name":"to","required":false},{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false},{"schema":{"type":"string","minLength":1,"maxLength":512},"in":"query","name":"cursor","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The page of audit events.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditEventList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Bad filter or cursor.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/audit/{id}":{"get":{"operationId":"getAuditEvent","summary":"One audit event, with its before/after documents","tags":["audit"],"description":"Nothing is redacted: these tables hold roles, names and settings documents, and secrets live in Secrets Manager or as KMS envelopes rather than in rows (plan §11).","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The audit event.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditEventDetail"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/impersonation":{"post":{"operationId":"startImpersonation","summary":"Open an audited impersonation session over one organisation","tags":["impersonation"],"description":"Needs `platform:impersonate`, which is checked on the ordinary app connection before the privileged one is opened at all. The session is a key to the one organisation named here and to nothing else, lasts at most an hour (`minutes` may shorten it, never extend it), and is itself recorded as an `impersonation.start` event in that organisation's audit log. One open session per admin: end the current one before opening another (409).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StartImpersonationBody"}}}},"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The open session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImpersonationSession"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"This admin already has an open session.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"No reason, a reason that says nothing, or a window over an hour.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/impersonation/current":{"get":{"operationId":"getCurrentImpersonation","summary":"The caller's own open impersonation session, if any","tags":["impersonation"],"description":"`{ session: null }` for everybody who is not impersonating, which is nearly every caller — so the UI banner can poll this unconditionally rather than first asking whether the signed-in user is a platform admin. Answered with no organisation pinned, because the banner has to render before an org is chosen; `impersonation_sessions_actor` is a SELECT policy on `app.user_id` for exactly that reason. A lapsed session reads as null here even though its row is still open — expiry is a property of the clock, not of a sweep.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The open session, or null.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CurrentImpersonation"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/impersonation/audit":{"get":{"operationId":"listImpersonatedAudit","summary":"The impersonated organisation's audit log","tags":["impersonation"],"description":"The one cross-tenant read in the API, and it is available only inside an open session: the request must carry `x-tablecast-impersonation-id`, that id must be a session the caller themselves opened, and the session is re-checked in SQL inside the transaction that serves the request — so a window that lapsed a second ago is a 403 rather than an hour of grace. Reading it writes an `impersonation.request` event into that organisation's own log, stamped with the impersonation id, so the customer can see what was looked at and not merely that a door was opened.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"actor","required":false},{"schema":{"type":"string","enum":["user","api_key","device","guest"]},"in":"query","name":"actorKind","required":false},{"schema":{"type":"string","minLength":1,"maxLength":120},"in":"query","name":"action","required":false},{"schema":{"type":"string","minLength":1,"maxLength":120},"in":"query","name":"resourceType","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"resourceId","required":false},{"schema":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"in":"query","name":"from","required":false},{"schema":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"in":"query","name":"to","required":false},{"schema":{"default":50,"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false},{"schema":{"type":"string","minLength":1,"maxLength":512},"in":"query","name":"cursor","required":false}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The page of audit events, from the impersonated organisation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditEventList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Bad filter or cursor.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/impersonation/{id}":{"delete":{"operationId":"endImpersonation","summary":"End an impersonation session early","tags":["impersonation"],"description":"An ordinary authenticated request on the app pool, not a second bypass: migration 0019 gives `tablecast_app` a column grant on `(ended_at, ended_by)` and a policy keyed on the acting admin, which is exactly wide enough for this and nothing else — in particular it cannot rewrite `org_id`, the one edit that would make the trail lie. Idempotent: ending an already-ended session returns it unchanged, because the caller's intent is satisfied either way. Somebody else's session id is a 404, not a 403 — RLS makes it invisible, and a 403 would confirm it exists.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The closed session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImpersonationSession"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/impersonations":{"get":{"operationId":"listOrgImpersonations","summary":"Every platform-admin session that entered this organisation","tags":["impersonation"],"description":"The tenant's own view of the record, newest first. Needs `org:manage`, like the audit log it sits beside. The filtering is `impersonation_sessions_tenant`, a SELECT-only policy — an organisation is entitled to know that a platform admin was inside their data and cannot forge or erase the record of it. The reason is included; see the module note.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The sessions that entered this organisation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImpersonationList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/platform/users":{"get":{"operationId":"searchPlatformUsers","summary":"Find an account by email or name, across every organisation","tags":["platform"],"description":"Needs `platform:admin`, checked on the ordinary app connection before the privileged one is opened at all. Matches `q` against the primary email and the display name; with no `q`, lists the most recently created accounts. Soft-deleted accounts are never returned — there is nothing a support engineer can do for one, and a session cannot be opened over it. Returns identity, never content: no organisation's data is reachable from here.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":200},"in":"query","name":"q","required":false},{"schema":{"type":"integer","minimum":1,"maximum":100},"in":"query","name":"limit","required":false}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The matching accounts, newest first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformUserList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"An empty or over-long search term, or a bad limit.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/platform/orgs":{"get":{"operationId":"listPlatformOrgs","summary":"Every organisation, with plan, size and where its media lives","tags":["platform"],"description":"Needs `platform:admin`. Matches `q` against the organisation's name and slug; with no `q`, lists the most recently created. `plan` is the code behind the org's live subscription rather than its materialised entitlements — an org whose entitlements were hand-overridden still reads as the plan it is paying for.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":200},"in":"query","name":"q","required":false},{"schema":{"type":"integer","minimum":1,"maximum":100},"in":"query","name":"limit","required":false}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The matching organisations, newest first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformOrgList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"An empty or over-long search term, or a bad limit.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/platform/users/{id}":{"get":{"operationId":"getPlatformUser","summary":"One account: how they sign in, where they belong, what they operate","tags":["platform"],"description":"Needs `platform:admin`. The page an operator lands on before deciding to open a session: the organisations to pick a target org from, the sign-in methods behind the account (`Signed in with Google`), when they were last seen, and — if they hold one — the platform grant that makes them un-emulatable until the two-admin approval flow exists.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformUserDetail"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such account, or it has been deleted.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/platform/sessions":{"get":{"operationId":"listPlatformSessions","summary":"Every impersonation session open right now","tags":["platform"],"description":"Needs `platform:admin`. What `/platform/sessions` lists so an operator can see, and end, a window somebody left open. 'Open' is `ended_at IS NULL AND expires_at > now()`, compared in the database's clock — a session past its hour is simply not here, whether or not its row has been swept. Ending one is `DELETE /v1/impersonation/{id}`, which only the admin who opened it may call; this list is the platform's *view* of the record, not a second way to write it.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The open sessions, newest first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformSessionList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/platform/orgs/{orgId}/plan":{"put":{"operationId":"setPlatformOrgPlan","summary":"Put an organisation on a plan","tags":["platform"],"description":"Needs `platform:admin`. Writes one `subscriptions` row with **no Stripe id** — the shape an enterprise org invoiced on terms already has — and re-materialises the org's entitlements from it, so the result is *derived* exactly as it is after a Stripe webhook rather than a hand-written entitlement override that would make the console and the behaviour disagree. Nothing here touches Stripe or charges anybody. An organisation whose live subscription **is** Stripe-backed is refused with 409: the next webhook delivery would revert the change, and an operator who thinks they have upgraded a paying customer and has not is worse off than one who was told no. Change that org in Stripe. `reason` is required and is the body of the audit event written into the customer's own log.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"planCode":{"type":"string","enum":["free","creator","studio","business","enterprise"]},"interval":{"default":"month","type":"string","enum":["month","year"]},"seats":{"default":1,"type":"integer","minimum":1,"maximum":1000},"reason":{"type":"string","minLength":8,"maxLength":500}},"required":["planCode","reason"]}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The organisation's plan, and the entitlements it resolved to.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformOrgPlan"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such organisation, or that plan is not seeded here.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The organisation's plan is billed through Stripe.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/platform/orgs/{orgId}/storage":{"put":{"operationId":"setPlatformOrgStorage","summary":"Point an organisation's next artefacts at a different object store","tags":["platform"],"description":"Needs `platform:admin`. Sets `organizations.storage_provider` (and optionally `storage_bucket`), which decides where this organisation's **next** recording, rendition, transcript, render and library asset is written. **Existing objects are not moved and not affected**: since TAB-593 every artefact row records the store it was written to, and every signer reads the row before the organisation, so a download issued after this call still resolves against the store the bytes are actually in. Nothing is copied and nothing is deleted. Asking for the provider the organisation is already on is a no-op that still writes the audit event, so a retry is safe. `reason` is required and is the body of the audit event written into the customer's own log.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"provider":{"type":"string","enum":["r2","s3"]},"bucket":{"anyOf":[{"type":"string","minLength":1,"maxLength":200},{"type":"null"}]},"reason":{"type":"string","minLength":8,"maxLength":500}},"required":["provider","reason"]}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Where this organisation's next artefacts will go.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformOrgStorage"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such organisation.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/platform/emulation-requests":{"post":{"operationId":"createEmulationRequest","summary":"Ask a second platform admin for permission to emulate a platform operator","tags":["platform"],"description":"Needs `platform:admin`. Emulating an *ordinary* account needs nothing of the sort — `POST /v1/impersonation` opens that session directly — and this route refuses a target who holds no platform grant (422) rather than filling every operator's inbox with an ask nobody has to decide. `reason` is required and at least eight characters: a second human is being asked to approve it, and it is written into both audit logs. The request expires on its own if nobody decides it. One pending request per (requester, target): asking twice returns 409 and the outstanding one stands. Every *other* platform admin is notified — a row in their inbox, a realtime hint, and an email; the requester is notified again when somebody decides.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"targetUserId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"orgId":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"reason":{"type":"string","minLength":8,"maxLength":500},"minutes":{"type":"integer","minimum":1,"maximum":60}},"required":["targetUserId","orgId","reason"]}}}},"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The request, waiting for a decision.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmulationRequest"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"You already have a request waiting on that account.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"No reason, or a target who needs no approval.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"get":{"operationId":"listEmulationRequests","summary":"Emulation approval requests: the inbox, or your own","tags":["platform"],"description":"Needs `platform:admin`. `scope=pending` (the default) is the approvals inbox and deliberately **excludes the caller's own** requests — they may not decide those, and an inbox containing rows you cannot act on is one people learn to skim. `scope=mine` is what you asked for and are waiting on; `scope=all` is the history. `status` is what was decided and `state` is what is true now: an approved request whose window has run out reads `approved` and `expired`, and one that has opened its session reads `approved` and `consumed`.","parameters":[{"schema":{"default":"pending","type":"string","enum":["pending","mine","all"]},"in":"query","name":"scope","required":false},{"schema":{"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The requests, newest first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmulationRequestList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"An unknown scope or a bad limit.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/platform/emulation-requests/{id}/approve":{"post":{"operationId":"approveEmulationRequest","summary":"Approve a colleague's request to emulate a platform operator","tags":["platform"],"description":"Needs `platform:admin`, and the approver may not be the requester — 403, enforced in the domain and again by a database CHECK. On approval the request's `expiresAt` is **re-stamped** to now plus the approval window (`EMULATION_APPROVAL_WINDOW_MINUTES`, 60 by default; `minutes` may shorten it, never extend it past the hour the session itself is capped at). The window is re-checked on every emulated request, not only when the session opens. The approval is **single-use**: it opens exactly one session and is spent. A decided request cannot be decided again (409), and one that expired before anybody got to it cannot be decided at all (409). Both audit logs are written — the platform's and the target organisation's — and the requester is notified in app and by email.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"note":{"type":"string","minLength":1,"maxLength":500},"minutes":{"type":"integer","minimum":1,"maximum":60}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The decided request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmulationRequest"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"A platform admin cannot decide their own request.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such request.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Already decided, or expired before it was.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/platform/emulation-requests/{id}/deny":{"post":{"operationId":"denyEmulationRequest","summary":"Decline a colleague's request to emulate a platform operator","tags":["platform"],"description":"Needs `platform:admin`, and the approver may not be the requester — 403, enforced in the domain and again by a database CHECK. Nothing is opened. A `note` is worth writing — it reaches the requester. A decided request cannot be decided again (409), and one that expired before anybody got to it cannot be decided at all (409). Both audit logs are written — the platform's and the target organisation's — and the requester is notified in app and by email.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"note":{"type":"string","minLength":1,"maxLength":500},"minutes":{"type":"integer","minimum":1,"maximum":60}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The decided request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmulationRequest"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"A platform admin cannot decide their own request.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such request.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Already decided, or expired before it was.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/webhooks":{"post":{"operationId":"createWebhookEndpoint","summary":"Create a webhook endpoint","tags":["webhooks"],"description":"Mints a 256-bit signing secret and returns it **once**. The secret is stored as a KMS envelope in `secret_ciphertext` (ADR-0002 §3) and no endpoint returns it again — a lost secret is rotated, not looked up. The URL must be https and must not resolve to a private address. See `docs/webhooks.md` for the signature scheme and a verification snippet.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The endpoint and its signing secret.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointCreated"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The organisation's endpoint allowance is already used.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"The URL or an event-type filter was rejected.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"KMS is not configured in this environment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"get":{"operationId":"listWebhookEndpoints","summary":"List an organisation's webhook endpoints","tags":["webhooks"],"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The endpoints. Signing secrets are not included.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/webhooks/{webhookId}":{"get":{"operationId":"getWebhookEndpoint","summary":"Read a webhook endpoint","tags":["webhooks"],"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"webhookId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpoint"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"patch":{"operationId":"updateWebhookEndpoint","summary":"Update, enable or disable a webhook endpoint","tags":["webhooks"],"description":"Re-enabling an endpoint the dispatcher auto-disabled also clears its consecutive-failure counter: a human turning it back on is asserting they fixed it, and resuming at 19 failures would switch it off again on the next hiccup.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWebhookBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"webhookId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The updated endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpoint"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"delete":{"operationId":"deleteWebhookEndpoint","summary":"Delete a webhook endpoint","tags":["webhooks"],"description":"The delivery log goes with it. That is deliberate: the log's only subject is the endpoint, and keeping orphaned rows would mean keeping payloads for a subscription the customer has revoked.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"webhookId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Deleted."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/webhooks/{webhookId}/rotate-secret":{"post":{"operationId":"rotateWebhookSecret","summary":"Rotate an endpoint's signing secret","tags":["webhooks"],"description":"Returns the new secret **once**. The previous secret keeps signing alongside it for 24 hours — every request in that window carries two `v1=` values — so a verifier can be redeployed without dropping an event. Rotating again inside that window is a 409 rather than a silent overwrite of a secret the customer may still be the only one holding.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"webhookId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The new secret and when the old one expires.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSecretRotated"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"A rotation is already in flight.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"KMS is not configured in this environment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/webhooks/{webhookId}/test":{"post":{"operationId":"testWebhookEndpoint","summary":"Send a test ping","tags":["webhooks"],"description":"POSTs a signed `webhook.ping` synchronously and reports what came back, including the response status and the latency. The ping ignores the endpoint's event filter and works on a disabled endpoint — \"does my endpoint work\" must not depend on the filter being right, and it is exactly the question asked before re-enabling one. Recorded in the delivery log like any other delivery.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"webhookId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"What the endpoint answered.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookTestResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"KMS or the database is not configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/webhooks/{webhookId}/deliveries":{"get":{"operationId":"listWebhookDeliveries","summary":"List an endpoint's deliveries","tags":["webhooks"],"description":"Newest first, filterable by `status` and `eventType`. Pagination is keyset (`before`), not offset: the log grows at the head, so an offset query silently repeats rows the moment a delivery lands between requests. Payloads are rendered with secret-shaped keys masked.","parameters":[{"schema":{"type":"string","enum":["pending","delivered","failed","dead"]},"in":"query","name":"status","required":false},{"schema":{"type":"string","maxLength":120},"in":"query","name":"eventType","required":false},{"schema":{"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false},{"schema":{"type":"string"},"in":"query","name":"before","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"webhookId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The delivery log.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/deliveries/{deliveryId}/redrive":{"post":{"operationId":"redriveWebhookDelivery","summary":"Re-send a failed or dead delivery","tags":["webhooks"],"description":"Sends the *stored* payload again, synchronously, and records a new attempt. `redriveCount` is a separate counter from `attempts`, so \"this endpoint needed eleven automatic attempts\" and \"an operator pushed this through twice by hand\" stay different facts in the log. Redriving a delivery that already succeeded is a 409 — that would send the event twice.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"deliveryId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"What the endpoint answered this time.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookTestResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"That delivery already succeeded.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"KMS or the database is not configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/api-keys":{"post":{"operationId":"createApiKey","summary":"Issue an API key","tags":["api-keys"],"description":"Mints a credential and returns it **once**. Only a scrypt digest of the secret half is stored, so a lost key is rotated rather than looked up. `scopes` is the public vocabulary — `read`, `control`, `admin` — which the policy engine expands into permissions; the four permissions no scope grants (billing, org deletion, SSO, platform) are listed in `packages/contexts/identity/src/policy/scopes.ts`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKey"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The key and its secret. The secret is not returned again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreated"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"An unknown scope, or an expiry in the past.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"get":{"operationId":"listApiKeys","summary":"List the organisation's API keys","tags":["api-keys"],"description":"Secrets are never included. `lastUsedAt` is coalesced to the minute.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The keys.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/api-keys/{keyId}":{"get":{"operationId":"getApiKey","summary":"Read one API key","tags":["api-keys"],"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"keyId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKey"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"patch":{"operationId":"updateApiKey","summary":"Rename an API key, or change its scopes, expiry or rate limit","tags":["api-keys"],"description":"Narrowing a key's scopes takes effect on its next request — the policy engine reads the row every time. A revoked key cannot be edited; issue a new one.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateApiKey"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"keyId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The updated key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKey"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The key is revoked.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"delete":{"operationId":"revokeApiKey","summary":"Revoke an API key","tags":["api-keys"],"description":"Immediate: the next request the key makes is refused, because its status is read on every request rather than cached. The row is kept — deleting it would take the audit trail with it and free the prefix for reissue. Idempotent.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"keyId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Revoked."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/api-keys/{keyId}/rotate":{"post":{"operationId":"rotateApiKey","summary":"Rotate an API key's secret","tags":["api-keys"],"description":"Mints a new secret and keeps the handle, so dashboards and audit rows still name the same key. The old secret keeps working for `overlapHours` (default 24) — that window is the reason this endpoint exists rather than 'revoke and create', because a rotation that breaks the customer's pipeline the instant it is called is a rotation nobody performs. Send `overlapHours: 0` for a leaked secret.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RotateApiKey"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"keyId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The key and its new secret.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyRotated"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The key is revoked.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/oauth-clients":{"post":{"operationId":"createOAuthClient","summary":"Create an OAuth client","tags":["api-keys"],"description":"For the `client_credentials` grant at `POST /v1/oauth/token`. Choose this over an API key when request volume matters: the client secret is exchanged once an hour for a token that is verified with no database read. The cost is that revoking the client does not revoke tokens already issued to it — those expire on their own, within the hour.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOAuthClient"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The client and its secret. The secret is not returned again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthClientCreated"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"get":{"operationId":"listOAuthClients","summary":"List the organisation's OAuth clients","tags":["api-keys"],"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The clients.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthClientList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/oauth-clients/{clientId}/rotate-secret":{"post":{"operationId":"rotateOAuthClientSecret","summary":"Rotate an OAuth client's secret","tags":["api-keys"],"description":"Same bounded overlap as an API key rotation: the old secret keeps working for `overlapHours`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RotateApiKey"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"clientId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The client and its new secret.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthClientRotated"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The client is revoked.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/orgs/{orgId}/oauth-clients/{clientId}":{"delete":{"operationId":"revokeOAuthClient","summary":"Revoke an OAuth client","tags":["api-keys"],"description":"Stops the client getting new tokens. Tokens already issued stay valid until they expire (at most an hour) — that is the trade a token-based credential makes, and it is why an integration that needs instant revocation should use an API key.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"orgId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"clientId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Revoked."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/openapi.json":{"get":{"operationId":"getPublicOpenApiDocument","summary":"This API's OpenAPI 3.1 document","tags":["public-api"],"description":"The published contract. Generated from the Zod schemas at build time, from a full app — every route group, whichever Lambda answers — so a client generated from it is complete. `packages/api-client` and the MCP server are both generated from this document. The third-party surface is the set of operations carrying the `apiKeyAuth` scheme.","security":[],"responses":{"200":{"description":"The OpenAPI 3.1 document.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenApiDocument"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/docs":{"get":{"operationId":"getApiReference","summary":"Human-readable API reference","tags":["public-api"],"description":"The same document as `/v1/openapi.json`, rendered. Server-side and self-contained — no CDN script, no external stylesheet — because a docs page that fetches a bundle from somebody else's origin is a supply chain in front of the thing integrators paste credentials next to.","security":[],"responses":{"200":{"description":"An HTML reference page.","content":{"text/html":{"schema":{"type":"string"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/oauth/token":{"post":{"operationId":"issueOAuthToken","summary":"Exchange client credentials for an access token","tags":["public-api"],"description":"RFC 6749 §4.4. Send the credentials as HTTP Basic (`Authorization: Basic base64(client_id:client_secret)`) or in the form body. The token is a one-hour HS256 JWT carrying the org and the granted scopes, verified with no database read — which is the point of this grant, and the reason revoking a client does not revoke tokens already issued to it.\n\nErrors are RFC 6749 error objects, not RFC 7807 problem documents: every OAuth client library in the world is written against the former.","requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/OAuthTokenRequest"}},"application/json":{"schema":{"$ref":"#/components/schemas/OAuthTokenRequest"}}}},"security":[],"responses":{"200":{"description":"The access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthTokenResponse"}}}},"400":{"description":"An OAuth error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"401":{"description":"Client authentication failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"503":{"description":"Token signing is not configured in this environment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/organization":{"get":{"operationId":"getPublicOrganization","summary":"The organisation this credential belongs to","tags":["public-api"],"description":"There is no `orgId` on any endpoint here: a credential belongs to exactly one organisation and cannot ask about another, so the tenant is read from the credential rather than claimed by the caller.","security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The organisation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicOrganization"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/teams":{"get":{"operationId":"listPublicTeams","summary":"List teams","tags":["public-api"],"parameters":[{"schema":{"default":25,"type":"integer","minimum":1,"maximum":100},"in":"query","name":"limit","required":false},{"schema":{"type":"string","minLength":1,"maxLength":512},"in":"query","name":"cursor","required":false}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The teams.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicTeamList"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/productions":{"get":{"operationId":"listPublicProductions","summary":"List shows","tags":["public-api"],"parameters":[{"schema":{"default":25,"type":"integer","minimum":1,"maximum":100},"in":"query","name":"limit","required":false},{"schema":{"type":"string","minLength":1,"maxLength":512},"in":"query","name":"cursor","required":false}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The shows.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicProductionList"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/sessions":{"get":{"operationId":"listPublicSessions","summary":"List sessions","tags":["public-api"],"description":"Newest first. Narrow with `productionId` and `status`.","parameters":[{"schema":{"default":25,"type":"integer","minimum":1,"maximum":100},"in":"query","name":"limit","required":false},{"schema":{"type":"string","minLength":1,"maxLength":512},"in":"query","name":"cursor","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"productionId","required":false},{"schema":{"type":"string","enum":["scheduled","lobby","live","ending","complete","failed","canceled"]},"in":"query","name":"status","required":false}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The sessions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicSessionList"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"post":{"operationId":"createPublicSession","summary":"Schedule a session","tags":["public-api"],"description":"The same use case the web app calls, with the same gates: the org must not be read-only (dunning), and the credential must hold `session:schedule`. Needs the `control` scope and the `api_full` entitlement (Business).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePublicSession"}}}},"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"201":{"description":"The scheduled session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicSession"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"402":{"description":"The plan does not include the writable API, or the org is read-only.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/sessions/{sessionId}":{"get":{"operationId":"getPublicSession","summary":"Read a session","tags":["public-api"],"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicSession"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/sessions/{sessionId}/participants":{"get":{"operationId":"listPublicParticipants","summary":"List a session's participants","tags":["public-api"],"parameters":[{"schema":{"default":25,"type":"integer","minimum":1,"maximum":100},"in":"query","name":"limit","required":false},{"schema":{"type":"string","minLength":1,"maxLength":512},"in":"query","name":"cursor","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The participants.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicParticipantList"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/recordings":{"get":{"operationId":"listPublicRecordings","summary":"List recordings","tags":["public-api"],"description":"A recording is a session that has tracks. Archived recordings are included and carry `archivedAt`; soft-deleted ones are not returned at all.","parameters":[{"schema":{"default":25,"type":"integer","minimum":1,"maximum":100},"in":"query","name":"limit","required":false},{"schema":{"type":"string","minLength":1,"maxLength":512},"in":"query","name":"cursor","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"productionId","required":false},{"schema":{"type":"string","enum":["scheduled","lobby","live","ending","complete","failed","canceled"]},"in":"query","name":"status","required":false}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The recordings.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicRecordingList"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/recordings/{sessionId}":{"get":{"operationId":"getPublicRecording","summary":"Read one recording: session, participants, tracks and transcripts","tags":["public-api"],"description":"Comms tracks (`kind = comms`) are excluded by construction — they are talkback, not programme, and `packages/db` states that exclusion as a property of the schema rather than as a filter each caller remembers.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The recording.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicRecordingDetail"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/sessions/{sessionId}/invites":{"post":{"operationId":"createPublicSessionInvite","summary":"Issue a join link for a session","tags":["public-api"],"description":"Returns the token **once**; only its sha256 is stored, so a lost link is revoked and re-created rather than looked up. The link admits whoever holds it, in the role given, until it expires or its uses run out.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePublicInvite"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"201":{"description":"The invite and its token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicInviteCreated"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"402":{"description":"Payment required","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The session can no longer be joined.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/sessions/{sessionId}/record":{"post":{"operationId":"sendPublicRecordCommand","summary":"Arm, start, stop or mark a recording","tags":["public-api"],"description":"Persists a `record_commands` row tagged `source: api` and stamped with the issuing credential, which is what devices in the room act on. Acknowledgements arrive asynchronously from each device and appear in `acks`; the response is the accepted command, not a report that every camera has rolled.\n\n`start` additionally checks the org's recording-minute cap, exactly as the first-party session start does — an API caller must not be able to route around a hard cap.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordCommand"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"202":{"description":"The accepted command.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordCommandAccepted"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"402":{"description":"The plan does not include the writable API, or the minute cap is exhausted.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The session is not in a state that can be recorded.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/productions/{productionId}":{"get":{"operationId":"getPublicProduction","summary":"Read one show","tags":["public-api"],"description":"The show itself. Its episodes are `GET /v1/public/productions/{id}/episodes`.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"productionId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The show.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicProduction"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/productions/{productionId}/episodes":{"get":{"operationId":"listPublicEpisodes","summary":"List a show's episodes","tags":["public-api"],"description":"A production **is** the series and a session is one episode of it — Tablecast has no separate `series` row, which is why each episode carries its show inline rather than a `seriesId` pointing at a table that does not exist.\n\nNewest first, and paged on the same `(createdAt, id)` keyset as every other list here. `episodeNo` is the show's own numbering and is **not** the sort order: it is nullable, and a keyset on a nullable column loses rows at the page boundary.\n\nEach entry carries `trackCount` (programme tracks; comms legs are never counted) and `hasTranscript`, so listing a season and deciding what to fetch is one call.","parameters":[{"schema":{"default":25,"type":"integer","minimum":1,"maximum":100},"in":"query","name":"limit","required":false},{"schema":{"type":"string","minLength":1,"maxLength":512},"in":"query","name":"cursor","required":false},{"schema":{"type":"string","enum":["scheduled","lobby","live","ending","complete","failed","canceled"]},"in":"query","name":"status","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"productionId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The episodes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicEpisodeList"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/episodes/{sessionId}":{"get":{"operationId":"getPublicEpisode","summary":"Read one episode","tags":["public-api"],"description":"The same row `GET /v1/public/sessions/{sessionId}` returns, framed as an episode: the show resolved inline, plus the two counts. Both spellings are supported and neither is deprecated — a scheduling integration thinks in sessions, a catalogue thinks in episodes.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The episode.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicEpisode"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/recordings/{sessionId}/renditions":{"get":{"operationId":"listPublicRenditions","summary":"List a recording's CMAF proxy ladder","tags":["public-api"],"description":"Both rungs of every ISO and of the programme master, with each row's status. A rendition is playable once it is `ready`; before that `segmentCount` is 0 and a playback ticket is refused with `conflict` rather than handed out over an empty manifest.\n\nNot paginated: a ladder is bounded by the recording, so a cursor would be ceremony over a list that cannot grow without bound.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"Every rendition of the recording.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicRenditionList"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/recordings/{sessionId}/playback-ticket":{"post":{"operationId":"issuePublicPlaybackTicket","summary":"Presign a batch of CMAF segments for playback","tags":["public-api"],"description":"Up to 200 presigned segment GETs from the rendition's manifest starting at `fromIndex`, plus the presigned init segment, all valid for an hour. `nextIndex` is what to ask for next, or null when the batch reached the end. The URLs are signed through the organisation's own storage target, so an R2 org and an S3 org get the same shape and the player never learns which it is.\n\nA **`POST` that reads**: it mints credentials rather than returning a cacheable representation, which is why it is not a `GET`, and it needs only the `read` scope, because playing a recording is reading it. A rendition that is not `ready` is `conflict` — a ticket over a manifest that does not exist yet is a player that 403s on every segment and blames the network.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicPlaybackTicketRequest"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The ticket.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicPlaybackTicket"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"That rendition has not finished encoding.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No object-storage credentials are configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/recordings/{sessionId}/rendition-assets":{"post":{"operationId":"issuePublicRenditionAssetUrl","summary":"Presign a rendition's waveform or thumbnail sprite","tags":["public-api"],"description":"A one-hour presigned GET for one of the three artefacts a rendition carries: `peaks` (the merged waveform JSON), `sprites` (the sprite index, which names the sheets) or `sprite-sheet` (one sheet image, naming its `sheetIndex`).\n\nThe request names which asset, **never a storage key** — the server resolves the object from the rendition row. A rendition on another session, or one with no such asset yet, is `not-found`: a caller probing ids learns nothing either way.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicRenditionAssetRequest"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The signed URL.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicRenditionAssetLink"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No object-storage credentials are configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/recordings/{sessionId}/transcripts":{"get":{"operationId":"listPublicTranscripts","summary":"A recording's transcripts, with word timings behind a signed URL","tags":["public-api"],"description":"One entry per programme track: the sentence-level segments inline, and a fifteen-minute presigned URL for the word-level JSON the caller fetches directly. `vttUrl` is the same for the WebVTT sidecar, where one has been written.\n\nThe **speaker comes from the track**, never from diarization: Tablecast records one ISO per person, so who is talking is known exactly rather than guessed at. Comms legs are excluded by construction — a producer's talkback is not programme and must never reach a transcript.\n\nFilter with `trackId` or `language`; every language a recording has been transcribed into is returned when neither is given.\n\nSegment times are **session-clock ms** (`timeBase: \"session\"`) — the one timeline that means anything across tracks, so a quote from a camera that joined late lines up with one from a camera that was there from the start.","parameters":[{"schema":{"default":25,"type":"integer","minimum":1,"maximum":100},"in":"query","name":"limit","required":false},{"schema":{"type":"string","minLength":1,"maxLength":512},"in":"query","name":"cursor","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"trackId","required":false},{"schema":{"type":"string","maxLength":20},"in":"query","name":"language","required":false,"description":"BCP 47, matched exactly. Omit for every language."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The transcripts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicTranscriptList"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No object-storage credentials are configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/assets":{"get":{"operationId":"listPublicAssets","summary":"List the media library","tags":["public-api"],"description":"Every file the organisation holds — bumpers, stings, logos, imported designs, and the outputs renders were registered as. Newest first.\n\nAn asset sits at one of three levels, and the filters follow: `productionId` for one show's own library, `teamId` for a team's, neither for the organisation-wide shelf **and everything below it**. Narrow further with `kind` and search names with `q`.","parameters":[{"schema":{"default":25,"type":"integer","minimum":1,"maximum":100},"in":"query","name":"limit","required":false},{"schema":{"type":"string","minLength":1,"maxLength":512},"in":"query","name":"cursor","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"productionId","required":false,"description":"A show's own library. Omit for the whole organisation's."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"teamId","required":false},{"schema":{"type":"string","enum":["video","audio","image","stinger","font","caption","document"]},"in":"query","name":"kind","required":false},{"schema":{"type":"string","maxLength":200},"in":"query","name":"q","required":false,"description":"Case-insensitive substring of the name."}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The library.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicAssetList"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Bad filter or cursor.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/assets/{assetId}":{"get":{"operationId":"getPublicAsset","summary":"Read one library file","tags":["public-api"],"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"assetId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The asset.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicAsset"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/assets/{assetId}/download":{"get":{"operationId":"downloadPublicAsset","summary":"Get a signed download URL for one library file","tags":["public-api"],"description":"A presigned GET on the organisation's own bucket, valid for fifteen minutes, with a filename derived from the asset's name **signed into it** so it cannot be rewritten.\n\nEvery call writes an `assets.download` row to `audit_events` whose actor is the credential — `api_key` and the key's id, not whoever created the key, who was asleep. The URL itself is never logged.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"assetId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The signed URL.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicDownloadLink"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No object-storage credentials are configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/render-presets":{"get":{"operationId":"listPublicRenderPresets","summary":"The export presets this deployment offers","tags":["public-api"],"description":"A fixed catalogue in code, so it is not paginated and it does not read the database. `id` is what `POST /v1/public/sessions/{id}/renders` takes as `presetId`.","security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The catalogue.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicRenderPresetCatalogue"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/renders":{"get":{"operationId":"listPublicRenders","summary":"List the export queue","tags":["public-api"],"description":"Every render the organisation has asked for, newest first — queued, running and finished. Narrow to one recording with `sessionId`, or to one state with `status`. `progressPct` is the durable copy of the worker's progress, so a client that missed every realtime event still shows a bar.","parameters":[{"schema":{"default":25,"type":"integer","minimum":1,"maximum":100},"in":"query","name":"limit","required":false},{"schema":{"type":"string","minLength":1,"maxLength":512},"in":"query","name":"cursor","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"sessionId","required":false},{"schema":{"type":"string","enum":["queued","running","succeeded","failed","canceled"]},"in":"query","name":"status","required":false}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The queue.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicRenderList"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/renders/{renderId}":{"get":{"operationId":"getPublicRender","summary":"Read one export","tags":["public-api"],"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"renderId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The render.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicRender"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/renders/{renderId}/download":{"get":{"operationId":"downloadPublicRender","summary":"Get a signed download URL for a finished export","tags":["public-api"],"description":"The same contract as the asset download, for a `succeeded` render — the programme master, a re-switch, a clip or the caption sidecar. A render that has not succeeded yet is `not-found`: there is no object to sign, and saying so by status would invite polling this endpoint instead of the queue.\n\nWrites a `recordings.download` audit row naming the credential.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"renderId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The signed URL.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicDownloadLink"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"No object-storage credentials are configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/sessions/{sessionId}/renders":{"post":{"operationId":"createPublicRender","summary":"Queue an export of a recording","tags":["public-api"],"description":"Opens a `renders` row in `queued` and hands it to a worker. Needs the `control` scope, the `editor:export` permission and the `editor` entitlement; the organisation must not be read-only, and may have ten renders outstanding at once.\n\n`mode` is not on the body. An API caller always gets a **server** render: `browser` mode reserves a row that the calling page then fills in with bytes it encoded itself, and there is no page here.\n\n202-shaped work behind a 201: the row exists when this returns, the file does not. Poll `GET /v1/public/renders/{id}` or list the queue.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePublicRender"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"201":{"description":"The queued render.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicRender"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"402":{"description":"The plan does not include the editor, or the org is read-only.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Too many renders are already outstanding.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/renders/{renderId}/cancel":{"post":{"operationId":"cancelPublicRender","summary":"Cancel a queued or running export","tags":["public-api"],"description":"Records the request. A render that has not started is cancelled outright; one that is already running stops within the worker's poll interval and writes `canceled` itself — so `stoppedImmediately` tells the two apart rather than leaving a caller to infer it. Idempotent: cancelling twice is not an error.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"renderId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The render, and whether it stopped immediately.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicRenderCanceled"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"402":{"description":"The plan does not include the writable API, or the org is read-only.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The render has already finished.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/renders/{renderId}/retry":{"post":{"operationId":"retryPublicRender","summary":"Retry a failed or cancelled export","tags":["public-api"],"description":"Puts the **same** row back to `queued` — same id, same preset, same key — so the worker finds the steps its previous attempt completed and skips them. Only a failed or cancelled render can be retried; anything else is `conflict`.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"renderId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The re-queued render.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicRender"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"402":{"description":"The plan does not include the writable API, or the org is read-only.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Only a failed or cancelled render can be retried.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/productions/{productionId}/graphics":{"get":{"operationId":"listPublicGraphics","summary":"A show's Supers library","tags":["public-api"],"description":"One card per person, newest first. `assetId` points at the transparent PNG in the media library, which is where the bytes are fetched from (`GET /v1/public/assets/{id}/download`).\n\n**Regenerated-over versions are hidden by default.** A regeneration is a new row rather than an overwrite — scenes hold asset ids, and rewriting bytes under a live show would change a take with nothing in the UI having moved — so a show whose straps have been recoloured four times would otherwise render five of everybody. Pass `history=true` for the whole chain.\n\nA `pending` super is a Canva autofill that has not come back yet; a `failed` one carries the reason in `error`.","parameters":[{"schema":{"default":25,"type":"integer","minimum":1,"maximum":100},"in":"query","name":"limit","required":false},{"schema":{"type":"string","minLength":1,"maxLength":512},"in":"query","name":"cursor","required":false},{"schema":{"type":"string","enum":["super","title","bug"]},"in":"query","name":"kind","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"generationId","required":false},{"schema":{"type":"string"},"in":"query","name":"history","required":false,"description":"Include superseded versions. Off by default: a regeneration is a new row."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"productionId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The show's supers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicGraphicList"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/comment-threads":{"get":{"operationId":"listPublicCommentThreads","summary":"Read the review threads on one thing","tags":["public-api"],"description":"Every thread on one target, newest first, each with its whole conversation inline — so a review report and the markers on a scrub bar come from one call.\n\n`targetKind` is `recording`, `render`, `media_asset`, `graphic_asset` or `edit_doc`. A recording's id is its **session** id. Resolved threads are omitted unless `includeResolved` is true: a review list is a list of things still to do. Deleted comments come back as tombstones — `deletedAt` set and `body` empty — rather than being dropped, because a conversation with a hole in it reads as a different conversation.\n\n**Read-only.** A comment's author is one of three kinds, each of which names a person: a member, a guest in the room, or somebody holding a share link. A credential is none of them, and the database's own check constraint says so. Posting as a credential needs a fourth author kind and the decision that goes with it, which is a deliberate change rather than a side effect of this one.","parameters":[{"schema":{"default":25,"type":"integer","minimum":1,"maximum":100},"in":"query","name":"limit","required":false},{"schema":{"type":"string","minLength":1,"maxLength":512},"in":"query","name":"cursor","required":false},{"schema":{"type":"string","enum":["recording","render","media_asset","graphic_asset","edit_doc"]},"in":"query","name":"targetKind","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"targetId","required":true},{"schema":{"type":"string"},"in":"query","name":"includeResolved","required":false}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The threads.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicCommentThreadList"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"An unknown target kind, or a bad cursor.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/shares":{"get":{"operationId":"listPublicShares","summary":"Every share link on one thing","tags":["public-api"],"description":"Newest first, **revoked ones included** — 'this was public for a fortnight in March' is a fact somebody will need back. A live public share carries its `url`; a private one carries only `recipientCount`, because its links are per-person tokens whose plaintext exists exactly once, in the response that minted them.","parameters":[{"schema":{"default":25,"type":"integer","minimum":1,"maximum":100},"in":"query","name":"limit","required":false},{"schema":{"type":"string","minLength":1,"maxLength":512},"in":"query","name":"cursor","required":false},{"schema":{"type":"string","enum":["recording","render","media_asset","edit_doc"]},"in":"query","name":"targetKind","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"targetId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The shares.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicShareList"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such target in this organisation.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"post":{"operationId":"createPublicShare","summary":"Publish a recording, render, file or edit as a public link","tags":["public-api"],"description":"Needs the **`admin`** scope and `recordings:manage`, not `control`. Every other write on this surface runs the show for the organisation; this one takes its media *outside* the organisation, to whoever holds a URL, and that is the same class of act as deleting a recording — not undoable by the person it happened to.\n\n**Public is a toggle, not a mint.** If a live public share already covers this target, that one comes back with `created: false` rather than a second URL nobody could tell apart from the first. The `url` is returned on every read, deliberately: a public token is not a secret about a person, and a link that could be shown only once would turn 'copy link' into 'rotate link'.\n\nPrivate shares — one token per named person, each emailed — are a first-party act: the delivery report is the point of that dialog, and a credential fanning branded mail out to addresses it named is a different thing from publishing a link.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePublicShare"}}}},"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The public share that already covered this target.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicShareCreated"}}}},"201":{"description":"The share.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicShareCreated"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"402":{"description":"The plan does not include the writable API, or the org is read-only.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/shares/{shareId}":{"delete":{"operationId":"revokePublicShare","summary":"Revoke a share link","tags":["public-api"],"description":"Stamps `revokedAt`; the row stays, so the history of who could see what does not disappear with the link, and `url` reads null from then on. Takes effect on the viewer's **next** request — there is no cached credential to outlive it, which is exactly why a share token is a row rather than a signed JWT — and a comment already left through it is not withdrawn.\n\nEvery named recipient of a private share is revoked with it. Idempotent: revoking twice is one revocation, and the **first** `revokedAt` is the honest one — the second call withdrew nothing.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"shareId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The revoked share.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicShare"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"402":{"description":"The plan does not include the writable API, or the org is read-only.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/sessions/{sessionId}/chat":{"get":{"operationId":"listPublicChat","summary":"A session's open chat log","tags":["public-api"],"description":"The room's **`all`** channel, newest first — the same keyset order as every other list here, which is the reverse of what a chat window renders, so reverse the page client-side.\n\nThe `producers` cue channel and private threads between two people are not on this surface and cannot be asked for. On the first-party API they are gated on the caller's *seat in the room* — a guest asking for `producers` by name gets a 403 and no row count — and a credential has no seat, so there is no honest answer to give it but the open channel.\n\n**Read-only**, for the same reason: posting resolves the author to a `participants` row, and fabricating one would put a robot in the grid of a live show.","parameters":[{"schema":{"default":25,"type":"integer","minimum":1,"maximum":100},"in":"query","name":"limit","required":false},{"schema":{"type":"string","minLength":1,"maxLength":512},"in":"query","name":"cursor","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"One page of chat, newest first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicChatList"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/sessions/{sessionId}/invite-recipients":{"get":{"operationId":"listPublicSessionInviteRecipients","summary":"The guest funnel for one episode","tags":["public-api"],"description":"Everybody named on this episode and how far through they have got: `invited` → `opened` → `checked_in` → `in_lobby` → `ready` → `on_air`, or one of the terminal states. `lastDeliveryError` says why the last invitation did not go, for the failures that happen before there is a message id to look up.\n\n**Never their token.** Only its sha256 is stored, and the link exists exactly once — in the response that mints it.","parameters":[{"schema":{"default":25,"type":"integer","minimum":1,"maximum":100},"in":"query","name":"limit","required":false},{"schema":{"type":"string","minLength":1,"maxLength":512},"in":"query","name":"cursor","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The people, and where they have got to.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicInviteRecipientList"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"post":{"operationId":"addPublicSessionInviteRecipients","summary":"Invite people to an episode","tags":["public-api"],"description":"One link and one branded email per person. Each link admits exactly the address it was sent to, in the role given, and `joinUrl` is returned **once** — only the token's sha256 is stored, so a lost link is re-sent rather than looked up.\n\nA send that fails does not fail the call: the people exist and their links are in the response, and the reason lands on the row as `lastDeliveryError`. An address already on this episode is rotated rather than duplicated, and gets the re-send copy — their previous link has just been retired.\n\nNeeds `control` and `session:schedule`, the same pair scheduling the episode needs. The session must still be joinable: a `complete`, `canceled` or `failed` one is `conflict`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddPublicInviteRecipients"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"201":{"description":"The people, and their links.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicInviteRecipientsCreated"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"402":{"description":"The plan does not include the writable API, or the org is read-only.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/productions/{productionId}/invite-recipients":{"get":{"operationId":"listPublicProductionInviteRecipients","summary":"A show's roster","tags":["public-api"],"description":"The people who travel with this show. A production-scoped invitation covers every episode and the planning room, which is what makes a recurring co-host one invitation rather than twelve.","parameters":[{"schema":{"default":25,"type":"integer","minimum":1,"maximum":100},"in":"query","name":"limit","required":false},{"schema":{"type":"string","minLength":1,"maxLength":512},"in":"query","name":"cursor","required":false},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"productionId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The roster.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicInviteRecipientList"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"post":{"operationId":"addPublicProductionInviteRecipients","summary":"Add people to a show's roster","tags":["public-api"],"description":"The same act, scoped to the show rather than to one episode. Their link resolves to whichever episode is open when they tap it: one that is live or in its lobby, else the soonest scheduled one, else the one that was due an hour ago and is running late. The invitation names the show rather than a date, because a roster entry has no one time.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddPublicInviteRecipients"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"productionId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"201":{"description":"The people, and their links.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicInviteRecipientsCreated"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"402":{"description":"The plan does not include the writable API, or the org is read-only.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/sessions/{sessionId}/events":{"get":{"operationId":"listPublicSessionEvents","summary":"What has happened in a session, since a cursor","tags":["public-api"],"description":"A merged, ordered feed of the durable rows a live show writes: the switching EDL (`production.state`), record commands (`session.record`), joins and leaves (`session.participant`), the room's open chat (`session.chat`) and the coordinator's own lifecycle (`session.coordinator`).\n\n**Oldest first**, which is the opposite of every other list on this surface and is the point: this is a log being followed forwards, not a page being browsed backwards.\n\nPass the `cursor` from the previous answer as `since` and you get exactly what has happened since — nothing repeated, nothing skipped. Omit it to start from the beginning of the session.\n\n`wait` turns the call into a long poll: the server holds the request open until an event arrives or the budget runs out, so a watcher sees a director's cut within about a second without hammering the endpoint. It is clamped to 10000 ms by the Lambda's own timeout.\n\nThis is **not** a socket, and the difference is in your favour: a dropped connection costs you nothing, because the cursor is a position in a table rather than a place in a stream that was still running while you were away.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":2048},"in":"query","name":"since","required":false,"description":"A `cursor` from a previous batch. Omit to start at the beginning."},{"schema":{"type":"string","maxLength":256},"in":"query","name":"kinds","required":false,"description":"Comma-separated. One or more of: production.state, session.record, session.participant, session.chat, session.coordinator. Omit for all of them."},{"schema":{"type":"integer","minimum":1,"maximum":200},"in":"query","name":"limit","required":false},{"schema":{"type":"integer","minimum":0,"maximum":60000},"in":"query","name":"wait","required":false,"description":"Long-poll: hold the request open up to this many ms for an event. Clamped to 10000."},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"Everything since the cursor, oldest first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicSessionEventPage"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/sessions/{sessionId}/event-subscriptions":{"post":{"operationId":"createPublicSessionEventSubscription","summary":"Start watching a session","tags":["public-api"],"description":"Mints a handle that remembers your place in the feed, so polling it needs nothing but its id and a reconnect loses nothing.\n\n`read` scope, and no scope is widened by watching: everything the feed carries is already readable through the endpoints beside this one.\n\n**It expires.** A subscription is a row with a lifetime — 3600 s by default — and the poll route says how long is left on every answer, then delivers one final batch naming the expiry rather than going quiet. Watching a whole show means either asking for a longer `ttlSeconds` up front or subscribing again with the last `cursor`, which costs one call and skips nothing.\n\n`fromNow: true` starts at the present moment. The default starts at the beginning of the session, which is what you want when you are asked to catch up on a show already running.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEventSubscription"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"201":{"description":"The subscription.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicEventSubscription"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/event-subscriptions/{subscriptionId}/poll":{"post":{"operationId":"pollPublicSessionEventSubscription","summary":"Take everything that has happened since the last time you asked","tags":["public-api"],"description":"The cursor advances **server-side**, so an agent never has to carry one — which is also why this is a `POST` on a `read` scope rather than a `GET`: it changes something, and a verb that says otherwise invites a retry that would drop a batch. Two concurrent polls of one subscription split the batch between them rather than each getting all of it: **one poller per subscription**.\n\n`wait` long-polls, up to 10000 ms.\n\nWhen the subscription has expired or been revoked, this answers **once** with `final` set to a sentence saying which and what to do — and the `cursor` in that same answer is still good, so subscribing again with it as `since` resumes without a gap. After that the subscription is gone.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PollSubscription"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"subscriptionId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"Everything since the last poll.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicEventSubscriptionPage"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/event-subscriptions/{subscriptionId}":{"delete":{"operationId":"deletePublicSessionEventSubscription","summary":"Stop watching","tags":["public-api"],"description":"Closes the subscription. Idempotent: closing one that is already closed answers 200 with the original `revokedAt` rather than 404, because a client retrying an unsubscribe it is not sure landed should not have to tell those two cases apart.\n\n`read` scope, not `control`: this stops the caller's own watch and is invisible to everybody else. A read-only integration that could start a watch and not stop it would be the worse design.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"subscriptionId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"Closed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevokedEventSubscription"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"The credential does not hold the scope or permission this needs.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Per-credential rate limit. See `Retry-After` and `X-RateLimit-*`.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Upstream unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/sessions/{sessionId}/control":{"post":{"operationId":"sendCompanionControl","summary":"Run a batch of Companion ops against a live session","tags":["public-api"],"description":"The endpoint a Bitfocus Companion module (or its Generic HTTP module) calls on a key press. Every op becomes an ordinary production-state op tagged `source: companion`, so a take from a Stream Deck and a take from the director's mouse are the same row in the EDL and replay identically in the editor.\n\nOps are executed where the thing they change lives: the switcher, the mixer, talkback and markers go to the session coordinator; `record.*` writes a `record_commands` row; `live.*` assembles stream URLs server-side. One request, one result per op, in order.\n\n**A batch runs to the end.** A key that is three ops must not lose its take because the graphic next to it was deleted last week, so a refused op is reported rather than aborting the request. Nothing is rolled back — each op is independently atomic.\n\nThe credential must be a Companion pairing for this session (or for its production). An ordinary API key with the `control` scope is **not** enough: a pairing names the participant the surface acts as, which is what the EDL records and what the comms plane checks a role against.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanionControl"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"Every op was accepted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanionControlResult"}}}},"207":{"description":"The batch ran; at least one op was refused. See each result's `code`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanionControlResult"}}}},"401":{"description":"The credential is missing, malformed, revoked or expired.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"402":{"description":"The plan does not include the writable API, or a cap is exhausted.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"No pairing for this session, a revoked or expired pairing, or a missing permission.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"The session is not in a state that can be controlled.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Validation failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Rate limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"The session coordinator is unreachable; nothing was applied.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/sessions/{sessionId}/companion-state":{"get":{"operationId":"getCompanionState","summary":"Tally and state for a Companion surface","tags":["public-api"],"description":"What a Stream Deck's keys are lit from (TAB-234): program and preview scene, per-scene tally, graphic visibility, per-source mute, who talkback is keyed to, the session clock and whether the program feed is live.\n\nRead rather than pushed, because a Companion sits behind the customer's firewall and reaches us. The module refreshes on connect, on reconnect and on a timer, and diffs locally so only changed keys are redrawn. The clock is quantised to whole seconds in the derivation, so polling faster than 1 Hz produces no extra key updates.\n\nNeeds the same session pairing the control endpoint does.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The tally picture.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanionState"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"No pairing for this session, or a revoked or expired one.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Rate limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"The session coordinator is unreachable.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/public/sessions/{sessionId}/tally-ticket":{"post":{"operationId":"createCompanionTallyTicket","summary":"Open the pushed tally feed instead of polling","tags":["public-api"],"description":"The replacement for polling `companion-state` (TAB-445). Answers a `wss://` URL and a short-lived ticket; the module opens it and receives a full snapshot on connect and a **diff whenever something changes** — and nothing at all while nothing does.\n\nThe split exists because the two halves of the check live in different processes. Only the API can authenticate a pairing (it needs the database, and the coordinator has none), and only the coordinator holds the production document. So the pairing is checked here, exactly as it is for a key press, and the ticket that comes out of it is what the coordinator can verify on its own — an HMAC over `(session, org, pairing, expiry)` with the service token the two already share. It names one session, it lasts five minutes, and the only thing that accepts it is a read-only feed.\n\nAsk for a new one on every dial rather than keeping it. That is also what makes revocation work: a revoked pairing's next reconnect fails *here*, where pairings are checked.\n\n**503 when this deployment has no public tally endpoint configured** — the module then keeps polling, which is slower and entirely correct. The pushed feed is an optimisation over a path that already works.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"sessionId","required":true}],"security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"Where to connect, and the ticket to do it with.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanionTallyTicket"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"No pairing for this session, or a revoked or expired one.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Rate limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"This deployment has no pushed tally endpoint configured.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/.well-known/oauth-authorization-server":{"get":{"operationId":"getOAuthAuthorizationServerMetadata","summary":"Authorization-server metadata (RFC 8414)","tags":["oauth"],"description":"Where an MCP client finds the authorize, token, registration and revocation endpoints. `code_challenge_methods_supported` is `[\"S256\"]` and nothing else: `plain` is a code challenge equal to its verifier, which protects against nothing once the authorization request has been observed.","security":[],"responses":{"200":{"description":"The metadata document.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthAuthorizationServerMetadata"}}}}}}},"/.well-known/oauth-protected-resource":{"get":{"operationId":"getOAuthProtectedResourceMetadata","summary":"Protected-resource metadata for /mcp (RFC 9728)","tags":["oauth"],"description":"The document the `WWW-Authenticate` challenge on `/mcp` points at. It names the resource and the authorization server that governs it; the client derives `/.well-known/oauth-authorization-server` from there.","security":[],"responses":{"200":{"description":"The metadata document.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthProtectedResourceMetadata"}}}}}}},"/.well-known/oauth-protected-resource/mcp":{"get":{"operationId":"getOAuthProtectedResourceMetadataForMcp","summary":"Protected-resource metadata for /mcp (RFC 9728)","tags":["oauth"],"description":"The document the `WWW-Authenticate` challenge on `/mcp` points at. It names the resource and the authorization server that governs it; the client derives `/.well-known/oauth-authorization-server` from there.","security":[],"responses":{"200":{"description":"The metadata document.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthProtectedResourceMetadata"}}}}}}},"/v1/oauth/mcp/register":{"post":{"operationId":"registerMcpOAuthClient","summary":"Register an MCP client (RFC 7591)","tags":["oauth"],"description":"Unauthenticated, by RFC 7591's design and safely: a registration is a piece of software announcing a callback URL. It carries no organisation, no user and no scope, and grants nothing at all until a signed-in human approves a connection on the consent page.\n\n`token_endpoint_auth_method` defaults to `none` — a public client authenticated by PKCE alone, which is what every desktop and browser MCP client is. Ask for `client_secret_post` or `client_secret_basic` and a secret comes back **once**, in this response, and is stored only as a digest.\n\n`redirect_uris` must be absolute `https` URLs, or `http` on `localhost`/`127.0.0.1` for a native application's loopback callback. They are matched exactly at authorize time, except that a loopback URI may come back on a different port (RFC 8252 §7.3) — which is what makes an ephemeral-port desktop callback work.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthClientRegistrationRequest"}},"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/OAuthClientRegistrationRequest"}}}},"security":[],"responses":{"201":{"description":"The registered client.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthClientRegistrationResponse"}}}},"400":{"description":"An OAuth error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpOAuthError"}}}},"429":{"description":"Rate limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/oauth/mcp/authorize":{"get":{"operationId":"startMcpOAuthAuthorization","summary":"Begin an authorization-code flow (RFC 6749 §4.1.1, PKCE required)","tags":["oauth"],"description":"Validates the request and redirects the browser to Tablecast's consent page, where the person signs in normally, picks the organisation the connection acts inside and approves the scopes.\n\nErrors in `client_id` or `redirect_uri` are answered **here**, as a 400: redirecting an error to a URI we have not established the client owns is an open redirector. Every later error redirects to the validated callback carrying `error` and the client's `state`, because that is the only place the client is listening.\n\n`code_challenge_method` must be `S256`.","parameters":[{"schema":{"type":"string","maxLength":64},"in":"query","name":"response_type","required":true},{"schema":{"type":"string","maxLength":200},"in":"query","name":"client_id","required":true},{"schema":{"type":"string","maxLength":2000},"in":"query","name":"redirect_uri","required":false},{"schema":{"type":"string","maxLength":200},"in":"query","name":"code_challenge","required":false},{"schema":{"type":"string","maxLength":32},"in":"query","name":"code_challenge_method","required":false},{"schema":{"type":"string","maxLength":200},"in":"query","name":"scope","required":false},{"schema":{"type":"string","maxLength":2000},"in":"query","name":"state","required":false},{"schema":{"type":"string","maxLength":2000},"in":"query","name":"resource","required":false}],"security":[],"responses":{"302":{"description":"To the consent page, or back to the client with an error."},"400":{"description":"An OAuth error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpOAuthError"}}}}}}},"/v1/oauth/mcp/authorize/request":{"get":{"operationId":"getMcpOAuthAuthorizationRequest","summary":"What the consent page is being asked to approve","tags":["oauth"],"description":"Reads a pending authorization back out of its signed ticket. First-party: the consent page in the web app calls this with the signed-in person's ordinary bearer token.\n\nThe ticket is not a credential — it authorises nothing, and this endpoint refuses it without a signed-in caller. What its signature guarantees is that the browser could not edit the client, the callback, the scopes or the PKCE challenge between the redirect and the click.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":4000},"in":"query","name":"ticket","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The pending authorization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpAuthorizationRequest"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"410":{"description":"The ticket has expired. The client must start the flow again.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/oauth/mcp/authorize/consent":{"post":{"operationId":"decideMcpOAuthAuthorization","summary":"Approve or deny a pending authorization","tags":["oauth"],"description":"The moment authority is created. The organisation and the scopes come from **this** request, made by the signed-in person, and the client, the callback and the PKCE challenge come from the signed ticket — so neither party can supply the other's half.\n\nThe acting user is taken from the bearer token and never from the body. The scopes are intersected with what the client asked for, so a page cannot grant more than was requested, and membership of the chosen organisation is checked before anything is written.\n\nReturns the URL the browser must be sent to next: the client's own callback, carrying either a 60-second authorization code or `error=access_denied`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpAuthorizationConsent"}}}},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Where to send the browser next.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpAuthorizationConsentResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"410":{"description":"The ticket has expired. The client must start the flow again.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/oauth/mcp/token":{"post":{"operationId":"issueMcpOAuthToken","summary":"Exchange an authorization code or refresh token","tags":["oauth"],"description":"RFC 6749 §4.1.3 and §6. `code_verifier` is **required** for the authorization-code grant: this server issues no code without a PKCE challenge, so there is no path here that does not verify one.\n\nThe access token is opaque, not a JWT, and is verified against the database on every request. That is the deliberate opposite trade from `/v1/oauth/token` (TAB-69), whose stateless JWT cannot be revoked before it expires: a person who disconnects an application expects it to stop working now, not within the hour.\n\nRefresh rotates. The presented refresh token is revoked, along with every access token issued from it, and a fresh pair is returned — so a stolen refresh token is useful exactly once, and the theft shows up as the real client being refused on its next refresh rather than as a silent parallel session.","requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/McpOAuthTokenRequest"}},"application/json":{"schema":{"$ref":"#/components/schemas/McpOAuthTokenRequest"}}}},"security":[],"responses":{"200":{"description":"The token pair.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpOAuthTokenResponse"}}}},"400":{"description":"An OAuth error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpOAuthError"}}}},"401":{"description":"Client authentication failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpOAuthError"}}}}}}},"/v1/oauth/mcp/revoke":{"post":{"operationId":"revokeMcpOAuthToken","summary":"Revoke a token (RFC 7009)","tags":["oauth"],"description":"Revoking either half of a grant revokes the whole grant — the access token, its refresh token and every other access token issued from it. RFC 7009 §2.1 permits this, and the alternative would leave a person who clicked Disconnect holding a live credential on the other side of the pair.\n\nAlways 200, even for a token this server has never seen: RFC 7009 §2.2 requires it, because answering differently is an oracle for whether a token exists.","requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/OAuthRevocationRequest"}},"application/json":{"schema":{"$ref":"#/components/schemas/OAuthRevocationRequest"}}}},"security":[],"responses":{"200":{"description":"The token is no longer usable, or never was."},"401":{"description":"Client authentication failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpOAuthError"}}}}}}},"/v1/oauth/mcp/principal":{"get":{"operationId":"getMcpPrincipal","summary":"Who this credential is, for the MCP tool gate","tags":["oauth"],"description":"`apps/mcp` holds no database, no secret and no credential of its own — that is the property that makes a compromised MCP Lambda leak nothing its callers did not already have. So it cannot decide which tools a caller may be *offered* without asking, and this is the ask: one call per MCP request, returning the organisation and the scopes behind the presented credential.\n\nIt is a convenience for the tool list, not the security boundary. Every tool call goes to a `/v1/public/*` route that re-checks the same scopes with `app.apiAuth`, so a client that ignores the list gains nothing.","security":[{"apiKeyAuth":[]},{"oauthToken":[]}],"responses":{"200":{"description":"The credential's organisation and scopes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpPrincipal"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Rate limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/notifications":{"get":{"operationId":"listNotifications","summary":"The caller's notifications, newest first","tags":["notifications"],"description":"Keyset-paginated on `(createdAt, id)` — not offset, because rows land continuously and an offset page 2 would re-show rows page 1 already had. `orgId` narrows the page to one organisation; rows with no organisation are always included, because they are about the account rather than the tenant. `unreadCount` counts every outstanding row for the caller, not just this page — it is the number the bell renders. No org is named in the path: a notification belongs to a person, and the row-level policy behind this route is `user_id = app.user_id`.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"orgId","required":false},{"schema":{"type":"string"},"in":"query","name":"unread","required":false},{"schema":{"default":25,"type":"integer","minimum":1,"maximum":100},"in":"query","name":"limit","required":false},{"schema":{"type":"string","minLength":1,"maxLength":512},"in":"query","name":"cursor","required":false}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The page of notifications.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Malformed cursor.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/notifications/read":{"post":{"operationId":"markNotificationsRead","summary":"Acknowledge notifications","tags":["notifications"],"description":"Pass `ids` to clear specific rows, or `all: true` to clear everything outstanding (optionally confined to one `orgId`). Idempotent: a row that was already read keeps its original `readAt`, so two tabs racing to clear the same bell agree on when it was read and the second learns it changed nothing. `marked` is therefore how many rows this call actually changed, which can legitimately be fewer than were asked for. Rows the caller cannot see are invisible to the update, so another person's notification and a nonexistent one are the same answer. On success a `user.notification.read` event is published to the caller's org channel so their other tabs clear without polling; that fan-out never fails the request.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkNotificationsReadBody"}}}},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"How many rows changed, and the caller's remaining unread count.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkNotificationsReadResult"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Neither `ids` nor `all` was given.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/notifications/unsubscribe":{"post":{"operationId":"unsubscribeOneClick","summary":"One-click unsubscribe (RFC 8058)","tags":["notifications"],"description":"The `List-Unsubscribe-Post` target a mail client calls from its own unsubscribe button. Unauthenticated: the signed `token` from the email carries the user and the category, so no bearer is required and none is accepted. Turns email off for exactly the category the email belonged to, leaving every other category's choice intact, and never anything but off — it cannot subscribe. Idempotent, and deliberately opaque on failure: an expired or forged token answers 400 with no hint as to which, because the reason is for our logs and not for whoever holds it.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":2048},"in":"query","name":"token","required":true}],"security":[],"responses":{"200":{"description":"The category is now unsubscribed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnsubscribeResult"}}}},"400":{"description":"The token was missing, malformed, expired or forged.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"get":{"operationId":"unsubscribeFromEmail","summary":"Unsubscribe from a category's email (footer link)","tags":["notifications"],"description":"The destination of the unsubscribe link in a notification email's footer, for a human who clicked it. Verifies the signed `token`, turns that category's email off for that recipient, and renders a small confirmation page. Same effect as the RFC 8058 POST; this one answers HTML because a browser opened it.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":2048},"in":"query","name":"token","required":true}],"security":[],"responses":{"200":{"description":"A confirmation page.","content":{"text/html":{"schema":{"type":"string"}}}},"400":{"description":"The link is no longer valid.","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/v1/shares":{"post":{"operationId":"createShare","summary":"Share a recording, render, file or edit","tags":["shares"],"description":"Needs `recordings:manage` on the show the target belongs to, or an org admin role for a library file that belongs to no show.\n\n**Public** is a toggle: if a live public share already covers this target, that one comes back with `created: false` rather than a second URL nobody could tell apart from the first. Its `url` is returned on every read, deliberately — a public token is not a secret about a person, and a dialog that could show the link only once would turn 'copy link' into 'rotate link'.\n\n**Private** mints a token per named person, stores only its sha256, and sends each of them the branded 'X shared a recording with you' email. The parent share has no link of its own. `delivery` says what happened to each address; a send that failed does not fail the share.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateShareBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The public share that already covered this target.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShareCreated"}}}},"201":{"description":"The share.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShareCreated"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"No such target in this organisation.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"Recipients on a public share, or none on a private one.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"get":{"operationId":"listShares","summary":"Every share link on one thing","tags":["shares"],"description":"Newest first, revoked ones included — 'this was shared publicly for a fortnight in March' is a fact a producer wants back. Needs `recordings:read` on the show, or org membership for a library file that belongs to no show. A live public share carries its `url`; a private one carries its `recipients` and their last-viewed times.","parameters":[{"schema":{"type":"string","enum":["recording","render","media_asset","edit_doc"]},"in":"query","name":"targetKind","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"query","name":"targetId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The shares.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShareList"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/shares/{shareId}":{"patch":{"operationId":"updateShare","summary":"Change a live link's permission, expiry or downloads","tags":["shares"],"description":"Needs `recordings:manage`. Deliberately cannot change `visibility` or the target: both would silently change what an already-circulated URL means, which is a revoke and a new share rather than an edit. Narrowing `permission` bites on the viewer's **next** request — nothing is cached — and a comment already left is not withdrawn.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateShareBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"shareId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The share.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Share"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"That share has been revoked; make a new one.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"delete":{"operationId":"revokeShare","summary":"Kill a share link","tags":["shares"],"description":"Needs `recordings:manage`. The link stops working on the **next** request — there is no cached credential to outlive it, which is exactly why a share token is a database row rather than a signed JWT: a signed token cannot be revoked without rotating a key every other outstanding token depends on.\n\nThe row is kept, marked revoked, and every recipient with it: 'shared publicly in March, withdrawn in April' is a fact worth having. A revoked link answers 410, which is distinguishable from an invented one's 404 only because it was real — nothing about the target leaks either way.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"shareId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Revoked."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/shares/{shareId}/recipients":{"post":{"operationId":"addShareRecipients","summary":"Add people to a private share","tags":["shares"],"description":"Each person gets a **token of their own**, hashed at rest, delivered by email and never returned by the API. Re-adding an address that is already on this share **rotates** that person's token and sends again — the previous link stops working, which is what makes 'I sent it to the wrong Ana' fixable.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddShareRecipientsBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"shareId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The share, and what happened to each invitation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShareRecipientsAdded"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"That share has been revoked.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"A public share has no named people.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/shares/{shareId}/recipients/{recipientId}/resend":{"post":{"operationId":"resendShareRecipient","summary":"Issue this person a fresh link","tags":["shares"],"description":"**Rotates the token.** The previous link stops working the instant this returns and answers the same 404 an invented one does — a link that went to the wrong address is not made safe by sending a second one alongside it. Their view history is kept: the action was about delivery, not about un-happening what they already did.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"shareId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"recipientId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"The person, and what happened to their new link.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShareRecipientResent"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"That share or that person has been revoked.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/shares/{shareId}/recipients/{recipientId}":{"delete":{"operationId":"revokeShareRecipient","summary":"Withdraw one person's access","tags":["shares"],"description":"Their link stops working on the next request; everybody else's keeps working. The row is kept with `revokedAt` set, so the panel can still say who was sent what and when they last looked.","parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"shareId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"path","name":"recipientId","required":true},{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Withdrawn."},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/s/{token}":{"get":{"operationId":"resolveShare","summary":"Open a share link (public)","tags":["shares"],"description":"**No authentication.** The token in the path is the credential — for a public link, one anybody may hold; for a private one, the personal token from that recipient's email.\n\nAnswers what the page needs and no more: the target's title and duration, who the token says is looking (`viewer`, populated only on a private link), and the `capabilities` this token grants. Send the same token back as `Authorization: Share <token>` on the renditions, playback-ticket, asset-download and comments routes — it is the credential for all of them.\n\nA link that never existed is `404`; one that was real and is expired or revoked is `410`. Rate-limited per client address. Every successful resolve counts a view, on the share and — on a private link — on that person's own row.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":512},"in":"path","name":"token","required":true,"description":"The token from the share link. Never logged."}],"security":[],"responses":{"200":{"description":"The shared thing, and what this token may do with it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResolvedShare"}}}},"404":{"description":"No such link.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"410":{"description":"That link has expired or been revoked.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"429":{"description":"Rate limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/telemetry":{"post":{"operationId":"ingestTelemetry","summary":"Ingest a batch of browser telemetry","tags":["telemetry"],"description":"Errors, Web Vitals, WebRTC stats and recorder stats from `apps/web`, written to CloudWatch as EMF in the `Tablecast/Web` namespace. Ids and numbers only: there is no field in the schema that can carry a name, an address or a URL, and error messages are scrubbed of emails, tokens and query strings before they are written. Returns 200 with `accepted: 0` — never an error — when the caller's organisation has `privacy.telemetryOptOut` set, or when no sample in the batch carried a measurable value. Telemetry must never argue with the app it is measuring.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TelemetryBatch"}}}},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"How many samples were written.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TelemetryAccepted"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/publish":{"post":{"operationId":"createPublishJob","summary":"Publish a render to a platform","tags":["publish"],"description":"Opens a publish job for a production's render/clip against a connected integration or a bare provider string, and returns it. The job is picked up asynchronously by the publish worker, which drives it queued → uploading → processing → published and emits publish.* events. No credential is accepted here — a publish reuses a connected integration's stored credentials.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePublishJobBody"}}}},"parameters":[{"schema":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"in":"header","name":"x-tablecast-org-id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"The created publish job.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublishJob"}}}},"401":{"description":"Unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"The production, integration or render was not found in this org.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"description":"The job named no target, or a field was invalid.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}}},"servers":[{"url":"https://api.tablecast.studio","description":"dev"}],"tags":[{"name":"system","description":"Health and metadata"},{"name":"account","description":"The signed-in user: identities, profile, preferences, organisations"},{"name":"productions","description":"Shows a team owns, and who works on them"},{"name":"sessions","description":"Episodes of a production: schedule, lifecycle, media placement"},{"name":"internal","description":"First-party service endpoints; service-token authenticated"},{"name":"orgs","description":"Organisations, settings, invitations and org membership"},{"name":"teams","description":"Teams, team settings and team membership"},{"name":"uploads","description":"Track recordings and their multipart uploads. Callable by a signed-in user, by a session participant with a guest token, or by a first-party service."},{"name":"invites","description":"Session join links"},{"name":"locations","description":"Physical rooms in a session: one mic in, one speaker out"},{"name":"join","description":"Joining a session — anonymous guests and members"},{"name":"recordings","description":"The recordings library: sessions, ISO tracks, renders and signed downloads"},{"name":"destinations","description":"Stream destinations for a production; stream keys are write-only"},{"name":"live","description":"Going live: destination runs, egress status and health"},{"name":"chat","description":"In-room text chat, persisted with session-clock timestamps"},{"name":"comments","description":"Comment threads on assets, recordings, renders, supers and edit timelines — anchored, resolvable, with mentions. The whiteboard's own comments live in its CRDT and are not here."},{"name":"billing","description":"Plans, entitlements, Stripe Checkout and the Customer Portal"},{"name":"audit","description":"The organisation's audit log: who changed what, and what it was before"},{"name":"impersonation","description":"The audited platform-admin bypass: opening a time-boxed session over one organisation, ending it early, and the organisation's own view of who entered it"},{"name":"platform","description":"The platform admin console: finding any account or organisation across every tenant, and the impersonation sessions that are open right now. Needs `platform:admin`; reads only — changing a customer's data means emulating one of their members, which their own audit log records"},{"name":"webhooks","description":"Outbound webhook endpoints, signing secrets and the delivery log"},{"name":"api-keys","description":"Issuing, rotating and revoking the credentials the public API is called with"},{"name":"public-api","description":"The third-party API: the published contract, the OAuth token endpoint, and the key-authenticated read and control surface under /v1/public"},{"name":"notifications","description":"The signed-in user's notification inbox and its read state"},{"name":"telemetry","description":"Browser error and performance ingest — ids and numbers only, never content"},{"name":"graphics","description":"The Supers library: a show's lower thirds, generated per person from a built-in template or a Canva Brand Template and stored as transparent PNGs"}]}