Docs
Outbound WebhooksOutbound Webhooks

Outbound Webhooks

Receive real-time events about videos, telemetry, embedding, and quotas.

Outbound webhooks notify your HTTPS endpoints when something important happens in Moviie. Each delivery is asynchronous, retried automatically, and can also be resent manually.

Delivery, timeouts, and retries

Moviie sends each notification as an HTTP POST and treats a delivery as successful when your server responds with any 2xx status. The request body is the JSON envelope described below.

Timeout. Each attempt waits up to 30 seconds for a response. If the connection times out or fails at the network layer (DNS, TLS, reset, etc.), Moviie classifies it as a transient failure and applies the same retry policy as for retryable HTTP responses.

Retryable HTTP responses. Moviie automatically retries when your endpoint returns 408, 429, or any status ≥ 500. Those cases are assumed to be temporary overload or upstream issues.

Non-retryable HTTP responses. Any other status (for example 401, 403, 404, 422) marks the delivery as failed without further automatic attempts. Fix the endpoint or credentials and then use a manual resend from the dashboard when available.

Backoff. Between automatic attempts, Moviie uses a quadratic backoff measured in minutes, capped at 6 hours between tries. The current attempt index is exposed in the X-Moviie-Attempt header (1 on the first try, then increments). The platform makes at most six HTTP attempts per delivery; if all fail, the delivery stays failed until you intervene.

Manual retries. After automatic retries are exhausted (or after a non-retryable response), you can trigger delivery again from Settings → Webhooks whenever that action is available for the endpoint.

Request format

Every request body is a JSON object with the following top-level fields:

FieldTypeDescription
idstringDelivery envelope id; equals X-Moviie-Event-Id (evt_ + UUID).
typestringCanonical event name; equals X-Moviie-Event.
created_atstringISO 8601 timestamp when the envelope was built.
organization_idstringUUID of the Moviie organization that owns the resource.
dataobjectEvent-specific payload: see the event reference pages.
{
  "id": "evt_7b9e4f2a-0c1d-4e8f-9a3b-2d6c8e1f4a5b",
  "type": "video.upload.started",
  "created_at": "2026-05-09T18:30:00.000Z",
  "organization_id": "11111111-1111-1111-1111-111111111111",
  "data": {
    "video": { "id": "00000000-0000-4000-8000-00000000aa01" }
  }
}

Subscribing to an event in Settings → Webhooks only controls whether deliveries are created; every delivery uses this same envelope. Request headers, authentication, and signature verification are covered in Signing & Verification.

Shared data.video object

Many events include a video object built from the library record. Unless an event's reference entry says otherwise, data.video contains:

FieldTypePresence
idstring (UUID)Always
external_idstringAlways
statusstringAlways
titlestringIncluded when non-empty
duration_msnumberIncluded when known
widthnumberIncluded when known
heightnumberIncluded when known
collection_idstring (UUID)Included when set

Some events send a minimal video containing only id; those are noted explicitly in the event entry.

Event catalog

Only events enabled for your endpoint are enqueued. Click any type to jump to the full body example and parameter table.

typeSummary
collection.createdNew collection created.
collection.deletedCollection soft-deleted.
embed.blockedEmbed blocked by referrer policy.
embed.token.invalidTelemetry refresh token rejected.
playback.cta.clickedCTA clicked during playback.
playback.errorPlayer error reported for a session.
playback.milestone.25Session reached ~25% of duration.
playback.milestone.50Session reached ~50% of duration.
playback.milestone.75Session reached ~75% of duration.
playback.milestone.100Session reached ~100% of duration.
playback.session.endedPlayback session ended (aggregated).
usage.bandwidth.thresholdBandwidth hits 80%, 95%, or 100% of plan.
usage.storage.thresholdStorage hits 80%, 95%, or 100% of plan.
video.caption.addedCaption track added.
video.caption.deletedCaption track removed.
video.chapter.createdChapter created.
video.chapter.deletedChapter deleted.
video.collection.changedVideo moved to another collection.
video.completion_rate.thresholdDaily completion rate crossed 40% or 50%.
video.cta.createdCTA created.
video.cta.deletedCTA deleted.
video.cta.updatedCTA updated.
video.deletedVideo soft-deleted.
video.encoding.completedA resolution finished encoding.
video.encoding.failedEncoding failed (confirmed).
video.encoding.progressEncoding progress update.
video.encoding.startedEncoding started.
video.failedFatal provider failure (confirmed).
video.publishedVideo first became fully playable.
video.readyAll renditions ready to play.
video.unpublishedVideo is no longer publicly available.
video.updatedVideo metadata updated.
video.upload.completedFile ingested at provider.
video.upload.failedUpload failed (confirmed).
video.upload.startedUpload slot reserved.
video.view.milestoneTotal views crossed 100, 1 000, or 10 000.

Event timing notes

Some failure paths are intentionally delayed: transient ingest-provider statuses are buffered, and only confirmed failures emit video.encoding.failed, video.upload.failed, or video.failed after the failure confirmer job checks the provider state: never from a single flaky callback alone.

Telemetry-driven engagement events (playback.*, milestones, thresholds) flush when the telemetry aggregator processes stored heartbeats. Latency depends on how often aggregation runs; in typical local development this is around five minutes.

On this page