Start with a match
Create a tenant-scoped fixture, start its canonical timeline and publish idempotent events.
PLATFORM GUIDE
Use the implemented guides and canonical OpenAPI contract for tenant-scoped publishing, live delivery, recovery and distribution.
Create a tenant-scoped fixture, start its canonical timeline and publish idempotent events.
Retrieve the match, calculated score and cursor-paginated timeline with cache-aware responses.
Use SSE and Last-Event-ID to reconnect to committed events without polling.
Validate the timestamp and HMAC signature before parsing or acting on a delivery.
Choose hosted match, iframe, JavaScript, score, results-centre or OBS outputs with revocable keys.
Map tenant membership, public feeds, output keys and the authenticated migration bridge.
OPENAPI 3.1
Search all implemented versioned endpoints. Expand an operation for authentication, parameters, request shape and documented responses.
Showing 96 of 96 operations
/auth/mobile/loginAuthenticate a verified account for organisation-scoped mobile publishingcreateMobileSession{
"type": "object",
"additionalProperties": false,
"required": [
"email",
"password"
],
"properties": {
"email": {
"type": "string",
"format": "email",
"maxLength": 254
},
"password": {
"type": "string",
"minLength": 1,
"maxLength": 128
}
}
}200Opaque revocable session token401Enumeration-safe invalid429Durable credential-attempt rate limit exceeded/auth/mobile/logoutRevoke the current mobile device sessionrevokeMobileSession200Session revoked401Missing or malformed mobile session429Logout rate limit exceeded/auth/mobile/logout-othersRevoke every other session owned by the mobile accountrevokeOtherMobileSessions200Other device sessions revoked401Missing429Session-change rate limit exceeded/auth/mobile/refreshExtend a valid mobile session within its bounded 90-day lifetimerefreshMobileSession200Refreshed expiry without returning or rotating the opaque token401Missing429Daily refresh rate limit exceeded/auth/mobile/invitations/acceptAccept a contributor invitation using the invited verified mobile accountacceptMobileContributorInvitation{
"type": "object",
"additionalProperties": false,
"required": [
"token"
],
"properties": {
"token": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{43}$"
}
}
}200Invitation accepted and current memberships refreshed400Invitation invalid401Missing403Signed-in account does not match the invited email429Invitation attempt rate limit exceeded/auth/sessionGet the authenticated browser user and organisation membershipsgetBrowserSession200Current verified browser session401Missing/reader/preferencesList the mobile account's follows and bookmarks for currently public live storieslistReaderStoryPreferences200Current public story preferences401Missing/reader/preferencesAtomically replace follow and bookmark state for a public live storyreplaceReaderStoryPreference{
"$ref": "#/components/schemas/ReaderStoryPreferenceWrite"
}200Updated preference401Missing404Live story is private422Invalid preference input429Preference mutation rate limit exceeded/auth/sessionsList active browser sessions owned by the authenticated accountlistBrowserSessions200Token-free device session list with current-device marker401Valid verified browser session required/auth/sessions/{sessionId}Idempotently revoke one session owned by the authenticated accountrevokeBrowserSessionsessionIdpath · required200Session revoked or already revoked; current-session cookie is cleared when applicable403Same-origin check failed404Session does not belong to the account or does not exist429Session-change rate limit exceeded/auth/sessions/revoke-othersRevoke every other active session while preserving the issuing browserrevokeOtherBrowserSessions200Other active sessions revoked and audited403Same-origin check failed429Session-change rate limit exceeded/auth/data-exportDownload the authenticated user’s portable account data as JSONExcludes passwords, token and credential hashes, encrypted secrets, IP hashes, and other subscribers’ contact details.
downloadAccountData200Private non-storable JSON attachment containing subject-attributed account and publishing data401Valid verified browser session required429Fixed-window request allowance exhausted/organisationsList the API credential's tenant organisationlistOrganisations200Tenant-scoped organisation collection401Missing or invalid credential403Credential lacks `organisation:read`/organisationsCreate an isolated workspace owned by the authenticated browser usercreateWorkspaceIdempotency-Keyheader · required{
"type": "object",
"additionalProperties": false,
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"minLength": 2,
"maxLength": 160
}
}
}200Exact idempotent replay returned the original workspace201Workspace401Verified browser session required403Same-origin check failed409Idempotency key was reused with different workspace input422Workspace name failed validation429Daily workspace creation limit exceeded/organisations/{organisationId}Idempotently update versioned workspace identity and brandingupdateOrganisationorganisationIdpath · requiredIdempotency-Keyheader · requiredIf-Matchheader · requiredCurrent integer workspace version
{
"$ref": "#/components/schemas/WorkspaceSettingsWrite"
}200Workspace identity updated403Credential actor is not a current owner or administrator404Resource unavailable409Stale version428If-Match version required/organisations/{organisationId}/membershipsList users and roles in the credential's tenantlistMembershipsorganisationIdpath · required200Tenant membership collection404Resource unavailable/organisations/{organisationId}/membershipsAdd an existing verified account to the organisationaddOrganisationMemberorganisationIdpath · required{
"$ref": "#/components/schemas/CreateMembership"
}201Membership created and audited403Actor lacks a current administrative role or an administrator attempted to grant owner404Organisation or verified account unavailable409Account is already a member/organisations/{organisationId}/memberships/{userId}Change a member role while retaining at least one ownerupdateOrganisationMemberorganisationIdpath · requireduserIdpath · required{
"type": "object",
"required": [
"role"
],
"additionalProperties": false,
"properties": {
"role": {
"$ref": "#/components/schemas/MembershipRole"
}
}
}200Membership role updated or an identical role replayed403Administrative role404Resource unavailable/organisations/{organisationId}/memberships/{userId}Remove a member while retaining at least one ownerremoveOrganisationMemberorganisationIdpath · requireduserIdpath · required200Membership removed and audited403Administrative role404Resource unavailable/organisations/{organisationId}/invitationsList masked tenant membership invitations and statuslistMembershipInvitationsorganisationIdpath · requiredcursorqueryOpaque cursor returned in `next_cursor`
limitquery200Cursor-paginated invitations with masked destinations404Resource unavailable/organisations/{organisationId}/invitationsCreate or safely reissue a seven-day membership invitationcreateMembershipInvitationorganisationIdpath · required{
"$ref": "#/components/schemas/CreateMembership"
}200Expired or revoked invitation reissued with a new capability201Invitation created and encrypted email queued403Actor lacks current authority or administrator attempted to invite an owner409Destination already belongs to the organisation429Invitation creation rate limit exceeded/organisations/{organisationId}/invitations/{invitationId}Idempotently revoke a pending membership invitationrevokeMembershipInvitationorganisationIdpath · requiredinvitationIdpath · required200Invitation revoked403Actor cannot manage the invited role404Resource unavailable409Invitation has already been accepted/invitations/acceptAccept an invitation using the matching verified browser accountRequires the secure browser session cookie and a same-origin request. The capability alone cannot grant membership.
acceptMembershipInvitation{
"$ref": "#/components/schemas/AcceptInvitation"
}200Membership granted400Invitation invalid401Matching verified browser session required403Session email does not match the encrypted invitation destination429Acceptance attempt rate limit exceeded/teamsList tenant teams using an opaque cursorlistTeamscursorqueryOpaque cursor returned in `next_cursor`
limitqueryqueryqueryexternal_idquery200Filtered tenant team collection400Invalid request/teamsIdempotently create a tenant teamcreateTeamIdempotency-Keyheader · required{
"$ref": "#/components/schemas/TeamWrite"
}200Identical creation safely replayed201Team created403Credential or actor cannot administer teams409Idempotency or external-ID uniqueness conflict422Team data failed validation/teams/{teamId}Retrieve one tenant teamgetTeamteamIdpath · required200Versioned team resource404Resource unavailable/teams/{teamId}Idempotently replace mutable team detailsreplaceTeamteamIdpath · requiredIdempotency-Keyheader · requiredIf-Matchheader · requiredCurrent integer team version
{
"$ref": "#/components/schemas/TeamWrite"
}200Team updated or identical command safely replayed403Credential or actor cannot administer teams404Resource unavailable409Stale version428If-Match version required/participantsList tenant participants or players using an opaque cursorlistParticipantscursorqueryOpaque cursor returned in `next_cursor`
limitqueryteam_idqueryqueryqueryexternal_idquery200Filtered tenant participant collection400Invalid request/participantsIdempotently create a participant under a tenant teamcreateParticipantIdempotency-Keyheader · required{
"$ref": "#/components/schemas/ParticipantWrite"
}200Identical creation safely replayed201Participant created403Credential or actor cannot administer participants404Team not found in the tenant409Idempotency or external-ID uniqueness conflict422Participant data failed validation/participants/{participantId}Retrieve one tenant participantgetParticipantparticipantIdpath · required200Versioned participant resource404Resource unavailable/participants/{participantId}Idempotently replace mutable participant detailsreplaceParticipantparticipantIdpath · requiredIdempotency-Keyheader · requiredIf-Matchheader · requiredCurrent integer participant version
{
"$ref": "#/components/schemas/ParticipantWrite"
}200Participant updated or identical command safely replayed403Credential or actor cannot administer participants404Resource unavailable409Stale version428If-Match version required/competitionsList tenant competitions using an opaque cursorlistCompetitionscursorqueryOpaque cursor returned in `next_cursor`
limitquery200Tenant competition collection/competitionsIdempotently create a tenant competitioncreateCompetitionIdempotency-Keyheader · required200Identical creation replayed201Competition created409Idempotency or uniqueness conflict429Fixed-window request allowance exhausted/seasonsList tenant seasons, optionally filtered by competitionlistSeasonscursorqueryOpaque cursor returned in `next_cursor`
limitquerycompetition_idquery200Tenant season collection/seasonsIdempotently create a season under a tenant competitioncreateSeasonIdempotency-Keyheader · required200Identical creation replayed201Season created404Competition not found in the tenant429Fixed-window request allowance exhausted/venuesList tenant venues using an opaque cursorlistVenuescursorqueryOpaque cursor returned in `next_cursor`
limitquery200Tenant venue collection/venuesIdempotently create a tenant venuecreateVenueIdempotency-Keyheader · required200Identical creation replayed201Venue created409Idempotency or uniqueness conflict429Fixed-window request allowance exhausted/tagsList canonical tenant-owned tagslistTagscursorqueryOpaque cursor returned in `next_cursor`
limitquery200Cursor-paginated tenant tag collection/tagsIdempotently create a canonical tenant tagcreateTagIdempotency-Keyheader · required{
"$ref": "#/components/schemas/TagWrite"
}200Identical creation safely replayed201Tag created with version 1403Actor cannot administer tags409Slug or idempotency conflict/tags/{tagId}Retrieve one tenant-owned taggetTagtagIdpath · requiredTenant-scoped tag identifier
200Current tag and version404Resource unavailable/tags/{tagId}Idempotently replace mutable tag detailsreplaceTagtagIdpath · requiredTenant-scoped tag identifier
Idempotency-Keyheader · requiredIf-Matchheader · requiredCurrent integer tag version
{
"$ref": "#/components/schemas/TagWrite"
}200Tag replaced or identical command safely replayed404Resource unavailable409Slug428If-Match version required/live-blogsList tenant standalone live blogslistLiveBlogscursorqueryOpaque cursor returned in `next_cursor`
limitquerystatusquery200Cursor-paginated tenant live-blog collection/live-blogsIdempotently create a scheduled standalone live blogcreateLiveBlogIdempotency-Keyheader · required{
"$ref": "#/components/schemas/CreateLiveBlog"
}200Identical creation replayed201Live blog and immutable created event committed409Idempotency or tenant uniqueness conflict429Fixed-window request allowance exhausted/live-blogs/publicDiscover public non-archived live blogs without authenticationdiscoverPublicLiveBlogsqqueryServer-side title and description search
statusquerycursorqueryOpaque cursor returned in `next_cursor`
limitquery200Cursor-paginated public cards without draft or contributor administration data400Invalid search429Fixed-window request allowance exhausted/live-blogs/{liveBlogId}Get a tenant live blog and ordered lifecycle historygetLiveBlogliveBlogIdpath · requiredTenant-scoped live-blog identifier
200Live-blog aggregate and immutable lifecycle events404Resource unavailable/live-blogs/{liveBlogId}/eventsAppend a lifecycle transition using optimistic concurrencytransitionLiveBlogliveBlogIdpath · requiredTenant-scoped live-blog identifier
Idempotency-Keyheader · requiredIf-Matchheader · requiredCurrent aggregate version
{
"type": "object",
"required": [
"action"
],
"additionalProperties": false,
"properties": {
"action": {
"type": "string",
"enum": [
"start",
"pause",
"resume",
"finish",
"archive"
]
},
"occurred_at": {
"type": "string",
"format": "date-time"
}
}
}200Identical transition replayed201Lifecycle event appended and status projection updated409Version or idempotency conflict422Invalid lifecycle transition429Fixed-window request allowance exhausted/live-blogs/{liveBlogId}/publicGet the cache-friendly public live-blog read modelgetPublicLiveBlogliveBlogIdpath · requiredTenant-scoped live-blog identifier
200Public live-blog summary with aggregate version404Live blog is unavailable429Fixed-window request allowance exhausted/live-blogs/{liveBlogId}/timelineRead the public standalone timeline from the durable outboxgetPublicLiveBlogTimelineliveBlogIdpath · requiredTenant-scoped live-blog identifier
cursorqueryOpaque cursor returned in `next_cursor`
limitquerylatestqueryReturn the newest bounded window in chronological order
beforequeryReturn the preceding chronological page before this aggregate version
event_idqueryReturn one public timeline event for a specific-update deep link; cannot be combined with cursors or latest
200Cursor-paginated canonical public events404Live blog is unavailable429Fixed-window request allowance exhausted/live-blogs/{liveBlogId}/streamResume a public standalone timeline using Server-Sent EventsstreamPublicLiveBlogliveBlogIdpath · requiredTenant-scoped live-blog identifier
Last-Event-IDheaderLast applied aggregate version
200SSE stream with stable aggregate-version IDs400Invalid request404Live blog is unavailable429Fixed-window request allowance exhausted/matchesList and filter tenant matches using an opaque cursorlistMatchescursorqueryOpaque cursor returned in `next_cursor`
limitquerystatusquerycompetitionqueryteam_idquerydate_fromquerydate_toquery200Filtered tenant match collection400Invalid request/matchesCreate a tenant match, teams, and initial output channels atomicallycreateMatchIdempotency-Keyheader · required{
"$ref": "#/components/schemas/CreateMatch"
}200Identical idempotent creation replayed201Match created once409Idempotency key reused with different match data422Invalid request/matches/{matchId}Get a public match and current scoreboardgetMatchmatchIdpath · requiredStable LiveStreamBlog match identifier
200Match404Resource unavailable429Fixed-window request allowance exhausted/matches/{matchId}/scoreGet a projected current scoreboardgetCurrentScorematchIdpath · requiredStable LiveStreamBlog match identifier
200Current score projection404Resource unavailable429Fixed-window request allowance exhausted/matches/{matchId}/eventsAppend an immutable match eventappendMatchEventmatchIdpath · requiredStable LiveStreamBlog match identifier
Idempotency-Keyheader · requiredIf-Matchheader · requiredCurrent aggregate version
{
"$ref": "#/components/schemas/AppendMatchEvent"
}200Identical idempotent request replayed201Event appended and score projection committed401Missing403Credential lacks `match:write` scope409Version or idempotency conflict422Invalid request429Fixed-window request allowance exhausted/matches/{matchId}/timelineGet an ordered cursor-paginated match timelinegetMatchTimelinematchIdpath · requiredStable LiveStreamBlog match identifier
cursorqueryOpaque cursor returned by the previous response
limitquery200Timeline page400Invalid request404Resource unavailable/matches/{matchId}/streamResume a public timeline using Server-Sent EventsstreamMatchTimelinematchIdpath · requiredStable LiveStreamBlog match identifier
Last-Event-IDheaderLast applied aggregate version
200SSE stream with stable event IDs400Invalid request404Resource unavailable/storiesList tenant-published stories with optional match or live-blog filteringlistStoriescursorqueryOpaque cursor returned in `next_cursor`
limitquerymatch_idquerylive_blog_idquerytagqueryStable tenant tag slug
200Cursor-paginated sanitised published stories with author attribution400Invalid request401Missing or invalid credential403Credential lacks `story:read`404The filtered match is unavailable in this tenant/storiesPublish a sanitised story and canonical timeline event atomicallypublishStoryIdempotency-Keyheader · requiredIf-Matchheader · requiredCurrent match or live-blog aggregate version
{
"$ref": "#/components/schemas/PublishStory"
}200Identical idempotent request replayed201Story and story.published event committed404Resource unavailable409Aggregate version or idempotency-content conflict422Invalid request428If-Match aggregate version is required429Story publication rate limit exceeded/analyticsRetrieve bounded operational and editorial tenant analyticsgetTenantAnalyticsperiodqueryUTC reporting window in days
200Canonical coverage400Period is not 7401Missing or invalid credential403Credential lacks `analytics:read`429Analytics read rate limit exceeded/audit-recordsList immutable tenant audit history with read-time metadata safetylistAuditRecordscursorqueryOpaque cursor returned in `next_cursor`
limitqueryactionqueryExact action filter
resource_typequeryExact resource-type filter
actor_idqueryfromqueryInclusive creation boundary
toqueryExclusive creation boundary
200Cursor-paginated records with actor attribution and recursively redacted metadata400Invalid cursor401Missing or invalid credential403Credential lacks `audit:read`429Audit read rate limit exceeded/stories/draftsList tenant editorial stories, optionally filtered to one live bloglistEditorialStorieslive_blog_idquerylimitquery200Current editorial story read models ordered by latest update400Invalid live-blog identifier401Missing or invalid credential403Credential lacks `story:read`/stories/draftsCreate a tenant-scoped sanitised story draftcreateStoryDraftIdempotency-Keyheader · required{
"$ref": "#/components/schemas/PublishStory"
}200Identical idempotent request replayed201Draft and immutable created revision committed403Current actor lacks an editorial role404Match or live-blog target unavailable in this tenant409Idempotency-content conflict422Invalid request429Editorial write rate limit exceeded/stories/{storyId}Retrieve a tenant story and its complete revision history for previewgetEditorialStorystoryIdpath · required200Current story plus ordered immutable revisions404Resource unavailable/stories/{storyId}Edit a draft with optimistic concurrency; editing a scheduled story returns it to draftupdateStoryDraftstoryIdpath · requiredIdempotency-Keyheader · requiredIf-Matchheader · requiredCurrent story revision version
{
"type": "object",
"required": [
"body_html"
],
"additionalProperties": false,
"properties": {
"title": {
"type": "string",
"maxLength": 200
},
"body_html": {
"type": "string",
"maxLength": 100000
}
}
}200Draft updated or identical command replayed409Story state422Invalid request428If-Match story version is required/stories/{storyId}/schedulePersist a future publication time and immutable scheduled revisionscheduleStorystoryIdpath · requiredIdempotency-Keyheader · requiredIf-Matchheader · requiredCurrent story revision version
{
"type": "object",
"required": [
"scheduled_at"
],
"additionalProperties": false,
"properties": {
"scheduled_at": {
"type": "string",
"format": "date-time"
}
}
}200Future schedule persisted or identical command replayed409Story state422Invalid or non-future publication time428If-Match story version is required/stories/{storyId}/tagsAtomically replace a story's canonical tenant tagsreplaceStoryTagsstoryIdpath · requiredIdempotency-Keyheader · requiredIf-Matchheader · requiredCurrent story revision version
{
"type": "object",
"required": [
"tag_ids"
],
"additionalProperties": false,
"properties": {
"tag_ids": {
"type": "array",
"maxItems": 20,
"uniqueItems": true,
"items": {
"type": "string",
"format": "uuid"
}
}
}
}200Current story with ordered tags; exact commands replay safely403Current actor lacks editorial access to this story404Resource unavailable409Story version or idempotency conflict422A supplied tag is invalid or belongs to another tenant428If-Match story version is required/stories/{storyId}/publishPublish an existing draft into its canonical match or live-blog timeline transactionpublishStoryDraftstoryIdpath · requiredIdempotency-Keyheader · requiredIf-Matchheader · requiredCurrent story revision version
{
"type": "object",
"required": [
"aggregate_version"
],
"additionalProperties": false,
"properties": {
"aggregate_version": {
"type": "integer",
"minimum": 0
}
}
}200Existing draft published409Story/aggregate version428If-Match story version is required/stories/{storyId}/submitSubmit the current story revision for editorial reviewsubmitStoryForReviewstoryIdpath · requiredIdempotency-Keyheader · requiredIf-Matchheader · requiredCurrent story revision version
200Story moved to pending review with an immutable submitted revision403Current actor lacks an editorial role409Story state428If-Match story version is required/stories/{storyId}/reviewApprove a pending story or request contributor changesreviewStorystoryIdpath · requiredIdempotency-Keyheader · requiredIf-Matchheader · requiredCurrent story revision version
{
"type": "object",
"required": [
"decision"
],
"additionalProperties": false,
"properties": {
"decision": {
"type": "string",
"enum": [
"approve",
"request_changes"
]
},
"reason": {
"type": "string",
"minLength": 3,
"maxLength": 1000
}
}
}200Immutable moderation decision and audit record committed403Only an owner409Story state422A change request requires a reason428If-Match story version is required/stories/{storyId}/correctionsCorrect a published story while preserving prior content and emitting correction.createdcorrectPublishedStorystoryIdpath · requiredIdempotency-Keyheader · requiredIf-Matchheader · requiredCurrent story revision version
{
"type": "object",
"required": [
"aggregate_version",
"body_html",
"reason"
],
"additionalProperties": false,
"properties": {
"aggregate_version": {
"type": "integer",
"minimum": 0
},
"title": {
"type": "string",
"maxLength": 200
},
"body_html": {
"type": "string",
"maxLength": 100000
},
"reason": {
"type": "string",
"minLength": 3,
"maxLength": 1000
}
}
}200Corrected read model409Story/aggregate version422Invalid request428If-Match story version is required/subscriptionsList masked tenant subscribers and their topic ruleslistSubscriberscursorqueryOpaque cursor returned in `next_cursor`
limitquery200Cursor-paginated subscriber metadata without contact addresses401Missing or invalid credential403Credential lacks `subscription:read`/subscriptionsRecord consent and queue double-opt-in verificationcreateSubscriptionIdempotency-Keyheader · required{
"$ref": "#/components/schemas/CreateSubscription"
}200Exact idempotent request replayed without another email202Pending subscriber stored and verification email queued409Destination already exists or idempotency-content conflict422Invalid request429Subscription creation rate limit exceeded/subscriptions/{subscriberId}Atomically replace an active subscriber's topic and event rulesreplaceSubscriberNotificationRulessubscriberIdpath · requiredIdempotency-Keyheader · requiredIf-Matchheader · requiredCurrent subscriber rule version
{
"type": "object",
"required": [
"topics"
],
"additionalProperties": false,
"properties": {
"topics": {
"type": "array",
"minItems": 1,
"maxItems": 20,
"uniqueItems": true,
"items": {
"$ref": "#/components/schemas/SubscriptionTopic"
}
}
}
}200Replaced rule set403Actor is not a current owner or administrator404Resource unavailable409Version or idempotency conflict422Invalid topic428If-Match subscriber version is required429Notification rule change rate limit exceeded/subscriptions/{subscriberId}Idempotently unsubscribe and deactivate all topic rulesunsubscribeSubscribersubscriberIdpath · required200Subscriber suppression state and timestamp404Resource unavailable/subscriptions/verifyActivate a pending subscription with a single-use capability tokenverifySubscription{
"type": "object",
"required": [
"token"
],
"additionalProperties": false,
"properties": {
"token": {
"type": "string",
"minLength": 43,
"maxLength": 43
}
}
}200Subscriber and topic rules activated400Token invalid429Verification attempt rate limit exceeded/subscriptions/unsubscribeIdempotently stop a subscription using its private per-message capabilityunsubscribeWithCapability{
"type": "object",
"required": [
"token"
],
"additionalProperties": false,
"properties": {
"token": {
"type": "string",
"minLength": 43,
"maxLength": 43
}
}
}200Subscriber suppressed and all topic rules disabled400Token invalid429Unsubscribe attempt rate limit exceeded/notificationsList tenant notification delivery status without revealing destinationslistNotificationDeliveriescursorqueryOpaque cursor returned in `next_cursor`
limitquery200Cursor-paginated masked delivery metadata401Missing or invalid credential403Credential lacks `subscription:read`/matches/{matchId}/outputsList the tenant match's managed output channels and public URLslistMatchOutputsmatchIdpath · requiredStable LiveStreamBlog match identifier
200All managed output channels for the match401Missing or invalid credential403Credential lacks `output:read`404Resource unavailable/matches/{matchId}/outputs/{outputId}Activate or deactivate a tenant match outputupdateMatchOutputmatchIdpath · requiredStable LiveStreamBlog match identifier
outputIdpath · required{
"$ref": "#/components/schemas/UpdateOutput"
}200Updated output metadata and public URL404Resource unavailable422Invalid request429Output mutation rate limit exceeded/matches/{matchId}/outputs/{outputId}/rotate-keyPermanently replace a public output key and URLrotateMatchOutputKeymatchIdpath · requiredStable LiveStreamBlog match identifier
outputIdpath · requiredIdempotency-Keyheader · requiredStable command ID; exact retries return the same replacement key
200New public key and URL; the prior key is immediately invalid404Resource unavailable429Output rotation rate limit exceeded/api-keysList tenant API-key metadata without token hashes or full tokenslistApiKeyscursorqueryOpaque cursor returned in `next_cursor`
limitquery200Cursor-paginated API-key metadata401Missing or invalid credential403Credential lacks `api_key:read`/api-keysCreate a scoped tenant credential and reveal its token oncecreateApiKey{
"$ref": "#/components/schemas/CreateApiKey"
}201Credential created; `token` is returned only in this response422Invalid request429Credential creation rate limit exceeded/api-keys/{keyId}Revoke an API key without deleting its metadata or audit historyrevokeApiKeykeyIdpath · required200Revocation timestamp; repeated revocation is idempotent404Resource unavailable/webhooksList tenant webhook endpoints without secret materiallistWebhookEndpoints200Tenant webhook endpoints401Missing or invalid credential403Credential lacks `webhook:read`/webhooksCreate a tenant webhook endpoint and reveal its signing secret oncecreateWebhookEndpoint{
"$ref": "#/components/schemas/CreateWebhook"
}201Endpoint created; `secret` is returned only in this response422Invalid or non-public webhook URL or invalid subscription/webhooks/{webhookId}Update endpoint name, URL, event subscriptions, or active stateupdateWebhookEndpointwebhookIdpath · required{
"$ref": "#/components/schemas/UpdateWebhook"
}200Updated endpoint without secret material404Resource unavailable422Invalid request/webhooks/{webhookId}Deactivate an endpoint without deleting its delivery historydeactivateWebhookEndpointwebhookIdpath · required204Endpoint deactivated404Resource unavailable/webhooks/{webhookId}/rotate-secretReplace and reveal a webhook signing secret oncerotateWebhookSecretwebhookIdpath · required200Secret rotated; `secret` is returned only in this response404Resource unavailable/webhooks/{webhookId}/deliveriesList tenant-scoped delivery attempts and terminal stateslistWebhookDeliverieswebhookIdpath · requiredcursorqueryOpaque cursor returned in `next_cursor`
limitquery200Cursor-paginated delivery log404Resource unavailable/webhooks/{webhookId}/deliveries/{deliveryId}/replayRequeue one dead-lettered delivery with a durable idempotent commandreplayDeadWebhookDeliverywebhookIdpath · requireddeliveryIdpath · requiredIdempotency-Keyheader · required200Identical replay command safely replayed202Dead letter reset to pending for immediate worker delivery404Endpoint or delivery unavailable in this tenant409Idempotency key reused for another command422Delivery is not dead or endpoint is inactive429Manual replay rate limit exceeded/mediaList the credential tenant's non-deleted media assetslistMediaAssetscursorqueryOpaque cursor returned in `next_cursor`
limitquery200Cursor-paginated media metadata and stable public URLs401Missing or invalid API credential403Credential lacks `media:read`/mediaValidate and upload an idempotent tenant imageuploadMediaAssetIdempotency-Keyheader · required200Exact idempotent replay201Image stored409Idempotency key was reused for different content or metadata413Upload exceeds 10 MiB422Missing accessibility text or unsupported content signature/media/{mediaId}Retrieve tenant-scoped media metadatagetMediaAssetmediaIdpath · requiredTenant-scoped media identifier
200Media metadata and immutable public URL404Resource unavailable/gaablogs/matchesSynchronize tenant matches and current score projections into GAABlogslistGaaBlogsMatchescursorqueryOpaque cursor returned in `next_cursor`
limitqueryupdated_afterqueryReturn records updated strictly after this timestamp; cursors are preferred while paging a snapshot
200Tenant-scoped canonical matches304Representation unchanged for the supplied If-None-Match value401Missing or invalid API credential403Credential lacks `gaablogs:read`/gaablogs/storiesSynchronize published match and standalone live-blog stories into GAABlogslistGaaBlogsStoriescursorqueryOpaque cursor returned in `next_cursor`
limitqueryupdated_afterqueryReturn records updated strictly after this timestamp; cursors are preferred while paging a snapshot
200Tenant-scoped published stories with canonical targets and source attribution304Representation unchanged for the supplied If-None-Match value401Missing or invalid API credential403Credential lacks `gaablogs:read`/gaablogs/mappingsSynchronize stable external identifiers for teams, competitions, and venueslistGaaBlogsMappingskindquery · requiredcursorqueryOpaque cursor returned in `next_cursor`
limitqueryupdated_afterqueryReturn records updated strictly after this timestamp; cursors are preferred while paging a snapshot
200Tenant-scoped external-to-canonical identifier mappings304Representation unchanged for the supplied If-None-Match value400Invalid mapping kind or incremental synchronization parameter401Missing or invalid API credential403Credential lacks `gaablogs:read`