ComfyPilotComfyPilot
文档 / 接口 / Upload an asset (single-call multipart)

Upload an asset (single-call multipart)

Single-call `multipart/form-data` upload. The platform streams the

接口 upload-an-asset-single-call-multipart 更新于 2026-11-13

查看官方原文

Upload an asset (single-call multipart)

Single-call multipart/form-data upload. The platform streams the

bytes through its trusted byte-path, dedups by the server-computed hash, and mints the asset record.

The blake3 hash is always computed server-side from the received bytes — a client-declared expected_hash is verified, never trusted (on Comfy Cloud and serverless, only when sent before file — see below). If location metadata (GPS, XMP, IPTC) is removed from an image before it is stored, the asset's hash is the hash of the stored bytes and can differ from expected_hash. On Comfy Cloud, an upload whose bytes open as a JPEG, PNG, WebP or classic TIFF (including TIFF-based camera RAW such as DNG, NEF, ARW and CR2, but not BigTIFF) and that is over 50 MiB is refused with 422 validation_error, whatever its content_type and whether or not it carries location metadata. A request that declares a Content-Length over Comfy Cloud's 200 MB (200,000,000-byte) body limit is refused before that check with a plain 413 that does not use the ErrorEnvelope shape. A body sent without a declared length is cut off when it passes that limit, and no 413 is sent: a cut-off while the fields before file are read fails with 422 invalid_body, and a cut-off inside the file usually fails with 500 upstream_error. Nothing after the file field is read, so a body that only passes the limit there is uploaded normally. Deployment-scoped surfaces neither strip location metadata nor apply the 50 MiB image cap; they limit the request body to 110 MB (110,000,000 bytes). A request that declares a Content-Length over that limit is refused with the same plain 413; a body sent without a declared length is cut off when it passes the limit, and the upload then fails with an error response that is not guaranteed to be a 413. The uploaded asset is referenceable immediately; any content scanning runs in the background and does not block the response.

Send file as the last part. Comfy Cloud and serverless deployments stream it straight into storage and read the other fields only up to it, so a part sent after file is never read: a content_type or file_path sent only after it is refused as missing (422); an optional field (expected_hash or tags) is silently ignored — the upload succeeds without the hash check or the caller's tags; on serverless deployments, a trailing expires_in is likewise ignored, losing the retention override; and a second file part is dropped, so only the first part's bytes are stored. Comfy Cloud ignores expires_in wherever it is sent, before or after file. The response does not say any of these were skipped.

An upload is a single request. v1 expects files of up to ~100 MB; the hard cap is the body limit of the surface it is sent to (above). Chunked/resumable large uploads are a deliberate open question.

Cloud's hosted API caps every part sent before file at 8 KiB, including a part it does not recognise (which it otherwise ignores), so an extra field over 8 KiB, such as file bytes sent under a misspelled name, is refused with 422 invalid_body. Parts sent after file are never read: the 8 KiB cap does not apply to them, and a metadata field sent there is treated as absent, so send every metadata field before file. A declared Content-Length still counts those bytes toward the size limit above; without one, a body that only exceeds the limit there is uploaded normally, since nothing after file is read.

On Comfy Cloud an upload fails with 422 upload_timed_out when no request bytes arrive for 30 seconds, or when the whole request takes longer than 10 minutes, however steadily bytes keep arriving. On Comfy Cloud an upload can also be refused with 503 upload_busy and Retry-After when the server is busy with other uploads: nothing was stored, so the same upload can be sent again after Retry-After. On a Developer Platform deployment the same 422 upload_timed_out applies when no request bytes arrive for 30 seconds, or when the whole request takes longer than 20 minutes.

OpenAPI

````yaml /openapi-v2.yaml post /api/v2/assets openapi: 3.0.3 info: title: Comfy API v2 version: 2.0.0

description: |

The official, versioned HTTP API for running ComfyUI workflows from external applications: upload inputs, submit a workflow, observe execution, retrieve results.

Design principles:

  • Poll-first. Every capability is reachable via plain GET polling; the SSE stream is a live enhancement, never the source of truth.
  • Everything is resumable. Submission is idempotent; job state and outputs are retrievable by ID until expires_at.
  • UUID identity, content-addressed dedup. Assets are UUID-identified records over blobs keyed by a server-computed blake3 hash. The hash is nullable and may be computed lazily.
  • Follow links, don't build URLs. Responses embed follow-up URLs.

Additive changes only within v2; breaking changes require v3. servers:

  • url: http://127.0.0.1:8189 description: Self-hosted (comfy-api-proxy)
  • url: https://cloud.comfy.org description: Comfy Cloud
  • url: https://{deployment}.run.comfy.app description: Serverless deployment variables: deployment: description: >- DNS-safe deployment id (subdomain label). Staging uses {deployment}.stg.run.comfy.app. default: dep-1234abcd-56ef-7890-abcd-ef1234567890 security:
  • bearerAuth: []
  • {} tags:
  • name: assets description: UUID-identified records over content-addressed blobs.
  • name: jobs description: One execution of a workflow — durable, pollable, cancelable. paths: /api/v2/assets: post: tags:
  • assets summary: Upload an asset (single-call multipart) description: > Single-call multipart/form-data upload. The platform streams the

bytes through its trusted byte-path, dedups by the server-computed

hash, and mints the asset record.

The blake3 hash is always computed server-side from the received

bytes — a client-declared expected_hash is verified, never trusted

(on Comfy Cloud and serverless, only when sent before file — see below).

If location metadata (GPS, XMP, IPTC) is removed from an image before

it is stored, the asset's hash is the hash of the stored bytes and

can differ from expected_hash. On Comfy Cloud, an upload whose

bytes open as a JPEG, PNG, WebP or classic TIFF (including TIFF-based

camera RAW such as DNG, NEF, ARW and CR2, but not BigTIFF) and that

is over 50 MiB is refused with 422 validation_error, whatever its

content_type and whether or not it carries location metadata. A

request that declares a Content-Length over Comfy Cloud's 200 MB

(200,000,000-byte) body limit is refused before that check with a

plain 413 that does not use the ErrorEnvelope shape. A body sent

without a declared length is cut off when it passes that limit, and

no 413 is sent: a cut-off while the fields before file are read

fails with 422 invalid_body, and a cut-off inside the file usually

fails with 500 upstream_error. Nothing after the file field is

read, so a body that only passes the limit there is uploaded

normally.

Deployment-scoped surfaces neither strip location metadata nor apply

the 50 MiB image cap; they limit the request body to 110 MB

(110,000,000 bytes). A request that declares a Content-Length over

that limit is refused with the same plain 413; a body sent without a

declared length is cut off when it passes the limit, and the upload

then fails with an error response that is not guaranteed to be a 413.

The uploaded asset is referenceable immediately; any content scanning

runs in the background and does not block the response.

Send file as the last part. Comfy Cloud and serverless deployments

stream it straight into storage and read the other fields only up to

it, so a part sent after file is never read: a content_type or

file_path sent only after it is refused as missing (422); an

optional field (expected_hash or tags) is silently ignored — the

upload succeeds without the hash check or the caller's tags; on

serverless deployments, a trailing expires_in is likewise ignored,

losing the retention override; and a second file part is dropped,

so only the first part's bytes are stored. Comfy Cloud ignores

expires_in wherever it is sent, before or after file. The

response does not say any of these were skipped.

An upload is a single request. v1 expects files of up to ~100 MB;

the hard cap is the body limit of the surface it is sent to (above).

Chunked/resumable large uploads are a deliberate open question.

Cloud's hosted API caps every part sent before file at 8 KiB,

including a part it does not recognise (which it otherwise

ignores), so an extra field over 8 KiB, such as file bytes sent

under a misspelled name, is refused with 422 invalid_body. Parts

sent after file are never read: the 8 KiB cap does not apply to

them, and a metadata field sent there is treated as absent, so send

every metadata field before file. A declared Content-Length

still counts those bytes toward the size limit above; without one,

a body that only exceeds the limit there is uploaded normally,

since nothing after file is read.

On Comfy Cloud an upload fails with 422 upload_timed_out when no

request bytes arrive for 30 seconds, or when the whole request takes

longer than 10 minutes, however steadily bytes keep arriving.

On Comfy Cloud an upload can also be refused with 503 upload_busy

and Retry-After when the server is busy with other uploads: nothing

was stored, so the same upload can be sent again after Retry-After.

On a Developer Platform deployment the same 422 upload_timed_out

applies when no request bytes arrive for 30 seconds, or when the whole

request takes longer than 20 minutes. operationId: postAssets parameters:

  • $ref: '#/components/parameters/IdempotencyKey' requestBody: required: true content: multipart/form-data: schema: type: object required:
  • file
  • content_type
  • file_path properties: content_type: type: string example: image/png description: >- The asset's media type. On Comfy Cloud it must be valid UTF-8, not blank, at most 255 bytes, must not start or end with whitespace, and must not contain control characters (C0, DEL, C1, U+2028, U+2029) or any invisible format or default-ignorable character (every Unicode general category Cf character, including the zero-width joiners and direction marks, and the other default-ignorable code points); otherwise the request is refused with 422 invalid_body before anything is stored. An accepted value is stored as sent. A deployment refuses a content_type that is blank, is not valid UTF-8 or contains a control character (C0 other than tab, DEL, C1) or a bidi embedding, override or isolate (U+202A-U+202E, U+2066-U+2069), has a tab outside the whitespace that opens a parameter after a ;, or whose part before its first ; is not type/subtype (two RFC 9110 tokens, optionally followed by spaces), with 422 invalid_request before anything is stored. file_path: type: string description: >- Placement path / filename (global-namespace-root form, e.g. photo.png or models/checkpoints/x.safetensors). On Comfy Cloud the last segment becomes the asset name, so it must be valid UTF-8, at most 255 bytes, must not start with ., and must not contain control characters (C0, DEL, C1, U+2028, U+2029) or invisible format and default-ignorable characters (Unicode general category Cf, which includes the bidi embedding, override and isolate controls, soft hyphen, zero-width space, word joiner, BOM and tag characters, plus the other default-ignorable code points such as U+034F, U+2065 and the Hangul fillers); otherwise the request is refused with 422 invalid_body before anything is stored. ZWNJ/ZWJ (U+200C, U+200D) are allowed only between two visible non-ASCII characters (so not first, last, next to ASCII or next to another of these invisible characters, though one may follow an allowed variation selector, one right after an Indic virama on a letter of the same script may end a word, and a ZWJ after an Arabic-script letter that joins on both sides, or after that letter's marks, may precede ASCII such as a '.' or end the name), for Persian, Indic and emoji names. The direction marks U+200E, U+200F and U+061C are allowed, for right-to-left names, and so are the visible Arabic number signs (prepended concatenation marks such as U+0600, but not U+070F); the direction marks can still change how a name is displayed. The emoji presentation selectors U+FE0E and U+FE0F are allowed only after an ASCII digit, # or * that U+20E3 (the keycap) follows, after U+2139, the punctuation emoji U+203C, U+2049, U+3030 and U+303D or the math symbols U+2194, U+25FB-U+25FE, U+2934 and U+2935, and after any other symbol of category So except a regional indicator, a skin-tone modifier, a hair component or U+2800. Other variation selectors are allowed only after a base that can have variants: the Mongolian ones after a Mongolian letter, the rest after a unified Han ideograph, plus U+FE00 after the Myanmar and Phags-pa characters it is registered for. The Mongolian and Han rules follow scripts, not the registered sequence list. The tag characters are allowed only as the tag run of the England, Scotland and Wales flag emoji. These allowed invisible characters, white space, U+2800 and combining marks must not make up the whole segment or come before a leading .. A deployment keeps the whole file_path as the asset name, so it must be valid UTF-8 and must not contain a control character (C0 U+0000-U+001F, including tab, CR and LF; DEL U+007F; C1 U+0080-U+009F) or a bidi embedding, override or isolate (U+202A-U+202E, U+2066-U+2069); otherwise the request is refused with 422 invalid_request before anything is stored. Directory segments and other characters stay allowed there. example: photo.png expected_hash: type: string description: >- Optional client-computed blake3 (blake3:<hex>) of the bytes sent. Omit the part to skip verification: on Comfy Cloud and on a deployment a part sent empty is refused with 422 as malformed. Send it before file: nothing after the file field is read, so a trailing expected_hash is neither verified nor refused, and the response does not say the check was skipped. Verified against the server-computed blake3 of the received bytes, not of the stored bytes; mismatch is rejected with 409 hash_mismatch. On serverless deployments no asset is minted and no existing asset's retention is changed; elsewhere the upload is stored before the check, so the 409 returns no asset and a retry with the corrected expectation (and the same file extension) dedups onto it. On Comfy Cloud nothing removes that asset, so when the bytes themselves were wrong they stay stored: as an asset named after the last segment of file_path (x.safetensors for models/checkpoints/x.safetensors), or, when the bytes deduped onto an asset you already had, as that asset under its earlier name. The rest of file_path is not recorded. On Comfy Cloud and on a deployment, when expected_hash is sent more than once before file only the last value is checked, but an empty one among them is still refused with 422. example: blake3:9f8a1c0d... tags: type: array items: type: string description: >- Category tags (e.g. input). A create carries at most 64 tags, each a non-empty UTF-8 string of at most 256 bytes (surrounding whitespace included) with no NUL byte. A deployment adds input to every asset and counts it, so it takes at most 63 of the caller's own; the model_type: tags derived for a model asset are not counted. A tag set breaking a limit is refused with 422. The rest of this paragraph describes Cloud's hosted API and a deployment, not the self-hosted proxy. Both cap the bytes of all tags parts sent before file combined: 8 KiB on Cloud's hosted API (422 invalid_body) and 64 KiB on a deployment (422 invalid_request). The cap counts duplicates before they are removed and JSON escapes before they are decoded, and is checked before the count and 256-byte limits above, so a tag set within them can still be refused. A quoted-printable part is counted and read after it is decoded; a part in any other Content-Transfer-Encoding is read as sent, so a base64 part is stored as one tag holding its encoded text, while a 7bit, 8bit or binary part is read like a part with no encoding. A part is read as a JSON array when it starts with [ (after leading whitespace) and is valid JSON or continues, after optional whitespace, with ": ["a","b"] is two tags, [] adds no tags (nor does an empty part), and a malformed array such as [1] or ["a is refused with 422. Any other non-empty part is one bare tag, kept as literal text, so foo\ud800 sent as a bare part is stored as written. Invalid UTF-8 in a tags part, or a lone surrogate escape (e.g. \ud800) in a JSON-array tags part, is refused with 422 (invalid_body on Cloud, invalid_request on a deployment). expires_in: type: integer minimum: 60 maximum: 604800 description: >- Optional retention override in seconds (60s–7d): the asset's expires_at becomes now + expires_in, replacing the platform's default retention. Implementations without configurable retention ignore it. The bounds apply to this override only — the platform default is operator-configured and may lie outside them. example: 86400 file: type: string format: binary description: >- The raw bytes. The order of properties in this schema does not set the order of parts on the wire: file must be the last part sent (see the field-order rule above). responses: '200': description: Bytes deduped to an existing blob; asset minted over it. content: application/json: schema: $ref: '#/components/schemas/Asset' '201': description: New blob stored; asset minted. content: application/json: schema: $ref: '#/components/schemas/Asset' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '409': description: 'hash_mismatch.' headers: Retry-After: $ref: '#/components/headers/RetryAfter' content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '413': description: >- The upload is over the body limit. On Comfy Cloud (200 MB, 200,000,000 bytes) and on deployment-scoped surfaces (110 MB, 110,000,000 bytes) the request declared a Content-Length over it; the server's body limit sends this before the upload is handled, so the body is not an ErrorEnvelope and its shape is not part of this contract. The self-hosted proxy instead refuses a file over its configured cap (100 MiB by default) with an ErrorEnvelope whose code is payload_too_large. '422': description: >- idempotency_key_reuse, input_blocked (the bytes are already stored and flagged by content moderation, so file_path is not registered for them), upload_timed_out (the client stopped sending or sent too slowly; the server cannot recover the upload, so retry it from a faster connection), or validation failure — including a tags set over the limits above or a disallowed tag (invalid_request on a deployment or the self-hosted proxy; a deployment answers a body that is not multipart/form-data with a boundary with a plain 500 instead). On a deployment that includes a truncated file part: the body ends inside the part, before the delimiter that ends it. A body that stops right at that delimiter, without the final --, is not rejected: the part reads as complete and is stored. Comfy Cloud's asset upload answers validation failure instead with invalid_content_type (the request is not multipart/form-data with a boundary), invalid_body (a malformed multipart body or upload field) or validation_error (the platform refused the upload, for example an image over the size cap or a disallowed tag). Cloud refuses a malformed multipart body with invalid_body when the fault is found before the file part. Cloud stops parsing at file, so parts after it are never read, and a body that ends inside the streamed file currently gets 500 upstream_error instead. The exception is an upload with expected_hash: if Cloud accepts the upload before the whole file is read, it reads the rest. A body that ends inside the file part, before its delimiter, then gets invalid_body, and a complete body gets 500 upstream_error instead of 201 or 409; in both cases the asset is already stored, holding only the part of the file read before the upload was accepted. invalid_body also covers tags parts over the 8 KiB combined cap described under tags, and a body sent without a declared length that is cut off at the body limit, as described for this operation. Cloud uses the same code for a missing file; a missing content_type, or one that breaks the rules under content_type; a missing file_path, or one that is absolute, starts with ., contains .., is not already in clean form (no //, no . segment such as /./, and no trailing /) or breaks the rules under file_path; a content_type, file_path or expected_hash part over 8 KiB; and a non-empty expected_hash that is not blake3: followed by 64 lowercase hex characters. A content_type or file_path sent only after file counts as missing (see the field-order rule above). Any Content-Type other than multipart/form-data with a boundary (including a missing or unparseable one) gets invalid_content_type on Cloud. On Comfy Cloud and on a deployment, an expected_hash part that is present but empty is a malformed field (invalid_body on Comfy Cloud, invalid_request on a deployment), not an omitted hash. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/UpstreamError' '503': $ref: '#/components/responses/DeploymentLookupUnavailable' components: parameters: IdempotencyKey: name: Idempotency-Key in: header required: false schema: type: string description: >- Client-generated UUID (recommended). Single-use: the first request to present a key is processed; any later request with the same key is rejected 422 idempotency_key_reuse (reject-on-duplicate, no response replay). Keys expire after 24h. schemas: Asset: type: object description: >- A user-owned record identified by a server-assigned UUID, backing an immutable blob whose content carries a server-computed blake3 hash. hash may be computed lazily: an asset record (and its retrievable bytes) can exist before its hash is filled in. On an output, job_metadata carries the labels its producing job was submitted with, so code holding only the asset knows which run it came from. It is absent for assets no job produced, for outputs of a job sent without labels, where the serving surface keeps no job labels, and when the labels could not be read: so job_id without job_metadata does not prove the job had no labels. A create whose file path and bytes match an existing output answers with that output, so its job_id and job_metadata are the output's. required:
  • id
  • hash
  • size_bytes
  • content_type
  • created_at
  • url
  • url_expires_at properties: id: type: string example: 9f8a1c0d-2b3e-4f56-8a7b-1c2d3e4f5a6b hash: type: string nullable: true description: 'blake3:<hex>; null while lazily computed.' example: blake3:9f8a1c0d... size_bytes: type: integer format: int64 example: 4816293 content_type: type: string example: image/png file_path: type: string nullable: true example: photo.png created_new: type: boolean description: >- On POST /api/v2/assets responses, distinguishes a brand-new blob (true) from a dedup hit against bytes the platform already had (false). On POST /api/v2/assets/from-hash responses, true means a new asset reference was created over existing bytes (201); false means an existing asset was reused (200). created_at: type: string format: date-time url: type: string format: uri description: Short-lived content URL (signed, or proxy-served). url_expires_at: type: string format: date-time expires_at: type: string format: date-time nullable: true description: >- Retention deadline for the asset itself (distinct from url_expires_at, the signed URL's validity). Null or absent means the asset is non-expiring. On a dedup-hit create response the deadline may be later than now + the requested/default retention: re-referencing content extends its retention, never shortens it. job_id: type: string nullable: true description: >- ID of the job that produced this asset. Absent for assets no job produced, such as a fresh upload. job_metadata: allOf:
  • $ref: '#/components/schemas/JobMetadata' description: >- The labels the producing job was submitted with, read through job_id on each read. Absent for assets no job produced, for outputs of a job sent without labels, where the serving surface keeps no job labels, when the job is gone, and when the labels could not be read: so job_id without job_metadata does not prove the job had no labels. ErrorEnvelope: type: object description: | Shared error envelope with machine-readable codes. Core codes (v1): invalid_workflow (422), workflow_format_ui (422), missing_asset (422), hash_mismatch (409), blob_not_found (404), idempotency_key_reuse (422), upload_timed_out (422: an asset upload stopped sending or was too slow; retry from a faster connection), queue_full (429 + Retry-After), rate_limited (429 + Retry-After: the caller is past a request rate limit; retry), insufficient_credits (402), not_found (404), unauthorized (401), forbidden (403). Deployment-scoped surfaces add: deployment_not_ready (429 + Retry-After — the deployment can still reach ready; retry), deployment_unavailable (429 + Retry-After: the deployment is ready but its GPU provider is not taking work on it yet; retry), deployment_lookup_unavailable (503 + Retry-After, on any operation: the deployment the address names could not be looked up just now; nothing was done, so retry), database_unavailable (503 + Retry-After, only on a job submit: the job could not be saved just now and nothing was started, so retry), auth_unavailable (503 + Retry-After, on any operation: the credential could not be checked just now, which says nothing about the credential, so retry), output_unavailable (503 + Retry-After, only on a job status read: an output of the job could not be prepared just now; the job is unchanged, so retry after Retry-After, and report the job if it keeps answering), deployment_stopped (422 — terminal deployment state; a retry cannot succeed without operator action), invalid_request (422: a malformed asset upload or asset-from-hash field, or a jobs-list limit that is not a positive integer), validation_error (422, Cloud: an asset upload or asset-from-hash field was refused, e.g. a tag over the tag limits), content_blocked (451: content moderation flagged the asset's bytes) and sso_required (403: the key is valid, but the account must sign in through its organization's single sign-on, which does not accept this key). Comfy Cloud's asset upload adds invalid_content_type (422: the request is not multipart/form-data with a boundary), invalid_body (422: a malformed multipart body or upload field), validation_error (422: the platform refused the upload, for example an image over the size cap or a disallowed tag) and upload_busy (503 + Retry-After: the server is busy with other uploads and nothing was stored, so retry). Job labels add: metadata_invalid (422: a submitted metadata breaks a limit; details.key names the key, except for too many pairs, which has no details), metadata_not_supported (422: this surface keeps no job labels yet), invalid_metadata_filter (400) and invalid_cursor (400). A surface that does not list jobs yet answers not_implemented (501). A 429 is disambiguated by error.code alone; clients should treat any 429 + Retry-After as "back off and retry". required:
  • error properties: error: type: object required:
  • code
  • message properties: code: type: string example: invalid_workflow message: type: string example: 'Node 12 (KSampler): required input ''model'' is not connected' organization_id: type: string description: >- On sso_required: the organization whose single sign-on governs this key, the one that holds the account, else the one that holds the key's workspace. It is the organization query parameter of Comfy Cloud's single sign-on start; treat it as opaque. Absent when the organization is unknown, and on every other code. example: org_01HXYZEXAMPLE details: type: object nullable: true additionalProperties: true description: >- Machine-readable detail for the code. When it carries node_errors, that is keyed by node id and each value is a JobNodeError, the same shape as a job's error.node_errors, whether the refusal came at submit (for example unknown_node_class, a node class the deployment's build does not contain) or from ComfyUI after dispatch. With unknown_node_class errors it carries unknown_node_classes, the missing classes; with waits_for_browser errors, browser_wait_node_classes, the classes that wait for a browser. A submit refusal names at most fifty nodes. For invalid_node, invalid_inputs and invalid_class_type, when it found more than fifty, node_errors_truncated is true and malformed_node_count carries the full count; for waits_for_browser, browser_wait_node_count does. For unknown_node_class it names only nodes whose class is one of the at most ten listed in unknown_node_classes; when those nodes number more than fifty, node_errors_truncated is true and unknown_node_count is how many nodes have a listed class. Nodes whose class is past those ten are neither named nor counted. example: node_errors: '12': class_type: SomeCustomNode errors:
  • type: unknown_node_class message: >- this deployment's build does not contain the node class SomeCustomNode. unknown_node_classes:
  • SomeCustomNode JobMetadata: type: object description: >- The labels the job was submitted with, exactly as sent. Absent when it was sent with none. additionalProperties: type: string example: client: acme responses: Unauthorized: description: >- unauthorized: missing or invalid credentials. A credential that could not be checked answers auth_unavailable (503) instead. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' Forbidden: description: >- forbidden — authenticated but not allowed. On a serverless deployment, also sso_required — the key is valid, but the account must sign in through its organization's single sign-on, which does not accept this key; it can come back on any operation. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' RateLimited: description: >- rate_limited — the caller has exceeded a request rate limit for this account. Account/rate-scoped, not resource-specific — this can be returned even for a job, asset or deployment the caller doesn't own or that doesn't exist. headers: Retry-After: $ref: '#/components/headers/RetryAfter' content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' UpstreamError: description: >- upstream_error — an unexpected failure reaching or processing the request in this implementation's backing services. The message is always a generic, safe-to-display string; implementation detail (the specific upstream, its error text, transport failures) is never included here — see each implementation's own error-mapping notes. Every operation in this contract can fail this way. content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' DeploymentLookupUnavailable: description: >- On deployment-scoped surfaces, deployment_lookup_unavailable: the deployment this address names could not be looked up just now. The request was refused before it did anything (nothing was created, read, changed or canceled), so the same request can be sent again after Retry-After. Every operation at a deployment's address can answer this way. A job submit can also answer this 503 with database_unavailable: the job could not be saved just now and nothing was started, so the same request can be sent again after Retry-After. No other operation answers database_unavailable. On Comfy Cloud an asset upload can also answer this 503 with upload_busy: the server is busy with other uploads and nothing was stored, so the same upload can be sent again after Retry-After. Any operation can also answer this 503 with auth_unavailable: the credential could not be checked just now, so the same request can be sent again after Retry-After. A job status read can also answer it with output_unavailable: an output of the job could not be prepared just now, and the job is unchanged. A transient storage failure clears on a later read after Retry-After; an output whose stored address is broken answers it on every read until the platform repairs the output. headers: Retry-After: $ref: '#/components/headers/RetryAfter' content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' headers: RetryAfter: schema: type: integer description: Seconds to wait before retrying. securitySchemes: bearerAuth: type: http scheme: bearer description: >- Authorization: Bearer <credential>. The credential is one of: an account-scoped API key (comfyui-…), accepted on Cloud and serverless; a Comfy Cloud session JWT; or an OAuth access token issued for the Comfy Cloud resource. Which kinds a given deployment accepts is deployment configuration — an API key always works on Cloud and serverless, and a deployment that does not accept JWT bearers answers 401 with a message saying so. Self-hosted accepts unauthenticated requests by default and can be configured with a static bearer token.

````