Skip to main content

Upload Order Documents

Some services require supporting documents from the customer — for example a signed form or a photo ID. You can attach these to an order programmatically with the document-upload endpoint.

Uploaded documents are linked to the order for our fulfillment team and are surfaced back to you under the documents collection on the Get order response — see Download Order Documents for how to retrieve them.

Upload a document​

Send the request as multipart/form-data (not JSON):

POST {baseUrl}/orders/{order_uuid}/documents
Authorization: Bearer {token}
Content-Type: multipart/form-data
FieldTypeNotes
order_uuidstring (path)UUID of the order you are tracking (the parent order for a bundle).
filefileThe document. Accepted types: PDF, DOC, DOCX, JPG, PNG. Max size 25 MB.
field_keystring (optional)The exact key of a document-type input field from the service configuration this upload satisfies (see Required files below) — must name a real document field for this order's service, or the request 422s. If the key names a currently-required document field, it must also still have an open rejection (i.e. not already satisfied) when sent without rejection_id, or the request 422s. Omit entirely for a plain attachment not tied to any field; use description for a human-readable label instead. Only meaningful when this upload isn't answering a rejection — omit it whenever you send rejection_id, and the server derives it from the targeted rejection instead.
rejection_idinteger (optional)The id of an open rejection from required_actions.rejections (see Respond to order rejections) this upload should resolve — a file request, a required document rejection, or an open scan-signature request (type: "scan-signature-request"; a wet-signature-request cannot be resolved this way — see Signature-required rejections). Sending it lets you omit field_key entirely for a required-document rejection — derived from the linked field's key. Required to resolve a file request or a scan-signature request — neither has a field key, so an upload with no rejection_id never resolves one.
descriptionstring (optional)Free-text label for the document, up to 1000 characters. Distinct from field_key/rejection_id (which identify what the document answers) — not matched against any field or rejection, just stored and echoed back on this response and on Get order. Defaults to the staff-entered request text when this upload answers a file request and no description was sent.

Example with curl — a plain attachment, no rejection or field involved:

curl -X POST "{baseUrl}/orders/a1b2c3d4-e5f6-7890-abcd-ef1234567890/documents" \
-H "Authorization: Bearer {token}" \
-F "description=Driver License" \
-F "file=@/path/to/driver-license.pdf"

Answering a specific rejection instead — field_key is derived automatically once rejection_id is sent:

curl -X POST "{baseUrl}/orders/a1b2c3d4-e5f6-7890-abcd-ef1234567890/documents" \
-H "Authorization: Bearer {token}" \
-F "rejection_id=220742" \
-F "file=@/path/to/articles-of-organization.pdf"

Success​

{
"data": {
"message": "Document uploaded successfully",
"order_uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"document": {
"name": "driver-license.pdf",
"type": "general",
"field_key": null,
"description": "Driver License",
"uploaded_at": "2026-07-01"
},
"resolved_rejection_id": 220742
},
"success": true
}

document.type is always general on upload — reg_agent restriction is a CRM staff action taken afterwards. document.field_key is the matching service input field's key when this upload's field_key named a real document-type field; null for a plain attachment. document.description echoes back the description you sent, if any (or the staff-entered request text for a file request answered with no description).

resolved_rejection_id echoes back the rejection this upload resolved (file request or required document) — the rejection_id you sent, or the required-document rejection field_key matched. It is null for a plain attachment upload that answered no rejection, and always null for a file request unless you sent rejection_id.

Documents can be uploaded at any point in the order's lifecycle, as long as the order belongs to your partner account.

Because of this, document-type fields (e.g. state_specific.professional_license_document, state_specific.veteran_verification_document) must never be sent as values in the order create (POST /orders) or order update (PATCH /orders/{order_uuid}) body. Sending one is rejected with a 422 — one validation error per document field found, so a request with several inline document values gets every offending field back in a single response:

{
"success": false,
"message": "Validation failed",
"errors": {
"state_specific.professional_license_document": [
"Documents must be uploaded via POST /orders/{order_uuid}/documents, not included in the request body."
],
"state_specific.veteran_verification_document": [
"Documents must be uploaded via POST /orders/{order_uuid}/documents, not included in the request body."
]
}
}

Required files​

Some services require a supporting file before they can be fulfilled. A required file is declared in the service configuration as an input field of type document with required: true

Because files are uploaded through this endpoint and never in the order-create body, a required file does not block order creation. Instead:

  1. When you create an order and a required file is missing, the order is automatically moved to Action Required, and an order_status_changed webhook is dispatched. The missing document is named in the order's required_actions as a rejection, with the same message guidance described in Respond to order rejections ("Upload the requested document via the documents EP to answer this rejection.").

  2. To satisfy the requirement, either:

    • Upload the file with field_key set to the required field's key, same as before this rejection got its own id — no rejection_id needed, as long as the requirement is still open (i.e. you haven't already satisfied it). Sending field_key for a required field that has nothing left to resolve — because it was already satisfied, or isn't actually missing — returns a 422 instead of silently accepting the upload, or
    • Upload the file with rejection_id set to that rejection's id and omit field_key entirely — the server derives it from the rejection automatically. If you send both, field_key must match the field's key or the request 422s. This also applies when rejection_id targets a file request instead — field_key must not name a different field that currently has its own open required-document rejection, or the request 422s rather than resolving both.

    Each missing required file is tracked independently — if a service requires more than one file, the order stays in Action Required until every required file is uploaded.

  3. Once the last required file is uploaded, the order leaves Action Required and another order_status_changed webhook is dispatched.

For example, if the service configuration lists a required document field with "key": "formation_document", either of these resolves it:

# By field_key (no rejection_id needed)
curl -X POST "{baseUrl}/orders/a1b2c3d4-e5f6-7890-abcd-ef1234567890/documents" \
-H "Authorization: Bearer {token}" \
-F "field_key=formation_document" \
-F "file=@/path/to/formation-document.pdf"

# By rejection_id (field_key omitted — derived from the rejection)
curl -X POST "{baseUrl}/orders/a1b2c3d4-e5f6-7890-abcd-ef1234567890/documents" \
-H "Authorization: Bearer {token}" \
-F "rejection_id=220745" \
-F "file=@/path/to/formation-document.pdf"

For a field belonging to an array (each element requires its own document, e.g. beneficial_owners_data.*.id_document), use the indexed key from that specific rejection's field (see Get order), e.g. beneficial_owners_data.0.id_document — not the literal key with *.

Resolving a scan-signature request​

A scan-signature-request rejection (see Signature-required rejections) closes the same way a file request does — there's no field key, so rejection_id is required:

curl -X POST "{baseUrl}/orders/a1b2c3d4-e5f6-7890-abcd-ef1234567890/documents" \
-H "Authorization: Bearer {token}" \
-F "rejection_id=220899" \
-F "file=@/path/to/signed-operating-agreement.pdf"

A wet-signature-request rejection cannot be resolved this way — sending its id as rejection_id 422s with the same "no open rejection eligible for document upload" error as an unrelated or already-answered rejection. A wet-signature request closes only once Swyft physically receives the signed document.

Errors and edge cases​

  • 422 Validation: file is missing, exceeds 25 MB, or is an unsupported type; field_key doesn't name a real document field for this order's service; field_key names a currently-required document field that no longer has an open rejection to resolve (already satisfied, or sent with no rejection_id for a requirement that isn't actually missing); field_key was sent alongside a rejection_id targeting a required-document rejection but doesn't match that field's key; field_key was sent alongside a rejection_id targeting a different rejection (e.g. a file request) while field_key itself names a field with its own separate open required-document rejection; or rejection_id was sent but does not match an open rejection eligible for document upload on this order (or one of its child lines).
  • 401 / 403: The order is not associated with your partner account.
  • Uploading a plain attachment (no field_key, no rejection_id) still succeeds (201) — it does not resolve any open file request. resolved_rejection_id is null in the response, and the file request stays open until you upload again with its rejection_id.