Create task

POST /v1/tasks: submit an image or video generation task, with request fields, validation rules, pricing and idempotency.

HTTP
POST https://genkiln.com/v1/tasks

Creates a generation task and returns it immediately with 202 Accepted. The task runs in the background; read its result with Get task or receive it on a webhook.

Request headers

HeaderRequiredDescription
AuthorizationYesBearer sk_..., see Authentication.
Content-TypeRecommendedapplication/json. The body is always parsed as JSON.
Idempotency-KeyNoAny string up to 255 characters. Makes retries safe, see Idempotency.

Request body

FieldTypeRequiredDescription
modelstringYesModel ID, e.g. veo-3.1-fast. See Models.
inputobjectYesModel parameters. Each model has its own schema: prompt, image_urls, duration, resolution, aspect_ratio, …
callback_urlstringNoPublic https URL we POST the finished task to. See Webhooks.
metadataobjectNoYour own data, returned unchanged on the task and in webhooks. At most 16 keys and 2 KB as JSON.

The body must be a JSON object of at most 64 KB. Unknown top-level fields are rejected with 400 invalid_request and the field name in error.param.

Example: image-to-video with a webhook

curl https://genkiln.com/v1/tasks \
  -H "Authorization: Bearer $GENKILN_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-abc123-video" \
  -d '{
    "model": "veo-3.1-fast",
    "input": {
      "prompt": "The camera slowly pushes in as snow starts to fall",
      "image_urls": ["https://example.com/first-frame.png"],
      "duration": 8,
      "resolution": "720p",
      "aspect_ratio": "16:9"
    },
    "callback_url": "https://your-app.com/webhooks/genkiln",
    "metadata": { "order_id": "abc123" }
  }'

Input validation

  • input is checked against the model's schema. Omitted optional fields take their default; unknown fields are rejected. The parameter table for each model is on Models, and GET /v1/models/{id} returns it as JSON Schema.
  • A validation error names the field, for example "param": "input.duration".
  • Every URL in image_urls, and callback_url, must:
    • use https on the default port (443), with no username or password in the URL;
    • be at most 2,048 characters;
    • point to a public host. Hostnames that resolve to private, loopback, link-local or other non-public addresses are rejected.
  • Image URLs must be directly downloadable by us. A URL that cannot be fetched or is not a supported image fails the task with invalid_input (refunded).
  • Prompts are screened before you are charged. Prompts that violate the Acceptable Use Policy are rejected with 403 content_rejected.

Pricing

The cost is computed on our side from the validated input (defaults included) and the model's public price list. It is returned as cost_credits and deducted when the task is created. If your balance is too low, you get 402 insufficient_credits and no task is created.

  • Per-video models (Veo) cost the same for any duration; the price depends on resolution.
  • Per-second models (Seedance, Kling) cost duration × price per second for the chosen resolution and audio option.
  • Image models cost a fixed price per image for the chosen resolution.

Failed, timed-out and canceled tasks are refunded in full, automatically.

Response

202 Accepted with a task object. A new task usually starts as queued:

JSON
{
  "id": "task_01JC8ZQ0E6H2P4N7K9T3W5Y8VA",
  "object": "task",
  "model": "veo-3.1-fast",
  "status": "queued",
  "cost_credits": 390,
  "refunded": false,
  "output": null,
  "error": null,
  "created_at": "2026-11-03T08:00:00.000Z",
  "started_at": null,
  "completed_at": null,
  "expires_at": null,
  "metadata": { "order_id": "abc123" }
}

When the task cannot be started

Rarely, a task is created and charged but cannot be started. We then fail it, refund it at once, and answer with an error instead of 202. The failed task is included as error.task so you can record it:

HTTP`error.code`Meaning
403content_rejectedThe prompt or input media was rejected by our content policy.
400invalid_requestThe input could not be processed (check image URLs and parameters).
503service_busyTemporarily busy. Retry after the Retry-After header (seconds).
JSON
{
  "error": {
    "code": "service_busy",
    "message": "The service is temporarily busy. Your credits have been refunded; please retry shortly.",
    "task": {
      "id": "task_01JC8ZQ0E6H2P4N7K9T3W5Y8VA",
      "status": "failed",
      "refunded": true,
      "...": "..."
    },
    "request_id": "req_01JC8ZQ0E8M3B1V6C2D4F7G9HJ"
  }
}

If you set a callback_url, a task.failed webhook is sent for it too.

Idempotency

Network errors happen. To retry a create safely, send an Idempotency-Key header with a value that is unique per logical task (an order ID, a UUID).

  • The same key used again within 24 hours returns the original task instead of creating and charging a new one. The response is 202 with the task's current state and the header Idempotent-Replayed: true.
  • Reusing a key with a different request body returns 409 idempotency_conflict. The body comparison covers model, input, callback_url and metadata exactly as you sent them, so send an identical body when retrying.
  • Keys are scoped to your account (shared by all your API keys) and can be reused after 24 hours.
  • Concurrent requests with the same key are safe: only one task is created.

Errors

HTTPCodeWhen
400invalid_requestInvalid JSON, unknown field, failed validation, unsafe URL, metadata too large.
401invalid_api_keyMissing or invalid key.
402insufficient_creditsBalance lower than the task cost.
403content_rejectedPrompt rejected by the content policy.
404not_foundUnknown model.
409idempotency_conflictSame Idempotency-Key, different body.
422model_unavailableThe model is temporarily unavailable.
429rate_limitedToo many requests, or too many tasks in progress. See Rate limits.

The full list is on Errors.