{"openapi":"3.1.0","info":{"title":"Haven Media — pay-per-call media generation for AI agents","version":"1.0.0","description":"Self-hosted music, image, video and 3D-flythrough generation, sold per call to AI agents over x402. No account or API key: pay in USDC and collect the artifact. Settle-on-delivery — you are charged only when your finished, content-screened artifact is ready for download.","contact":{"name":"Haven","email":"support@havenagent.ai","url":"https://havenagent.ai/terms/terms-of-service"},"x-guidance":"Haven Media sells GPU media generation to AI agents for USDC over the x402 protocol — no account, no API key, no signup. You pay per call from a wallet.\n\nHOW TO USE THIS API\n1. GET /x402/api/v1/catalog (free) for live prices, request schemas, limits and policies. Prices there are authoritative.\n2. POST to the endpoint for what you want, e.g. /x402/api/v1/image/generate. Unauthenticated, it returns 402 with an x402 quote (base64 in the `Payment-Required` header). Sign the EIP-3009 authorization and retry with the **`Payment-Signature`** header — this gateway speaks x402 **v2** and does NOT accept the v1 `X-PAYMENT` header; sending it gets you `unsupported_payment_header` and a 402 loop.\n3. A verified submit returns 202 with { jobId, statusUrl, resultUrl, etaSeconds, pollBudgetSeconds }. You have NOT been charged yet.\n4. Poll statusUrl. While the job is queued or running the poll is free.\n5. When the artifact is finished and has passed content screening, that SAME status poll returns 402. This is the charge point — pay it, and the poll returns 200 with status `completed`.\n6. GET resultUrl to download the artifact. Free and unmetered.\n\nUSE ONE WALLET PER JOB\nSettle from the SAME wallet that submitted. The job is bound to the submitting payer, so a completing poll signed by a different wallet is refused with 403 `wallet_mismatch` and that job can never be settled. If you rotate wallets, do not rotate between submitting a job and collecting it.\nRe-presenting the same authorization is safe: submission is idempotent and a replay returns 200 with the existing job's status and `replayed: true`, never a duplicate job or a double charge.\n\nWHAT THIS COSTS YOU\nYou are charged only on delivery — the step-5 poll that finds your screened artifact ready. Jobs that fail, are withheld by screening, or that you abandon before it never settle. The step-6 download is free; skipping it does not undo the charge.\nIn the rare case a charge lands before the artifact reaches you, keep polling the job rather than paying again — reconciliation completes almost all of these on its own. A job it cannot resolve is worked by a human — report the jobId to support@havenagent.ai. Payments are final (x402 exact-scheme transfers are irreversible).\nThat state is visible as a 502 `settlement_indeterminate` on a settling poll; the same advice applies, and the claim is held meanwhile.\nVerify every 402 quote's payTo matches 0xa8f73f2fDFDa46f6591759458614BC2883529d77 on eip155:8453 before signing; that is what the catalog publishes it for.\n\nTIMING\nGPU workers scale to zero. The first request after an idle period can add 2-10 minutes of cold start before the job even begins. Honour the pollBudgetSeconds each endpoint advertises instead of giving up early — a job you abandon before the step-5 poll still costs you nothing, but you also get nothing.\n\nCONTENT AND RETENTION\nSFW only. Prompts and input images are moderated before the job is enqueued, so a content rejection costs nothing. Every output is screened again before payment is ever requested: images and video/world frames by an image classifier, generated audio by transcription plus a text classifier. Screening we cannot run withholds the artifact rather than serving it, and a withheld job is never charged.\nArtifacts are deleted 7 days after creation.\n\nLICENSED OUTPUT\nOutput of video, video_sound is governed by the use terms at https://havenagent.ai/terms/terms-of-service; paying accepts them, and they bind anyone you redistribute the output to.\nWhere the result response carries `X-Ai-Disclosure-Embedded: true` the file itself contains an embedded AI-generated disclosure — keep it. Where that header is `false` we could not rewrite the container safely and delivered the original bytes, so the disclosure travels only in the response envelope. Either way, disclose AI generation wherever you publish the output."},"servers":[{"url":"https://api.havenagent.ai","description":"Haven cloud gateway"}],"paths":{"/x402/api/v1/catalog":{"get":{"operationId":"catalog","summary":"List sellable endpoints, live prices, limits and request schemas.","description":"Free and unauthenticated. The machine-readable Haven catalog: every mounted endpoint with its live USD price, request/response schema, ETA, poll budget, content policy and retention policy. Read this first — prices here are authoritative and change without a version bump.","tags":["discovery"],"security":[],"responses":{"200":{"description":"Gateway catalog.","content":{"application/json":{"schema":{"type":"object","properties":{"service":{"type":"string"},"protocol":{"type":"object","description":"x402 wire parameters. `payTo` is published so a buyer can pin the recipient and reject any 402 quote that names a different address.","properties":{"x402Version":{"type":"number"},"scheme":{"type":"string"},"network":{"type":"string"},"asset":{"type":"string"},"payTo":{"type":"string"}}},"endpoints":{"type":"array","items":{"type":"object"}},"limits":{"type":"object"},"settlement":{"type":"object"},"policies":{"type":"object"}},"required":["service","protocol","endpoints"]}}}},"429":{"description":"`rate_limit_exceeded` — the gateway's shared burst limiter (600 requests/minute per caller) shed this request before it reached any handler. Applies to every route here, free ones included. `retryAfterMs` is in the body; back off and retry."},"500":{"description":"Unexpected gateway error. Do not infer a payment state from it — re-poll the job's `statusUrl` to establish where the job actually stands before signing anything new."}}}},"/x402/agents.txt":{"get":{"operationId":"agents_txt","summary":"Human/agent-readable summary of the gateway and its prices.","description":"Free and unauthenticated. Plain-text mirror of the catalog's headline facts.","tags":["discovery"],"security":[],"responses":{"200":{"description":"Plain-text gateway summary.","content":{"text/plain":{"schema":{"type":"string"}}}},"429":{"description":"`rate_limit_exceeded` — the gateway's shared burst limiter (600 requests/minute per caller) shed this request before it reached any handler. Applies to every route here, free ones included. `retryAfterMs` is in the body; back off and retry."},"500":{"description":"Unexpected gateway error. Do not infer a payment state from it — re-poll the job's `statusUrl` to establish where the job actually stands before signing anything new."}}}},"/x402/api/v1/music":{"post":{"operationId":"submit_music","summary":"Generate a music track. Poll statusUrl, download at resultUrl. SFW only.","description":"Generate a music track. Poll statusUrl, download at resultUrl. SFW only.\nSubmitting VERIFIES your x402 authorization but does not charge you — under settle-on-delivery the completing status poll is the charge point. An unauthenticated request returns 402 with the quote to sign. A verified submit returns 202 with a `jobId`, a `statusUrl` to poll and a `resultUrl` to download from.\nSubmission is idempotent: re-presenting the SAME authorization (say, after you lost the first response to a timeout) does not create a second job and does not charge you twice. That replay answers **200** with the existing job's current status and `replayed: true` — not another 202 — so accept both.\nSettle from the SAME wallet that submitted. The job is bound to the submitting payer; a completing poll signed by a different wallet is refused with 403 `wallet_mismatch` and can never settle this job.\nTypical time to a finished artifact is ~60s, but GPU workers scale to zero, so the first request after an idle period can add 2-10 minutes before the job even starts. Poll for up to `pollBudgetSeconds` (900s) rather than giving up early.\nSFW only. The prompt and any input image are moderated BEFORE the job is enqueued, so a content rejection happens before any charge.","tags":["media generation"],"x-payment-info":{"price":{"mode":"fixed","currency":"USD","amount":"0.10"},"protocols":[{"x402":{}}]},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["prompt"],"properties":{"prompt":{"type":"string","minLength":1,"maxLength":2000},"lyrics":{"type":"string","maxLength":5000},"durationSeconds":{"type":"number","minimum":1,"maximum":60,"default":20}}}}}},"responses":{"200":{"description":"Idempotent replay: this authorization was already accepted, so no second job was created and you were not charged twice. Body is the existing job's current status with `replayed: true`.","content":{"application/json":{"schema":{"allOf":[{"type":"object","properties":{"jobId":{"type":"string"},"kind":{"type":"string","enum":["music","image_generate","image_edit","image_upscale","image_restore","video","video_sound"]},"createdAt":{"type":"string","description":"Job creation time — the start of the 7-day retention clock."},"status":{"type":"string","enum":["queued","running","completed","failed","expired"],"description":"`completed` is only ever returned AFTER the settling payment succeeds — an unpaid poll on a finished artifact returns 402 instead."},"statusUrl":{"type":"string"},"resultUrl":{"type":"string"},"pollBudgetSeconds":{"type":"number"},"pollBudgetRemainingSeconds":{"type":"number"},"error":{"type":"string","description":"Present when `status` is `failed`."},"settled":{"type":"boolean"},"output":{"type":"object","description":"Rendered output shape. Present on completed video kinds only.","properties":{"width":{"type":"number"},"height":{"type":"number"},"numFrames":{"type":"number"},"fps":{"type":"number"},"durationSeconds":{"type":"number","description":"numFrames / fps — the delivered length."}}},"aiDisclosure":{"type":"object","properties":{"aiGenerated":{"type":"boolean"},"label":{"type":"string"},"statement":{"type":"string"},"embeddedIn":{"type":"string"},"termsUrl":{"type":"string"}},"required":["aiGenerated","statement","termsUrl"]}},"required":["jobId","kind","createdAt","status","statusUrl","resultUrl","pollBudgetSeconds"]}],"properties":{"replayed":{"type":"boolean"}}}}}},"202":{"description":"Authorization verified and job enqueued. You have NOT been charged yet — poll `statusUrl`.","content":{"application/json":{"schema":{"type":"object","properties":{"jobId":{"type":"string"},"kind":{"type":"string","enum":["music","image_generate","image_edit","image_upscale","image_restore","video","video_sound"]},"status":{"type":"string"},"etaSeconds":{"type":"number"},"pollBudgetSeconds":{"type":"number"},"statusUrl":{"type":"string"},"resultUrl":{"type":"string"},"aiDisclosure":{"type":"object","properties":{"aiGenerated":{"type":"boolean"},"label":{"type":"string"},"statement":{"type":"string"},"embeddedIn":{"type":"string"},"termsUrl":{"type":"string"}},"required":["aiGenerated","statement","termsUrl"]}},"required":["jobId","status","statusUrl","resultUrl","etaSeconds","pollBudgetSeconds"]}}}},"400":{"description":"The body parsed but was invalid — a missing/oversized FIELD, a bad mediaType, or image bytes that did not decode. No charge."},"402":{"description":"Payment Required"},"403":{"description":"`wallet_blocked` — this payer is blocklisted. The request was cancelled before any work or settlement; nothing was charged."},"413":{"description":"`request_too_large` — the request body exceeded the read cap (64 KiB for the music kinds, 20 MiB for kinds accepting an input image) and was rejected before parsing. Send a smaller image. No charge."},"422":{"description":"The prompt or input image was rejected by the SFW content policy, before the job was enqueued. No charge; retrying the same content will fail again."},"429":{"description":"Two causes — check `error`. `rate_limit_exceeded`: the shared 600/minute burst limiter; back off per `retryAfterMs`. Otherwise a per-wallet limit — too many jobs submitted-but-undelivered, or too many rejected attempts this hour — with `Retry-After` set; backing off will not help until you collect or abandon your outstanding jobs."},"500":{"description":"Unexpected gateway error. Do not infer a payment state from it — re-poll the job's `statusUrl` to establish where the job actually stands before signing anything new."},"502":{"description":"`facilitator_unavailable` — the x402 payment facilitator could not be reached. Transient and on our side; nothing was charged. Retry."},"503":{"description":"Transient and retryable. On a FIRST submit the causes are pre-enqueue — `queue_full` (render queue at capacity, `Retry-After` set), content screening unavailable, or the kind not configured here — and nothing was charged. On a REPLAY of an authorization that already settled, this means the existing job could not be read right now: you may already have paid, so re-poll `statusUrl` rather than signing a replacement authorization."}}}},"/x402/api/v1/music/jobs/{jobId}":{"get":{"operationId":"status_music","summary":"Poll a music job — and settle it when the artifact is ready.","description":"Free while the job is queued or running: returns 200 with a `status` of `queued` or `running`.\n\nTHIS IS THE CHARGE POINT. Once the artifact is finished and has passed output content screening, this same poll returns 402 with the quote you already agreed to at submit. Pay it and the poll returns 200 `completed`; then download from `resultUrl` for free.\n\nYou are charged only on delivery — this poll, when it finds your screened artifact ready to download. Jobs that fail, are withheld by content screening, or that you abandon before this poll never settle; once it has been paid, the free `resultUrl` download does not affect the charge. In the rare case a charge lands before the artifact reaches you, keep polling the job rather than paying again — reconciliation completes almost all of these on its own. A job it cannot resolve is worked by a human — report the jobId to support@havenagent.ai. Because the 402 here is conditional on a finished artifact, this operation is not declared payable in this document — an unpaid poll of an unfinished job is genuinely free, and an unknown job id returns 404.","tags":["media generation"],"security":[],"parameters":[{"name":"jobId","in":"path","required":true,"description":"Job id returned by the submit call's 202 response.","schema":{"type":"string"}}],"responses":{"200":{"description":"Job status. `completed` is only returned once the job has settled.","content":{"application/json":{"schema":{"type":"object","properties":{"jobId":{"type":"string"},"kind":{"type":"string","enum":["music","image_generate","image_edit","image_upscale","image_restore","video","video_sound"]},"createdAt":{"type":"string","description":"Job creation time — the start of the 7-day retention clock."},"status":{"type":"string","enum":["queued","running","completed","failed","expired"],"description":"`completed` is only ever returned AFTER the settling payment succeeds — an unpaid poll on a finished artifact returns 402 instead."},"statusUrl":{"type":"string"},"resultUrl":{"type":"string"},"pollBudgetSeconds":{"type":"number"},"pollBudgetRemainingSeconds":{"type":"number"},"error":{"type":"string","description":"Present when `status` is `failed`."},"settled":{"type":"boolean"},"output":{"type":"object","description":"Rendered output shape. Present on completed video kinds only.","properties":{"width":{"type":"number"},"height":{"type":"number"},"numFrames":{"type":"number"},"fps":{"type":"number"},"durationSeconds":{"type":"number","description":"numFrames / fps — the delivered length."}}},"aiDisclosure":{"type":"object","properties":{"aiGenerated":{"type":"boolean"},"label":{"type":"string"},"statement":{"type":"string"},"embeddedIn":{"type":"string"},"termsUrl":{"type":"string"}},"required":["aiGenerated","statement","termsUrl"]}},"required":["jobId","kind","createdAt","status","statusUrl","resultUrl","pollBudgetSeconds"]}}}},"402":{"description":"Payment Required — the artifact is finished and deliverable; this poll settles it."},"403":{"description":"`wallet_mismatch` — this job was submitted by a different wallet and can only be paid by that one; signing a fresh quote from another wallet will never settle it, so settle from the submitting wallet. `wallet_blocked` — the payer is blocklisted. Neither was charged."},"404":{"description":"No such job (or it was swept after retention)."},"409":{"description":"Retryable settlement states, all expected under concurrency: `settlement_in_progress` (another poll holds the claim) and `settlement_reopened` (a held claim was released by reconciliation) — both carry `Retry-After`, so poll again. `authorization_bound_to_other_job` means you reused a signature already tied to a different job; sign a fresh authorization for THIS job."},"429":{"description":"`rate_limit_exceeded` — the gateway's shared burst limiter (600 requests/minute per caller) shed this request before it reached any handler. Applies to every route here, free ones included. `retryAfterMs` is in the body; back off and retry."},"500":{"description":"Unexpected gateway error. Do not infer a payment state from it — re-poll the job's `statusUrl` to establish where the job actually stands before signing anything new."},"502":{"description":"Two distinct outcomes — check `error`. `facilitator_unavailable`: the facilitator could not be reached, settlement never started, nothing was charged; retry. `settlement_indeterminate`: the settle call's outcome is unknown and the transfer MAY ALREADY HAVE MOVED — do not assume you were not charged and do not re-sign a replacement authorization; the claim is held, so keep polling — reconciliation completes almost all of these on its own. A job it cannot resolve is worked by a human — report the jobId to support@havenagent.ai."},"503":{"description":"Two distinct outcomes — check `error`. `moderation_rejected` with category `moderation_unavailable`: output screening is down, settlement never happened, no charge; retry. `settlement_unavailable`: this job's settlement is being reconciled and the payment may already have moved — do not assume you were not charged and do not sign a replacement authorization; `Retry-After` is set, so keep polling — reconciliation completes almost all of these on its own. A job it cannot resolve is worked by a human — report the jobId to support@havenagent.ai."}}}},"/x402/api/v1/music/jobs/{jobId}/result":{"get":{"operationId":"result_music","summary":"Download a settled music artifact.","description":"Free and unmetered once the job has settled. The concrete `Content-Type` is set per artifact. Artifacts are deleted 7 days after creation — download promptly.\n\nThis route never issues a 402 — it has no payment leg. If the job is unpaid you get `409 not_paid` with the `statusUrl` to settle on; if it is still rendering you get `409 not_ready`. Settle on the status route, then come back here.","tags":["media generation"],"security":[],"parameters":[{"name":"jobId","in":"path","required":true,"description":"Job id returned by the submit call's 202 response.","schema":{"type":"string"}}],"responses":{"200":{"description":"The generated artifact.","content":{"audio/mpeg":{"schema":{"type":"string","format":"binary"}},"audio/flac":{"schema":{"type":"string","format":"binary"}},"audio/wav":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"No such job."},"409":{"description":"`not_ready` (still rendering) or `not_paid` (finished but unsettled — settle on `statusUrl`, which is included in the body). Also `artifact_changed`: the stored bytes no longer match the hash they were screened against, so the stale verdict was cleared and the artifact is being re-screened — you are already settled, so simply retry this download rather than returning to the payment flow."},"410":{"description":"Job failed, output withheld by content screening, or the artifact expired (7-day retention)."},"429":{"description":"`rate_limit_exceeded` — the gateway's shared burst limiter (600 requests/minute per caller) shed this request before it reached any handler. Applies to every route here, free ones included. `retryAfterMs` is in the body; back off and retry."},"500":{"description":"Unexpected gateway error. Do not infer a payment state from it — re-poll the job's `statusUrl` to establish where the job actually stands before signing anything new."},"503":{"description":"Transient and retryable: output content screening is temporarily unavailable, or the artifact metadata could not be read. The job is not lost — retry."}}}},"/x402/api/v1/image/generate":{"post":{"operationId":"submit_image_generate","summary":"Generate an image from text. SFW only.","description":"Generate an image from text. SFW only.\nSubmitting VERIFIES your x402 authorization but does not charge you — under settle-on-delivery the completing status poll is the charge point. An unauthenticated request returns 402 with the quote to sign. A verified submit returns 202 with a `jobId`, a `statusUrl` to poll and a `resultUrl` to download from.\nSubmission is idempotent: re-presenting the SAME authorization (say, after you lost the first response to a timeout) does not create a second job and does not charge you twice. That replay answers **200** with the existing job's current status and `replayed: true` — not another 202 — so accept both.\nSettle from the SAME wallet that submitted. The job is bound to the submitting payer; a completing poll signed by a different wallet is refused with 403 `wallet_mismatch` and can never settle this job.\nTypical time to a finished artifact is ~120s, but GPU workers scale to zero, so the first request after an idle period can add 2-10 minutes before the job even starts. Poll for up to `pollBudgetSeconds` (900s) rather than giving up early.\nSFW only. The prompt and any input image are moderated BEFORE the job is enqueued, so a content rejection happens before any charge.","tags":["media generation"],"x-payment-info":{"price":{"mode":"fixed","currency":"USD","amount":"0.05"},"protocols":[{"x402":{}}]},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["prompt"],"properties":{"prompt":{"type":"string","minLength":1,"maxLength":2000},"width":{"type":"number","minimum":256,"maximum":1536,"default":1024},"height":{"type":"number","minimum":256,"maximum":1536,"default":1024}}}}}},"responses":{"200":{"description":"Idempotent replay: this authorization was already accepted, so no second job was created and you were not charged twice. Body is the existing job's current status with `replayed: true`.","content":{"application/json":{"schema":{"allOf":[{"type":"object","properties":{"jobId":{"type":"string"},"kind":{"type":"string","enum":["music","image_generate","image_edit","image_upscale","image_restore","video","video_sound"]},"createdAt":{"type":"string","description":"Job creation time — the start of the 7-day retention clock."},"status":{"type":"string","enum":["queued","running","completed","failed","expired"],"description":"`completed` is only ever returned AFTER the settling payment succeeds — an unpaid poll on a finished artifact returns 402 instead."},"statusUrl":{"type":"string"},"resultUrl":{"type":"string"},"pollBudgetSeconds":{"type":"number"},"pollBudgetRemainingSeconds":{"type":"number"},"error":{"type":"string","description":"Present when `status` is `failed`."},"settled":{"type":"boolean"},"output":{"type":"object","description":"Rendered output shape. Present on completed video kinds only.","properties":{"width":{"type":"number"},"height":{"type":"number"},"numFrames":{"type":"number"},"fps":{"type":"number"},"durationSeconds":{"type":"number","description":"numFrames / fps — the delivered length."}}},"aiDisclosure":{"type":"object","properties":{"aiGenerated":{"type":"boolean"},"label":{"type":"string"},"statement":{"type":"string"},"embeddedIn":{"type":"string"},"termsUrl":{"type":"string"}},"required":["aiGenerated","statement","termsUrl"]}},"required":["jobId","kind","createdAt","status","statusUrl","resultUrl","pollBudgetSeconds"]}],"properties":{"replayed":{"type":"boolean"}}}}}},"202":{"description":"Authorization verified and job enqueued. You have NOT been charged yet — poll `statusUrl`.","content":{"application/json":{"schema":{"type":"object","properties":{"jobId":{"type":"string"},"kind":{"type":"string","enum":["music","image_generate","image_edit","image_upscale","image_restore","video","video_sound"]},"status":{"type":"string"},"etaSeconds":{"type":"number"},"pollBudgetSeconds":{"type":"number"},"statusUrl":{"type":"string"},"resultUrl":{"type":"string"},"aiDisclosure":{"type":"object","properties":{"aiGenerated":{"type":"boolean"},"label":{"type":"string"},"statement":{"type":"string"},"embeddedIn":{"type":"string"},"termsUrl":{"type":"string"}},"required":["aiGenerated","statement","termsUrl"]}},"required":["jobId","status","statusUrl","resultUrl","etaSeconds","pollBudgetSeconds"]}}}},"400":{"description":"The body parsed but was invalid — a missing/oversized FIELD, a bad mediaType, or image bytes that did not decode. No charge."},"402":{"description":"Payment Required"},"403":{"description":"`wallet_blocked` — this payer is blocklisted. The request was cancelled before any work or settlement; nothing was charged."},"413":{"description":"`request_too_large` — the request body exceeded the read cap (64 KiB for the music kinds, 20 MiB for kinds accepting an input image) and was rejected before parsing. Send a smaller image. No charge."},"422":{"description":"The prompt or input image was rejected by the SFW content policy, before the job was enqueued. No charge; retrying the same content will fail again."},"429":{"description":"Two causes — check `error`. `rate_limit_exceeded`: the shared 600/minute burst limiter; back off per `retryAfterMs`. Otherwise a per-wallet limit — too many jobs submitted-but-undelivered, or too many rejected attempts this hour — with `Retry-After` set; backing off will not help until you collect or abandon your outstanding jobs."},"500":{"description":"Unexpected gateway error. Do not infer a payment state from it — re-poll the job's `statusUrl` to establish where the job actually stands before signing anything new."},"502":{"description":"`facilitator_unavailable` — the x402 payment facilitator could not be reached. Transient and on our side; nothing was charged. Retry."},"503":{"description":"Transient and retryable. On a FIRST submit the causes are pre-enqueue — `queue_full` (render queue at capacity, `Retry-After` set), content screening unavailable, or the kind not configured here — and nothing was charged. On a REPLAY of an authorization that already settled, this means the existing job could not be read right now: you may already have paid, so re-poll `statusUrl` rather than signing a replacement authorization."}}}},"/x402/api/v1/image/generate/jobs/{jobId}":{"get":{"operationId":"status_image_generate","summary":"Poll a image_generate job — and settle it when the artifact is ready.","description":"Free while the job is queued or running: returns 200 with a `status` of `queued` or `running`.\n\nTHIS IS THE CHARGE POINT. Once the artifact is finished and has passed output content screening, this same poll returns 402 with the quote you already agreed to at submit. Pay it and the poll returns 200 `completed`; then download from `resultUrl` for free.\n\nYou are charged only on delivery — this poll, when it finds your screened artifact ready to download. Jobs that fail, are withheld by content screening, or that you abandon before this poll never settle; once it has been paid, the free `resultUrl` download does not affect the charge. In the rare case a charge lands before the artifact reaches you, keep polling the job rather than paying again — reconciliation completes almost all of these on its own. A job it cannot resolve is worked by a human — report the jobId to support@havenagent.ai. Because the 402 here is conditional on a finished artifact, this operation is not declared payable in this document — an unpaid poll of an unfinished job is genuinely free, and an unknown job id returns 404.","tags":["media generation"],"security":[],"parameters":[{"name":"jobId","in":"path","required":true,"description":"Job id returned by the submit call's 202 response.","schema":{"type":"string"}}],"responses":{"200":{"description":"Job status. `completed` is only returned once the job has settled.","content":{"application/json":{"schema":{"type":"object","properties":{"jobId":{"type":"string"},"kind":{"type":"string","enum":["music","image_generate","image_edit","image_upscale","image_restore","video","video_sound"]},"createdAt":{"type":"string","description":"Job creation time — the start of the 7-day retention clock."},"status":{"type":"string","enum":["queued","running","completed","failed","expired"],"description":"`completed` is only ever returned AFTER the settling payment succeeds — an unpaid poll on a finished artifact returns 402 instead."},"statusUrl":{"type":"string"},"resultUrl":{"type":"string"},"pollBudgetSeconds":{"type":"number"},"pollBudgetRemainingSeconds":{"type":"number"},"error":{"type":"string","description":"Present when `status` is `failed`."},"settled":{"type":"boolean"},"output":{"type":"object","description":"Rendered output shape. Present on completed video kinds only.","properties":{"width":{"type":"number"},"height":{"type":"number"},"numFrames":{"type":"number"},"fps":{"type":"number"},"durationSeconds":{"type":"number","description":"numFrames / fps — the delivered length."}}},"aiDisclosure":{"type":"object","properties":{"aiGenerated":{"type":"boolean"},"label":{"type":"string"},"statement":{"type":"string"},"embeddedIn":{"type":"string"},"termsUrl":{"type":"string"}},"required":["aiGenerated","statement","termsUrl"]}},"required":["jobId","kind","createdAt","status","statusUrl","resultUrl","pollBudgetSeconds"]}}}},"402":{"description":"Payment Required — the artifact is finished and deliverable; this poll settles it."},"403":{"description":"`wallet_mismatch` — this job was submitted by a different wallet and can only be paid by that one; signing a fresh quote from another wallet will never settle it, so settle from the submitting wallet. `wallet_blocked` — the payer is blocklisted. Neither was charged."},"404":{"description":"No such job (or it was swept after retention)."},"409":{"description":"Retryable settlement states, all expected under concurrency: `settlement_in_progress` (another poll holds the claim) and `settlement_reopened` (a held claim was released by reconciliation) — both carry `Retry-After`, so poll again. `authorization_bound_to_other_job` means you reused a signature already tied to a different job; sign a fresh authorization for THIS job."},"429":{"description":"`rate_limit_exceeded` — the gateway's shared burst limiter (600 requests/minute per caller) shed this request before it reached any handler. Applies to every route here, free ones included. `retryAfterMs` is in the body; back off and retry."},"500":{"description":"Unexpected gateway error. Do not infer a payment state from it — re-poll the job's `statusUrl` to establish where the job actually stands before signing anything new."},"502":{"description":"Two distinct outcomes — check `error`. `facilitator_unavailable`: the facilitator could not be reached, settlement never started, nothing was charged; retry. `settlement_indeterminate`: the settle call's outcome is unknown and the transfer MAY ALREADY HAVE MOVED — do not assume you were not charged and do not re-sign a replacement authorization; the claim is held, so keep polling — reconciliation completes almost all of these on its own. A job it cannot resolve is worked by a human — report the jobId to support@havenagent.ai."},"503":{"description":"Two distinct outcomes — check `error`. `moderation_rejected` with category `moderation_unavailable`: output screening is down, settlement never happened, no charge; retry. `settlement_unavailable`: this job's settlement is being reconciled and the payment may already have moved — do not assume you were not charged and do not sign a replacement authorization; `Retry-After` is set, so keep polling — reconciliation completes almost all of these on its own. A job it cannot resolve is worked by a human — report the jobId to support@havenagent.ai."}}}},"/x402/api/v1/image/generate/jobs/{jobId}/result":{"get":{"operationId":"result_image_generate","summary":"Download a settled image_generate artifact.","description":"Free and unmetered once the job has settled. The concrete `Content-Type` is set per artifact. Artifacts are deleted 7 days after creation — download promptly.\n\nThis route never issues a 402 — it has no payment leg. If the job is unpaid you get `409 not_paid` with the `statusUrl` to settle on; if it is still rendering you get `409 not_ready`. Settle on the status route, then come back here.","tags":["media generation"],"security":[],"parameters":[{"name":"jobId","in":"path","required":true,"description":"Job id returned by the submit call's 202 response.","schema":{"type":"string"}}],"responses":{"200":{"description":"The generated artifact.","content":{"image/png":{"schema":{"type":"string","format":"binary"}},"image/jpeg":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"No such job."},"409":{"description":"`not_ready` (still rendering) or `not_paid` (finished but unsettled — settle on `statusUrl`, which is included in the body). Also `artifact_changed`: the stored bytes no longer match the hash they were screened against, so the stale verdict was cleared and the artifact is being re-screened — you are already settled, so simply retry this download rather than returning to the payment flow."},"410":{"description":"Job failed, output withheld by content screening, or the artifact expired (7-day retention)."},"429":{"description":"`rate_limit_exceeded` — the gateway's shared burst limiter (600 requests/minute per caller) shed this request before it reached any handler. Applies to every route here, free ones included. `retryAfterMs` is in the body; back off and retry."},"500":{"description":"Unexpected gateway error. Do not infer a payment state from it — re-poll the job's `statusUrl` to establish where the job actually stands before signing anything new."},"503":{"description":"Transient and retryable: output content screening is temporarily unavailable, or the artifact metadata could not be read. The job is not lost — retry."}}}},"/x402/api/v1/image/edit":{"post":{"operationId":"submit_image_edit","summary":"Edit an image from a text instruction. SFW only.","description":"Edit an image from a text instruction. SFW only.\nSubmitting VERIFIES your x402 authorization but does not charge you — under settle-on-delivery the completing status poll is the charge point. An unauthenticated request returns 402 with the quote to sign. A verified submit returns 202 with a `jobId`, a `statusUrl` to poll and a `resultUrl` to download from.\nSubmission is idempotent: re-presenting the SAME authorization (say, after you lost the first response to a timeout) does not create a second job and does not charge you twice. That replay answers **200** with the existing job's current status and `replayed: true` — not another 202 — so accept both.\nSettle from the SAME wallet that submitted. The job is bound to the submitting payer; a completing poll signed by a different wallet is refused with 403 `wallet_mismatch` and can never settle this job.\nTypical time to a finished artifact is ~300s, but GPU workers scale to zero, so the first request after an idle period can add 2-10 minutes before the job even starts. Poll for up to `pollBudgetSeconds` (1200s) rather than giving up early.\nSFW only. The prompt and any input image are moderated BEFORE the job is enqueued, so a content rejection happens before any charge.","tags":["media generation"],"x-payment-info":{"price":{"mode":"fixed","currency":"USD","amount":"0.10"},"protocols":[{"x402":{}}]},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["prompt","imageBase64","mediaType"],"properties":{"prompt":{"type":"string","minLength":1,"maxLength":2000},"imageBase64":{"type":"string","description":"base64 image bytes, no data-URI prefix"},"mediaType":{"type":"string","enum":["image/png","image/jpeg"]},"negativePrompt":{"type":"string"}}}}}},"responses":{"200":{"description":"Idempotent replay: this authorization was already accepted, so no second job was created and you were not charged twice. Body is the existing job's current status with `replayed: true`.","content":{"application/json":{"schema":{"allOf":[{"type":"object","properties":{"jobId":{"type":"string"},"kind":{"type":"string","enum":["music","image_generate","image_edit","image_upscale","image_restore","video","video_sound"]},"createdAt":{"type":"string","description":"Job creation time — the start of the 7-day retention clock."},"status":{"type":"string","enum":["queued","running","completed","failed","expired"],"description":"`completed` is only ever returned AFTER the settling payment succeeds — an unpaid poll on a finished artifact returns 402 instead."},"statusUrl":{"type":"string"},"resultUrl":{"type":"string"},"pollBudgetSeconds":{"type":"number"},"pollBudgetRemainingSeconds":{"type":"number"},"error":{"type":"string","description":"Present when `status` is `failed`."},"settled":{"type":"boolean"},"output":{"type":"object","description":"Rendered output shape. Present on completed video kinds only.","properties":{"width":{"type":"number"},"height":{"type":"number"},"numFrames":{"type":"number"},"fps":{"type":"number"},"durationSeconds":{"type":"number","description":"numFrames / fps — the delivered length."}}},"aiDisclosure":{"type":"object","properties":{"aiGenerated":{"type":"boolean"},"label":{"type":"string"},"statement":{"type":"string"},"embeddedIn":{"type":"string"},"termsUrl":{"type":"string"}},"required":["aiGenerated","statement","termsUrl"]}},"required":["jobId","kind","createdAt","status","statusUrl","resultUrl","pollBudgetSeconds"]}],"properties":{"replayed":{"type":"boolean"}}}}}},"202":{"description":"Authorization verified and job enqueued. You have NOT been charged yet — poll `statusUrl`.","content":{"application/json":{"schema":{"type":"object","properties":{"jobId":{"type":"string"},"kind":{"type":"string","enum":["music","image_generate","image_edit","image_upscale","image_restore","video","video_sound"]},"status":{"type":"string"},"etaSeconds":{"type":"number"},"pollBudgetSeconds":{"type":"number"},"statusUrl":{"type":"string"},"resultUrl":{"type":"string"},"aiDisclosure":{"type":"object","properties":{"aiGenerated":{"type":"boolean"},"label":{"type":"string"},"statement":{"type":"string"},"embeddedIn":{"type":"string"},"termsUrl":{"type":"string"}},"required":["aiGenerated","statement","termsUrl"]}},"required":["jobId","status","statusUrl","resultUrl","etaSeconds","pollBudgetSeconds"]}}}},"400":{"description":"The body parsed but was invalid — a missing/oversized FIELD, a bad mediaType, or image bytes that did not decode. No charge."},"402":{"description":"Payment Required"},"403":{"description":"`wallet_blocked` — this payer is blocklisted. The request was cancelled before any work or settlement; nothing was charged."},"413":{"description":"`request_too_large` — the request body exceeded the read cap (64 KiB for the music kinds, 20 MiB for kinds accepting an input image) and was rejected before parsing. Send a smaller image. No charge."},"422":{"description":"The prompt or input image was rejected by the SFW content policy, before the job was enqueued. No charge; retrying the same content will fail again."},"429":{"description":"Two causes — check `error`. `rate_limit_exceeded`: the shared 600/minute burst limiter; back off per `retryAfterMs`. Otherwise a per-wallet limit — too many jobs submitted-but-undelivered, or too many rejected attempts this hour — with `Retry-After` set; backing off will not help until you collect or abandon your outstanding jobs."},"500":{"description":"Unexpected gateway error. Do not infer a payment state from it — re-poll the job's `statusUrl` to establish where the job actually stands before signing anything new."},"502":{"description":"`facilitator_unavailable` — the x402 payment facilitator could not be reached. Transient and on our side; nothing was charged. Retry."},"503":{"description":"Transient and retryable. On a FIRST submit the causes are pre-enqueue — `queue_full` (render queue at capacity, `Retry-After` set), content screening unavailable, or the kind not configured here — and nothing was charged. On a REPLAY of an authorization that already settled, this means the existing job could not be read right now: you may already have paid, so re-poll `statusUrl` rather than signing a replacement authorization."}}}},"/x402/api/v1/image/edit/jobs/{jobId}":{"get":{"operationId":"status_image_edit","summary":"Poll a image_edit job — and settle it when the artifact is ready.","description":"Free while the job is queued or running: returns 200 with a `status` of `queued` or `running`.\n\nTHIS IS THE CHARGE POINT. Once the artifact is finished and has passed output content screening, this same poll returns 402 with the quote you already agreed to at submit. Pay it and the poll returns 200 `completed`; then download from `resultUrl` for free.\n\nYou are charged only on delivery — this poll, when it finds your screened artifact ready to download. Jobs that fail, are withheld by content screening, or that you abandon before this poll never settle; once it has been paid, the free `resultUrl` download does not affect the charge. In the rare case a charge lands before the artifact reaches you, keep polling the job rather than paying again — reconciliation completes almost all of these on its own. A job it cannot resolve is worked by a human — report the jobId to support@havenagent.ai. Because the 402 here is conditional on a finished artifact, this operation is not declared payable in this document — an unpaid poll of an unfinished job is genuinely free, and an unknown job id returns 404.","tags":["media generation"],"security":[],"parameters":[{"name":"jobId","in":"path","required":true,"description":"Job id returned by the submit call's 202 response.","schema":{"type":"string"}}],"responses":{"200":{"description":"Job status. `completed` is only returned once the job has settled.","content":{"application/json":{"schema":{"type":"object","properties":{"jobId":{"type":"string"},"kind":{"type":"string","enum":["music","image_generate","image_edit","image_upscale","image_restore","video","video_sound"]},"createdAt":{"type":"string","description":"Job creation time — the start of the 7-day retention clock."},"status":{"type":"string","enum":["queued","running","completed","failed","expired"],"description":"`completed` is only ever returned AFTER the settling payment succeeds — an unpaid poll on a finished artifact returns 402 instead."},"statusUrl":{"type":"string"},"resultUrl":{"type":"string"},"pollBudgetSeconds":{"type":"number"},"pollBudgetRemainingSeconds":{"type":"number"},"error":{"type":"string","description":"Present when `status` is `failed`."},"settled":{"type":"boolean"},"output":{"type":"object","description":"Rendered output shape. Present on completed video kinds only.","properties":{"width":{"type":"number"},"height":{"type":"number"},"numFrames":{"type":"number"},"fps":{"type":"number"},"durationSeconds":{"type":"number","description":"numFrames / fps — the delivered length."}}},"aiDisclosure":{"type":"object","properties":{"aiGenerated":{"type":"boolean"},"label":{"type":"string"},"statement":{"type":"string"},"embeddedIn":{"type":"string"},"termsUrl":{"type":"string"}},"required":["aiGenerated","statement","termsUrl"]}},"required":["jobId","kind","createdAt","status","statusUrl","resultUrl","pollBudgetSeconds"]}}}},"402":{"description":"Payment Required — the artifact is finished and deliverable; this poll settles it."},"403":{"description":"`wallet_mismatch` — this job was submitted by a different wallet and can only be paid by that one; signing a fresh quote from another wallet will never settle it, so settle from the submitting wallet. `wallet_blocked` — the payer is blocklisted. Neither was charged."},"404":{"description":"No such job (or it was swept after retention)."},"409":{"description":"Retryable settlement states, all expected under concurrency: `settlement_in_progress` (another poll holds the claim) and `settlement_reopened` (a held claim was released by reconciliation) — both carry `Retry-After`, so poll again. `authorization_bound_to_other_job` means you reused a signature already tied to a different job; sign a fresh authorization for THIS job."},"429":{"description":"`rate_limit_exceeded` — the gateway's shared burst limiter (600 requests/minute per caller) shed this request before it reached any handler. Applies to every route here, free ones included. `retryAfterMs` is in the body; back off and retry."},"500":{"description":"Unexpected gateway error. Do not infer a payment state from it — re-poll the job's `statusUrl` to establish where the job actually stands before signing anything new."},"502":{"description":"Two distinct outcomes — check `error`. `facilitator_unavailable`: the facilitator could not be reached, settlement never started, nothing was charged; retry. `settlement_indeterminate`: the settle call's outcome is unknown and the transfer MAY ALREADY HAVE MOVED — do not assume you were not charged and do not re-sign a replacement authorization; the claim is held, so keep polling — reconciliation completes almost all of these on its own. A job it cannot resolve is worked by a human — report the jobId to support@havenagent.ai."},"503":{"description":"Two distinct outcomes — check `error`. `moderation_rejected` with category `moderation_unavailable`: output screening is down, settlement never happened, no charge; retry. `settlement_unavailable`: this job's settlement is being reconciled and the payment may already have moved — do not assume you were not charged and do not sign a replacement authorization; `Retry-After` is set, so keep polling — reconciliation completes almost all of these on its own. A job it cannot resolve is worked by a human — report the jobId to support@havenagent.ai."}}}},"/x402/api/v1/image/edit/jobs/{jobId}/result":{"get":{"operationId":"result_image_edit","summary":"Download a settled image_edit artifact.","description":"Free and unmetered once the job has settled. The concrete `Content-Type` is set per artifact. Artifacts are deleted 7 days after creation — download promptly.\n\nThis route never issues a 402 — it has no payment leg. If the job is unpaid you get `409 not_paid` with the `statusUrl` to settle on; if it is still rendering you get `409 not_ready`. Settle on the status route, then come back here.","tags":["media generation"],"security":[],"parameters":[{"name":"jobId","in":"path","required":true,"description":"Job id returned by the submit call's 202 response.","schema":{"type":"string"}}],"responses":{"200":{"description":"The generated artifact.","content":{"image/png":{"schema":{"type":"string","format":"binary"}},"image/jpeg":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"No such job."},"409":{"description":"`not_ready` (still rendering) or `not_paid` (finished but unsettled — settle on `statusUrl`, which is included in the body). Also `artifact_changed`: the stored bytes no longer match the hash they were screened against, so the stale verdict was cleared and the artifact is being re-screened — you are already settled, so simply retry this download rather than returning to the payment flow."},"410":{"description":"Job failed, output withheld by content screening, or the artifact expired (7-day retention)."},"429":{"description":"`rate_limit_exceeded` — the gateway's shared burst limiter (600 requests/minute per caller) shed this request before it reached any handler. Applies to every route here, free ones included. `retryAfterMs` is in the body; back off and retry."},"500":{"description":"Unexpected gateway error. Do not infer a payment state from it — re-poll the job's `statusUrl` to establish where the job actually stands before signing anything new."},"503":{"description":"Transient and retryable: output content screening is temporarily unavailable, or the artifact metadata could not be read. The job is not lost — retry."}}}},"/x402/api/v1/image/upscale":{"post":{"operationId":"submit_image_upscale","summary":"Upscale an image 2x or 4x. SFW only.","description":"Upscale an image 2x or 4x. SFW only.\nSubmitting VERIFIES your x402 authorization but does not charge you — under settle-on-delivery the completing status poll is the charge point. An unauthenticated request returns 402 with the quote to sign. A verified submit returns 202 with a `jobId`, a `statusUrl` to poll and a `resultUrl` to download from.\nSubmission is idempotent: re-presenting the SAME authorization (say, after you lost the first response to a timeout) does not create a second job and does not charge you twice. That replay answers **200** with the existing job's current status and `replayed: true` — not another 202 — so accept both.\nSettle from the SAME wallet that submitted. The job is bound to the submitting payer; a completing poll signed by a different wallet is refused with 403 `wallet_mismatch` and can never settle this job.\nTypical time to a finished artifact is ~300s, but GPU workers scale to zero, so the first request after an idle period can add 2-10 minutes before the job even starts. Poll for up to `pollBudgetSeconds` (1200s) rather than giving up early.\nSFW only. The prompt and any input image are moderated BEFORE the job is enqueued, so a content rejection happens before any charge.","tags":["media generation"],"x-payment-info":{"price":{"mode":"fixed","currency":"USD","amount":"0.06"},"protocols":[{"x402":{}}]},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["imageBase64","mediaType"],"properties":{"imageBase64":{"type":"string","description":"base64 image bytes, no data-URI prefix"},"mediaType":{"type":"string","enum":["image/png","image/jpeg"]},"factor":{"type":"number","enum":[2,4],"default":2},"mode":{"type":"string","enum":["auto","face"],"default":"auto"}}}}}},"responses":{"200":{"description":"Idempotent replay: this authorization was already accepted, so no second job was created and you were not charged twice. Body is the existing job's current status with `replayed: true`.","content":{"application/json":{"schema":{"allOf":[{"type":"object","properties":{"jobId":{"type":"string"},"kind":{"type":"string","enum":["music","image_generate","image_edit","image_upscale","image_restore","video","video_sound"]},"createdAt":{"type":"string","description":"Job creation time — the start of the 7-day retention clock."},"status":{"type":"string","enum":["queued","running","completed","failed","expired"],"description":"`completed` is only ever returned AFTER the settling payment succeeds — an unpaid poll on a finished artifact returns 402 instead."},"statusUrl":{"type":"string"},"resultUrl":{"type":"string"},"pollBudgetSeconds":{"type":"number"},"pollBudgetRemainingSeconds":{"type":"number"},"error":{"type":"string","description":"Present when `status` is `failed`."},"settled":{"type":"boolean"},"output":{"type":"object","description":"Rendered output shape. Present on completed video kinds only.","properties":{"width":{"type":"number"},"height":{"type":"number"},"numFrames":{"type":"number"},"fps":{"type":"number"},"durationSeconds":{"type":"number","description":"numFrames / fps — the delivered length."}}},"aiDisclosure":{"type":"object","properties":{"aiGenerated":{"type":"boolean"},"label":{"type":"string"},"statement":{"type":"string"},"embeddedIn":{"type":"string"},"termsUrl":{"type":"string"}},"required":["aiGenerated","statement","termsUrl"]}},"required":["jobId","kind","createdAt","status","statusUrl","resultUrl","pollBudgetSeconds"]}],"properties":{"replayed":{"type":"boolean"}}}}}},"202":{"description":"Authorization verified and job enqueued. You have NOT been charged yet — poll `statusUrl`.","content":{"application/json":{"schema":{"type":"object","properties":{"jobId":{"type":"string"},"kind":{"type":"string","enum":["music","image_generate","image_edit","image_upscale","image_restore","video","video_sound"]},"status":{"type":"string"},"etaSeconds":{"type":"number"},"pollBudgetSeconds":{"type":"number"},"statusUrl":{"type":"string"},"resultUrl":{"type":"string"},"aiDisclosure":{"type":"object","properties":{"aiGenerated":{"type":"boolean"},"label":{"type":"string"},"statement":{"type":"string"},"embeddedIn":{"type":"string"},"termsUrl":{"type":"string"}},"required":["aiGenerated","statement","termsUrl"]}},"required":["jobId","status","statusUrl","resultUrl","etaSeconds","pollBudgetSeconds"]}}}},"400":{"description":"The body parsed but was invalid — a missing/oversized FIELD, a bad mediaType, or image bytes that did not decode. No charge."},"402":{"description":"Payment Required"},"403":{"description":"`wallet_blocked` — this payer is blocklisted. The request was cancelled before any work or settlement; nothing was charged."},"413":{"description":"`request_too_large` — the request body exceeded the read cap (64 KiB for the music kinds, 20 MiB for kinds accepting an input image) and was rejected before parsing. Send a smaller image. No charge."},"422":{"description":"The prompt or input image was rejected by the SFW content policy, before the job was enqueued. No charge; retrying the same content will fail again."},"429":{"description":"Two causes — check `error`. `rate_limit_exceeded`: the shared 600/minute burst limiter; back off per `retryAfterMs`. Otherwise a per-wallet limit — too many jobs submitted-but-undelivered, or too many rejected attempts this hour — with `Retry-After` set; backing off will not help until you collect or abandon your outstanding jobs."},"500":{"description":"Unexpected gateway error. Do not infer a payment state from it — re-poll the job's `statusUrl` to establish where the job actually stands before signing anything new."},"502":{"description":"`facilitator_unavailable` — the x402 payment facilitator could not be reached. Transient and on our side; nothing was charged. Retry."},"503":{"description":"Transient and retryable. On a FIRST submit the causes are pre-enqueue — `queue_full` (render queue at capacity, `Retry-After` set), content screening unavailable, or the kind not configured here — and nothing was charged. On a REPLAY of an authorization that already settled, this means the existing job could not be read right now: you may already have paid, so re-poll `statusUrl` rather than signing a replacement authorization."}}}},"/x402/api/v1/image/upscale/jobs/{jobId}":{"get":{"operationId":"status_image_upscale","summary":"Poll a image_upscale job — and settle it when the artifact is ready.","description":"Free while the job is queued or running: returns 200 with a `status` of `queued` or `running`.\n\nTHIS IS THE CHARGE POINT. Once the artifact is finished and has passed output content screening, this same poll returns 402 with the quote you already agreed to at submit. Pay it and the poll returns 200 `completed`; then download from `resultUrl` for free.\n\nYou are charged only on delivery — this poll, when it finds your screened artifact ready to download. Jobs that fail, are withheld by content screening, or that you abandon before this poll never settle; once it has been paid, the free `resultUrl` download does not affect the charge. In the rare case a charge lands before the artifact reaches you, keep polling the job rather than paying again — reconciliation completes almost all of these on its own. A job it cannot resolve is worked by a human — report the jobId to support@havenagent.ai. Because the 402 here is conditional on a finished artifact, this operation is not declared payable in this document — an unpaid poll of an unfinished job is genuinely free, and an unknown job id returns 404.","tags":["media generation"],"security":[],"parameters":[{"name":"jobId","in":"path","required":true,"description":"Job id returned by the submit call's 202 response.","schema":{"type":"string"}}],"responses":{"200":{"description":"Job status. `completed` is only returned once the job has settled.","content":{"application/json":{"schema":{"type":"object","properties":{"jobId":{"type":"string"},"kind":{"type":"string","enum":["music","image_generate","image_edit","image_upscale","image_restore","video","video_sound"]},"createdAt":{"type":"string","description":"Job creation time — the start of the 7-day retention clock."},"status":{"type":"string","enum":["queued","running","completed","failed","expired"],"description":"`completed` is only ever returned AFTER the settling payment succeeds — an unpaid poll on a finished artifact returns 402 instead."},"statusUrl":{"type":"string"},"resultUrl":{"type":"string"},"pollBudgetSeconds":{"type":"number"},"pollBudgetRemainingSeconds":{"type":"number"},"error":{"type":"string","description":"Present when `status` is `failed`."},"settled":{"type":"boolean"},"output":{"type":"object","description":"Rendered output shape. Present on completed video kinds only.","properties":{"width":{"type":"number"},"height":{"type":"number"},"numFrames":{"type":"number"},"fps":{"type":"number"},"durationSeconds":{"type":"number","description":"numFrames / fps — the delivered length."}}},"aiDisclosure":{"type":"object","properties":{"aiGenerated":{"type":"boolean"},"label":{"type":"string"},"statement":{"type":"string"},"embeddedIn":{"type":"string"},"termsUrl":{"type":"string"}},"required":["aiGenerated","statement","termsUrl"]}},"required":["jobId","kind","createdAt","status","statusUrl","resultUrl","pollBudgetSeconds"]}}}},"402":{"description":"Payment Required — the artifact is finished and deliverable; this poll settles it."},"403":{"description":"`wallet_mismatch` — this job was submitted by a different wallet and can only be paid by that one; signing a fresh quote from another wallet will never settle it, so settle from the submitting wallet. `wallet_blocked` — the payer is blocklisted. Neither was charged."},"404":{"description":"No such job (or it was swept after retention)."},"409":{"description":"Retryable settlement states, all expected under concurrency: `settlement_in_progress` (another poll holds the claim) and `settlement_reopened` (a held claim was released by reconciliation) — both carry `Retry-After`, so poll again. `authorization_bound_to_other_job` means you reused a signature already tied to a different job; sign a fresh authorization for THIS job."},"429":{"description":"`rate_limit_exceeded` — the gateway's shared burst limiter (600 requests/minute per caller) shed this request before it reached any handler. Applies to every route here, free ones included. `retryAfterMs` is in the body; back off and retry."},"500":{"description":"Unexpected gateway error. Do not infer a payment state from it — re-poll the job's `statusUrl` to establish where the job actually stands before signing anything new."},"502":{"description":"Two distinct outcomes — check `error`. `facilitator_unavailable`: the facilitator could not be reached, settlement never started, nothing was charged; retry. `settlement_indeterminate`: the settle call's outcome is unknown and the transfer MAY ALREADY HAVE MOVED — do not assume you were not charged and do not re-sign a replacement authorization; the claim is held, so keep polling — reconciliation completes almost all of these on its own. A job it cannot resolve is worked by a human — report the jobId to support@havenagent.ai."},"503":{"description":"Two distinct outcomes — check `error`. `moderation_rejected` with category `moderation_unavailable`: output screening is down, settlement never happened, no charge; retry. `settlement_unavailable`: this job's settlement is being reconciled and the payment may already have moved — do not assume you were not charged and do not sign a replacement authorization; `Retry-After` is set, so keep polling — reconciliation completes almost all of these on its own. A job it cannot resolve is worked by a human — report the jobId to support@havenagent.ai."}}}},"/x402/api/v1/image/upscale/jobs/{jobId}/result":{"get":{"operationId":"result_image_upscale","summary":"Download a settled image_upscale artifact.","description":"Free and unmetered once the job has settled. The concrete `Content-Type` is set per artifact. Artifacts are deleted 7 days after creation — download promptly.\n\nThis route never issues a 402 — it has no payment leg. If the job is unpaid you get `409 not_paid` with the `statusUrl` to settle on; if it is still rendering you get `409 not_ready`. Settle on the status route, then come back here.","tags":["media generation"],"security":[],"parameters":[{"name":"jobId","in":"path","required":true,"description":"Job id returned by the submit call's 202 response.","schema":{"type":"string"}}],"responses":{"200":{"description":"The generated artifact.","content":{"image/png":{"schema":{"type":"string","format":"binary"}},"image/jpeg":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"No such job."},"409":{"description":"`not_ready` (still rendering) or `not_paid` (finished but unsettled — settle on `statusUrl`, which is included in the body). Also `artifact_changed`: the stored bytes no longer match the hash they were screened against, so the stale verdict was cleared and the artifact is being re-screened — you are already settled, so simply retry this download rather than returning to the payment flow."},"410":{"description":"Job failed, output withheld by content screening, or the artifact expired (7-day retention)."},"429":{"description":"`rate_limit_exceeded` — the gateway's shared burst limiter (600 requests/minute per caller) shed this request before it reached any handler. Applies to every route here, free ones included. `retryAfterMs` is in the body; back off and retry."},"500":{"description":"Unexpected gateway error. Do not infer a payment state from it — re-poll the job's `statusUrl` to establish where the job actually stands before signing anything new."},"503":{"description":"Transient and retryable: output content screening is temporarily unavailable, or the artifact metadata could not be read. The job is not lost — retry."}}}},"/x402/api/v1/image/restore":{"post":{"operationId":"submit_image_restore","summary":"Restore a degraded photo (denoise, deblur, dehaze). SFW only.","description":"Restore a degraded photo (denoise, deblur, dehaze). SFW only.\nSubmitting VERIFIES your x402 authorization but does not charge you — under settle-on-delivery the completing status poll is the charge point. An unauthenticated request returns 402 with the quote to sign. A verified submit returns 202 with a `jobId`, a `statusUrl` to poll and a `resultUrl` to download from.\nSubmission is idempotent: re-presenting the SAME authorization (say, after you lost the first response to a timeout) does not create a second job and does not charge you twice. That replay answers **200** with the existing job's current status and `replayed: true` — not another 202 — so accept both.\nSettle from the SAME wallet that submitted. The job is bound to the submitting payer; a completing poll signed by a different wallet is refused with 403 `wallet_mismatch` and can never settle this job.\nTypical time to a finished artifact is ~300s, but GPU workers scale to zero, so the first request after an idle period can add 2-10 minutes before the job even starts. Poll for up to `pollBudgetSeconds` (1200s) rather than giving up early.\nSFW only. The prompt and any input image are moderated BEFORE the job is enqueued, so a content rejection happens before any charge.","tags":["media generation"],"x-payment-info":{"price":{"mode":"fixed","currency":"USD","amount":"0.10"},"protocols":[{"x402":{}}]},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["imageBase64","mediaType"],"properties":{"imageBase64":{"type":"string","description":"base64 image bytes, no data-URI prefix"},"mediaType":{"type":"string","enum":["image/png","image/jpeg"]},"restorationMode":{"type":"string","enum":["auto","denoise","deblur","dehaze"],"default":"auto"}}}}}},"responses":{"200":{"description":"Idempotent replay: this authorization was already accepted, so no second job was created and you were not charged twice. Body is the existing job's current status with `replayed: true`.","content":{"application/json":{"schema":{"allOf":[{"type":"object","properties":{"jobId":{"type":"string"},"kind":{"type":"string","enum":["music","image_generate","image_edit","image_upscale","image_restore","video","video_sound"]},"createdAt":{"type":"string","description":"Job creation time — the start of the 7-day retention clock."},"status":{"type":"string","enum":["queued","running","completed","failed","expired"],"description":"`completed` is only ever returned AFTER the settling payment succeeds — an unpaid poll on a finished artifact returns 402 instead."},"statusUrl":{"type":"string"},"resultUrl":{"type":"string"},"pollBudgetSeconds":{"type":"number"},"pollBudgetRemainingSeconds":{"type":"number"},"error":{"type":"string","description":"Present when `status` is `failed`."},"settled":{"type":"boolean"},"output":{"type":"object","description":"Rendered output shape. Present on completed video kinds only.","properties":{"width":{"type":"number"},"height":{"type":"number"},"numFrames":{"type":"number"},"fps":{"type":"number"},"durationSeconds":{"type":"number","description":"numFrames / fps — the delivered length."}}},"aiDisclosure":{"type":"object","properties":{"aiGenerated":{"type":"boolean"},"label":{"type":"string"},"statement":{"type":"string"},"embeddedIn":{"type":"string"},"termsUrl":{"type":"string"}},"required":["aiGenerated","statement","termsUrl"]}},"required":["jobId","kind","createdAt","status","statusUrl","resultUrl","pollBudgetSeconds"]}],"properties":{"replayed":{"type":"boolean"}}}}}},"202":{"description":"Authorization verified and job enqueued. You have NOT been charged yet — poll `statusUrl`.","content":{"application/json":{"schema":{"type":"object","properties":{"jobId":{"type":"string"},"kind":{"type":"string","enum":["music","image_generate","image_edit","image_upscale","image_restore","video","video_sound"]},"status":{"type":"string"},"etaSeconds":{"type":"number"},"pollBudgetSeconds":{"type":"number"},"statusUrl":{"type":"string"},"resultUrl":{"type":"string"},"aiDisclosure":{"type":"object","properties":{"aiGenerated":{"type":"boolean"},"label":{"type":"string"},"statement":{"type":"string"},"embeddedIn":{"type":"string"},"termsUrl":{"type":"string"}},"required":["aiGenerated","statement","termsUrl"]}},"required":["jobId","status","statusUrl","resultUrl","etaSeconds","pollBudgetSeconds"]}}}},"400":{"description":"The body parsed but was invalid — a missing/oversized FIELD, a bad mediaType, or image bytes that did not decode. No charge."},"402":{"description":"Payment Required"},"403":{"description":"`wallet_blocked` — this payer is blocklisted. The request was cancelled before any work or settlement; nothing was charged."},"413":{"description":"`request_too_large` — the request body exceeded the read cap (64 KiB for the music kinds, 20 MiB for kinds accepting an input image) and was rejected before parsing. Send a smaller image. No charge."},"422":{"description":"The prompt or input image was rejected by the SFW content policy, before the job was enqueued. No charge; retrying the same content will fail again."},"429":{"description":"Two causes — check `error`. `rate_limit_exceeded`: the shared 600/minute burst limiter; back off per `retryAfterMs`. Otherwise a per-wallet limit — too many jobs submitted-but-undelivered, or too many rejected attempts this hour — with `Retry-After` set; backing off will not help until you collect or abandon your outstanding jobs."},"500":{"description":"Unexpected gateway error. Do not infer a payment state from it — re-poll the job's `statusUrl` to establish where the job actually stands before signing anything new."},"502":{"description":"`facilitator_unavailable` — the x402 payment facilitator could not be reached. Transient and on our side; nothing was charged. Retry."},"503":{"description":"Transient and retryable. On a FIRST submit the causes are pre-enqueue — `queue_full` (render queue at capacity, `Retry-After` set), content screening unavailable, or the kind not configured here — and nothing was charged. On a REPLAY of an authorization that already settled, this means the existing job could not be read right now: you may already have paid, so re-poll `statusUrl` rather than signing a replacement authorization."}}}},"/x402/api/v1/image/restore/jobs/{jobId}":{"get":{"operationId":"status_image_restore","summary":"Poll a image_restore job — and settle it when the artifact is ready.","description":"Free while the job is queued or running: returns 200 with a `status` of `queued` or `running`.\n\nTHIS IS THE CHARGE POINT. Once the artifact is finished and has passed output content screening, this same poll returns 402 with the quote you already agreed to at submit. Pay it and the poll returns 200 `completed`; then download from `resultUrl` for free.\n\nYou are charged only on delivery — this poll, when it finds your screened artifact ready to download. Jobs that fail, are withheld by content screening, or that you abandon before this poll never settle; once it has been paid, the free `resultUrl` download does not affect the charge. In the rare case a charge lands before the artifact reaches you, keep polling the job rather than paying again — reconciliation completes almost all of these on its own. A job it cannot resolve is worked by a human — report the jobId to support@havenagent.ai. Because the 402 here is conditional on a finished artifact, this operation is not declared payable in this document — an unpaid poll of an unfinished job is genuinely free, and an unknown job id returns 404.","tags":["media generation"],"security":[],"parameters":[{"name":"jobId","in":"path","required":true,"description":"Job id returned by the submit call's 202 response.","schema":{"type":"string"}}],"responses":{"200":{"description":"Job status. `completed` is only returned once the job has settled.","content":{"application/json":{"schema":{"type":"object","properties":{"jobId":{"type":"string"},"kind":{"type":"string","enum":["music","image_generate","image_edit","image_upscale","image_restore","video","video_sound"]},"createdAt":{"type":"string","description":"Job creation time — the start of the 7-day retention clock."},"status":{"type":"string","enum":["queued","running","completed","failed","expired"],"description":"`completed` is only ever returned AFTER the settling payment succeeds — an unpaid poll on a finished artifact returns 402 instead."},"statusUrl":{"type":"string"},"resultUrl":{"type":"string"},"pollBudgetSeconds":{"type":"number"},"pollBudgetRemainingSeconds":{"type":"number"},"error":{"type":"string","description":"Present when `status` is `failed`."},"settled":{"type":"boolean"},"output":{"type":"object","description":"Rendered output shape. Present on completed video kinds only.","properties":{"width":{"type":"number"},"height":{"type":"number"},"numFrames":{"type":"number"},"fps":{"type":"number"},"durationSeconds":{"type":"number","description":"numFrames / fps — the delivered length."}}},"aiDisclosure":{"type":"object","properties":{"aiGenerated":{"type":"boolean"},"label":{"type":"string"},"statement":{"type":"string"},"embeddedIn":{"type":"string"},"termsUrl":{"type":"string"}},"required":["aiGenerated","statement","termsUrl"]}},"required":["jobId","kind","createdAt","status","statusUrl","resultUrl","pollBudgetSeconds"]}}}},"402":{"description":"Payment Required — the artifact is finished and deliverable; this poll settles it."},"403":{"description":"`wallet_mismatch` — this job was submitted by a different wallet and can only be paid by that one; signing a fresh quote from another wallet will never settle it, so settle from the submitting wallet. `wallet_blocked` — the payer is blocklisted. Neither was charged."},"404":{"description":"No such job (or it was swept after retention)."},"409":{"description":"Retryable settlement states, all expected under concurrency: `settlement_in_progress` (another poll holds the claim) and `settlement_reopened` (a held claim was released by reconciliation) — both carry `Retry-After`, so poll again. `authorization_bound_to_other_job` means you reused a signature already tied to a different job; sign a fresh authorization for THIS job."},"429":{"description":"`rate_limit_exceeded` — the gateway's shared burst limiter (600 requests/minute per caller) shed this request before it reached any handler. Applies to every route here, free ones included. `retryAfterMs` is in the body; back off and retry."},"500":{"description":"Unexpected gateway error. Do not infer a payment state from it — re-poll the job's `statusUrl` to establish where the job actually stands before signing anything new."},"502":{"description":"Two distinct outcomes — check `error`. `facilitator_unavailable`: the facilitator could not be reached, settlement never started, nothing was charged; retry. `settlement_indeterminate`: the settle call's outcome is unknown and the transfer MAY ALREADY HAVE MOVED — do not assume you were not charged and do not re-sign a replacement authorization; the claim is held, so keep polling — reconciliation completes almost all of these on its own. A job it cannot resolve is worked by a human — report the jobId to support@havenagent.ai."},"503":{"description":"Two distinct outcomes — check `error`. `moderation_rejected` with category `moderation_unavailable`: output screening is down, settlement never happened, no charge; retry. `settlement_unavailable`: this job's settlement is being reconciled and the payment may already have moved — do not assume you were not charged and do not sign a replacement authorization; `Retry-After` is set, so keep polling — reconciliation completes almost all of these on its own. A job it cannot resolve is worked by a human — report the jobId to support@havenagent.ai."}}}},"/x402/api/v1/image/restore/jobs/{jobId}/result":{"get":{"operationId":"result_image_restore","summary":"Download a settled image_restore artifact.","description":"Free and unmetered once the job has settled. The concrete `Content-Type` is set per artifact. Artifacts are deleted 7 days after creation — download promptly.\n\nThis route never issues a 402 — it has no payment leg. If the job is unpaid you get `409 not_paid` with the `statusUrl` to settle on; if it is still rendering you get `409 not_ready`. Settle on the status route, then come back here.","tags":["media generation"],"security":[],"parameters":[{"name":"jobId","in":"path","required":true,"description":"Job id returned by the submit call's 202 response.","schema":{"type":"string"}}],"responses":{"200":{"description":"The generated artifact.","content":{"image/png":{"schema":{"type":"string","format":"binary"}},"image/jpeg":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"No such job."},"409":{"description":"`not_ready` (still rendering) or `not_paid` (finished but unsettled — settle on `statusUrl`, which is included in the body). Also `artifact_changed`: the stored bytes no longer match the hash they were screened against, so the stale verdict was cleared and the artifact is being re-screened — you are already settled, so simply retry this download rather than returning to the payment flow."},"410":{"description":"Job failed, output withheld by content screening, or the artifact expired (7-day retention)."},"429":{"description":"`rate_limit_exceeded` — the gateway's shared burst limiter (600 requests/minute per caller) shed this request before it reached any handler. Applies to every route here, free ones included. `retryAfterMs` is in the body; back off and retry."},"500":{"description":"Unexpected gateway error. Do not infer a payment state from it — re-poll the job's `statusUrl` to establish where the job actually stands before signing anything new."},"503":{"description":"Transient and retryable: output content screening is temporarily unavailable, or the artifact metadata could not be read. The job is not lost — retry."}}}},"/x402/api/v1/video":{"post":{"operationId":"submit_video","summary":"Generate a short video from text or an init image, in any aspect ratio (landscape, portrait or square). SFW only.","description":"Generate a short video from text or an init image, in any aspect ratio (landscape, portrait or square). SFW only.\nSubmitting VERIFIES your x402 authorization but does not charge you — under settle-on-delivery the completing status poll is the charge point. An unauthenticated request returns 402 with the quote to sign. A verified submit returns 202 with a `jobId`, a `statusUrl` to poll and a `resultUrl` to download from.\nSubmission is idempotent: re-presenting the SAME authorization (say, after you lost the first response to a timeout) does not create a second job and does not charge you twice. That replay answers **200** with the existing job's current status and `replayed: true` — not another 202 — so accept both.\nSettle from the SAME wallet that submitted. The job is bound to the submitting payer; a completing poll signed by a different wallet is refused with 403 `wallet_mismatch` and can never settle this job.\nTypical time to a finished artifact is ~600s, but GPU workers scale to zero, so the first request after an idle period can add 2-10 minutes before the job even starts. Poll for up to `pollBudgetSeconds` (1800s) rather than giving up early.\nSFW only. The prompt and any input image are moderated BEFORE the job is enqueued, so a content rejection happens before any charge.\nLicensed output: submitting accepts the use terms at https://havenagent.ai/terms/terms-of-service, which bind you and anyone you redistribute the output to. Where the delivered file carries an embedded AI-generated disclosure — signalled by `X-Ai-Disclosure-Embedded: true` on the result response — do not strip it. An MP4 layout we cannot rewrite safely is delivered unmodified with that header `false`, and the disclosure travels in the response envelope instead; either way, disclose AI generation wherever you publish it.","tags":["media generation"],"x-payment-info":{"price":{"mode":"fixed","currency":"USD","amount":"0.80"},"protocols":[{"x402":{}}]},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["prompt"],"properties":{"prompt":{"type":"string","minLength":1,"maxLength":2000},"durationSeconds":{"type":"number","minimum":1,"maximum":10,"default":5},"width":{"type":"number","minimum":256,"maximum":1280,"default":1024},"height":{"type":"number","minimum":256,"maximum":1280,"default":576},"imageBase64":{"type":"string","description":"base64 image bytes, no data-URI prefix"},"mediaType":{"type":"string","enum":["image/png","image/jpeg"]}},"dependentRequired":{"imageBase64":["mediaType"]}}}}},"responses":{"200":{"description":"Idempotent replay: this authorization was already accepted, so no second job was created and you were not charged twice. Body is the existing job's current status with `replayed: true`.","content":{"application/json":{"schema":{"allOf":[{"type":"object","properties":{"jobId":{"type":"string"},"kind":{"type":"string","enum":["music","image_generate","image_edit","image_upscale","image_restore","video","video_sound"]},"createdAt":{"type":"string","description":"Job creation time — the start of the 7-day retention clock."},"status":{"type":"string","enum":["queued","running","completed","failed","expired"],"description":"`completed` is only ever returned AFTER the settling payment succeeds — an unpaid poll on a finished artifact returns 402 instead."},"statusUrl":{"type":"string"},"resultUrl":{"type":"string"},"pollBudgetSeconds":{"type":"number"},"pollBudgetRemainingSeconds":{"type":"number"},"error":{"type":"string","description":"Present when `status` is `failed`."},"settled":{"type":"boolean"},"output":{"type":"object","description":"Rendered output shape. Present on completed video kinds only.","properties":{"width":{"type":"number"},"height":{"type":"number"},"numFrames":{"type":"number"},"fps":{"type":"number"},"durationSeconds":{"type":"number","description":"numFrames / fps — the delivered length."}}},"aiDisclosure":{"type":"object","properties":{"aiGenerated":{"type":"boolean"},"label":{"type":"string"},"statement":{"type":"string"},"embeddedIn":{"type":"string"},"termsUrl":{"type":"string"}},"required":["aiGenerated","statement","termsUrl"]}},"required":["jobId","kind","createdAt","status","statusUrl","resultUrl","pollBudgetSeconds"]}],"properties":{"replayed":{"type":"boolean"}}}}}},"202":{"description":"Authorization verified and job enqueued. You have NOT been charged yet — poll `statusUrl`.","content":{"application/json":{"schema":{"type":"object","properties":{"jobId":{"type":"string"},"kind":{"type":"string","enum":["music","image_generate","image_edit","image_upscale","image_restore","video","video_sound"]},"status":{"type":"string"},"etaSeconds":{"type":"number"},"pollBudgetSeconds":{"type":"number"},"statusUrl":{"type":"string"},"resultUrl":{"type":"string"},"aiDisclosure":{"type":"object","properties":{"aiGenerated":{"type":"boolean"},"label":{"type":"string"},"statement":{"type":"string"},"embeddedIn":{"type":"string"},"termsUrl":{"type":"string"}},"required":["aiGenerated","statement","termsUrl"]}},"required":["jobId","status","statusUrl","resultUrl","etaSeconds","pollBudgetSeconds"]}}}},"400":{"description":"The body parsed but was invalid — a missing/oversized FIELD, a bad mediaType, or image bytes that did not decode. No charge."},"402":{"description":"Payment Required"},"403":{"description":"`wallet_blocked` — this payer is blocklisted. The request was cancelled before any work or settlement; nothing was charged."},"413":{"description":"`request_too_large` — the request body exceeded the read cap (64 KiB for the music kinds, 20 MiB for kinds accepting an input image) and was rejected before parsing. Send a smaller image. No charge."},"422":{"description":"The prompt or input image was rejected by the SFW content policy, before the job was enqueued. No charge; retrying the same content will fail again."},"429":{"description":"Two causes — check `error`. `rate_limit_exceeded`: the shared 600/minute burst limiter; back off per `retryAfterMs`. Otherwise a per-wallet limit — too many jobs submitted-but-undelivered, or too many rejected attempts this hour — with `Retry-After` set; backing off will not help until you collect or abandon your outstanding jobs."},"500":{"description":"Unexpected gateway error. Do not infer a payment state from it — re-poll the job's `statusUrl` to establish where the job actually stands before signing anything new."},"502":{"description":"`facilitator_unavailable` — the x402 payment facilitator could not be reached. Transient and on our side; nothing was charged. Retry."},"503":{"description":"Transient and retryable. On a FIRST submit the causes are pre-enqueue — `queue_full` (render queue at capacity, `Retry-After` set), content screening unavailable, or the kind not configured here — and nothing was charged. On a REPLAY of an authorization that already settled, this means the existing job could not be read right now: you may already have paid, so re-poll `statusUrl` rather than signing a replacement authorization."}}}},"/x402/api/v1/video/jobs/{jobId}":{"get":{"operationId":"status_video","summary":"Poll a video job — and settle it when the artifact is ready.","description":"Free while the job is queued or running: returns 200 with a `status` of `queued` or `running`.\n\nTHIS IS THE CHARGE POINT. Once the artifact is finished and has passed output content screening, this same poll returns 402 with the quote you already agreed to at submit. Pay it and the poll returns 200 `completed`; then download from `resultUrl` for free.\n\nYou are charged only on delivery — this poll, when it finds your screened artifact ready to download. Jobs that fail, are withheld by content screening, or that you abandon before this poll never settle; once it has been paid, the free `resultUrl` download does not affect the charge. In the rare case a charge lands before the artifact reaches you, keep polling the job rather than paying again — reconciliation completes almost all of these on its own. A job it cannot resolve is worked by a human — report the jobId to support@havenagent.ai. Because the 402 here is conditional on a finished artifact, this operation is not declared payable in this document — an unpaid poll of an unfinished job is genuinely free, and an unknown job id returns 404.","tags":["media generation"],"security":[],"parameters":[{"name":"jobId","in":"path","required":true,"description":"Job id returned by the submit call's 202 response.","schema":{"type":"string"}}],"responses":{"200":{"description":"Job status. `completed` is only returned once the job has settled.","content":{"application/json":{"schema":{"type":"object","properties":{"jobId":{"type":"string"},"kind":{"type":"string","enum":["music","image_generate","image_edit","image_upscale","image_restore","video","video_sound"]},"createdAt":{"type":"string","description":"Job creation time — the start of the 7-day retention clock."},"status":{"type":"string","enum":["queued","running","completed","failed","expired"],"description":"`completed` is only ever returned AFTER the settling payment succeeds — an unpaid poll on a finished artifact returns 402 instead."},"statusUrl":{"type":"string"},"resultUrl":{"type":"string"},"pollBudgetSeconds":{"type":"number"},"pollBudgetRemainingSeconds":{"type":"number"},"error":{"type":"string","description":"Present when `status` is `failed`."},"settled":{"type":"boolean"},"output":{"type":"object","description":"Rendered output shape. Present on completed video kinds only.","properties":{"width":{"type":"number"},"height":{"type":"number"},"numFrames":{"type":"number"},"fps":{"type":"number"},"durationSeconds":{"type":"number","description":"numFrames / fps — the delivered length."}}},"aiDisclosure":{"type":"object","properties":{"aiGenerated":{"type":"boolean"},"label":{"type":"string"},"statement":{"type":"string"},"embeddedIn":{"type":"string"},"termsUrl":{"type":"string"}},"required":["aiGenerated","statement","termsUrl"]}},"required":["jobId","kind","createdAt","status","statusUrl","resultUrl","pollBudgetSeconds"]}}}},"402":{"description":"Payment Required — the artifact is finished and deliverable; this poll settles it."},"403":{"description":"`wallet_mismatch` — this job was submitted by a different wallet and can only be paid by that one; signing a fresh quote from another wallet will never settle it, so settle from the submitting wallet. `wallet_blocked` — the payer is blocklisted. Neither was charged."},"404":{"description":"No such job (or it was swept after retention)."},"409":{"description":"Retryable settlement states, all expected under concurrency: `settlement_in_progress` (another poll holds the claim) and `settlement_reopened` (a held claim was released by reconciliation) — both carry `Retry-After`, so poll again. `authorization_bound_to_other_job` means you reused a signature already tied to a different job; sign a fresh authorization for THIS job."},"429":{"description":"`rate_limit_exceeded` — the gateway's shared burst limiter (600 requests/minute per caller) shed this request before it reached any handler. Applies to every route here, free ones included. `retryAfterMs` is in the body; back off and retry."},"500":{"description":"Unexpected gateway error. Do not infer a payment state from it — re-poll the job's `statusUrl` to establish where the job actually stands before signing anything new."},"502":{"description":"Two distinct outcomes — check `error`. `facilitator_unavailable`: the facilitator could not be reached, settlement never started, nothing was charged; retry. `settlement_indeterminate`: the settle call's outcome is unknown and the transfer MAY ALREADY HAVE MOVED — do not assume you were not charged and do not re-sign a replacement authorization; the claim is held, so keep polling — reconciliation completes almost all of these on its own. A job it cannot resolve is worked by a human — report the jobId to support@havenagent.ai."},"503":{"description":"Two distinct outcomes — check `error`. `moderation_rejected` with category `moderation_unavailable`: output screening is down, settlement never happened, no charge; retry. `settlement_unavailable`: this job's settlement is being reconciled and the payment may already have moved — do not assume you were not charged and do not sign a replacement authorization; `Retry-After` is set, so keep polling — reconciliation completes almost all of these on its own. A job it cannot resolve is worked by a human — report the jobId to support@havenagent.ai."}}}},"/x402/api/v1/video/jobs/{jobId}/result":{"get":{"operationId":"result_video","summary":"Download a settled video artifact.","description":"Free and unmetered once the job has settled. The concrete `Content-Type` is set per artifact. Artifacts are deleted 7 days after creation — download promptly.\n\nThis route never issues a 402 — it has no payment leg. If the job is unpaid you get `409 not_paid` with the `statusUrl` to settle on; if it is still rendering you get `409 not_ready`. Settle on the status route, then come back here.","tags":["media generation"],"security":[],"parameters":[{"name":"jobId","in":"path","required":true,"description":"Job id returned by the submit call's 202 response.","schema":{"type":"string"}}],"responses":{"200":{"description":"The generated artifact. `X-Ai-Disclosure-Embedded` reports whether THESE bytes carry the disclosure in their container (`false` means the container could not be rewritten safely and the disclosure is envelope-only).","headers":{"X-Ai-Generated":{"schema":{"type":"string"},"description":"Always `true` for licensed kinds."},"X-Ai-Disclosure":{"schema":{"type":"string"},"description":"Human-readable AI-generated disclosure."},"X-Ai-Disclosure-Embedded":{"schema":{"type":"string","enum":["true","false"]},"description":"`true` when the delivered bytes carry the disclosure in-container — do not strip it. `false` when the container could not be rewritten and the bytes are unmodified."},"X-Use-Terms":{"schema":{"type":"string"},"description":"URL of the governing use terms."}},"content":{"video/mp4":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"No such job."},"409":{"description":"`not_ready` (still rendering) or `not_paid` (finished but unsettled — settle on `statusUrl`, which is included in the body). Also `artifact_changed`: the stored bytes no longer match the hash they were screened against, so the stale verdict was cleared and the artifact is being re-screened — you are already settled, so simply retry this download rather than returning to the payment flow."},"410":{"description":"Job failed, output withheld by content screening, or the artifact expired (7-day retention)."},"429":{"description":"`rate_limit_exceeded` — the gateway's shared burst limiter (600 requests/minute per caller) shed this request before it reached any handler. Applies to every route here, free ones included. `retryAfterMs` is in the body; back off and retry."},"500":{"description":"Unexpected gateway error. Do not infer a payment state from it — re-poll the job's `statusUrl` to establish where the job actually stands before signing anything new."},"503":{"description":"Transient and retryable: output content screening is temporarily unavailable, or the artifact metadata could not be read. The job is not lost — retry."}}}},"/x402/api/v1/video-sound":{"post":{"operationId":"submit_video_sound","summary":"Generate a short 2K video with a synchronized soundtrack (dialogue, effects, music) from text or an init image. SFW only.","description":"Generate a short 2K video with a synchronized soundtrack (dialogue, effects, music) from text or an init image. SFW only.\nSubmitting VERIFIES your x402 authorization but does not charge you — under settle-on-delivery the completing status poll is the charge point. An unauthenticated request returns 402 with the quote to sign. A verified submit returns 202 with a `jobId`, a `statusUrl` to poll and a `resultUrl` to download from.\nSubmission is idempotent: re-presenting the SAME authorization (say, after you lost the first response to a timeout) does not create a second job and does not charge you twice. That replay answers **200** with the existing job's current status and `replayed: true` — not another 202 — so accept both.\nSettle from the SAME wallet that submitted. The job is bound to the submitting payer; a completing poll signed by a different wallet is refused with 403 `wallet_mismatch` and can never settle this job.\nTypical time to a finished artifact is ~900s, but GPU workers scale to zero, so the first request after an idle period can add 2-10 minutes before the job even starts. Poll for up to `pollBudgetSeconds` (1800s) rather than giving up early.\nSFW only. The prompt and any input image are moderated BEFORE the job is enqueued, so a content rejection happens before any charge.\nLicensed output: submitting accepts the use terms at https://havenagent.ai/terms/terms-of-service, which bind you and anyone you redistribute the output to. Where the delivered file carries an embedded AI-generated disclosure — signalled by `X-Ai-Disclosure-Embedded: true` on the result response — do not strip it. An MP4 layout we cannot rewrite safely is delivered unmodified with that header `false`, and the disclosure travels in the response envelope instead; either way, disclose AI generation wherever you publish it.","tags":["media generation"],"x-payment-info":{"price":{"mode":"fixed","currency":"USD","amount":"1.50"},"protocols":[{"x402":{}}]},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["prompt"],"properties":{"prompt":{"type":"string","minLength":1,"maxLength":2000},"durationSeconds":{"type":"integer","minimum":4,"maximum":10,"default":5},"imageBase64":{"type":"string","description":"base64 image bytes, no data-URI prefix"},"mediaType":{"type":"string","enum":["image/png","image/jpeg"]}},"dependentRequired":{"imageBase64":["mediaType"]}}}}},"responses":{"200":{"description":"Idempotent replay: this authorization was already accepted, so no second job was created and you were not charged twice. Body is the existing job's current status with `replayed: true`.","content":{"application/json":{"schema":{"allOf":[{"type":"object","properties":{"jobId":{"type":"string"},"kind":{"type":"string","enum":["music","image_generate","image_edit","image_upscale","image_restore","video","video_sound"]},"createdAt":{"type":"string","description":"Job creation time — the start of the 7-day retention clock."},"status":{"type":"string","enum":["queued","running","completed","failed","expired"],"description":"`completed` is only ever returned AFTER the settling payment succeeds — an unpaid poll on a finished artifact returns 402 instead."},"statusUrl":{"type":"string"},"resultUrl":{"type":"string"},"pollBudgetSeconds":{"type":"number"},"pollBudgetRemainingSeconds":{"type":"number"},"error":{"type":"string","description":"Present when `status` is `failed`."},"settled":{"type":"boolean"},"output":{"type":"object","description":"Rendered output shape. Present on completed video kinds only.","properties":{"width":{"type":"number"},"height":{"type":"number"},"numFrames":{"type":"number"},"fps":{"type":"number"},"durationSeconds":{"type":"number","description":"numFrames / fps — the delivered length."}}},"aiDisclosure":{"type":"object","properties":{"aiGenerated":{"type":"boolean"},"label":{"type":"string"},"statement":{"type":"string"},"embeddedIn":{"type":"string"},"termsUrl":{"type":"string"}},"required":["aiGenerated","statement","termsUrl"]}},"required":["jobId","kind","createdAt","status","statusUrl","resultUrl","pollBudgetSeconds"]}],"properties":{"replayed":{"type":"boolean"}}}}}},"202":{"description":"Authorization verified and job enqueued. You have NOT been charged yet — poll `statusUrl`.","content":{"application/json":{"schema":{"type":"object","properties":{"jobId":{"type":"string"},"kind":{"type":"string","enum":["music","image_generate","image_edit","image_upscale","image_restore","video","video_sound"]},"status":{"type":"string"},"etaSeconds":{"type":"number"},"pollBudgetSeconds":{"type":"number"},"statusUrl":{"type":"string"},"resultUrl":{"type":"string"},"aiDisclosure":{"type":"object","properties":{"aiGenerated":{"type":"boolean"},"label":{"type":"string"},"statement":{"type":"string"},"embeddedIn":{"type":"string"},"termsUrl":{"type":"string"}},"required":["aiGenerated","statement","termsUrl"]}},"required":["jobId","status","statusUrl","resultUrl","etaSeconds","pollBudgetSeconds"]}}}},"400":{"description":"The body parsed but was invalid — a missing/oversized FIELD, a bad mediaType, or image bytes that did not decode. No charge."},"402":{"description":"Payment Required"},"403":{"description":"`wallet_blocked` — this payer is blocklisted. The request was cancelled before any work or settlement; nothing was charged."},"413":{"description":"`request_too_large` — the request body exceeded the read cap (64 KiB for the music kinds, 20 MiB for kinds accepting an input image) and was rejected before parsing. Send a smaller image. No charge."},"422":{"description":"The prompt or input image was rejected by the SFW content policy, before the job was enqueued. No charge; retrying the same content will fail again."},"429":{"description":"Two causes — check `error`. `rate_limit_exceeded`: the shared 600/minute burst limiter; back off per `retryAfterMs`. Otherwise a per-wallet limit — too many jobs submitted-but-undelivered, or too many rejected attempts this hour — with `Retry-After` set; backing off will not help until you collect or abandon your outstanding jobs."},"500":{"description":"Unexpected gateway error. Do not infer a payment state from it — re-poll the job's `statusUrl` to establish where the job actually stands before signing anything new."},"502":{"description":"`facilitator_unavailable` — the x402 payment facilitator could not be reached. Transient and on our side; nothing was charged. Retry."},"503":{"description":"Transient and retryable. On a FIRST submit the causes are pre-enqueue — `queue_full` (render queue at capacity, `Retry-After` set), content screening unavailable, or the kind not configured here — and nothing was charged. On a REPLAY of an authorization that already settled, this means the existing job could not be read right now: you may already have paid, so re-poll `statusUrl` rather than signing a replacement authorization."}}}},"/x402/api/v1/video-sound/jobs/{jobId}":{"get":{"operationId":"status_video_sound","summary":"Poll a video_sound job — and settle it when the artifact is ready.","description":"Free while the job is queued or running: returns 200 with a `status` of `queued` or `running`.\n\nTHIS IS THE CHARGE POINT. Once the artifact is finished and has passed output content screening, this same poll returns 402 with the quote you already agreed to at submit. Pay it and the poll returns 200 `completed`; then download from `resultUrl` for free.\n\nYou are charged only on delivery — this poll, when it finds your screened artifact ready to download. Jobs that fail, are withheld by content screening, or that you abandon before this poll never settle; once it has been paid, the free `resultUrl` download does not affect the charge. In the rare case a charge lands before the artifact reaches you, keep polling the job rather than paying again — reconciliation completes almost all of these on its own. A job it cannot resolve is worked by a human — report the jobId to support@havenagent.ai. Because the 402 here is conditional on a finished artifact, this operation is not declared payable in this document — an unpaid poll of an unfinished job is genuinely free, and an unknown job id returns 404.","tags":["media generation"],"security":[],"parameters":[{"name":"jobId","in":"path","required":true,"description":"Job id returned by the submit call's 202 response.","schema":{"type":"string"}}],"responses":{"200":{"description":"Job status. `completed` is only returned once the job has settled.","content":{"application/json":{"schema":{"type":"object","properties":{"jobId":{"type":"string"},"kind":{"type":"string","enum":["music","image_generate","image_edit","image_upscale","image_restore","video","video_sound"]},"createdAt":{"type":"string","description":"Job creation time — the start of the 7-day retention clock."},"status":{"type":"string","enum":["queued","running","completed","failed","expired"],"description":"`completed` is only ever returned AFTER the settling payment succeeds — an unpaid poll on a finished artifact returns 402 instead."},"statusUrl":{"type":"string"},"resultUrl":{"type":"string"},"pollBudgetSeconds":{"type":"number"},"pollBudgetRemainingSeconds":{"type":"number"},"error":{"type":"string","description":"Present when `status` is `failed`."},"settled":{"type":"boolean"},"output":{"type":"object","description":"Rendered output shape. Present on completed video kinds only.","properties":{"width":{"type":"number"},"height":{"type":"number"},"numFrames":{"type":"number"},"fps":{"type":"number"},"durationSeconds":{"type":"number","description":"numFrames / fps — the delivered length."}}},"aiDisclosure":{"type":"object","properties":{"aiGenerated":{"type":"boolean"},"label":{"type":"string"},"statement":{"type":"string"},"embeddedIn":{"type":"string"},"termsUrl":{"type":"string"}},"required":["aiGenerated","statement","termsUrl"]}},"required":["jobId","kind","createdAt","status","statusUrl","resultUrl","pollBudgetSeconds"]}}}},"402":{"description":"Payment Required — the artifact is finished and deliverable; this poll settles it."},"403":{"description":"`wallet_mismatch` — this job was submitted by a different wallet and can only be paid by that one; signing a fresh quote from another wallet will never settle it, so settle from the submitting wallet. `wallet_blocked` — the payer is blocklisted. Neither was charged."},"404":{"description":"No such job (or it was swept after retention)."},"409":{"description":"Retryable settlement states, all expected under concurrency: `settlement_in_progress` (another poll holds the claim) and `settlement_reopened` (a held claim was released by reconciliation) — both carry `Retry-After`, so poll again. `authorization_bound_to_other_job` means you reused a signature already tied to a different job; sign a fresh authorization for THIS job."},"429":{"description":"`rate_limit_exceeded` — the gateway's shared burst limiter (600 requests/minute per caller) shed this request before it reached any handler. Applies to every route here, free ones included. `retryAfterMs` is in the body; back off and retry."},"500":{"description":"Unexpected gateway error. Do not infer a payment state from it — re-poll the job's `statusUrl` to establish where the job actually stands before signing anything new."},"502":{"description":"Two distinct outcomes — check `error`. `facilitator_unavailable`: the facilitator could not be reached, settlement never started, nothing was charged; retry. `settlement_indeterminate`: the settle call's outcome is unknown and the transfer MAY ALREADY HAVE MOVED — do not assume you were not charged and do not re-sign a replacement authorization; the claim is held, so keep polling — reconciliation completes almost all of these on its own. A job it cannot resolve is worked by a human — report the jobId to support@havenagent.ai."},"503":{"description":"Two distinct outcomes — check `error`. `moderation_rejected` with category `moderation_unavailable`: output screening is down, settlement never happened, no charge; retry. `settlement_unavailable`: this job's settlement is being reconciled and the payment may already have moved — do not assume you were not charged and do not sign a replacement authorization; `Retry-After` is set, so keep polling — reconciliation completes almost all of these on its own. A job it cannot resolve is worked by a human — report the jobId to support@havenagent.ai."}}}},"/x402/api/v1/video-sound/jobs/{jobId}/result":{"get":{"operationId":"result_video_sound","summary":"Download a settled video_sound artifact.","description":"Free and unmetered once the job has settled. The concrete `Content-Type` is set per artifact. Artifacts are deleted 7 days after creation — download promptly.\n\nThis route never issues a 402 — it has no payment leg. If the job is unpaid you get `409 not_paid` with the `statusUrl` to settle on; if it is still rendering you get `409 not_ready`. Settle on the status route, then come back here.","tags":["media generation"],"security":[],"parameters":[{"name":"jobId","in":"path","required":true,"description":"Job id returned by the submit call's 202 response.","schema":{"type":"string"}}],"responses":{"200":{"description":"The generated artifact. `X-Ai-Disclosure-Embedded` reports whether THESE bytes carry the disclosure in their container (`false` means the container could not be rewritten safely and the disclosure is envelope-only).","headers":{"X-Ai-Generated":{"schema":{"type":"string"},"description":"Always `true` for licensed kinds."},"X-Ai-Disclosure":{"schema":{"type":"string"},"description":"Human-readable AI-generated disclosure."},"X-Ai-Disclosure-Embedded":{"schema":{"type":"string","enum":["true","false"]},"description":"`true` when the delivered bytes carry the disclosure in-container — do not strip it. `false` when the container could not be rewritten and the bytes are unmodified."},"X-Use-Terms":{"schema":{"type":"string"},"description":"URL of the governing use terms."}},"content":{"video/mp4":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"No such job."},"409":{"description":"`not_ready` (still rendering) or `not_paid` (finished but unsettled — settle on `statusUrl`, which is included in the body). Also `artifact_changed`: the stored bytes no longer match the hash they were screened against, so the stale verdict was cleared and the artifact is being re-screened — you are already settled, so simply retry this download rather than returning to the payment flow."},"410":{"description":"Job failed, output withheld by content screening, or the artifact expired (7-day retention)."},"429":{"description":"`rate_limit_exceeded` — the gateway's shared burst limiter (600 requests/minute per caller) shed this request before it reached any handler. Applies to every route here, free ones included. `retryAfterMs` is in the body; back off and retry."},"500":{"description":"Unexpected gateway error. Do not infer a payment state from it — re-poll the job's `statusUrl` to establish where the job actually stands before signing anything new."},"503":{"description":"Transient and retryable: output content screening is temporarily unavailable, or the artifact metadata could not be read. The job is not lost — retry."}}}}},"x-x402":{"x402Version":2,"scheme":"exact","network":"eip155:8453","asset":"USDC","payTo":"0xa8f73f2fDFDa46f6591759458614BC2883529d77","settlement":{"model":"on-delivery","settlesAt":"status","freeRoutes":["status-poll-while-incomplete","result"]}}}