Aurous-Version: 2026-05-15 (or any prior date) on every request to opt into a specific contract; we echo the applied version on every response. Missing the header falls back to your team’s default_api_version (current at signup).
Breaking changes ship under a new date; non-breaking additions are listed under the current date.
2026-07-31 — Machine-readable error_code, honest interruption messaging, retryable 503s on account/billing checks
A non-breaking batch that makes generation failures easier to handle programmatically: a machine-readable error_code on every generation, honest interruption messaging, and 503 responses with a Retry-After header when the platform can’t momentarily verify your account or billing state.
- New
error_codefield on every generation.POST/GET /v1/images(create, detail, list — video rows included) andPOST /v1/videos(create), plus webhookdatapayloads, now carryerror_code— a machine-readable, closed-enum reason forstatus: failed:generation_interrupted,reference_preparation_failed,content_filtered, orgeneration_failed. Switch onerror_code, not onerror_message(human-readable prose that can change without notice).nullon non-failed generations and on failures recorded before this field existed — always keep a default branch in your switch statement. Purely additive. Related transient failures that don’t reach a terminal generation row instead surface as503 provider_unavailable— see that page for retry guidance. - Interrupted generations say so plainly. A generation interrupted before completion that couldn’t be recovered now reports
error_code: "generation_interrupted"witherror_message: "The generation was interrupted before it could complete and could not be recovered. Credits have been refunded."— and the refund is guaranteed on this path. - Account/billing checks now answer
503with aRetry-Afterheader when the platform can’t momentarily verify your account or billing state. Any V1 endpoint that checks that state — includingPOST /v1/images,POST /v1/videos, their/estimatecounterparts, andGET /v1/team//v1/usage//v1/balance//v1/loras— returns503 provider_unavailablewith aRetry-Afterheader (typically5). Seeprovider_unavailable. 503responses are retry-safe underIdempotency-Key. A503is never cached against your key — retry the exact sameIdempotency-Keyand it re-executes normally; you will not see409 idempotency_key_in_useas a result of a503. See Idempotency.- No action required beyond honoring
Retry-Afteron503and preferringerror_codeovererror_messagegoing forward.
2026-07-24 — Styles are back: prompt styles, auto-matching with opt-out, compose with acts
Styles return toPOST /v1/images as prompt styles — richer look control on the current image models — with automatic matching, full composability with composition acts and subjects, and honest signaling (style echo + warnings[]) so the applied outcome is always observable. Batch count now works on every request shape. Existing integrations keep working; the behavior notes below are worth reading if you pin lora_id or key off estimate quotes today.
lora_idis now tri-state. Omit it and style matching runs automatically when your prompt clearly names a look (conservative — most prompts resolve to no style); sendnullto disable matching for a request; send an id (or slug) fromGET /v1/lorasto pin a style. See Create an image → Using a style.- Every generation echoes
style: { id, name }(ornull) — on creates, GETs, list rows, and webhookdatapayloads.style.idis the samelora_*identifier the catalog serves and round-trips intolora_id. A new, always-present, nullable key: integrations that don’t read it are unaffected. - Styles compose with acts and subjects.
lora_id+action_idandlora_id+subjects[]are now valid combinations (previously400 parameter_invalid_combination). One rule to know: a composition-act id sent aslora_idacts as the pin itself, so it can’t be combined with a differentaction_id.lora_id+context_imagesremains invalid. - Retired style ids keep working. An id with a designated successor applies the successor style (echoed in
style); other retired ids generate without a style and add awarnings[]entry (style_retired_plain). A small set of discontinued styles return the new400 style_retired— on create and estimate. - New
warnings[]on create + estimate responses. Non-fatal request adjustments — a retired style pin, or a parameter with no effect on the selected generation path (parameter_ignored). Omitted when empty; never on GETs, lists, or webhooks; the code set is open, so ignore unknown codes. Idempotent replays return the original warnings verbatim. countworks everywhere, in parallel. Every request shape — plain,subjects[],context_images— now delivers up to 4 images per request, billed per image; if part of a batch fails you receive the successful images and the difference is refunded automatically (image_countreflects delivered). Estimates quotecount ×accordingly:subjects/context_imagesestimates previously quoted a single image and now quote the full batch.countmust be a whole number.count: 3.5now returns a validation400(it was previously accepted and rounded down).- Behavior change: with
lora_idomitted, a prompt that clearly names a look may now generate with a matched style where it previously always generated plain. Thestyleecho makes this visible; sendlora_id: nullif you need guaranteed-unstyled output. Applies across all version pins. - Behavior change: styled generations run on the current image models, which serve prompt + size and ignore the legacy sampler fields; a sampler field sent on such a request is reported via
warnings[]rather than silently dropped.
2026-07-22 — Multi-person video casts, reference-media uploads, and a contract-closure batch
One breaking removal (a webhook field with zero recorded uses) plus a set of additive capabilities and precision fixes that close the drift between the documented contract and live behavior.- Breaking: the per-request
webhook_urlfield is removed fromPOST /v1/imagesandPOST /v1/videos. It never delivered what the docs promised (wrong event names, an internal id, an unsanitized field subset) and has zero recorded uses across all teams, all-time — which is why it is removed outright rather than gated behind a newAurous-Versiondate. Requests that still send it receive400 parameter_unknownunder every version pin. Use registered webhook endpoints instead (POST /v1/webhook_endpoints): the full event catalog (image.*andvideo.*including.cancelled,character.*, balance events), signed payloads, delivery logs, and retries. - Cast up to 2 people in a video via
subjects[]onPOST /v1/videos— each entry a saved character (char_<ulid>) or its own group of 1–4 reference images (file_<ulid>or HTTPS URLs), freely mixed; the same wire grammar the images surface uses. A cast may ride areference_video_url— the clip supplies the motion while the cast anchors identity. Casts don’t change the price. The legacy single-subject fields (character_id,reference_image_urls) remain fully supported and now carry the advisoryDeprecation: trueheader on the video routes too. - Upload reference media.
POST /v1/filesacceptspurpose: reference_video(MP4/MOV, ≤50 MB) andpurpose: reference_audio(WAV/MP3, ≤15 MB);reference_video_url/reference_audio_urlaccept the returnedfile_<ulid>anywhere an HTTPS URL was accepted. Format, duration, and resolution validate at upload time — an accepted upload can never fail those checks at create.FileResponsegainsduration_s(parsed clip duration;nullfor images). - Audio references pair with more companions.
reference_audio_urlnow anchors to a reference video, a subject, or a pinned model — previously it required a reference video. - Video generations echo their cast.
subjects[]appears on video generation responses with the same ordered, opaque projection images use:[{type, character_id, image_count}]— never URLs, never internal ids.nullon generations without a cast. enhancer_outcomejoins the documented contract — the informational field behind “Enhancer declined this prompt — your raw text was used.” Always present, nullable. Treat it as an open set: new values may be added without a version bump, so don’t branch control flow on it.GET /v1/usagespeakssucceeded. Status facet values now use the same public vocabulary as the generations surface (previously the internalcompletedspelling leaked through). Thestatusfilter accepts both spellings.- List / webhook / detail parity fixes.
GET /v1/imageslist rows and webhookdatapayloads now carry the full documentedGenerationResponseshape (negative_prompt,cfg_rescale,denoise_strength,seed,aurous_version,creation_request_idhad drifted off some surfaces), and the generationstatusenum is defensively frozen to its 5 documented values.
2026-07-18 — Raw Seedance video API + per-model, token-based video pricing
Two video additions ship together: a provider-native “raw” Seedance surface for teams that already build against the Seedance SDK, and per-model, token-based video pricing so the rate you read is the rate you pay. Both are additive — existingPOST /v1/videos integrations are unaffected.
- Raw Seedance task API. New
POST /v1/contents/generations/tasks,GET /v1/contents/generations/tasks/{id},GET /v1/contents/generations/tasks, andDELETE /v1/contents/generations/tasks/{id}. The request and task bodies are byte-exact to the native Seedance video API — Aurous forwards your Ark-SDK payload verbatim, including fields not yet documented here, wrapped in the same credit holds,Idempotency-Keysupport, rate limits, and typed error envelope as the rest of the V1 API. Point your existing Seedance SDK client at Aurous and go — see raw Seedance access. - Video generation is priced per model on your actual token usage. Each video model carries its own token rate, and you are billed for the tokens a generation actually consumes. Published rates exactly match what you are charged: a ceiling is held while the task runs, and the final charge settles on the provider’s reported tokens at success — a
failed,expired, or queued-cancelledtask charges nothing and releases the hold in full. Read the live rate for any model fromGET /v1/models; the day-one rate card and the formula behind it are on Seedance models & pricing. - Two new lower-cost tiers.
seedance-2.0-fastandseedance-2.0-minijoin the full-qualityseedance-2.0, giving you cheaper options when you don’t need the flagship model. Address any model by id or alias. - Video-input tasks meter reference-video seconds. A task whose
content[]includes a reference video is billed for the reference footage it ingests in addition to the video it generates, so the receipt reflects the full cost of the work. - No version bump. Video rates are per-model and DB-driven — they can change without a new
Aurous-Version, the same way chat and embedding rates do. Always read the current rate fromGET /v1/modelsrather than hard-coding it; see the rate-mutability asymmetry. Each charged task snapshots the rate in force when it was created, so the amount you were quoted is the amount you pay.
2026-07-16 — Reference video/audio on POST /v1/videos
POST /v1/videos accepts a third input mode alongside text and frames: reference_video_url, an HTTPS URL to your own clip. The generation either borrows the clip’s motion for a new scene (video_task: "reference", the default) or continues the clip itself (video_task: "extend"). Optionally pair it with reference_audio_url to guide the generated soundtrack. This is additive — existing text-to-video and image-to-video requests are unaffected.
- New request fields on
POST /v1/videosandPOST /v1/videos/estimate.reference_video_url,reference_audio_url,video_task(reference|extend),extend_direction(forward|backward). See the endpoint reference for size/duration/format limits and the full parameter-combination rules. - New response fields.
video_task,extend_direction,reference_video_url,reference_audio_url—nullon every generation that did not use this mode, including all pre-existing rows. inference_typegainsr2v(reference-to-video). A generation that used your ownreference_video_urlreportsinference_type: "r2v"; frame-driven, subject-driven, and pinned Action/template videos continue to reporti2v. Treat unrecognizedinference_typevalues as opaque.- New error codes.
reference_fetch_failed(couldn’t fetch your reference URL) andreference_media_invalid(fetched, but failed format/duration/resolution validation) — see Errors. - Timing. The reference is prepared inside
processing— typically well under a minute, with a 10-minute budget before the generation fails with a full refund. A reused reference (identical bytes) skips preparation. If preparation fails, retry with a newIdempotency-Key.
2026-07-16 — Subject-driven video, a video model catalog, and adaptive duration by default
POST /v1/videos now has the same generation surface as POST /v1/images: pin or auto-match a video model, drive a video from a saved character or reference images, and price adaptive-length videos honestly. All changes are additive — existing integrations keep working — with two behavior notes below worth reading if you key off video_lora_id or inference_type today.
POST /v1/videos. Acceptscharacter_idandreference_image_urls(up to 6), mirroring the same fields onPOST /v1/images— the subject is anchored across the generated video. Mutually exclusive with each other (400 mutually_exclusive_input) and withfirst_frame_url/last_frame_url(400 parameter_invalid_combination). See Create a video.GET /v1/video_lorasnow lists real, pinnable video models (previously effectively empty). Pin one withvideo_lora_id, or omit it and the platform matches your prompt to a suitable model automatically. Whichever model ends up behind a generation — pinned or matched — is echoed back on the response asvideo_lora_idandvideo_lora_name.- Adaptive duration is the default. Omit
duration(or pass-1) and the model picks the natural length for your prompt, 4–15 seconds, and you’re billed for the length actually delivered — not a flat ceiling.POST /v1/videos/estimateprices this as a range (amount_min–amount_max); the ceiling is reserved up front and the difference is released once the real length is known. Pin a whole-number duration (4–15) for a fixed length at a fixed price. cost.refundedis a new field. A failed generation now reportscost: {"amount": 0, "refunded": true}instead of a bare zero, so you can tell “never charged” apart from “charged nothing this time.” Present on both image and video generations.- Behavior change: a prompt-only video request that previously always rendered as plain text-to-video may now be auto-matched to a video model, producing a different look than before. Pin
video_lora_idexplicitly if you need the exact prior output character. - Behavior change: a subject-driven video (
character_idorreference_image_urls) reportsinference_type: "i2v"on the generation response — the same value used for first/last-frame videos. There is no separate subject-specific value.
2026-06-21 — Transient storage failures now return 503 + Retry-After
Upload and character-create routes that touch object storage now surface a transient storage failure as a retryable 503 provider_unavailable carrying a Retry-After header, instead of a generic 500. This is a non-breaking improvement — no action required beyond honoring the new retry signal.
POST /v1/characters/uploads/init,POST /v1/characters/uploads/classify, andPOST /v1/characters. A transient storage error now returns503 provider_unavailablewith aRetry-Afterheader instead of a generic500. Retry on503after the number of seconds inRetry-After. Applies across all version pins.
2026-06-21 — Character synthesis rate limit raised to 15/min
The per-team rate limit on character synthesis increased from 6 requests/minute (burst 12) to 15 requests/minute (burst 30). This is a loosening — no action required.POST /v1/characters,POST /v1/characters/:id/refs/regenerate,POST /v1/characters/:id/resynthesize. These three routes share one per-team bucket, now 15/minute sustained with a burst of 30. Each create or resynthesize fans out to four image generations internally, so the bucket is sized to match thePOST /v1/imagesbudget — 15 × 4 = 60 image generations/minute. A burst of regenerations or resynthesizes draws down the same budget as new creates.- Rate limits are not version-pinned. The live
X-RateLimit-Limitresponse header is always the authoritative current value; we raise limits without notice and announce any reduction in advance here. Applies across all version pins.
2026-06-19 — Character synthesis is now asynchronous + character webhook events
POST /v1/characters with generate: true no longer blocks until the character is rendered. It returns immediately with status: "synthesizing"; the synthesis runs in the background and the character transitions to ready (or failed). Poll GET /v1/characters/:id, or subscribe to the new character webhook events.
POST /v1/characters. Now responds201withstatus: "synthesizing"instead of returning the finished character. An optionalclient_reference_id(≤ 256 chars) is echoed on the response and on every character webhook. Behavior change: callers that relied on the synchronous response must switch to pollingGET /v1/characters/:iduntilstatusisreadyorfailed, or subscribe to webhooks. Applies across all version pins.- New webhook events.
character.completed(reachedready; payload carries the reference renders),character.failed(provider error, moderation, or timeout —error_messagecarries a customer-safe code), andcharacter.cancelled(DELETE /v1/characters/:idon an in-flight synthesis). Subscribe via theeventsarray on a webhook endpoint, or in the dashboard webhook settings.
2026-06-03 — Documented platform defaults now applied to omitted generation params
POST /v1/images now applies the platform default for steps, guidance_scale, cfg_rescale, and denoise_strength when you omit the parameter and the selected style does not define its own default. Previously these defaults were documented on the request fields but only enforced by the dashboard, so an API caller who omitted them fell through to the provider’s raw default.
POST /v1/images. Omittingcfg_rescaleresolves to0.7,stepsto11, andguidance_scaleto4.3. Omittingdenoise_strengthon a request that has references resolves to0.6(it remains ignored on bare text-to-image requests). Precedence is unchanged: an explicit request value wins, then the selected style’s default, then the platform default.- No request or response shape change. No fields added or removed; the response continues to echo only the values you supplied — omitted params are resolved server-side and not reflected back.
default values on the request fields, so it applies across all version pins rather than shipping under a new date. The only observable change is for a caller who omitted cfg_rescale and relied on the provider’s raw default. Zero customers were affected at release time.
2026-05-24 — Embedding video rate removed; video_url input rejected
The embedding_pricing.video field on GET /v1/models has been removed. Embedding billing has always folded video_url input into the visual bucket — the provider extracts frames from videos and bills them as visual tokens. The previously published embedding_pricing.video rate never actually fired. To prevent the contract from misleading integrators, the field is now gone.
GET /v1/models. Theembedding_pricingobject on embedding-kind rows no longer contains avideokey.textandvisualare unchanged. Thevisualfield description now notes explicitly thatimage_urlparts bill at this rate;video_urlinput is no longer accepted (see below).POST /v1/embeddings+POST /v1/embeddings/estimate. Anyvideo_urlcontent part returns400 embeddings_video_unsupported(new error code). To embed visual content from a video, extract a representative frame in your pipeline and submit it asimage_url; it bills at the visual rate.- Renamed error code.
embeddings_video_too_many_parts(which previously fired for ≥ 2 video parts) was renamed toembeddings_video_unsupportedand now fires for ≥ 1 video part. Update any integration that caught the old code on a single-video payload. - Response shape compatibility.
POST /v1/embeddings/estimateretainstokens.videoandbreakdown.input.videoon the response shape, both always0, for one release cycle so existing SDKs that read them don’t break. Both fields will be dropped in a follow-up release.
embedding_pricing.video field or sent video_url parts. Embedding rates are explicitly mutable per the rate-card mutability note on /v1/models and /v1/chat/completions, so the change applies across all version pins.
Zero customers were affected at release time.
2026-05-11
- Image size presets reshaped.
sizenow follows<tier>_<ratio>form:2k_1_1,2k_3_2,2k_2_3,2k_4_3,2k_3_4,2k_16_9,2k_9_16,2k_21_9, plus the same eight ratios at4k_*. Sixteen presets total. The previous six names (square,landscape,portrait,hd_square,hd_landscape,hd_portrait) return400 invalid_format. - Custom dimensions.
widthandheightnow require both, in[1024, 4096]per side, and are snapped server-side to multiples of 32. The responsewidth/heightreflect the post-snap value (may differ from the request by up to 31 px per side). - Mutual exclusion. Sending both
sizeandwidth/heightreturns400 parameter_invalid_combination. Sending only one ofwidth/heightreturns400 missing_field.
2026-05-15 (v1.0)
The first stable date-pin. Everything documented elsewhere on this site is what2026-05-15 means.
Resource shape
/v1/imagesand/v1/videosare the create + read paths for image and video generations./v1/filesis the upload path for reference images you reuse across generations./v1/webhook_endpointsregisters + manages signed webhook deliveries./v1/lorasand/v1/video_lorasare the public catalog of styles./v1/team,/v1/balance,/v1/usage,/v1/usage/eventscover account and metering. Per-modelchat_pricing/embedding_pricingships onGET /v1/modelsand reflects the caller’s effective rate including any per-team overrides.
Pricing
- Image cost =
image.base × countplus an optionalenhanceline whenenhance_prompt: true. Refs andcharacter_idare free — neither shifts the price. - Video cost =
base_per_second × resolution_factor × duration_s(multiplicative). The breakdown carries the inputs;cost.amountis the product. - Team-level discounts surface as a
discount_factorline in the breakdown when the team has a negotiated rate.
Webhook event types — v1.0 lineup
v1.0 ships eight event types. Four additional event types are reserved for a later date-pin — the underlying status transitions aren’t yet wired into the production code path, so they’re documented but never fire today. Currently fired:image.completed/image.failed/image.cancelledvideo.completed/video.failed/video.cancelledusage.balance_low(per-team threshold; 1-hour debounce)webhook.endpoint_disabled(auto-disable notification)
image.expired/video.expired— fires when a generation exceeds the 5-minute polling window without a terminal status. Requires a polling-timeout sweeper that flipsstatusfromprocessingtoexpired. Until then, stuck generations remainprocessing; the worker eventually fails them or the cancel endpoint resolves the hold.image.moderation_rejected/video.moderation_rejected— moderation rejection currently throws400 invalid_requestfrom the create endpoint without inserting aninferencesrow, so there is nothing to fire a webhook against. Future change: insert the row inmoderation_rejectedstate and fire the event.
events: ["*"], the snapshot taken at registration time includes only the events that exist when you register. A future date-pin that adds *.expired or *.moderation_rejected will require you to update your registration explicitly — ["*"] does NOT auto-subscribe across date-pins.
Status enum
Generationstatus is one of: pending | processing | succeeded | failed | cancelled. expired and moderation_rejected are reserved enum values per the spec but never fire as wire values in 2026-05-15 (see “Webhook event types” above for the same reason).
Usage events — fields reserved for a later date-pin
GET /v1/usage/events returns line items with id, type, amount, currency, generation_id, hold_id, description, and created_at. Two fields commonly seen in similar ledger APIs (Stripe events, etc.) are intentionally NOT in 2026-05-15:
balance_before/balance_after— the team’s credit balance immediately before and after the event. Computing these accurately requires an authoritative balance-tracking column onusage_events(or a transactional bookkeeping query at write time). Reserved for a follow-up date-pin once the bookkeeping column is added.
amount across rows in chronological order with the type-derived sign (hold / charge / adjustment with negative description debit; release / refund / topup / positive adjustment credit).
2026-05-22
LLM pricing transparency redesign. Three breaking changes, one removal, one units change.Model rename (breaking)
Four customer-facing slugs were renamed. Old slugs return404 model_not_found.
Three Grow 2.0 chat models (
aurous-grow-2.0-mini, aurous-grow-2.0-lite, aurous-grow-2.0-code) are unchanged but now share the family-wide 50% markup (previously 15%). No call-site change required.
/v1/pricing removed (breaking)
GET /v1/pricing now returns 404. Per-model rates are embedded directly in GET /v1/models under chat_pricing (chat models) and embedding_pricing (embedding models). One less round-trip; the data is identical and now lives alongside model metadata.
/v1/models response shape (breaking)
The model response gained two nullable siblings — chat_pricing and embedding_pricing — replacing the previous aurous_metadata.credits_per_1k_* fields. Per founder direction the unit is now per million tokens (credits_per_M), not per 1K.
Important — 1000× scale change. Customers who hardcoded the previous credits_per_1k_* values into FinOps spreadsheets, monitoring dashboards, or rate-card exports must either (a) divide credits_per_M by 1000 to recover the per-1K number, or (b) update math to use per-million directly.
New response shape:
chat_pricing is null and embedding_pricing carries text / visual / video per-modality rates.
60-second jq recipe — migrate a FinOps script
If you were pulling aurous_metadata.credits_per_1k_input_tokens from /v1/models, the equivalent value lives at chat_pricing.input.credits_per_M / 1000:
chat_pricing.input/output for embedding_pricing.text/visual/video.
What didn’t change
- Receipt shape on
/v1/chat/completionsand/v1/embeddingsresponses (usage,credits_charged,breakdown) is unchanged. pricing_versionin the receipt continues to snapshot the effective rate at hold time, so replays remain deterministic.- Image and video pricing on
/v1/images,/v1/videos,/v1/loras,/v1/video_lorasis unaffected by this release. Aurous-Versionpinning continues to work; chat and embedding rates intentionally track the most-recently published rate version (per spec — LLM provider economics shift on weekly cadence).

