Documentação da API

Primeira leitura

Do zero ao primeiro JSON de um Bill of Lading.

1. Tenha uma chave

Criada no portal, em Conta › Chaves de API. O passo a passo está em Autenticação, e vale para todos os produtos.

2. Envie o arquivo

O arquivo vai como multipart/form-data, num campo chamado file. É o arquivo cru, do mesmo jeito que um formulário HTML envia.

ItemValor
Content-Type da requisiçãomultipart/form-data
Nome do campofile
Formatos aceitosPDF, JPEG, PNG
Tamanhoaté 5 MB
Páginasaté 5
Não mande base64, nem JSON com o arquivo dentro, nem um link para baixar. Um corpo application/json com o PDF em base64 é recusado. Se a sua linguagem chama isso de Blob, File, FormData ou stream, tudo bem: o que importa é a requisição sair como multipart, com os bytes no campo file.
curl
curl -X POST https://comexdoc.com.br/v1/ebl/documents \
  -H "Authorization: Bearer cxd_live_..." \
  -H "Idempotency-Key: 3f7c1e9a-2b44-4d1e-9a10-7c5b2e8d4f11" \
  -F "file=@bl.pdf"
Node
import { readFile } from "node:fs/promises";
import { randomUUID } from "node:crypto";

const form = new FormData();
form.append(
  "file",
  new Blob([await readFile("bl.pdf")], { type: "application/pdf" }),
  "bl.pdf",
);

const res = await fetch("https://comexdoc.com.br/v1/ebl/documents", {
  method: "POST",
  headers: {
    Authorization: "Bearer cxd_live_...",
    "Idempotency-Key": randomUUID(),
    // Não defina Content-Type aqui: o FormData põe o boundary sozinho.
  },
  body: form,
});
Python
import uuid, requests

with open("bl.pdf", "rb") as f:
    r = requests.post(
        "https://comexdoc.com.br/v1/ebl/documents",
        headers={
            "Authorization": "Bearer cxd_live_...",
            "Idempotency-Key": str(uuid.uuid4()),
        },
        files={"file": ("bl.pdf", f, "application/pdf")},
    )
PHP
$ch = curl_init("https://comexdoc.com.br/v1/ebl/documents");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer cxd_live_...",
        "Idempotency-Key: " . bin2hex(random_bytes(16)),
    ],
    CURLOPT_POSTFIELDS => [
        "file" => new CURLFile("bl.pdf", "application/pdf", "bl.pdf"),
    ],
]);
$resposta = curl_exec($ch);
Um erro comum em Node e em JavaScript de navegador: definir Content-Type: multipart/form-data na mão. Isso apaga o boundary que o FormData gera, e o servidor não consegue separar as partes. Deixe esse cabeçalho de fora.

Além do arquivo, o corpo aceita um campo opcional webhook_url, para mandar o resultado desta leitura para um endereço diferente do destino cadastrado no portal.

A resposta é 202, na hora. Ela não traz o resultado, traz como buscá-lo:

{
  "schema_version": "1.0",
  "job_id": "6fbb2659-6e9f-4f00-a211-dd1a7fef2dda",
  "status": "queued",
  "document_hash": "efca21eda0263a4c47c508719b3151d1787b472e9223e30b057a3ee37affa380",
  "status_url": "/v1/ebl/documents/6fbb2659-6e9f-4f00-a211-dd1a7fef2dda",
  "created_at": "2026-09-05T23:28:30.214Z"
}
Mande sempre o Idempotency-Key. É um UUID seu, um por arquivo. Se a resposta se perder na rede e você repetir a requisição com a mesma chave, devolvemos o mesmo job_id em vez de ler o documento outra vez. Sem ele, um retry automático do seu cliente HTTP vira uma segunda leitura paga. A chave vale por 24 horas.

3. Pegue o resultado

curl https://comexdoc.com.br/v1/ebl/documents/6fbb2659-... \
  -H "Authorization: Bearer cxd_live_..."

Enquanto a leitura corre, status é queued ou processing e data vem null. Quando termina, data traz o documento inteiro.

statusO que significa
queuedAceito, esperando a vez na fila
processingSendo lido agora
completedPronto. O JSON está em data
review_requiredPronto, mas algo pede conferência humana. Não é erro: vem com 200 e o JSON completo
reviewedConferido por uma pessoa no portal
failedNão foi possível ler. O motivo está em error_code
Consultar em laço funciona, mas o webhook poupa requisição. Uma leitura leva cerca de dois minutos; perguntar de dez em dez segundos são doze chamadas para uma resposta. Se você já tem um servidor exposto, veja Receber por webhook.

O arquivo original continua disponível

GET /v1/ebl/documents/{job_id}/file devolve o arquivo exatamente como você enviou. Ele não expira.