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.
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
| Field | Type | Notes |
|---|---|---|
order_uuid | string (path) | UUID of the order you are tracking (the parent order for a bundle). |
file | file | The document. Accepted types: PDF, DOC, DOCX, JPG, PNG. Max size 25 MB. |
field_key | string (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_id | integer (optional) | The id of an open rejection from required_actions.rejections (see Respond to order rejections) this upload should resolve — either a file request or a required document rejection. 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 — a file request has no field key, so an upload with no rejection_id never resolves one. |
description | string (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:
-
When you create an order and a required file is missing, the order is automatically moved to
Action Required, and anorder_status_changedwebhook is dispatched. The missing document is named in the order'srequired_actionsas a rejection, with the samemessageguidance described in Respond to order rejections ("Upload the requested document via the documents EP to answer this rejection."). -
To satisfy the requirement, either:
- Upload the file with
field_keyset to the required field'skey, same as before this rejection got its ownid— norejection_idneeded, as long as the requirement is still open (i.e. you haven't already satisfied it). Sendingfield_keyfor a required field that has nothing left to resolve — because it was already satisfied, or isn't actually missing — returns a422instead of silently accepting the upload, or - Upload the file with
rejection_idset to that rejection'sidand omitfield_keyentirely — the server derives it from the rejection automatically. If you send both,field_keymust match the field's key or the request 422s.
Each missing required file is tracked independently — if a service requires more than one file, the order stays in
Action Requireduntil every required file is uploaded. - Upload the file with
-
Once the last required file is uploaded, the order leaves
Action Requiredand anotherorder_status_changedwebhook 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 *.
Retrieving uploaded documents
The Get order response includes a documents array of the order's partner-visible documents:
"documents": [
{
"name": "driver-license.pdf",
"type": "general",
"field_key": null,
"description": null,
"uploaded_by": "customer",
"uploaded_at": "2026-07-01",
"download_url": "https://swyftfilings-docs.s3.amazonaws.com/documents/driver-license.pdf?X-Amz-Signature=..."
},
{
"name": "ra-agreement.pdf",
"type": "reg_agent",
"field_key": null,
"description": "Signed registered agent agreement",
"uploaded_by": "agent",
"uploaded_at": "2026-07-02",
"download_url": "https://swyftfilings-docs.s3.amazonaws.com/documents/ra-agreement.pdf?X-Amz-Signature=..."
}
]
- A document is included when it's visible to everyone (
type: "general") or restricted to the registered agent (type: "reg_agent") — internal-only documents are excluded. field_keyis thekeyof the service'sdocument-type input field this document answers (e.g.state_specific.county_liquor_license_document), when it's related to one. Null for a plain attachment not tied to any input field.descriptionechoes back the free-textdescriptionsent at upload time, if any.download_urlis a short-lived signed link that expires 15 minutes after the response is generated. Download the file promptly, or request the order again to obtain a fresh link.- The array is empty (
"documents": []) when no visible documents exist on the order.
Errors and edge cases
- 422 Validation:
fileis missing, exceeds 25 MB, or is an unsupported type;field_keydoesn't name a real document field for this order's service;field_keynames a currently-required document field that no longer has an open rejection to resolve (already satisfied, or sent with norejection_idfor a requirement that isn't actually missing);field_keywas sent alongside arejection_idtargeting a required-document rejection but doesn't match that field's key; orrejection_idwas 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, norejection_id) still succeeds (201) — it does not resolve any open file request.resolved_rejection_idisnullin the response, and the file request stays open until you upload again with itsrejection_id.