Enterprise API

API Docs

Approved Enterprise customers can request API access for automated generation workflows. Wallet accounts use MotionLab credits. Approved accounts configured for external settlement record usage without debiting the website wallet; commercial terms are agreed separately.

This public guide documents the API contract. Generation requires an approved Enterprise account and a scoped API key. The public partner page is the product overview and access path. MotionLab may change its rendering providers without changing this contract; always use /api/v1/capabilities as the source of truth for live options.

Authentication

curl https://www.motionlab.art/api/v1/jobs \
  -H "Authorization: Bearer ml_live_your_key" \
  -H "Content-Type: application/json"

A machine-readable OpenAPI 3.1 specification is available at GET /api/v1/openapi.json.

Keys can carry jobs:create and/or jobs:read. Uploads, job creation, and webhook administration require jobs:create; listing and status require jobs:read. Never expose a MotionLab key in browser or mobile client code.

Discover live capabilities

curl https://www.motionlab.art/api/v1/capabilities

Returns the currently enabled modes, resolutions, per-resolution duration limits, FPS controls, camera moves, inputs, and live pricing for MotionLab. Features that are disabled do not appear here and are rejected at job creation with a stable error code — always read capabilities instead of hard-coding values.

Live configuration

  • 720p320 seconds
  • 1080p320 seconds

Upload an input image

Standard image-to-video mode requires a start image. First create a short-lived upload URL, then upload the image directly to storage with PUT. Use only the returned inputUrl when creating the job; uploadUrl is a one-minute upload credential and must not be stored or logged.

curl -X POST https://www.motionlab.art/api/v1/uploads \
  -H "Authorization: Bearer ml_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "filename": "source.png",
    "contentType": "image/png",
    "fileSize": 1234567
  }'
{
  "uploadUrl": "https://...",
  "inputUrl": "enterprise-api/user_id/uploads/upload_id-source.png",
  "contentType": "image/png",
  "maxSize": 52428800,
  "expiresIn": 60
}
curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: image/png" \
  --data-binary "@source.png"

Supported contentType values: image/png, image/jpeg, image/webp, image/gif, and image/avif.

fileSize is required and must be no larger than 50 MB. The upload URL expires after 60 seconds.

Uploaded API inputs are ephemeral and scheduled for deletion within seven days, even if they are never attached to a job.

Upload an audio track

When capabilities lists image_audio_to_video, upload WAV or MP3 with kind: "audio", then pass the returned inputUrl as audioUrl. The upload is limited to 50 MB and uses the same presigned PUT flow. The requested whole-second video duration must not exceed the audio remaining after audioStartTime, rounded down.

{
  "filename": "soundtrack.wav",
  "contentType": "audio/wav",
  "fileSize": 10485760,
  "kind": "audio"
}

Upload a reference video (motion control)

When the capabilities response lists the image_reference_video_to_video mode, you can upload an MP4 whose motion guides the generation. Pass kind: "reference_video" to the uploads endpoint, then use the returned inputUrl as referenceVideoUrl when creating the job (priced with the reference-video add-on).

{
  "filename": "motion-reference.mp4",
  "contentType": "video/mp4",
  "fileSize": 10485760,
  "kind": "reference_video"
}

MP4 only, up to 50 MB.

Create a job

Currently supported: IMAGE_TO_VIDEO and TEXT_TO_VIDEO using MotionLab.

{
  "type": "IMAGE_TO_VIDEO",
  "prompt": "Slow atmospheric camera drift with subtle light trails",
  "inputUrl": "enterprise-api/user_id/uploads/upload_id-source.png",
  "cameraMove": "none",
  "resolution": "1080p",
  "duration": 4,
  "model": "motionlab/fast",
  "clientRequestId": "video-0001",
  "webhookUrl": "https://your-service.example/motionlab/webhook"
}

Prompt-only generation is currently enabled. Send type: "TEXT_TO_VIDEO" and omit inputUrl. It uses the same customer credit price as the equivalent MotionLab video job.

{
  "type": "TEXT_TO_VIDEO",
  "prompt": "A cinematic coastal city at blue hour, slow atmospheric camera drift",
  "cameraMove": "none",
  "resolution": "1080p",
  "duration": 4,
  "model": "motionlab/fast",
  "clientRequestId": "text-video-0001"
}

prompt is required and supports up to 8,000 characters.

model defaults to motionlab/fast; use motionlab/max for Max. The short aliases motionlab and motionlab-max are also accepted. Earlier model IDs remain supported for existing integrations.

duration uses whole-second values from the selected entry in resolutionLimits. The example above is generated from the current Admin configuration.

The output aspect ratio is derived from the uploaded input image. Choose the output tier with resolution.

resolution should always be sent using a tier listed by the capabilities endpoint. If omitted, the server prefers 1080p when enabled, otherwise the first enabled tier. Sending an explicit tier makes your integration predictable.

cameraMove is optional and defaults to none (Auto).

Current values: none, static, dolly-in, dolly-out, pan-left, pan-right, jib-up, jib-down, orbit-right, orbit-left, orbit-up, orbit-down.

clientRequestId is your idempotency key: retrying with the same value returns the original job (idempotent: true) without a second charge. The deprecated requestId alias remains accepted for existing clients.

referenceVideoUrl optionally supplies an owned MP4 reference video (see the reference-video section above).

webhookUrl optionally overrides your account's default webhook for this job.

Optional generation controls

Send these fields only when the matching capability is present. Disabled controls are rejected; absence means unavailable, not false.

{
  "type": "IMAGE_TO_VIDEO",
  "model": "motionlab/fast",
  "prompt": "A cinematic clip using the currently enabled generation controls",
  "inputUrl": "enterprise-api/user_id/uploads/start.png",
  "audioUrl": "enterprise-api/user_id/uploads/soundtrack.wav",
  "audioStartTime": 0,
  "loop": true,
  "loopAudio": "crossfade",
  "duration": 8,
  "resolution": "1080p",
  "clientRequestId": "video-advanced-0001"
}

endImageUrl adds an end frame; endFrameStrength accepts values from 0 to 1.

loop enables seamless-loop mode; loopAudio is crossfade, keep, or mute. Loop mode cannot be combined with endImageUrl.

enableAudio defaults to true. audioUrl enables audio-driven motion; audioStartTime must be at least zero. improveAudio applies only to generated audio.

The live capabilities response is authoritative for modes, accepted inputs, curated FPS choices, resolutions, and add-on pricing.

Webhooks

Instead of polling, configure a default HTTPS callback once (or pass webhookUrl per job). MotionLab creates one logical terminal event per job: job.completed, job.failed, or job.canceled. Delivery is at-least-once, so retries can deliver the same event ID more than once.

curl -X POST https://www.motionlab.art/api/v1/account/webhooks \
  -H "Authorization: Bearer ml_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://your-service.example/motionlab/webhook"}'
POST <your url>
X-MotionLab-Event: job.completed
X-MotionLab-Event-Id: evt_id
X-MotionLab-Signature: t=1767139200,v1=<hmac-sha256 hex>

{
  "id": "evt_id",
  "type": "job.completed",
  "apiVersion": "v1",
  "data": {
    "jobId": "job_id",
    "status": "COMPLETED",
    "outputUrl": "https://.../output.mp4",
    "outputAvailable": true,
    "outputExpiresAt": "2026-08-03T12:00:00.000Z",
    "artifactsDeletedAt": null,
    "clientRequestId": "video-0001",
    "error": null
  }
}

Verify X-MotionLab-Signature: v1 = HMAC_SHA256(signingSecret, "<t>.<rawBody>"), use a constant-time comparison, and reject timestamps more than 300 seconds in the past or future. Your signing secret appears in GET /api/v1/account under webhooks.signingSecret; rotate it with {"rotateSecret": true}. Rotation is immediate and can overlap an in-flight delivery, so keep polling available during the switchover.

Failed deliveries are retried with exponential backoff for about an hour; deduplicate on X-MotionLab-Event-Id and respond with any 2xx to acknowledge.

List and cancel jobs

curl "https://www.motionlab.art/api/v1/jobs?limit=20&status=COMPLETED" \
  -H "Authorization: Bearer ml_live_your_key"
curl -X POST https://www.motionlab.art/api/v1/jobs/job_id/cancel \
  -H "Authorization: Bearer ml_live_your_key"

The list endpoint returns only this Enterprise workspace's API-created jobs, newest-first, with cursor pagination (nextCursor). API jobs never appear in the creative Dashboard. Cancel releases an active job's charge exactly once and fires the job.canceled webhook; canceling an already finished job is a safe no-op.

Get job status

curl https://www.motionlab.art/api/v1/jobs/job_id \
  -H "Authorization: Bearer ml_live_your_key"
{
  "jobId": "job_id",
  "status": "PROCESSING",
  "stage": "rendering",
  "progress": 42,
  "statusMessage": "Generating motion",
  "outputUrl": null,
  "outputAvailable": false,
  "outputExpiresAt": null
}
{
  "jobId": "job_id",
  "status": "FAILED",
  "stage": "failed",
  "statusMessage": "The input image could not be read. Upload it again and retry the request.",
  "outputUrl": null,
  "failureReason": "The input image could not be read. Upload it again and retry the request.",
  "error": {
    "code": "INPUT_NOT_ACCESSIBLE",
    "message": "The input image could not be read. Upload it again and retry the request.",
    "refunded": true
  }
}

Poll every 3-5 seconds. stage tells you whether the job is queued, waiting for the renderer, rendering, finalizing, completed, failed, or canceled. progress reaches 100 only when completed. Completed jobs include outputAvailable and outputExpiresAt; copy the result before expiry. After artifact deletion the job record remains queryable with outputAvailable: false. Failed jobs include a sanitized error.code, actionable message, and explicit refund state.

Get account, billing, and usage

curl https://www.motionlab.art/api/v1/account \
  -H "Authorization: Bearer ml_live_your_key"
{
  "requestId": "req_id",
  "account": {
    "userId": "user_id",
    "balance": 240,
    "creditsAvailable": 240
  },
  "enterprise": {
    "customerId": "customer_id",
    "status": "ACTIVE",
    "companyName": "Example Studio",
    "planName": "Enterprise",
    "supportTier": "STANDARD",
    "allowedJobTypes": ["IMAGE_TO_VIDEO"],
    "allowedModels": ["motionlab"],
    "integrationType": "DIRECT_API"
  },
  "billing": {
    "mode": "WALLET_CREDITS",
    "walletDebited": true,
    "settledBy": "motionlab_wallet"
  },
  "apiKey": {
    "id": "key_id",
    "name": "Production",
    "keyPrefix": "ml_live_abcd",
    "scopes": ["jobs:create", "jobs:read"],
    "lastUsedAt": "2026-08-02T12:00:00.000Z",
    "expiresAt": null
  },
  "limits": {
    "apiRateLimitPerMinute": 60,
    "monthlyApiRequestLimit": 1000,
    "monthlyCreditLimit": 5000
  },
  "usage": {
    "monthStart": "2026-08-01T00:00:00.000Z",
    "requestsThisMonth": 42,
    "remainingMonthlyRequests": 958,
    "creditsSpentThisMonth": 120,
    "jobsThisMonth": 40,
    "remainingMonthlyCredits": 4880
  },
  "retention": {
    "outputArtifactsHours": 24,
    "inputUploadsDays": 7,
    "jobMetadataRetained": true
  },
  "webhooks": {
    "defaultUrl": "https://your-service.example/motionlab/webhook",
    "signingSecret": "whsec_...",
    "signatureHeader": "X-MotionLab-Signature",
    "signatureScheme": "hmac-sha256",
    "events": ["job.completed", "job.failed", "job.canceled"]
  },
  "serverTime": "2026-08-02T12:00:00.000Z"
}

Use this endpoint to check billing mode, Enterprise status, API key scopes, rate limits, monthly job-submission usage, quoted usage, and artifact retention. In EXTERNAL_SETTLEMENT mode the wallet fields are null by design, and creditsSpentThisMonth represents the same quoted quantity used for external settlement rather than a wallet debit.

Responses and billing

Successful create responses include requestId, jobId, status, model, type, cost, billing mode, output retention, idempotent, clientRequestId, and createdAt.

Status responses include stage, progress, and statusMessage. outputUrl is included only after completion and before retention expiry; provider metadata and raw infrastructure errors are never exposed.

Every error response carries a human-readable error string plus a stable machine-readable code (for example INSUFFICIENT_CREDITS, RESOLUTION_DISABLED, REFERENCE_VIDEO_DISABLED, RATE_LIMITED) and a requestId for support. Common statuses are 400, 401, 402, 403, 429, 500, and 503.

creditsSpentThisMonth and monthly credit limits use net accepted usage and exclude released/refunded jobs. For externally settled accounts this is a unit counter despite the backward-compatible field name.

The monthly limit counts job-submission attempts, including invalid or idempotent retries. Status reads, list calls, webhook administration, and cancellation do not consume that monthly counter.

Per-minute rate limits apply to authenticated operational endpoints and default to 60 requests per minute when unset. The public, cacheable capabilities endpoint is exempt.