Skip to content
Scalpels home

Scalpels is officially launching on October 31, 2026. Special early adopter pricing is available now.

← All documentation

Documentation

Slate

Runs and results

Updated

A run is one render of one recipe. You submit it, Slate renders it on the queue, and a run that succeeds has exactly one result: an MP4 in your bucket that you download from a signed URL.

Submit a run

POST /api/v1/runs:

{
  "id": "41414141-4141-4141-8141-414141414141",
  "recipe": { "delivery": { … } },
  "trust": "untrusted",
  "webhook_url": "https://app.example/hooks/slate"
}
Field Rules
id A UUID that you generate. It is the run's ID from then on.
recipe One delivery or highlights object. See Recipes.
trust Optional. untrusted (the default) or trusted-origin. Slate records this value but applies the same checks either way.
webhook_url Optional. A public HTTPS address on port 443 that Slate calls when the run finishes.

A valid submission returns 202 with {"id": "…"}. If you supplied a webhook_url, the response also contains webhook_secret. It is shown only once, so store it now.

Retrying safely

Because you choose the id, a retry cannot start a second render. Submitting an id that already exists returns 409 with reason run_id_conflict, whatever state the original run is in, and nothing new is queued. If a submission times out or returns 502, which Laravel Cloud sends while the instance wakes from hibernation, resend it with the same id. Then read the run to see where it got to.

Rejected submissions

Rejected submissions return 422 with a reason. No run is created.

Reason Cause
malformed_recipe The recipe does not match its schema. The errors field explains why.
source_not_found A source ID is unknown or belongs to another credential's subject.
source_not_ready A source you own has not reached ready yet.
invalid_webhook_destination webhook_url is not a public HTTPS address.
operation_count_exceeded, total_output_duration_exceeded, render_job_count_exceeded The run is too large. See Recipes.

Follow a run

GET /api/v1/runs/{id} returns:

{
  "id": "41414141-4141-4141-8141-414141414141",
  "status": "running",
  "trust": "untrusted",
  "rejection_reason": null
}

status moves from queued to running, then ends at succeeded or failed. A failed run may have a rejection_reason, such as clip_range_invalid or one of the source reasons listed in Sources. A run that belongs to another subject returns the same 404 (reason run_not_found) as an unknown ID.

Download the result

GET /api/v1/runs/{id}/result on a succeeded run returns:

{
  "id": "41414141-4141-4141-8141-414141414141",
  "expires_at": "2026-10-08T12:15:00.000000Z",
  "url": "https://…/slate-runs/…/output.mp4?…"
}

The URL is signed and lasts 15 minutes. To download again later, call the endpoint again for a fresh URL. The video itself stays in your bucket. Slate has no scheduled job that deletes results.

Add include_intermediates=1 to also get an intermediates array. Each entry has an operation_index and a signed url for that page's or clip's own rendered output. These files are for diagnosing problems, not for viewers.

If the result is not available, the endpoint returns 409:

  • result_not_ready while the run is queued or running;
  • run_failed for a failed run, with its status and rejection_reason.

Webhooks

When a run with a webhook_url finishes, Slate sends one JSON POST to that address:

{
  "event": "run.succeeded",
  "run": {
    "id": "41414141-4141-4141-8141-414141414141",
    "status": "succeeded",
    "trust": "untrusted",
    "rejection_reason": null
  },
  "timestamp": 1791460800
}

event is run.succeeded or run.failed. The payload never contains the video or any URL. Fetch the result from the API.

Verify the signature

Each delivery has two headers:

  • X-Slate-Timestamp: Unix time in seconds.
  • X-Slate-Signature: lowercase hex HMAC-SHA256 of {X-Slate-Timestamp}.{raw request body}, keyed with the run's webhook_secret.

Compute the HMAC over the exact raw body, before any JSON parsing. Compare it in constant time. Reject old timestamps. Deduplicate by run ID and event, because a delivery can be retried.

Delivery behaviour

Slate tries each webhook up to three times, waiting 30 seconds and then 120 seconds between attempts. It does not follow redirects. It allows 3 seconds to connect and 10 seconds for a response. Answer quickly and do any heavy work after you respond.