Tenant API

Render video through the Decent Render network via a simple REST API. Standard Remotion bundle + inputProps.

Authentication

All requests require a bearer token (API key):

Authorization: Bearer <your-api-key>

Create keys at decent-render.farm/api-keys. Keys are shown once at creation.

POST /api/v1/renders

Enqueue a render. Required scope: render:enqueue

Body

{ "bundleSha256": "<sha256 of your Remotion bundle>", "inputProps": {}, "compositionWidth": 1920, "compositionHeight": 1080, "fps": 30, "durationFrames": 90, "codec": "h264", "kind": "standard" }

Response (202)

{ "renderId": "job-render-...", "status": "pending", "taskId": "render-...", "creditsReserved": 5 }

GET /api/v1/renders/:id

Check render status. Required scope: render:read

Response

{ "renderId": "job-render-...", "status": "complete", "progress": 1, "outputUrl": "https://...", "creditsReserved": 5, "creditsSettled": 5, "verification": "passed", "createdAt": "2026-07-12T10:00:00.000Z", "completedAt": "2026-07-12T10:00:30.000Z", "error": null }

Statuses: pendingassignedrenderingcomplete | failed | canceled

POST /api/v1/renders/:id/cancel

Cancel a render. Required scope: render:cancel. Credits are not charged for canceled renders.

POST /api/v1/bundles

Request a short-lived R2 upload URL. Required scope: bundle:write. Send {sha256, remotionVersion, sizeBytes}, PUT the tar.gz bytes to uploadUrl, then call POST /api/v1/bundles/:sha256/complete. Completion downloads the object, verifies its SHA-256 and size, and registers it only for the calling workspace.

If the farm has no active runner for the requested Remotion version, the API returns UNSUPPORTED_REMOTION_VERSION and the current supportedRemotionVersions.

GET /api/v1/versions

Preflight the active farm-managed runner matrix. Required scope: bundle:write. Returns {supportedRemotionVersions: [{remotionVersion, payloadVersion}]}. Bundle registration and enqueue return 422 UNSUPPORTED_REMOTION_VERSION with the supported version names when no matching runner exists.

Farm operators add support by creating a versioned runner app, adding it to scripts/publish-runner-payload.ts, and running bun scripts/publish-runner-payload.ts --version=<version>. Tenants never publish runner binaries.

GET /api/v1/balance

Required scope: balance:read.

{"balance": 100, "holds": 5, "available": 95}

/api/v1/webhooks

GET requires webhook:read. POST, PATCH /:id, and DELETE /:id require webhook:write. Every operation is scoped to the API key's workspace. The signing secret is returned once on creation.

Webhooks

Terminal status deliveries are sent to your configured URL:

POST <your-webhook-url> X-Decent-Signature: <HMAC-SHA256> X-Decent-Timestamp: <unix-seconds> { "event": "render.complete", "renderId": "job-render-...", "status": "complete", "outputUrl": "https://...", "creditsReserved": 5, "creditsSettled": 5, "verification": "pending" }

Completed output is delivered immediately with verification pending. The referee later emits render.verification with passed or flagged. Flagged renders retain their outputUrl; only operator payout is voided. The signature is hex HMAC-SHA256 over <timestamp>.<raw-body>.