Skip to content

Upload a document to a deal

POST
/api/integrations/v1/deals/{id}/documents
curl --request POST \
--url https://app.quatlasglobal.com/api/integrations/v1/deals/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/documents \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: multipart/form-data' \
--form file=binary \
--form category=example

Requires the documents:write scope and an X-Idempotency-Key header. multipart/form-data with the file in ‘file’ and its category in ‘category’. The category must be one the app offers your side at the deal’s stage (400 Document.CategoryNotOffered otherwise): in the closing room the shipper uploads PackingList, CommercialInvoice, DangerousGoodsCertificate (hazardous cargo only), Other and LoadingInstruction, the carrier its transport document for the deal’s mode, WeighbridgeTicket and Other; once the deal is live either side uploads any current category, the dangerous goods certificate still the shipper’s for hazardous cargo and the loading instruction still the shipper’s; a withdrawn deal takes none (409 Document.DealNotOpen). CustomsDeclaration, CertificateOfOrigin and InsuranceCertificate are retired and AdditionalCostReceipt belongs to a cost line in the app, so the API takes none of them. The app’s own upload rules apply: at most 25 MB; PDF, JPEG, PNG, DOCX or XLSX, checked against the file’s contents (a DOCX or XLSX must be a Word or Excel package, recognised through its main part as Office reads it; a Word document sent as XLSX, or a workbook sent as DOCX, is kept as what it is and answered with its real contentType and a fileName with that type’s extension; any other archive or an empty file is refused, 415 saying what the file is); a malware scan before anything is stored (an infected file is refused with 422 Document.Infected and never stored); the loading instruction is the shipper’s; the carrier’s transport document waits for the shipper’s approved papers. The same X-Idempotency-Key with a different file or category answers 422 Integration.IdempotencyKeyReused. The other party is told as in the app, and document.uploaded goes to both parties’ webhooks. Your company’s uploads through the API are capped per UTC day (by default 50 files and 250 MB on Exchange, 200 files and 1 GiB on Enterprise; 429 Integration.DailyUploadLimit). A test key’s upload is checked and scanned the same way; the sandbox keeps the record, never the file.

id
required
string format: uuid
X-Idempotency-Key
string
Media typemultipart/form-data
object
file
string format: binary
category
string

Created

Media typeapplication/json
object
category
required

The document’s category, as the app names it.

string
contentType
required
string
dealId
required
string format: uuid
dealNumber

The deal’s business number (DEAL-206447).

null | string
fileName
required
string
id
required
string format: uuid
livemode
required

True for a live key. False for a test key: the sandbox checked and scanned the file like a live upload, then kept only this record, never the file.

boolean
scanStatus
required

Clean. A file is scanned for malware before it is stored, as in the app: an infected file is refused (422 Document.Infected) and never stored, so an accepted upload is always Clean.

string
sizeBytes
required
integer | string format: int64
/^-?(?:0|[1-9]\d*)$/
uploadedAt
required
string format: date-time
uploadedBySide
required

Which side uploaded it: your company’s side on the deal.

string
Examplegenerated
{
"category": "example",
"contentType": "example",
"dealId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"dealNumber": "example",
"fileName": "example",
"id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"livemode": true,
"scanStatus": "example",
"sizeBytes": 1,
"uploadedAt": "2026-04-15T12:00:00Z",
"uploadedBySide": "example"
}

Bad Request

Media typeapplication/problem+json
object
detail
null | string
instance
null | string
status
null | integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
title
null | string
type
null | string
Examplegenerated
{
"detail": "example",
"instance": "example",
"status": 1,
"title": "example",
"type": "example"
}

Unauthorized

Media typeapplication/problem+json
object
detail
null | string
instance
null | string
status
null | integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
title
null | string
type
null | string
Examplegenerated
{
"detail": "example",
"instance": "example",
"status": 1,
"title": "example",
"type": "example"
}

Forbidden

Media typeapplication/problem+json
object
detail
null | string
instance
null | string
status
null | integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
title
null | string
type
null | string
Examplegenerated
{
"detail": "example",
"instance": "example",
"status": 1,
"title": "example",
"type": "example"
}

Not Found

Media typeapplication/problem+json
object
detail
null | string
instance
null | string
status
null | integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
title
null | string
type
null | string
Examplegenerated
{
"detail": "example",
"instance": "example",
"status": 1,
"title": "example",
"type": "example"
}

Conflict

Media typeapplication/problem+json
object
detail
null | string
instance
null | string
status
null | integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
title
null | string
type
null | string
Examplegenerated
{
"detail": "example",
"instance": "example",
"status": 1,
"title": "example",
"type": "example"
}

Payload Too Large

Media typeapplication/problem+json
object
detail
null | string
instance
null | string
status
null | integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
title
null | string
type
null | string
Examplegenerated
{
"detail": "example",
"instance": "example",
"status": 1,
"title": "example",
"type": "example"
}

Unsupported Media Type

Media typeapplication/problem+json
object
detail
null | string
instance
null | string
status
null | integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
title
null | string
type
null | string
Examplegenerated
{
"detail": "example",
"instance": "example",
"status": 1,
"title": "example",
"type": "example"
}

Unprocessable Entity

Media typeapplication/problem+json
object
detail
null | string
instance
null | string
status
null | integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
title
null | string
type
null | string
Examplegenerated
{
"detail": "example",
"instance": "example",
"status": 1,
"title": "example",
"type": "example"
}

Too Many Requests

Media typeapplication/problem+json
object
detail
null | string
instance
null | string
status
null | integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
title
null | string
type
null | string
Examplegenerated
{
"detail": "example",
"instance": "example",
"status": 1,
"title": "example",
"type": "example"
}

Bad Gateway

Media typeapplication/problem+json
object
detail
null | string
instance
null | string
status
null | integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
title
null | string
type
null | string
Examplegenerated
{
"detail": "example",
"instance": "example",
"status": 1,
"title": "example",
"type": "example"
}

Service Unavailable

Media typeapplication/problem+json
object
detail
null | string
instance
null | string
status
null | integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
title
null | string
type
null | string
Examplegenerated
{
"detail": "example",
"instance": "example",
"status": 1,
"title": "example",
"type": "example"
}