{"openapi":"3.1.0","info":{"title":"Wandura API","version":"2","description":"Start with the Jobs API: POST /v2/jobs {model, style, ...} -> job_id; the result by signed webhook_url or GET /v2/jobs/{job_id} (tag Jobs). Image, video and audio styles over HTTP. One call per result, a fixed price per lane. Failed, blocked and late Priority runs are free. The runs endpoints (POST /v2/styles/{style}, GET /v2/runs/{id}) run the same engine and stay: send an Idempotency-Key and an end_user_id on every run submit; poll status_url or verify signed webhooks."},"servers":[{"url":"https://api.wandura.ai"}],"security":[{"bearer":[]}],"x-wandura":{"limits":{"end_user_runs_per_day":20,"product_concurrency":20,"relax_active_per_end_user":1,"webhook_tolerance_s":300},"polling":{"initial_s":2,"factor":1.5,"max_s":10,"jitter_s":0.5,"relax_initial_s":10,"relax_max_s":60},"output_retention_h":72,"free_run_reasons":[{"reason":"priority_late","message":"Priority did not finish within its promise."},{"reason":"provider_error","message":"The run failed on our side."},{"reason":"bad_input","message":"An input could not be used (for example a URL that is not http(s))."},{"reason":"content_blocked","message":"The input or output was blocked by the content filter (status nsfw)."}],"errors":[{"code":"invalid_request","status":400,"retry":false,"message":"The body or a parameter is wrong; the message says which."},{"code":"idempotency_key_required","status":400,"retry":false,"message":"Send an Idempotency-Key header (max 255 chars) on every submit."},{"code":"end_user_id_required","status":400,"retry":false,"message":"end_user_id is required: the id of the person in your app this run is for."},{"code":"invalid_api_key","status":401,"retry":false,"message":"Missing, unknown or revoked API key."},{"code":"api_key_expired","status":401,"retry":false,"message":"This API key has expired. Create or rotate a key."},{"code":"wallet_empty","status":402,"retry":false,"message":"The product wallet and the org pool cannot cover this run. Add money, then retry."},{"code":"style_not_found","status":404,"retry":false,"message":"No style with that id for your org (or not public)."},{"code":"run_not_found","status":404,"retry":false,"message":"No run with that id for this product."},{"code":"not_found","status":404,"retry":false,"message":"No such endpoint."},{"code":"idempotency_key_reused","status":409,"retry":false,"message":"This Idempotency-Key was already used with a different body. Use a new key for a new run."},{"code":"run_not_cancelable","status":409,"retry":false,"message":"Only queued runs can be canceled."},{"code":"file_too_large","status":413,"retry":false,"message":"The file is over the size limit for its type."},{"code":"unsupported_content_type","status":415,"retry":false,"message":"Uploads accept JPEG, PNG, WEBP, MP4, MOV and WEBM."},{"code":"end_user_cap_reached","status":429,"retry":true,"message":"This end user reached the daily run cap. Retry after the Retry-After header (resets 00:00 UTC)."},{"code":"gateway_rate_limited","status":429,"retry":true,"message":"A gateway key's product reached its runs-per-minute limit. Retry after the Retry-After header."},{"code":"uploads_rate_limited","status":429,"retry":true,"message":"Your org reached its presigned uploads per minute. Retry after the Retry-After header."},{"code":"daily_cap_reached","status":429,"retry":false,"message":"Your product reached its daily spend cap (live charges + open holds per Vietnam calendar day, resets 00:00 UTC+7). Ask your org admin to raise the cap; nothing was created or charged."},{"code":"product_busy","status":429,"retry":true,"message":"The product is at its provider concurrency and its queue is full. Retry after the Retry-After header (about 5 s)."},{"code":"internal_error","status":500,"retry":true,"message":"Something went wrong on our side. Retry with the same Idempotency-Key; quote X-Request-Id to support."},{"code":"style_unavailable","status":503,"retry":true,"message":"This style cannot run on a live key right now (no model behind it). Test keys still return a sample."},{"code":"uploads_unavailable","status":503,"retry":true,"message":"Uploads are not available on this deployment right now."},{"code":"service_unavailable","status":503,"retry":true,"message":"The job queue is briefly unavailable; nothing was created or charged. Retry with the same Idempotency-Key after the Retry-After header."}],"integration":{"may_change":[{"field":"X-Job-Ref (header)","waits_for":"Q1","note":"Optional. Whether ai-workflow can send its job id in a header, and under which name; without it a retry is matched on the request body."},{"field":"webhook URL and headers","waits_for":"Q4","note":"Optional: ai-workflow polls status_url. No webhook URL has been given yet."}],"request":{"method":"POST","path":"/v2/styles/sty_ghibli_portrait","headers":{"Authorization":"Bearer wdr_live_<gateway key>","Content-Type":"application/json","X-Job-Ref":"job_8f3a21"},"body":{"lane":"priority","inputs":{"image_url":"https://example.com/photo.jpg"}}},"response":{"status":202,"body":{"id":"run_Qk2m9XaB7Lc4","object":"run","status":"queued","state":"queued","style":"sty_ghibli_portrait","lane":"priority","end_user_id":null,"product":"prod_lensie_app","livemode":true,"price":{"credits":5.2,"usd":0.052},"charge":"held","moderation":"on","path":"gateway","created_at":"2026-10-13T08:00:00.000Z","request_id":"run_Qk2m9XaB7Lc4","status_url":"https://api.wandura.ai/v2/runs/run_Qk2m9XaB7Lc4","cancel_url":"https://api.wandura.ai/v2/runs/run_Qk2m9XaB7Lc4/cancel"}},"replay":{"status":200,"headers":{"Idempotent-Replayed":"true"}},"poll":{"request":{"method":"GET","url":"https://api.wandura.ai/v2/runs/run_Qk2m9XaB7Lc4","headers":{"Authorization":"Bearer wdr_live_<gateway key>"}},"response":{"status":200,"body":{"id":"run_Qk2m9XaB7Lc4","object":"run","status":"completed","state":"succeeded","style":"sty_ghibli_portrait","lane":"priority","end_user_id":null,"product":"prod_lensie_app","livemode":true,"price":{"credits":5.2,"usd":0.052},"charge":"charged","moderation":"on","path":"gateway","created_at":"2026-10-13T08:00:00.000Z","request_id":"run_Qk2m9XaB7Lc4","status_url":"https://api.wandura.ai/v2/runs/run_Qk2m9XaB7Lc4","cancel_url":"https://api.wandura.ai/v2/runs/run_Qk2m9XaB7Lc4/cancel","output":[{"type":"image","url":"https://examplestorageref.supabase.co/storage/v1/object/public/outputs/2026-10-13/kJ3w9QzR1mXb7TcY0aLp4vNe.png","content_type":"image/png","width":1024,"height":1024,"duration":null,"fps":null,"size_bytes":1482113,"expires_at":"2026-10-16T08:00:21.000Z"}],"share_url":"https://api.wandura.ai/s/run_Qk2m9XaB7Lc4"}}},"rate_limited":{"status":429,"headers":{"Retry-After":"40"},"body":{"error":{"code":"gateway_rate_limited","message":"This product reached its limit of 60 runs per minute on gateway keys. Retry after the Retry-After header.","limit":60,"retry_after_s":40,"retryable":true,"docs_url":"https://docs.wandura.ai/developers/api-v2/errors#gateway-rate-limited"}}},"webhook":{"headers":{"Content-Type":"application/json","User-Agent":"Wandura-Webhooks/1","Wandura-Event-Id":"evt_h3Zk1uPq8Wd2","Wandura-Event-Type":"run.completed","Wandura-Signature":"t=1791878460,v1=<hex HMAC-SHA256(secret, t + \".\" + raw body)>"},"body":{"id":"evt_h3Zk1uPq8Wd2","object":"event","type":"run.completed","created_at":"2026-10-13T08:00:21.000Z","sequence":3,"data":{"id":"run_Qk2m9XaB7Lc4","object":"run","status":"completed","state":"succeeded","style":"sty_ghibli_portrait","lane":"priority","end_user_id":null,"product":"prod_lensie_app","livemode":true,"price":{"credits":5.2,"usd":0.052},"charge":"charged","moderation":"on","path":"gateway","created_at":"2026-10-13T08:00:00.000Z","request_id":"run_Qk2m9XaB7Lc4","status_url":"https://api.wandura.ai/v2/runs/run_Qk2m9XaB7Lc4","cancel_url":"https://api.wandura.ai/v2/runs/run_Qk2m9XaB7Lc4/cancel","output":[{"type":"image","url":"https://examplestorageref.supabase.co/storage/v1/object/public/outputs/2026-10-13/kJ3w9QzR1mXb7TcY0aLp4vNe.png","content_type":"image/png","width":1024,"height":1024,"duration":null,"fps":null,"size_bytes":1482113,"expires_at":"2026-10-16T08:00:21.000Z"}],"share_url":"https://api.wandura.ai/s/run_Qk2m9XaB7Lc4"}}}},"jobs":{"limits":{"submits_per_min_per_product":60,"product_concurrency":20,"end_user_jobs_per_day":20},"statuses":{"initial":"queued","running":"processing","final":["succeeded","failed","cancelled","timeout"]},"webhook":{"signature":"X-Signature = hex(HMAC-SHA256(secret, X-Timestamp + \".\" + raw body)); X-Timestamp = unix seconds","retry_delays_s":[30,120,600,3600,21600,86400],"attempts":7,"attempt_timeout_s":10},"errors":[{"code":"INVALID_INPUT","status":422,"retryable":false,"message":"The body or a header is wrong; details[] lists each {field, reason}."},{"code":"UNAUTHORIZED","status":401,"retryable":false,"message":"Missing, unknown, revoked or expired API key."},{"code":"INSUFFICIENT_CREDITS","status":402,"retryable":false,"message":"This product's wallet cannot cover the job. Add funds, then retry."},{"code":"FORBIDDEN","status":403,"retryable":false,"message":"The key is valid but may not do this. Reserved: not returned today."},{"code":"NOT_FOUND","status":404,"retryable":false,"message":"No such job, model or endpoint for this key."},{"code":"IDEMPOTENCY_KEY_REUSED","status":409,"retryable":false,"message":"This Idempotency-Key was already used with a different body. Use a new key for a new job."},{"code":"JOB_NOT_CANCELLABLE","status":409,"retryable":false,"message":"Only queued jobs can be cancelled."},{"code":"RATE_LIMITED","status":429,"retryable":true,"message":"Too many jobs submitted this minute for this product. Retry after the Retry-After header."},{"code":"PRODUCT_BUSY","status":429,"retryable":true,"message":"This product is at its provider concurrency and its queue is full. Retry after the Retry-After header (about 5 s)."},{"code":"END_USER_LIMIT_REACHED","status":429,"retryable":true,"message":"This end user reached the daily job cap. Retry after the Retry-After header (resets 00:00 UTC)."},{"code":"DAILY_CAP_REACHED","status":429,"retryable":false,"message":"This product reached its daily spend cap (the day resets at 00:00 Vietnam time, UTC+7). Ask your org admin to raise the cap."},{"code":"INTERNAL_ERROR","status":500,"retryable":true,"message":"Something went wrong on our side. Retry with the same Idempotency-Key."},{"code":"SERVICE_UNAVAILABLE","status":503,"retryable":true,"message":"This style cannot run on a live key right now. Test keys still return a sample. Or: The job queue is briefly unavailable; nothing was created or charged. Retry with the same Idempotency-Key after the Retry-After header."}],"job_errors":[{"code":"CONTENT_MODERATION_BLOCKED","status":"failed","retryable":false,"message":"The input or the result was blocked by content moderation. Not charged."},{"code":"INPUT_UNUSABLE","status":"failed","retryable":false,"message":"An input file could not be used (unreachable, unsupported format, or too long). Not charged."},{"code":"TIMEOUT","status":"timeout","retryable":true,"message":"The job did not finish within the maximum job time of its style. Not charged."},{"code":"GENERATION_FAILED","status":"failed","retryable":true,"message":"Generation failed on our side. Not charged; submitting again usually works."}]}},"paths":{"/v2/styles/{style}":{"post":{"operationId":"runStyle","summary":"Run a style (style in the path)","description":"Gateway keys (staff-created, for a customer's middle layer such as ai-workflow) may leave out end_user_id and the Idempotency-Key; see x-wandura.integration for the full example.","parameters":[{"in":"path","name":"style","required":true,"schema":{"type":"string"},"description":"Style id from the catalog."},{"in":"header","name":"Idempotency-Key","required":true,"schema":{"type":"string","minLength":1,"maxLength":255},"description":"Any unique string per logical request. A retry with the same key and body replays the first run instead of running twice."},{"in":"header","name":"X-Job-Ref","required":false,"schema":{"type":"string","maxLength":255},"description":"Gateway keys only (may change, #116): the caller's own job id. With no Idempotency-Key, Wandura derives one from your key and this reference, so the same job within 10 minutes replays the same run (200, Idempotent-Replayed: true). Same job with a different body: 409 idempotency_key_reused."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunRequest"},"examples":{"gateway":{"summary":"Gateway key through a middle layer (#116)","value":{"lane":"priority","inputs":{"image_url":"https://example.com/photo.jpg"}}}}}}},"responses":{"200":{"description":"Replay of an earlier request with the same Idempotency-Key (header Idempotent-Replayed: true).","headers":{"X-Concurrency-Limit":{"schema":{"type":"integer"},"description":"Your product's concurrency slots (runs at once). Its queue holds as many again."},"X-Concurrency-Running":{"schema":{"type":"integer"},"description":"Runs of your product using a slot right now."},"X-Concurrency-Waiting":{"schema":{"type":"integer"},"description":"Priority runs of your product queued for a slot. When Running and Waiting both reach Limit, new priority runs are refused (429 product_busy)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Run"}}}},"202":{"description":"Accepted: the run is queued. Poll status_url or wait for your webhook.","headers":{"X-Concurrency-Limit":{"schema":{"type":"integer"},"description":"Your product's concurrency slots (runs at once). Its queue holds as many again."},"X-Concurrency-Running":{"schema":{"type":"integer"},"description":"Runs of your product using a slot right now."},"X-Concurrency-Waiting":{"schema":{"type":"integer"},"description":"Priority runs of your product queued for a slot. When Running and Waiting both reach Limit, new priority runs are refused (429 product_busy)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Run"},"examples":{"gateway":{"summary":"Gateway key through a middle layer (#116)","value":{"id":"run_Qk2m9XaB7Lc4","object":"run","status":"queued","state":"queued","style":"sty_ghibli_portrait","lane":"priority","end_user_id":null,"product":"prod_lensie_app","livemode":true,"price":{"credits":5.2,"usd":0.052},"charge":"held","moderation":"on","path":"gateway","created_at":"2026-10-13T08:00:00.000Z","request_id":"run_Qk2m9XaB7Lc4","status_url":"https://api.wandura.ai/v2/runs/run_Qk2m9XaB7Lc4","cancel_url":"https://api.wandura.ai/v2/runs/run_Qk2m9XaB7Lc4/cancel"}}}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"429":{"description":"This end user reached the daily cap (end_user_cap_reached): retry after the Retry-After header (seconds). Or daily_cap_reached: your product reached its daily spend cap; ask your org admin to raise it (no Retry-After).","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until the cap resets."},"X-Concurrency-Limit":{"schema":{"type":"integer"},"description":"Your product's concurrency slots (runs at once). Its queue holds as many again."},"X-Concurrency-Running":{"schema":{"type":"integer"},"description":"Runs of your product using a slot right now."},"X-Concurrency-Waiting":{"schema":{"type":"integer"},"description":"Priority runs of your product queued for a slot. When Running and Waiting both reach Limit, new priority runs are refused (429 product_busy)."},"Wandura-Error-Code":{"schema":{"type":"string"},"description":"The same value as error.code."},"Wandura-Should-Retry":{"schema":{"type":"string","enum":["true","false"]},"description":"The same value as error.retryable: retry the same request later (with the same Idempotency-Key) only when true."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"gateway_rate_limited":{"summary":"Gateway key through a middle layer (#116)","value":{"error":{"code":"gateway_rate_limited","message":"This product reached its limit of 60 runs per minute on gateway keys. Retry after the Retry-After header.","limit":60,"retry_after_s":40,"retryable":true,"docs_url":"https://docs.wandura.ai/developers/api-v2/errors#gateway-rate-limited"}}}}}}}}}},"/v2/runs":{"post":{"operationId":"createRun","summary":"Run a style (style in the body)","parameters":[{"in":"header","name":"Idempotency-Key","required":true,"schema":{"type":"string","minLength":1,"maxLength":255},"description":"Any unique string per logical request. A retry with the same key and body replays the first run instead of running twice."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRunRequest"}}}},"responses":{"200":{"description":"Replay of an earlier request with the same Idempotency-Key (header Idempotent-Replayed: true).","headers":{"X-Concurrency-Limit":{"schema":{"type":"integer"},"description":"Your product's concurrency slots (runs at once). Its queue holds as many again."},"X-Concurrency-Running":{"schema":{"type":"integer"},"description":"Runs of your product using a slot right now."},"X-Concurrency-Waiting":{"schema":{"type":"integer"},"description":"Priority runs of your product queued for a slot. When Running and Waiting both reach Limit, new priority runs are refused (429 product_busy)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Run"}}}},"202":{"description":"Accepted: the run is queued. Poll status_url or wait for your webhook.","headers":{"X-Concurrency-Limit":{"schema":{"type":"integer"},"description":"Your product's concurrency slots (runs at once). Its queue holds as many again."},"X-Concurrency-Running":{"schema":{"type":"integer"},"description":"Runs of your product using a slot right now."},"X-Concurrency-Waiting":{"schema":{"type":"integer"},"description":"Priority runs of your product queued for a slot. When Running and Waiting both reach Limit, new priority runs are refused (429 product_busy)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Run"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"402":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"429":{"description":"This end user reached the daily cap (end_user_cap_reached): retry after the Retry-After header (seconds). Or daily_cap_reached: your product reached its daily spend cap; ask your org admin to raise it (no Retry-After).","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until the cap resets."},"X-Concurrency-Limit":{"schema":{"type":"integer"},"description":"Your product's concurrency slots (runs at once). Its queue holds as many again."},"X-Concurrency-Running":{"schema":{"type":"integer"},"description":"Runs of your product using a slot right now."},"X-Concurrency-Waiting":{"schema":{"type":"integer"},"description":"Priority runs of your product queued for a slot. When Running and Waiting both reach Limit, new priority runs are refused (429 product_busy)."},"Wandura-Error-Code":{"schema":{"type":"string"},"description":"The same value as error.code."},"Wandura-Should-Retry":{"schema":{"type":"string","enum":["true","false"]},"description":"The same value as error.retryable: retry the same request later (with the same Idempotency-Key) only when true."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v2/quote":{"post":{"operationId":"quote","summary":"Price and ETA before running (same body as a run; nothing is charged)","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteRequest"}}}},"responses":{"200":{"description":"Price for this lane.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Quote"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"}}}},"/v2/runs/{id}":{"get":{"operationId":"getRun","summary":"Run status and output (the status_url)","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"},"description":"The request_id from the submit response."}],"responses":{"200":{"description":"The run.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Run"},"examples":{"gateway":{"summary":"Gateway key through a middle layer (#116)","value":{"id":"run_Qk2m9XaB7Lc4","object":"run","status":"completed","state":"succeeded","style":"sty_ghibli_portrait","lane":"priority","end_user_id":null,"product":"prod_lensie_app","livemode":true,"price":{"credits":5.2,"usd":0.052},"charge":"charged","moderation":"on","path":"gateway","created_at":"2026-10-13T08:00:00.000Z","request_id":"run_Qk2m9XaB7Lc4","status_url":"https://api.wandura.ai/v2/runs/run_Qk2m9XaB7Lc4","cancel_url":"https://api.wandura.ai/v2/runs/run_Qk2m9XaB7Lc4/cancel","output":[{"type":"image","url":"https://examplestorageref.supabase.co/storage/v1/object/public/outputs/2026-10-13/kJ3w9QzR1mXb7TcY0aLp4vNe.png","content_type":"image/png","width":1024,"height":1024,"duration":null,"fps":null,"size_bytes":1482113,"expires_at":"2026-10-16T08:00:21.000Z"}],"share_url":"https://api.wandura.ai/s/run_Qk2m9XaB7Lc4"}}}}}},"401":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"}}}},"/v2/metrics":{"get":{"operationId":"getMetrics","summary":"Request counts, failures, fallback rate and latency per style and day","description":"Every run of your org (test runs too unless livemode narrows), by UTC day. Window: from/to inclusive, default the last 30 days, at most 90. `failed` is runs that ended failed; `failures` counts free runs by reason (content_blocked and canceled are not in `failed`). `success_rate` = completed / (completed + failed) in percent; `failure_rate` is its complement. `fallback_runs` = runs that needed a backup model after the first failed; `fallback_rate` = fallback_runs / runs that reached a model. p50_ms / p90_ms = created to finished of completed runs. Cached for 60 s.","parameters":[{"in":"query","name":"from","schema":{"type":"string","format":"date"},"description":"First UTC day (inclusive). Default: 29 days before `to`."},{"in":"query","name":"to","schema":{"type":"string","format":"date"},"description":"Last UTC day (inclusive). Default: today."},{"in":"query","name":"style","schema":{"type":"string"},"description":"Only this style id."},{"in":"query","name":"group_by","schema":{"type":"string","enum":["day","style","day_style"],"default":"day"}},{"in":"query","name":"path","schema":{"type":"string","enum":["gateway","direct","jobs","standard"]}},{"in":"query","name":"livemode","schema":{"type":"string","enum":["true","false"]},"description":"Only live or only test runs."}],"responses":{"200":{"description":"Metrics rows.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["metrics"]},"org":{"type":"string"},"from":{"type":"string"},"to":{"type":"string"},"group_by":{"type":"string"},"data":{"type":"array","items":{"type":"object","properties":{"day":{"type":"string","description":"Present for group_by day and day_style."},"style":{"type":"string","description":"Present for group_by style and day_style."},"runs":{"type":"integer"},"completed":{"type":"integer"},"failed":{"type":"integer"},"failures":{"type":"object","properties":{"provider_error":{"type":"integer"},"priority_late":{"type":"integer"},"bad_input":{"type":"integer"},"content_blocked":{"type":"integer"},"canceled":{"type":"integer"}}},"success_rate":{"type":["number","null"]},"failure_rate":{"type":["number","null"]},"fallback_runs":{"type":"integer"},"fallback_rate":{"type":["number","null"]},"p50_ms":{"type":["integer","null"]},"p90_ms":{"type":["integer","null"]}}}}}}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"}}}},"/v2/queue":{"get":{"operationId":"getQueue","summary":"Your product's live queue: slots, running, waiting, runs this minute, refusals","description":"The product and mode of your key. `concurrency_limit` = your concurrency slots; `queue_limit` = how many priority runs may wait for one (the same number). When running and waiting both reach it, new priority runs are refused (429 product_busy). `per_minute_limit` applies to gateway keys and the Jobs API (429 gateway_rate_limited / RATE_LIMITED); `accepted_this_minute` = the runs counted against it since the start of the current UTC minute. `refusals` = refused submits in the last 15 minutes and 24 hours (see GET /v2/queue/refusals). Test and live keys count apart.","responses":{"200":{"description":"The live queue state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueueState"}}}},"401":{"$ref":"#/components/responses/Error"}}}},"/v2/queue/refusals":{"get":{"operationId":"listQueueRefusals","summary":"Your product's refused submits, newest first","description":"Each submit refused because your product was at its limits: `product_busy` (slots and queue full) or `rate_limited` (the per-minute run limit). No run was created and nothing was charged. A per-end-user daily cap is not a refusal. Page with `before` = the `id` of the last item of the previous page.","parameters":[{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"in":"query","name":"before","schema":{"type":"string"},"description":"The id of the last refusal of the previous page."}],"responses":{"200":{"description":"Refusals, newest first.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/QueueRefusal"}},"has_more":{"type":"boolean"}}}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"}}}},"/v2/queue/log":{"get":{"operationId":"getQueueLog","summary":"Your product's queue log: per minute or per hour, accepted runs, refusals, load, wait and run time","description":"The product and mode of your key, oldest bucket first. Per bucket: runs accepted, refusals by reason, the most runs running and waiting at once, your concurrency slots, the runs that ended, the median wait before start (priority runs) and the median run time (start to end) of the runs that ended in it. Buckets with no activity are left out (all zero). The current minute shows up about a minute after it ends; the current hour fills as it goes. Minutes are kept 7 days, hours 90 days. Test and live keys count apart.","parameters":[{"in":"query","name":"granularity","schema":{"type":"string","enum":["minute","hour"],"default":"minute"}},{"in":"query","name":"from","schema":{"type":"string","format":"date-time"},"description":"Start (included; a time inside a bucket includes that bucket). Default: 24 hours before `to`."},{"in":"query","name":"to","schema":{"type":"string","format":"date-time"},"description":"End (excluded). Default: now. A range spans at most 7 days in minutes, 90 days in hours."}],"responses":{"200":{"description":"The buckets of the range.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"const":"list"},"product":{"type":"string"},"livemode":{"type":"boolean"},"granularity":{"type":"string","enum":["minute","hour"]},"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"},"data":{"type":"array","items":{"$ref":"#/components/schemas/QueueLogBucket"}}}}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"}}}},"/v2/queue/episodes":{"get":{"operationId":"listQueueEpisodes","summary":"Your product's congestion episodes, newest first","description":"A congestion episode is a stretch of time your product was in one kind of congestion: `refusing` (one or more refusals in the last 5 minutes; ends after 10 minutes with none), `near_full` (80% or more of your concurrency slots in use for 1 minute; ends below 60% for 5 minutes) or `slow` (at least 3 runs ended in the last 15 minutes with a median run time over 10 minutes; ends when it is back under 8 minutes). The same kind does not start again within 30 minutes of its end. Live keys only: test keys never start one. Each start and end is also a webhook event (queue.congestion_started / queue.congestion_ended) if your endpoint subscribes. Page with `before` = the `id` of the last item of the previous page.","parameters":[{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"in":"query","name":"before","schema":{"type":"string"},"description":"The id of the last episode of the previous page."}],"responses":{"200":{"description":"Episodes, newest first.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/CongestionEpisode"}},"has_more":{"type":"boolean"}}}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"}}}},"/v2/runs/{id}/cancel":{"post":{"operationId":"cancelRun","summary":"Cancel a queued run (the cancel_url)","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"},"description":"The request_id from the submit response."}],"responses":{"200":{"description":"Canceled; the hold is released.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Run"}}}},"401":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"}}}},"/v2/runs/{id}/share":{"post":{"operationId":"shareRunToLibrary","summary":"Share this completed run's outputs to the Wandura library (private unless you call this)","description":"consent: true is required: the library may display the output, offer it as a free download and use it to recreate similar videos inside the Wandura library; never for model training. Shared outputs are safety-checked first (status pending, then approved = listed, or held for a reviewer); library.item_published fires when an item is listed. Item ids are <run id>.<output index>.","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"},"description":"The request_id from the submit response."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["consent"],"properties":{"consent":{"const":true},"outputs":{"type":"array","items":{"type":"integer"},"description":"Output indexes to share; default all."},"title":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}}}}}}},"responses":{"200":{"description":"The shared items and their state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LibraryShare"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"}}},"delete":{"operationId":"unshareRunFromLibrary","summary":"Take this run's outputs out of the library (library.item_removed fires; copies already downloaded stay with whoever took them)","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"},"description":"The request_id from the submit response."}],"responses":{"200":{"description":"Unshared.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LibraryShare"}}}},"401":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"}}}},"/v2/uploads":{"post":{"operationId":"createUpload","summary":"Presigned URL for one input file","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadRequest"}}}},"responses":{"201":{"description":"PUT the bytes to upload_url with the given headers, then pass file_url in inputs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Upload"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"413":{"$ref":"#/components/responses/Error"},"415":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/Error"},"503":{"$ref":"#/components/responses/Error"}}},"get":{"operationId":"listUploads","summary":"Your org's own recent uploads, newest first (never another org's)","parameters":[{"in":"query","name":"kind","schema":{"type":"string","enum":["image","video"]}},{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":100},"description":"1-100, default 30."}],"responses":{"200":{"description":"Uploads whose files are still stored (a file_url works as a run input until expires_at).","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"const":"list"},"data":{"type":"array","items":{"type":"object","properties":{"object":{"const":"upload"},"file_url":{"type":"string","format":"uri"},"content_type":{"type":"string"},"kind":{"type":"string"},"size_bytes":{"type":"integer"},"created_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"}}}}}}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"}}}},"/v2/workflows":{"get":{"operationId":"listWorkflows","summary":"Styles your key can run (shared + your org's own)","responses":{"200":{"description":"Styles.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Workflow"}}}}}}},"401":{"$ref":"#/components/responses/Error"}}}},"/v2/public/styles":{"get":{"operationId":"listPublicStyles","summary":"Public catalog of shared styles (no key)","security":[],"responses":{"200":{"description":"Public catalog of shared styles (no key)","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/PublicStyle"}}}}}}},"404":{"$ref":"#/components/responses/Error"}}}},"/v2/public/workflows":{"get":{"operationId":"listPublicWorkflows","summary":"Public workflows that take no style (Wandura C1.1): inputs and price per output second by resolution (no key)","security":[],"responses":{"200":{"description":"Public workflows that take no style (Wandura C1.1): inputs and price per output second by resolution (no key)","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"const":"list"},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The model id to send to POST /v2/jobs."},"object":{"const":"workflow"},"name":{"type":"string"},"output":{"type":"string","enum":["image","video","audio"]},"description":{"type":"string"},"inputs":{"type":"object","additionalProperties":{"type":"object"},"description":"Each body field of POST /v2/jobs for this model: its JSON Schema (as GET /v2/models/{model_id}/schema) plus required."},"price_per_second":{"type":"object","additionalProperties":{"type":"object","properties":{"credits":{"type":"number"},"usd":{"type":"number"}}},"description":"By resolution. A job holds it for the requested seconds and is charged for the seconds delivered."},"max_job_seconds":{"type":"integer"}}}}}}}}},"404":{"$ref":"#/components/responses/Error"}}}},"/v2/public/styles/{id}":{"get":{"operationId":"getPublicStyle","summary":"One public style with its inputs schema (no key)","security":[],"responses":{"200":{"description":"One public style with its inputs schema (no key)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicStyleDetail"}}}},"404":{"$ref":"#/components/responses/Error"}},"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}]}},"/v2/public/styles/{id}/openapi.json":{"get":{"operationId":"getPublicStyleOpenapi","summary":"OpenAPI 3.1 for one style, with its exact inputs (no key)","security":[],"responses":{"200":{"description":"OpenAPI 3.1 for one style, with its exact inputs (no key)","content":{"application/json":{"schema":{"type":"object"}}}},"404":{"$ref":"#/components/responses/Error"}},"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}]}},"/v2/public/styles/{id}/examples":{"get":{"operationId":"getPublicStyleExamples","summary":"Sample outputs of one style (no key)","security":[],"responses":{"200":{"description":"Sample outputs of one style (no key)","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"const":"list"},"data":{"type":"array","items":{"type":"object","properties":{"object":{"const":"example"},"output":{"type":"string"},"url":{"type":"string","format":"uri"}}}}}}}}},"404":{"$ref":"#/components/responses/Error"}},"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}]}},"/v2/content":{"get":{"operationId":"listContent","summary":"Content library: templates, hooks, scenes, creators, music, skills, formulas (no key for public items); films and episodes metadata only with a key whose org is linked to an entitled partner org","security":[{},{"bearer":[]}],"parameters":[{"in":"query","name":"type","schema":{"type":"string"},"description":"template, hook, scene, creator, music, skill or formula."},{"in":"query","name":"q","schema":{"type":"string"},"description":"Search words in title, description and tags."},{"in":"query","name":"category","schema":{"type":"string"},"description":"Exact category."},{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":200},"description":"1-200, default 50."},{"in":"query","name":"offset","schema":{"type":"integer","minimum":0},"description":"Default 0."}],"responses":{"200":{"description":"Items, from a cache of the main app's library.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/ContentItem"}},"total":{"type":"integer"},"has_more":{"type":"boolean"}}}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"}}}},"/v2/content/{id}":{"get":{"operationId":"getContent","summary":"One content item (no key for public items; films and episodes need an entitled key)","security":[{},{"bearer":[]}],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"},"description":"For example template:aurelis-arrow-diamond-teaser."}],"responses":{"200":{"description":"The item.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentItem"}}}},"401":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"}}}},"/v2/library":{"get":{"operationId":"listLibrary","summary":"Shared-output library: videos creators shared with Wandura. No key. Free to watch and download","security":[],"parameters":[{"in":"query","name":"kind","schema":{"type":"string"},"description":"cinematic_ad or supermagic."},{"in":"query","name":"q","schema":{"type":"string"},"description":"Search words in title and tags."},{"in":"query","name":"aspect","schema":{"type":"string"},"description":"For example 9:16."},{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":200},"description":"1-200, default 50."},{"in":"query","name":"offset","schema":{"type":"integer","minimum":0},"description":"Default 0."}],"responses":{"200":{"description":"Items, from a cache of the main app's library.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/LibraryItem"}},"total":{"type":"integer"},"has_more":{"type":"boolean"}}}}}},"400":{"$ref":"#/components/responses/Error"}}}},"/v2/library/{id}":{"get":{"operationId":"getLibraryItem","summary":"One library item (no key)","security":[],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The item.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LibraryItem"}}}},"404":{"$ref":"#/components/responses/Error"}}}},"/v2/library/{id}/preview":{"get":{"operationId":"previewLibraryItem","summary":"Redirects to a one-hour link to play the video (no key)","security":[],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"302":{"description":"Redirect to the signed link."},"404":{"$ref":"#/components/responses/Error"}}}},"/v2/library/{id}/download":{"post":{"operationId":"downloadLibraryItem","summary":"Free download: a signed link valid 10 minutes to the original file; the download is logged. Key optional (attributes it to your org)","security":[{},{"bearer":[]}],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The link.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"const":"library_download"},"id":{"type":"string"},"url":{"type":"string","format":"uri"},"expires_at":{"type":"string","format":"date-time"},"free":{"const":true}}}}}},"401":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"}}}},"/v2/library/{id}/report":{"post":{"operationId":"reportLibraryItem","summary":"Report a library item (not mine, adult, real person, copyright, other). Staff hide or take it down, target within 48 hours. Key optional","security":[{},{"bearer":[]}],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["reason"],"properties":{"reason":{"type":"string","enum":["not_mine","adult","real_person","copyright","other"]},"note":{"type":"string","maxLength":1000}}}}}},"responses":{"202":{"description":"Report received.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"const":"library_report"},"id":{"type":["string","null"]},"status":{"const":"open"},"takedown_due_at":{"type":"string","format":"date-time"}}}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"}}}},"/v2/openapi.json":{"get":{"operationId":"getOpenapi","summary":"This document (no key)","security":[],"responses":{"200":{"description":"OpenAPI 3.1.","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/v2/jobs":{"post":{"operationId":"createJob","summary":"Submit a job","description":"Validated at once (422 INVALID_INPUT with details). The job runs in the background; the answer comes in well under 3 s.","parameters":[{"in":"header","name":"Idempotency-Key","required":false,"schema":{"type":"string","minLength":1,"maxLength":255},"description":"Same key + same body = the same job (200, Idempotent-Replayed: true); same key + other body = 409. Without it, Wandura derives one from your key and the body: the same body within 10 minutes is the same job."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobRequest"}}}},"responses":{"200":{"description":"Replay of an earlier submit (header Idempotent-Replayed: true); status is the job's current one.","headers":{"X-Concurrency-Limit":{"schema":{"type":"integer"},"description":"Your product's concurrency slots (runs at once). Its queue holds as many again."},"X-Concurrency-Running":{"schema":{"type":"integer"},"description":"Runs of your product using a slot right now."},"X-Concurrency-Waiting":{"schema":{"type":"integer"},"description":"Priority runs of your product queued for a slot. When Running and Waiting both reach Limit, new priority runs are refused (429 product_busy)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Submitted"}}}},"202":{"description":"Accepted.","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Job submits allowed per minute for this product (per key mode)."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Submits left in the current minute."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix seconds when the current minute window ends."},"X-Concurrency-Limit":{"schema":{"type":"integer"},"description":"Your product's concurrency slots (runs at once). Its queue holds as many again."},"X-Concurrency-Running":{"schema":{"type":"integer"},"description":"Runs of your product using a slot right now."},"X-Concurrency-Waiting":{"schema":{"type":"integer"},"description":"Priority runs of your product queued for a slot. When Running and Waiting both reach Limit, new priority runs are refused (429 product_busy)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Submitted"}}}},"401":{"$ref":"#/components/responses/JobsApiErrorResponse"},"402":{"$ref":"#/components/responses/JobsApiErrorResponse"},"409":{"$ref":"#/components/responses/JobsApiErrorResponse"},"422":{"$ref":"#/components/responses/JobsApiErrorResponse"},"429":{"description":"RATE_LIMITED, PRODUCT_BUSY or END_USER_LIMIT_REACHED (retry after Retry-After), or DAILY_CAP_REACHED (not retryable: ask your org admin to raise the product's daily spend cap).","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait."},"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Job submits allowed per minute for this product (per key mode)."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Submits left in the current minute."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix seconds when the current minute window ends."},"X-Concurrency-Limit":{"schema":{"type":"integer"},"description":"Your product's concurrency slots (runs at once). Its queue holds as many again."},"X-Concurrency-Running":{"schema":{"type":"integer"},"description":"Runs of your product using a slot right now."},"X-Concurrency-Waiting":{"schema":{"type":"integer"},"description":"Priority runs of your product queued for a slot. When Running and Waiting both reach Limit, new priority runs are refused (429 product_busy)."},"Wandura-Error-Code":{"schema":{"type":"string"},"description":"The same value as error.code."},"Wandura-Should-Retry":{"schema":{"type":"string","enum":["true","false"]},"description":"The same value as error.retryable: retry the same request later (with the same Idempotency-Key) only when true."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobsApiError"}}}},"500":{"$ref":"#/components/responses/JobsApiErrorResponse"},"503":{"$ref":"#/components/responses/JobsApiErrorResponse"}},"tags":["Jobs"]}},"/v2/jobs/{job_id}":{"get":{"operationId":"getJob","summary":"Job status and output (same body as the webhook)","parameters":[{"in":"path","name":"job_id","required":true,"schema":{"type":"string"},"description":"job_id from the submit response."}],"responses":{"200":{"description":"The job. Works for as long as the job exists (kept well past 7 days).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"401":{"$ref":"#/components/responses/JobsApiErrorResponse"},"404":{"$ref":"#/components/responses/JobsApiErrorResponse"}},"tags":["Jobs"]}},"/v2/jobs/{job_id}/cancel":{"post":{"operationId":"cancelJob","summary":"Cancel a queued job","parameters":[{"in":"path","name":"job_id","required":true,"schema":{"type":"string"},"description":"job_id from the submit response."}],"responses":{"200":{"description":"Cancelled (status cancelled, not charged); the webhook fires too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"401":{"$ref":"#/components/responses/JobsApiErrorResponse"},"404":{"$ref":"#/components/responses/JobsApiErrorResponse"},"409":{"description":"JOB_NOT_CANCELLABLE: the job is already processing or has ended.","headers":{"Wandura-Error-Code":{"schema":{"type":"string"},"description":"The same value as error.code."},"Wandura-Should-Retry":{"schema":{"type":"string","enum":["true","false"]},"description":"The same value as error.retryable: retry the same request later (with the same Idempotency-Key) only when true."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobsApiError"}}}}},"tags":["Jobs"]}},"/v2/models":{"get":{"operationId":"listModels","summary":"Models (versioned): one per output kind over the styles, plus workflows that take no style (wandura-c1-1)","responses":{"200":{"description":"Models.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Model"}}}}}}},"401":{"$ref":"#/components/responses/JobsApiErrorResponse"}},"tags":["Jobs"]}},"/v2/models/{model_id}":{"get":{"operationId":"getModel","summary":"One model","parameters":[{"in":"path","name":"model_id","required":true,"schema":{"type":"string","enum":["wandura-image-v1","wandura-video-v1","wandura-c1-1"]}}],"responses":{"200":{"description":"The model.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Model"}}}},"401":{"$ref":"#/components/responses/JobsApiErrorResponse"},"404":{"$ref":"#/components/responses/JobsApiErrorResponse"}},"tags":["Jobs"]}},"/v2/models/{model_id}/schema":{"get":{"operationId":"getModelSchema","summary":"JSON Schema (2020-12) of POST /v2/jobs for this model","parameters":[{"in":"path","name":"model_id","required":true,"schema":{"type":"string","enum":["wandura-image-v1","wandura-video-v1","wandura-c1-1"]}}],"description":"Types, required fields, defaults, min/max, lengths, enums (style ids) and per-style rules (if/then: which fields a style requires and which it does not take).","responses":{"200":{"description":"JSON Schema.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"$ref":"#/components/responses/JobsApiErrorResponse"},"404":{"$ref":"#/components/responses/JobsApiErrorResponse"}},"tags":["Jobs"]}},"/v2/styles":{"get":{"operationId":"listStyles","summary":"Live styles with preview URLs","parameters":[{"in":"query","name":"model","required":false,"schema":{"type":"string","enum":["wandura-image-v1","wandura-video-v1","wandura-c1-1"]}}],"responses":{"200":{"description":"Styles.","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Style"}}}}}}},"401":{"$ref":"#/components/responses/JobsApiErrorResponse"},"422":{"$ref":"#/components/responses/JobsApiErrorResponse"}},"tags":["Jobs"]}}},"webhooks":{"run.event":{"post":{"summary":"A run changed status (POSTed to your product's webhook URL)","description":"Verify before trusting: HMAC-SHA256 of \"<t>.<raw body>\" with your webhook secret must equal v1, and t must be within 300 s of now. Answer 2xx fast; non-2xx is retried (30 s, 2 min, 10 min, 1 h, 6 h, 24 h). Dedupe on Wandura-Event-Id; keep the highest sequence per run.","parameters":[{"in":"header","name":"Wandura-Signature","required":true,"schema":{"type":"string"},"description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, t + \".\" + raw body)>"},{"in":"header","name":"Wandura-Event-Id","required":true,"schema":{"type":"string"},"description":"evt_…, the same on every retry of this event."},{"in":"header","name":"Wandura-Event-Type","required":true,"schema":{"type":"string"},"description":"Same as type in the body."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Event"},"examples":{"completed":{"summary":"A gateway run that completed (#116)","value":{"id":"evt_h3Zk1uPq8Wd2","object":"event","type":"run.completed","created_at":"2026-10-13T08:00:21.000Z","sequence":3,"data":{"id":"run_Qk2m9XaB7Lc4","object":"run","status":"completed","state":"succeeded","style":"sty_ghibli_portrait","lane":"priority","end_user_id":null,"product":"prod_lensie_app","livemode":true,"price":{"credits":5.2,"usd":0.052},"charge":"charged","moderation":"on","path":"gateway","created_at":"2026-10-13T08:00:00.000Z","request_id":"run_Qk2m9XaB7Lc4","status_url":"https://api.wandura.ai/v2/runs/run_Qk2m9XaB7Lc4","cancel_url":"https://api.wandura.ai/v2/runs/run_Qk2m9XaB7Lc4/cancel","output":[{"type":"image","url":"https://examplestorageref.supabase.co/storage/v1/object/public/outputs/2026-10-13/kJ3w9QzR1mXb7TcY0aLp4vNe.png","content_type":"image/png","width":1024,"height":1024,"duration":null,"fps":null,"size_bytes":1482113,"expires_at":"2026-10-16T08:00:21.000Z"}],"share_url":"https://api.wandura.ai/s/run_Qk2m9XaB7Lc4"}}}}}}},"responses":{"200":{"description":"Any 2xx acknowledges the event."}}}},"account.event":{"post":{"summary":"Your wallet, an API key or your queue needs attention (wallet.low, wallet.empty, wallet.topup_failed, key.expiring, key.revoked, library.item_published, library.item_removed, queue.congestion_started, queue.congestion_ended)","description":"Same signature, retries, Wandura-Event-Id and delivery log as run.event; opt in by listing the type in `events` of your product webhook. wallet.low fires once each time a product wallet drops below its line (re-arms when the balance goes back above); wallet.empty on the first 402 wallet_empty after the wallet had money (once per empty period); wallet.topup_failed when an auto top-up could not be filled (pool) or charged (card); key.expiring 7 days and 1 day before an API key expires; key.revoked on revoke and on rotate (reason); queue.congestion_started / queue.congestion_ended once when a congestion episode of your product starts and once when it ends (live keys only; see GET /v2/queue/episodes), checked every minute.","parameters":[{"in":"header","name":"Wandura-Signature","required":true,"schema":{"type":"string"},"description":"t=<unix seconds>,v1=<hex HMAC-SHA256(secret, t + \".\" + raw body)>"},{"in":"header","name":"Wandura-Event-Id","required":true,"schema":{"type":"string"},"description":"evt_…, the same on every retry of this event."},{"in":"header","name":"Wandura-Event-Type","required":true,"schema":{"type":"string"},"description":"Same as type in the body."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountEvent"},"examples":{"low":{"summary":"A wallet crossed below its low-balance line","value":{"id":"evt_x3J9aQ0bW1k","object":"event","type":"wallet.low","created_at":"2026-10-10T08:15:00.000Z","data":{"object":"wallet","org":"org_acme","product":"prod_app","available_usd":4.948,"threshold_usd":5}}}}}}},"responses":{"200":{"description":"Any 2xx acknowledges the event."}}}},"job.ended":{"post":{"summary":"A job reached a final status (POSTed to the webhook_url of its submit)","description":"Body = exactly GET /v2/jobs/{job_id} at that moment. Verify: hex HMAC-SHA256 of X-Timestamp + \".\" + raw body with your product's signing secret (its Jobs API signing secret, or, when it has none, its Webhook endpoint's signing secret) equals X-Signature, and X-Timestamp is within 5 minutes of now. Answer 2xx within 10 s; a non-2xx or timeout is retried after 30 s, 2 min, 10 min, 1 h, 6 h, 24 h (7 attempts). At least once: dedupe on job_id (or Wandura-Event-Id). Only final statuses are sent.","parameters":[{"in":"header","name":"X-Signature","required":true,"schema":{"type":"string"},"description":"hex(HMAC-SHA256(secret, X-Timestamp + \".\" + raw body))"},{"in":"header","name":"X-Timestamp","required":true,"schema":{"type":"string"},"description":"Unix seconds when this attempt was signed."},{"in":"header","name":"Wandura-Event-Id","required":true,"schema":{"type":"string"},"description":"evt_…, the same on every retry."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"responses":{"200":{"description":"Any 2xx acknowledges."}}}}},"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","description":"Your API key (wdr_test_… returns a sample for $0, wdr_live_… runs for real). Keep it on your server."}},"responses":{"Error":{"description":"Error. Branch on error.code; X-Request-Id identifies the request.","headers":{"Wandura-Error-Code":{"schema":{"type":"string"},"description":"The same value as error.code."},"Wandura-Should-Retry":{"schema":{"type":"string","enum":["true","false"]},"description":"The same value as error.retryable: retry the same request later (with the same Idempotency-Key) only when true."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"JobsApiErrorResponse":{"description":"Error. Branch on error.code; X-Request-Id identifies the request.","headers":{"Wandura-Error-Code":{"schema":{"type":"string"},"description":"The same value as error.code."},"Wandura-Should-Retry":{"schema":{"type":"string","enum":["true","false"]},"description":"The same value as error.retryable: retry the same request later (with the same Idempotency-Key) only when true."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobsApiError"}}}}},"schemas":{"Price":{"type":"object","required":["credits","usd"],"properties":{"credits":{"type":"number","description":"1 credit = $0.01"},"usd":{"type":"number"}}},"Quote":{"type":"object","properties":{"style":{"type":"string"},"lane":{"$ref":"#/components/schemas/Lane"},"price":{"$ref":"#/components/schemas/Price"},"resolution":{"type":"string","description":"The output resolution this price is for (only styles that offer a choice)."},"eta_s":{"type":["integer","null"]},"promise":{"type":"string"},"livemode":{"type":"boolean"}}},"Lane":{"type":"string","enum":["priority","relax"],"description":"priority: done within the style's promise or free. relax: waits in line with no deadline, one active run per end user; never priced above priority."},"Run":{"type":"object","required":["id","object","status","style","lane","end_user_id","livemode","price","charge","created_at"],"properties":{"id":{"type":"string"},"request_id":{"type":"string","description":"Same as id."},"object":{"const":"run"},"status":{"type":"string","enum":["queued","running","completed","failed","nsfw","canceled"],"description":"completed, failed, nsfw and canceled are final."},"state":{"type":"string","enum":["queued","processing","succeeded","failed","cancelled","timeout"],"description":"The same status in the words the Jobs API uses (one vocabulary for both): running = processing, completed = succeeded, nsfw = failed (error CONTENT_MODERATION_BLOCKED), canceled = cancelled, failed past the promise = timeout."},"status_url":{"type":"string","format":"uri"},"cancel_url":{"type":"string","format":"uri"},"style":{"type":"string"},"lane":{"$ref":"#/components/schemas/Lane"},"end_user_id":{"type":["string","null"],"description":"Null only for a run made with a gateway key that sent none."},"product":{"type":"string"},"livemode":{"type":"boolean"},"price":{"$ref":"#/components/schemas/Price"},"charge":{"type":"string","enum":["held","charged","released","none"],"description":"held while running; charged on completed; released when free; none for test keys."},"queue_position":{"type":"integer"},"output":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["image","video","audio"]},"url":{"type":"string","format":"uri","description":"A Wandura storage link (or, if the copy failed, a 24 h Wandura link): download it directly."},"content_type":{"type":["string","null"],"description":"MIME type read from the file, e.g. image/png, video/mp4."},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]},"duration":{"type":["number","null"],"description":"Seconds (video, audio)."},"fps":{"type":["number","null"]},"size_bytes":{"type":["integer","null"]},"expires_at":{"type":"string","format":"date-time","description":"The link works at least until this (7 days after the run ends; 24 h for a fallback link). Keep a copy if you need the file longer."}}},"description":"Files in order. Each link works at least 7 days (`expires_at`); copy what you need longer. Metadata is null when it could not be read."},"share_url":{"type":"string","format":"uri"},"path":{"type":"string","enum":["gateway","direct","jobs"],"description":"How the run came in: gateway (a gateway key), direct (the Lensie-compatible endpoints) or jobs (the /v1 Jobs API). Absent on ordinary calls."},"moderation":{"type":"string","enum":["on","off"],"description":"Whether the product's content filters were on or off when this run was created."},"free_run":{"type":"object","properties":{"reason":{"type":"string","enum":["priority_late","provider_error","bad_input","content_blocked"]}}},"error":{"type":"object","required":["code","message","retryable"],"description":"Only when state is failed or timeout: why, in the Jobs API words. The request itself succeeded; this is not an HTTP error.","properties":{"code":{"type":"string","enum":["CONTENT_MODERATION_BLOCKED","INPUT_UNUSABLE","TIMEOUT","GENERATION_FAILED"]},"message":{"type":"string"},"retryable":{"type":"boolean","description":"Submitting the same request again may work."}}},"created_at":{"type":"string","format":"date-time"}}},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["invalid_request","idempotency_key_required","end_user_id_required","invalid_api_key","api_key_expired","wallet_empty","style_not_found","run_not_found","not_found","idempotency_key_reused","run_not_cancelable","file_too_large","unsupported_content_type","end_user_cap_reached","gateway_rate_limited","uploads_rate_limited","daily_cap_reached","product_busy","internal_error","style_unavailable","uploads_unavailable","service_unavailable"]},"message":{"type":"string","description":"For people; branch on code, never on the message."},"cap":{"type":"integer","description":"end_user_cap_reached: the daily cap."},"cap_usd":{"type":"number","description":"daily_cap_reached: the product's daily spend cap in USD."},"limit":{"type":"integer","description":"gateway_rate_limited: the product's runs per minute."},"retry_after_s":{"type":"integer"},"retryable":{"type":"boolean","description":"True when the same request can succeed later unchanged (every 429 and 5xx). Retry only these, with the same Idempotency-Key."},"docs_url":{"type":"string","format":"uri","description":"This code's section of https://docs.wandura.ai/developers/api-v2/errors (the page itself for a code without its own section)."}}}}},"QueueState":{"type":"object","properties":{"object":{"const":"queue_state"},"product":{"type":"string"},"livemode":{"type":"boolean"},"concurrency_limit":{"type":"integer"},"running":{"type":"integer"},"waiting":{"type":"integer"},"queue_limit":{"type":"integer"},"accepted_this_minute":{"type":"integer"},"per_minute_limit":{"type":"integer"},"refusals":{"type":"object","properties":{"last_15m":{"type":"integer"},"last_24h":{"type":"integer"}}},"at":{"type":"string","format":"date-time"},"spend_tier":{"type":"object","description":"Only while spend tiers are on (see x-wandura.limits.concurrency_tiers): your org's tier from card top-ups in the last 28 days. `applies` is false when Wandura set a custom limit for this product, which then stays as set.","properties":{"tier":{"type":"string"},"window_days":{"type":"integer"},"topped_up_usd":{"type":"number"},"concurrency_limit":{"type":"integer"},"applies":{"type":"boolean"},"next":{"type":["object","null"],"properties":{"tier":{"type":"string"},"min_usd":{"type":"number"},"concurrency_limit":{"type":"integer"}}}}}}},"QueueLogBucket":{"type":"object","properties":{"object":{"const":"queue_log_bucket"},"bucket":{"type":"string","format":"date-time","description":"Start of the minute or hour (UTC)."},"accepted":{"type":"integer"},"refusals":{"type":"object","properties":{"product_busy":{"type":"integer"},"rate_limited":{"type":"integer"}}},"max_running":{"type":"integer","description":"The most runs using a concurrency slot at once."},"max_waiting":{"type":"integer","description":"The most priority runs queued for a slot at once."},"concurrency_limit":{"type":["integer","null"],"description":"Your concurrency slots."},"ended":{"type":"integer","description":"Runs that ended in the bucket after running (a run canceled while queued is not counted)."},"median_wait_ms":{"type":["integer","null"],"description":"Median wait from submit to start of the priority runs that started in the bucket."},"median_duration_ms":{"type":["integer","null"],"description":"Median run time (start to end, wait not included) of the runs that ended in the bucket."}}},"QueueRefusal":{"type":"object","properties":{"object":{"const":"queue_refusal"},"id":{"type":"string"},"at":{"type":"string","format":"date-time"},"reason":{"type":"string","enum":["product_busy","rate_limited"],"description":"product_busy: concurrency slots and queue full. rate_limited: the per-minute run limit."},"style":{"type":["string","null"]},"end_user_id":{"type":["string","null"],"description":"Null when the call sent none."},"retry_after_s":{"type":"integer"},"livemode":{"type":"boolean"}}},"ContentItem":{"type":"object","required":["id","type","title","used_by"],"properties":{"object":{"const":"content"},"id":{"type":"string"},"type":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"category":{"type":["string","null"]},"preview":{"type":["object","null"],"description":"image_url, video_url, poster_url or audio_url."},"licence":{"type":["object","null"],"description":"Name, url and credit (music)."},"used_by":{"type":"array","items":{"type":"string"},"description":"Live style ids that can use this item."}}},"LibraryItem":{"type":"object","required":["id","kind","title","preview_url"],"properties":{"object":{"const":"library_item"},"id":{"type":"string"},"kind":{"type":"string","enum":["cinematic_ad","supermagic","run_output"]},"title":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"aspect":{"type":["string","null"]},"duration_s":{"type":["number","null"]},"content_type":{"type":["string","null"]},"size_bytes":{"type":["integer","null"]},"shared_at":{"type":"string","format":"date-time"},"preview_url":{"type":"string","format":"uri"},"download":{"type":"object","properties":{"method":{"const":"POST"},"url":{"type":"string","format":"uri"},"free":{"const":true}}}}},"RunRequest":{"type":"object","required":["lane","end_user_id","inputs"],"properties":{"lane":{"$ref":"#/components/schemas/Lane"},"end_user_id":{"type":"string","minLength":1,"maxLength":255,"description":"The id of the person in your app this run is for (daily caps are per end user)."},"inputs":{"type":"object","additionalProperties":true,"description":"Fields of this style; see its OpenAPI (/v2/public/styles/{id}/openapi.json). Styles that can honour it take an optional `resolution` (for example \"480p\", \"720p\", \"1080p\"; the values are in the style's inputs schema); omitted = the style's default, at today's price. Another value changes the price: POST /v2/quote with the same inputs. A value the style does not offer is 400 INVALID_INPUT with details [{field: \"resolution\"}]."}}},"CreateRunRequest":{"allOf":[{"$ref":"#/components/schemas/RunRequest"},{"type":"object","required":["style"],"properties":{"style":{"type":"string"}}}]},"QuoteRequest":{"type":"object","required":["style","lane"],"properties":{"style":{"type":"string"},"lane":{"$ref":"#/components/schemas/Lane"},"end_user_id":{"type":"string"},"inputs":{"type":"object"}}},"UploadRequest":{"type":"object","required":["content_type","size_bytes"],"properties":{"content_type":{"type":"string","enum":["image/jpeg","image/png","image/webp","video/mp4","video/quicktime","video/webm"]},"size_bytes":{"type":"integer","minimum":1}}},"Upload":{"type":"object","properties":{"object":{"const":"upload"},"method":{"const":"PUT"},"headers":{"type":"object","additionalProperties":{"type":"string"}},"upload_url":{"type":"string","format":"uri"},"file_url":{"type":"string","format":"uri"},"max_size_bytes":{"type":"integer"},"expires_at":{"type":"string","format":"date-time"},"file_expires_at":{"type":"string","format":"date-time"}}},"Workflow":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"trend":{"type":["string","null"]},"owner":{"type":"string","enum":["shared","org"]},"workflow":{"type":"string","description":"Pinned version, e.g. \"<id>@<version>\"."},"output":{"type":"string","enum":["image","video","audio"]},"price":{"type":"object","properties":{"priority":{"$ref":"#/components/schemas/Price"},"relax":{"$ref":"#/components/schemas/Price"}}},"priority_promise_s":{"type":"integer"},"sample_url":{"type":["string","null"]}}},"PublicStyle":{"type":"object","properties":{"id":{"type":"string"},"object":{"const":"style"},"name":{"type":"string"},"trend":{"type":["string","null"]},"output":{"type":"string","enum":["image","video","audio"]},"price":{"type":"object","properties":{"priority":{"$ref":"#/components/schemas/Price"},"relax":{"$ref":"#/components/schemas/Price"}}},"priority_promise_s":{"type":"integer"},"sample_url":{"type":["string","null"],"format":"uri"},"description":{"type":"string","description":"One line about the style (absent when not set)."},"input_hint":{"type":"string","description":"What to send, e.g. \"A clear full-body photo of one person\" (absent when not set)."},"runs_7d":{"type":"integer"},"success_rate_7d":{"type":["number","null"]}}},"LibraryShare":{"type":"object","properties":{"object":{"const":"library_share"},"run_id":{"type":"string"},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["pending","held","approved"],"description":"approved = listed"}}}}}},"PublicStyleDetail":{"allOf":[{"$ref":"#/components/schemas/PublicStyle"},{"type":"object","properties":{"inputs":{"type":"object","description":"JSON Schema of inputs."},"endpoints":{"type":"object","additionalProperties":{"type":"string"}},"openapi_url":{"type":"string","format":"uri"}}}]},"Event":{"type":"object","required":["id","object","type","created_at","sequence","data"],"properties":{"id":{"type":"string"},"object":{"const":"event"},"type":{"type":"string","enum":["run.running","run.completed","run.failed","run.nsfw","run.canceled"]},"created_at":{"type":"string","format":"date-time"},"sequence":{"type":"integer","description":"Grows per run; ignore an event older than one you already handled."},"data":{"$ref":"#/components/schemas/Run"}}},"AccountEvent":{"type":"object","required":["id","object","type","created_at","data"],"properties":{"id":{"type":"string"},"object":{"const":"event"},"type":{"type":"string","enum":["wallet.low","wallet.empty","wallet.topup_failed","key.expiring","key.revoked","library.item_published","library.item_removed","queue.congestion_started","queue.congestion_ended"]},"created_at":{"type":"string","format":"date-time"},"data":{"oneOf":[{"$ref":"#/components/schemas/WalletEventData"},{"$ref":"#/components/schemas/KeyEventData"},{"$ref":"#/components/schemas/LibraryItem"},{"$ref":"#/components/schemas/CongestionEpisode"}],"description":"wallet.* carry a wallet, key.* an API key (never its secret), library.item_published the public library item, library.item_removed {object: library_item, id, kind, removed: true}, queue.congestion_* the congestion episode as it is when the event is made. Library events go to every endpoint that lists them."}}},"CongestionStats":{"type":"object","description":"Your product's numbers when the episode started or ended.","properties":{"running":{"type":"integer","description":"Runs using one of your concurrency slots."},"waiting":{"type":"integer","description":"Priority runs queued for a slot."},"concurrency_limit":{"type":"integer","description":"Your concurrency slots."},"refusals_5m":{"type":"integer","description":"Refused submits in the last 5 minutes."},"refusals_10m":{"type":"integer","description":"Refused submits in the last 10 minutes."},"runs_ended_15m":{"type":"integer","description":"Runs that ended in the last 15 minutes."},"median_duration_ms":{"type":["integer","null"],"description":"Median run time (started to ended, the wait before it not included) of those runs; null when none ended."}}},"CongestionEpisode":{"type":"object","properties":{"object":{"const":"congestion_episode"},"id":{"type":"string"},"product":{"type":"string"},"livemode":{"type":"boolean"},"kind":{"type":"string","enum":["refusing","near_full","slow"]},"status":{"type":"string","enum":["open","ended"]},"started_at":{"type":"string","format":"date-time"},"ended_at":{"type":["string","null"],"format":"date-time"},"duration_s":{"type":["integer","null"]},"start":{"$ref":"#/components/schemas/CongestionStats"},"end":{"oneOf":[{"$ref":"#/components/schemas/CongestionStats"},{"type":"null"}]},"affected":{"type":["object","null"],"description":"Once ended: refused submits (counted from 5 minutes before the start, the ones that start a refusing episode), runs accepted and runs ended during the episode.","properties":{"refusals":{"type":"integer"},"runs_accepted":{"type":"integer"},"runs_ended":{"type":"integer"}}}}},"WalletEventData":{"type":"object","required":["object","org","product"],"properties":{"object":{"const":"wallet"},"org":{"type":"string"},"product":{"type":"string"},"available_usd":{"type":"number","description":"wallet.low: balance after the run that crossed the line. wallet.empty: balance that could not cover the run."},"threshold_usd":{"type":"number","description":"wallet.low: the line crossed (the auto top-up threshold if set, else low_balance_usd). wallet.topup_failed (pool): the auto top-up threshold."},"source":{"type":"string","enum":["pool","card"],"description":"wallet.topup_failed: pool = the org pool could not fill the wallet, card = the saved card was declined."},"reason":{"type":"string","description":"wallet.topup_failed: pool_insufficient, or the card error code."},"amount_usd":{"type":"number","description":"wallet.topup_failed: the top-up that was asked for."},"added_usd":{"type":"number","description":"wallet.topup_failed (pool): what the pool could add (may be 0)."}}},"KeyEventData":{"type":"object","required":["object","id","product"],"properties":{"object":{"const":"api_key"},"id":{"type":"string"},"prefix":{"type":"string"},"label":{"type":["string","null"]},"product":{"type":"string"},"livemode":{"type":"boolean"},"scopes":{"type":"array","items":{"type":"string"}},"gateway":{"type":"boolean"},"expires_at":{"type":["string","null"],"format":"date-time"},"last_used_at":{"type":["string","null"],"format":"date-time"},"revoked_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"},"expires_in_days":{"type":"integer","enum":[7,1],"description":"key.expiring: which reminder this is (7 days or 1 day before expires_at)."},"reason":{"type":"string","enum":["revoked","rotated"],"description":"key.revoked: a rotated key keeps working until its expires_at (the rotation grace end)."},"replaced_by":{"type":"string","description":"key.revoked with reason rotated: id of the new key."}}},"JobRequest":{"type":"object","required":["model"],"additionalProperties":false,"description":"Exact rules per model and style for the common fields: GET /v2/models/{model_id}/schema. lane and inputs are described here; each style's full body (inputs included) is in its OpenAPI at GET /v2/public/styles/{style}/openapi.json.","properties":{"model":{"type":"string","enum":["wandura-image-v1","wandura-video-v1","wandura-c1-1"]},"style":{"type":"string","description":"Style id from GET /v2/styles. Required by the styled models (wandura-image-v1, wandura-video-v1); wandura-c1-1 takes none."},"prompt":{"type":"string","maxLength":4000},"negative_prompt":{"type":"string","maxLength":4000},"image_url":{"type":"string","format":"uri","pattern":"^https://"},"image_urls":{"type":"array","minItems":1,"maxItems":10,"items":{"type":"string","format":"uri","pattern":"^https://"}},"video_url":{"type":"string","format":"uri","pattern":"^https://","description":"wandura-video-v1, and wandura-c1-1 (a source ad to remake: MP4 or MOV, at most 60 s) only."},"aspect_ratio":{"type":"string","pattern":"^[0-9]{1,2}:[0-9]{1,2}$"},"resolution":{"type":"string","pattern":"^[0-9]{3,4}p$","description":"Only for styles that list it (GET /v2/styles, resolutions); omitted = the style default. The price depends on it."},"duration":{"type":"number","exclusiveMinimum":0,"maximum":60,"description":"wandura-video-v1 and wandura-c1-1 (whole seconds, 5-60) only."},"seed":{"type":"integer","minimum":0,"maximum":4294967295},"num_outputs":{"type":"integer","enum":[1],"default":1},"webhook_url":{"type":"string","format":"uri","pattern":"^https://","maxLength":2048,"description":"Public https URL that gets the final job body, signed (X-Signature, X-Timestamp) with the product's Jobs API signing secret or, when it has none, its Webhook endpoint's signing secret. Needs one of the two."},"end_user_id":{"type":"string","minLength":1,"maxLength":255},"lane":{"type":"string","enum":["priority","relax"],"default":"priority","description":"priority (default): done within the style's max_job_seconds, or the job ends timeout and is not charged. relax: waits in line with no deadline, never priced above priority."},"inputs":{"type":"object","description":"The style's own inputs that no field above carries (for example keep_audio, a second text field, mask_url), by name as in the style's input schema (GET /v2/public/styles/{style} -> inputs). An input a field above carries is refused here (422 \"send it as <field>\")."}}},"Submitted":{"type":"object","required":["job_id","status","model","created_at"],"properties":{"job_id":{"type":"string"},"status":{"$ref":"#/components/schemas/Status"},"model":{"type":"string"},"created_at":{"type":"string","format":"date-time"}}},"Status":{"type":"string","enum":["queued","processing","succeeded","failed","cancelled","timeout"],"description":"queued -> processing -> succeeded | failed | cancelled | timeout. The last four are final and never change."},"Job":{"type":"object","required":["job_id","status","model","style","output","error","created_at","completed_at"],"properties":{"job_id":{"type":"string"},"status":{"$ref":"#/components/schemas/Status"},"model":{"type":"string"},"style":{"type":"string"},"output":{"type":["array","null"],"items":{"$ref":"#/components/schemas/JobFile"},"description":"succeeded: one file (one output per job). null otherwise."},"error":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/JobError"}],"description":"failed / timeout: why. null otherwise (cancelled included)."},"created_at":{"type":"string","format":"date-time"},"completed_at":{"type":["string","null"],"format":"date-time","description":"When it reached its final status."},"notes":{"type":"array","items":{"$ref":"#/components/schemas/JobNote"},"description":"Present only when there is something to tell the end user about their inputs (absent otherwise)."}}},"JobNote":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["photos_reordered","photos_same_gender"],"description":"photos_reordered: the photos came the other way round for their roles and were swapped before the video was made (the result is right). photos_same_gender: both photos show the same gender, so one person plays a role written for the other. New codes may be added."},"message":{"type":"string","description":"For people."}}},"InputSlot":{"type":"object","properties":{"pronoun":{"type":["string","null"],"enum":["she","he","they",null]},"role":{"type":["string","null"]},"hint":{"type":["string","null"],"description":"What photo to take."},"subject":{"type":["string","null"],"enum":["person","dog","cat","animal",null],"description":"What this photo must show: person = a person, never an animal; dog / cat = that animal, never a person; animal = a cat or a dog, never a person. Show it on the upload box so a user does not send a face to a pet role or a pet to a person role. null = the style does not say. New values may be added."},"preview_url":{"type":["string","null"],"format":"uri","description":"A still of the person this photo replaces in the template video."},"roles":{"type":"array","description":"Only on a style that takes ONE photo of everyone together (the single entry of input_slots): who must be in that photo, in the style's order. `side` = where the role stands in the photo when the style tells them apart by place (left, center, right), else null.","items":{"type":"object","properties":{"pronoun":{"type":["string","null"],"enum":["she","he","they",null]},"role":{"type":["string","null"]},"side":{"type":["string","null"]},"subject":{"type":"string","enum":["person","animal"],"description":"person = a person; animal = a cat or a dog."}}}}}},"JobFile":{"type":"object","required":["url","type"],"properties":{"url":{"type":"string","format":"uri","description":"HTTPS, direct download. Copy it to your storage before expires_at."},"type":{"type":["string","null"],"description":"MIME type, e.g. video/mp4."},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]},"duration":{"type":["number","null"],"description":"Seconds (video)."},"fps":{"type":["number","null"]},"size_bytes":{"type":["integer","null"]},"seed":{"type":["integer","null"]},"expires_at":{"type":["string","null"],"format":"date-time"}}},"JobError":{"type":"object","required":["code","message","retryable"],"properties":{"code":{"type":"string","enum":["CONTENT_MODERATION_BLOCKED","INPUT_UNUSABLE","TIMEOUT","GENERATION_FAILED"]},"message":{"type":"string"},"retryable":{"type":"boolean"}}},"JobsApiError":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable"],"properties":{"code":{"type":"string","enum":["INVALID_INPUT","UNAUTHORIZED","INSUFFICIENT_CREDITS","FORBIDDEN","NOT_FOUND","IDEMPOTENCY_KEY_REUSED","JOB_NOT_CANCELLABLE","RATE_LIMITED","PRODUCT_BUSY","END_USER_LIMIT_REACHED","DAILY_CAP_REACHED","INTERNAL_ERROR","SERVICE_UNAVAILABLE"]},"message":{"type":"string","description":"For people; branch on code."},"retryable":{"type":"boolean"},"details":{"type":"array","items":{"type":"object","required":["field","reason"],"properties":{"field":{"type":"string"},"reason":{"type":"string"}}},"description":"INVALID_INPUT only."},"docs_url":{"type":"string","format":"uri","description":"This code's section of https://docs.wandura.ai/developers/api-v2/errors (the page itself for a code without its own section)."}}}}},"Model":{"type":"object","properties":{"id":{"type":"string"},"object":{"const":"model"},"output":{"type":"string","enum":["image","video"]},"description":{"type":"string"},"styles":{"type":"integer","description":"Live styles under it for this key."},"max_job_seconds":{"type":"integer","description":"Longest maximum job time of its styles; past a style's own maximum a job ends timeout."},"schema_url":{"type":"string","format":"uri"},"styles_url":{"type":"string","format":"uri"},"name":{"type":"string","description":"Only on a model that takes no style (wandura-c1-1: \"Wandura C1.1\")."},"price_per_second":{"type":"object","additionalProperties":{"type":"object","properties":{"credits":{"type":"number","description":"1 credit = $0.01"},"usd":{"type":"number"}}},"description":"Only on a model priced per output second (wandura-c1-1): the price per second by resolution. A job holds it for the requested seconds and is charged for the seconds delivered."}}},"Style":{"type":"object","properties":{"id":{"type":"string"},"object":{"const":"style"},"model":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"input_hint":{"type":"string"},"preview_url":{"type":["string","null"],"format":"uri"},"inputs":{"type":"object","additionalProperties":{"type":"string","enum":["required","optional"]},"description":"Each field of POST /v2/jobs this style takes. Fields not listed are refused (422)."},"input_slots":{"type":"array","items":{"$ref":"#/components/schemas/InputSlot"},"description":"Styles that put one person (or pet) per photo: one entry per photo of image_urls, IN ORDER (entry 1 = image_urls[0]); `subject` says what each photo must show (a person, or which animal). Show it on each upload box. A style that takes ONE photo of everyone together has a single entry whose `roles` lists who must be in it. Absent for other styles."},"max_job_seconds":{"type":"integer"},"price":{"type":"object","properties":{"credits":{"type":"number","description":"1 credit = $0.01"},"usd":{"type":"number"}},"description":"Charged only when the job succeeds (live keys)."}}}}},"tags":[{"name":"Jobs","description":"The recommended way in. The provider-standard Jobs API: POST /v2/jobs -> job_id; the result by signed webhook or by polling. Models wandura-image-v1 and wandura-video-v1 (the model version, not the API version). Test keys (wdr_test_…) are the sandbox."}]}