Upload a document to a deal
using System.Net.Http.Headers;var client = new HttpClient();var request = new HttpRequestMessage{ Method = HttpMethod.Post, RequestUri = new Uri("https://app.quatlasglobal.com/api/integrations/v1/deals/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/documents"), Headers = { { "Authorization", "Bearer <token>" }, }, Content = new MultipartFormDataContent { new StringContent("binary") { Headers = { ContentDisposition = new ContentDispositionHeaderValue("form-data") { Name = "file", } } }, new StringContent("example") { Headers = { ContentDisposition = new ContentDispositionHeaderValue("form-data") { Name = "category", } } }, },};using (var response = await client.SendAsync(request)){ response.EnsureSuccessStatusCode(); var body = await response.Content.ReadAsStringAsync(); Console.WriteLine(body);}HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://app.quatlasglobal.com/api/integrations/v1/deals/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/documents")) .header("Authorization", "Bearer <token>") .header("Content-Type", "multipart/form-data; boundary=---011000010111000001101001") .method("POST", HttpRequest.BodyPublishers.ofString("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"\r\n\r\nbinary\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"category\"\r\n\r\nexample\r\n-----011000010111000001101001--\r\n")) .build();HttpResponse<String> response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());System.out.println(response.body());const url = 'https://app.quatlasglobal.com/api/integrations/v1/deals/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/documents';const form = new FormData();form.append('file', 'binary');form.append('category', 'example');
const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
options.body = form;
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}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=exampleRequires 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.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Header Parameters
Section titled “Header Parameters”Request Bodyrequired
Section titled “Request Bodyrequired”object
Responses
Section titled “Responses”Created
object
The document’s category, as the app names it.
The deal’s business number (DEAL-206447).
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.
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.
Which side uploaded it: your company’s side on the deal.
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
object
Examplegenerated
{ "detail": "example", "instance": "example", "status": 1, "title": "example", "type": "example"}Unauthorized
object
Examplegenerated
{ "detail": "example", "instance": "example", "status": 1, "title": "example", "type": "example"}Forbidden
object
Examplegenerated
{ "detail": "example", "instance": "example", "status": 1, "title": "example", "type": "example"}Not Found
object
Examplegenerated
{ "detail": "example", "instance": "example", "status": 1, "title": "example", "type": "example"}Conflict
object
Examplegenerated
{ "detail": "example", "instance": "example", "status": 1, "title": "example", "type": "example"}Payload Too Large
object
Examplegenerated
{ "detail": "example", "instance": "example", "status": 1, "title": "example", "type": "example"}Unsupported Media Type
object
Examplegenerated
{ "detail": "example", "instance": "example", "status": 1, "title": "example", "type": "example"}Unprocessable Entity
object
Examplegenerated
{ "detail": "example", "instance": "example", "status": 1, "title": "example", "type": "example"}Too Many Requests
object
Examplegenerated
{ "detail": "example", "instance": "example", "status": 1, "title": "example", "type": "example"}Bad Gateway
object
Examplegenerated
{ "detail": "example", "instance": "example", "status": 1, "title": "example", "type": "example"}Service Unavailable
object
Examplegenerated
{ "detail": "example", "instance": "example", "status": 1, "title": "example", "type": "example"}