Skip to Content
eventdoordocs home

Create broadcast

POST
/organizations/{orgSlug}/hubs/{hubSlug}/broadcasts

Authorization

ApiCredential
headerAuthorizationBearer <edk_<id>_<secret>>

A scoped API credential (edk_...), issued in the admin under Settings > Integrations and sent as Authorization: Bearer edk_.... Its level, scope and refusals are in Authentication; its rate limit and the 429 response are in Errors.

Path Parameters

orgSlug*string
hubSlug*string

Request Body

application/json
  1. body
title*string
Lengthlength <= 72
description?|
Lengthlength <= 240
posterUrl?|
Formaturi
streamId?string
Formatuuid
startsAt?|
Formatdate-time
endsAt?|
Formatdate-time
timezone?|
Lengthlength <= 64
archiveEnabled?boolean

Live-mode only. When true (default), the broadcast is archived to a replay VOD after it ends; when false, no archive is produced. Ignored for VOD-mode broadcasts.

Defaulttrue
visibility?BroadcastVisibility

Who can find and open the broadcast. published is listed everywhere. unlisted is absent from every public list, search, schedule, collection, clip list and live-now feed, but its page, playback and purchases keep working for anyone with the link or a purchase. draft is unlisted AND its page returns 404 to anyone who is not an org member; a draft can still go live. Org admins see all three in lists.

Default"published"
Value in"published""unlisted""draft"
archivePreRollMinutes?|

Live mode, dedicated stream key only. Minutes before startsAt that the live-archive merge window covers. Null (the default) uses the platform default. A broadcast whose stream key is shared with other broadcasts ignores padding entirely, because padding would pull in the neighbouring broadcast's footage. Applies only to an archive that has not run yet; changing it later does not re-create an existing recording. Set through the API only; the admin has no field for it.

Range0 <= value <= 240
archivePostRollMinutes?|

Live mode, dedicated stream key only. Minutes after endsAt that the live-archive merge window may cover. The merge ends at whichever comes first, the stream actually stopping or this padded end, so raising it does not extend a replay past the footage. Null (the default) uses the platform default. Raise it for an event that legitimately runs long rather than raising the platform default for everyone. Applies only to an archive that has not run yet; changing it later does not re-create an existing recording.

Range0 <= value <= 720
tagIds?array<>
collectionItems?array<>

Response Body

Broadcast created

application/json
  1. response
id*string
Formatuuid
mode*string
Value in"live""vod"
kind*string

UI discriminator: 'live'/'vod' track mode, plus 'archive' when the attached VOD was captured from a live stream (source='live-archive'). Archiving attaches the VOD to the same broadcast and flips it to mode='vod' — the slug, URL, and schedule are unchanged.

Value in"live""vod""archive"
hubId?|
Formatuuid
organizationId*string
Formatuuid
vodId?|

ID of the attached VOD, or null when the broadcast is live.

Formatuuid
attachedVod?|null
clipStartsAt?|

Archive-only. The in-point of the WHOLE-RECORDING archive window - the admin shows this as the "Archive trim".

NOT a clip. A clip is a separate resource with its own startTime/endTime; this pair bounds the entire recording, of which there is exactly one per broadcast. Set at archive time: the scheduled start minus the pre-roll padding on a dedicated stream key, the exact scheduled start on a shared key or a forced archive.

Formatdate-time
clipEndsAt?|

Archive-only. The out-point of the WHOLE-RECORDING archive window - the admin shows this as the "Archive trim".

NOT a clip; see clipStartsAt. Set at archive time: on a dedicated stream key, whichever comes first of the stream stopping and the scheduled end plus the post-roll padding; the exact scheduled end on a shared key; the operator's cut time on a forced archive.

Formatdate-time
archiveStartedAt?|

The recorded footage's REAL first-frame time — the VOD playback timeline's 0:00. A stream rarely goes live exactly at its scheduled startsAt, so this is later by the "late-start delta". Display and recording-bounds only. It is not a clip anchor: clip positions come from offsetIntoReplaySec. Null for a broadcast that is not a completed live archive, or one archived before this field existed.

Formatdate-time
endedAt?|

Operator-declared end of the live window, or null. When set the broadcast is NOT live regardless of whether its stream key is still publishing, and this instant is the end bound of its archive merge. Set by endBroadcastLive, cleared by reopenBroadcastLive.

Formatdate-time
endedBy?|

The user who force-ended this broadcast, or null.

endReason?|

Operator-supplied note explaining the force-end, or null.

archiveAttemptedAt?|

When an archive run last finished without retrying. A forced run sets it on every outcome; a scheduled run sets it when the merge comes back empty or fails its last retry, and it is cleared when the schedule is saved again (a fresh attempt). This is how a caller tells "ended, archive still running" (null) from "ended, archive ran and produced no replay" (set, with mode still live). When mode is vod the run produced the attached replay.

Formatdate-time
programId?|

The auto-created default program for this broadcast. Null for VOD-mode broadcasts.

Formatuuid
activeProgramId?|

The resolved active program — linked program from a program collection if one exists, otherwise falls back to programId. Null for VOD-mode broadcasts.

Formatuuid
hub?
organization?
title*string
slug*string
description?string|null
posterUrl?|

The EFFECTIVE poster: a manual poster wins over a template-generated one; null falls through to the surface placeholder.

generatedPosterUrl?|

The raw template-generated poster, if one is baked. Exposed alongside posterUrl so a client can tell which poster is in effect.

posterBakeFailed?boolean

Transient, mutation responses only: this save's poster re-bake hit an operational failure (storage/composition), so generatedPosterUrl may be STALE — the previous bake's URL, not this save's. Without this flag a failed RE-bake is indistinguishable from success (the old non-null URL rides along). Absent means the bake succeeded, was skipped, or wasn't attempted.

graphicsTemplateId?|

The graphics template applied to this broadcast, or null.

Formatuuid
useGeneratedPoster?boolean

Per-broadcast poster-generation toggle.

streamId?|

Null for VOD-mode broadcasts.

Formatuuid
startsAt?|
Formatdate-time
endsAt?|
Formatdate-time
timezone?string|null
archiveEnabled?boolean

Live-mode only. Whether this broadcast will be archived to a replay VOD after it ends. Always true for archives created before this toggle existed.

visibility?BroadcastVisibility

Who can find and open the broadcast. published is listed everywhere. unlisted is absent from every public list, search, schedule, collection, clip list and live-now feed, but its page, playback and purchases keep working for anyone with the link or a purchase. draft is unlisted AND its page returns 404 to anyone who is not an org member; a draft can still go live. Org admins see all three in lists.

Default"published"
Value in"published""unlisted""draft"
archivePreRollMinutes?|

Live mode, dedicated stream key only. Minutes before startsAt that the live-archive merge window covers. Null (the default) uses the platform default. A broadcast whose stream key is shared with other broadcasts ignores padding entirely, because padding would pull in the neighbouring broadcast's footage. Applies only to an archive that has not run yet; changing it later does not re-create an existing recording. Set through the API only; the admin has no field for it.

Range0 <= value <= 240
archivePostRollMinutes?|

Live mode, dedicated stream key only. Minutes after endsAt that the live-archive merge window may cover. The merge ends at whichever comes first, the stream actually stopping or this padded end, so raising it does not extend a replay past the footage. Null (the default) uses the platform default. Raise it for an event that legitimately runs long rather than raising the platform default for everyone. Applies only to an archive that has not run yet; changing it later does not re-create an existing recording.

Range0 <= value <= 720
live?boolean
manifestUrl?string|null
defaultStreamPublicKey?string|null
streams?array<>
tags?array<>
collectionItems?array<>
madeFrom?|null

Single-broadcast reads (hub- and org-scoped), ORG ADMINS ONLY: the replay this broadcast was made from, or null when it was not made from one. Absent for everyone else — the source may be a draft or unlisted.

isMadeReplay?boolean

List reads (listBroadcasts, listOrgBroadcasts), ORG ADMINS ONLY: true when this broadcast is a made replay (a window of another broadcast's recording), false otherwise. Absent for everyone else — viewers see a made replay as an ordinary replay. A flag rather than a kind value because kind is public and drives viewer watchability: a made replay's kind stays archive. Single reads omit it; madeFrom != null answers the same question there.

gate?|null

PUBLIC monetization badge. Null when the content is free / ungated. Present on list responses; single-broadcast responses omit it. Carries no viewer entitlement data.

created?string
Formatdate-time
updated?string
Formatdate-time
Example Request
curl -X POST "$EVENTDOOR_API/organizations/acme/hubs/home-games/broadcasts" \
  -H "Authorization: Bearer $EVENTDOOR_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
  "title": "string"
}'
Example Response
{
  "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  "mode": "live",
  "kind": "live",
  "hubId": "f4636d70-a85d-42d1-9299-df21b1467acd",
  "organizationId": "7bc05553-4b68-44e8-b7bc-37be63c6d9e9",
  "vodId": "636916d7-6441-4ec9-979c-6e434b8f65ca",
  "attachedVod": {
    "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
    "status": "pending",
    "videoUrl": "string",
    "videoDuration": 0,
    "posterUrl": "string",
    "source": "upload",
    "fileName": "string",
    "xcodeUploadId": 0,
    "originalArchiveAvailable": true,
    "archiveReplacedAt": "2019-08-24T14:15:22Z"
  },
  "clipStartsAt": "2019-08-24T14:15:22Z",
  "clipEndsAt": "2019-08-24T14:15:22Z",
  "archiveStartedAt": "2019-08-24T14:15:22Z",
  "endedAt": "2019-08-24T14:15:22Z",
  "endedBy": "string",
  "endReason": "string",
  "archiveAttemptedAt": "2019-08-24T14:15:22Z",
  "programId": "bc59f66b-913a-48ec-ae2b-7ee29d7bcfbb",
  "activeProgramId": "a0295b95-6947-449e-8c5d-ad4c6fef1acb",
  "hub": {
    "name": "string",
    "slug": "string"
  },
  "organization": {
    "name": "string",
    "slug": "string",
    "avatarUrl": "string"
  },
  "title": "string",
  "slug": "string",
  "description": "string",
  "posterUrl": "string",
  "generatedPosterUrl": "string",
  "posterBakeFailed": true,
  "graphicsTemplateId": "67d00d5f-2d4f-4c08-8270-1a32c67eb9e1",
  "useGeneratedPoster": true,
  "streamId": "22cb260b-fac8-4ce4-8488-8f1949304300",
  "startsAt": "2019-08-24T14:15:22Z",
  "endsAt": "2019-08-24T14:15:22Z",
  "timezone": "string",
  "archiveEnabled": true,
  "visibility": "published",
  "archivePreRollMinutes": 0,
  "archivePostRollMinutes": 0,
  "live": true,
  "manifestUrl": "string",
  "defaultStreamPublicKey": "string",
  "streams": [
    {
      "live": true,
      "manifestUrl": "string",
      "previewManifestUrl": "string",
      "snapshotUrl": "string",
      "ingress": {
        "rtmp": "string",
        "webrtc": "string"
      },
      "transcode": true,
      "authType": "string",
      "appData": "string",
      "rtmpPush": "string",
      "mode": "string",
      "name": "string",
      "publicKey": "string",
      "authKey": "string",
      "streamKey": "string",
      "projectId": "string"
    }
  ],
  "tags": [
    {
      "category": {
        "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
        "slug": "string",
        "name": "string"
      },
      "values": [
        {
          "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
          "slug": "string",
          "label": "string"
        }
      ]
    }
  ],
  "collectionItems": [
    {
      "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
      "collectionId": "0ffe69e2-b7af-4b1e-835c-867376165f50",
      "collection": {
        "name": "string",
        "slug": "string",
        "category": {
          "name": "string",
          "slug": "string"
        }
      },
      "type": {
        "slug": "string",
        "name": "string",
        "system": true,
        "imageStyle": "avatar",
        "icon": "string"
      },
      "entityId": "156e622c-6cdf-4c27-9bc9-2f2db69919f5",
      "name": "string",
      "slug": "string",
      "description": "string",
      "avatarUrl": "string",
      "bannerUrl": "string",
      "location": "string",
      "colors": {
        "primary": "string",
        "secondary": "string"
      },
      "fieldValues": {},
      "position": "string",
      "tags": [
        {
          "category": {
            "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
            "slug": "string",
            "name": "string"
          },
          "values": [
            {
              "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
              "slug": "string",
              "label": "string"
            }
          ]
        }
      ],
      "created": "2019-08-24T14:15:22Z",
      "streams": [
        {
          "live": true,
          "manifestUrl": "string",
          "previewManifestUrl": "string",
          "snapshotUrl": "string",
          "ingress": {
            "rtmp": "string",
            "webrtc": "string"
          },
          "transcode": true,
          "authType": "string",
          "appData": "string",
          "rtmpPush": "string",
          "mode": "string",
          "name": "string",
          "publicKey": "string",
          "authKey": "string",
          "streamKey": "string",
          "projectId": "string"
        }
      ],
      "entity": {},
      "gate": {
        "tier": "free",
        "origin": "direct",
        "gatedContentType": "broadcast",
        "gatedContentId": "6a2155c7-3a80-4718-80d0-2c42044f9b75",
        "label": "string",
        "buttonLabel": "string",
        "showPrice": true,
        "unitAmount": 0,
        "currency": "string",
        "recurringInterval": "day"
      },
      "organizations": [
        {
          "name": "string",
          "slug": "string",
          "avatarUrl": "string"
        }
      ],
      "qualifier": {
        "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
        "key": "string",
        "label": "string"
      }
    }
  ],
  "madeFrom": {
    "sourceBroadcastId": "e5e6245b-b89a-437f-aaeb-91152fc85658",
    "sourceTitle": "string",
    "sourceDeleted": true,
    "sourceSlug": "string",
    "sourceHubSlug": "string",
    "startTime": "2019-08-24T14:15:22Z",
    "endTime": "2019-08-24T14:15:22Z"
  },
  "isMadeReplay": true,
  "gate": {
    "tier": "free",
    "origin": "direct",
    "gatedContentType": "broadcast",
    "gatedContentId": "6a2155c7-3a80-4718-80d0-2c42044f9b75",
    "label": "string",
    "buttonLabel": "string",
    "showPrice": true,
    "unitAmount": 0,
    "currency": "string",
    "recurringInterval": "day"
  },
  "created": "2019-08-24T14:15:22Z",
  "updated": "2019-08-24T14:15:22Z"
}