Documentação da API

O que vem no JSON

O objeto data é o Bill of Lading virado dado. As cargas estão dentro do contêiner que as leva, então você não precisa cruzar listas.

A forma

BlocoO que traz
bl_numberO número do BL, junto de tipo, situação de emissão, datas e vias originais
reference_numbersBooking, waybill, master BL, contrato de serviço e as demais referências
shipperQuem embarca: nome, identificação fiscal, endereço separado, contato
consigneeQuem recebe, na mesma forma
notify_partiesLista, porque um BL pode ter mais de um notify
agentsArmador e agentes, cada um com o papel
other_partiesQualquer papel que não seja nenhum dos acima
voyageNavio, viagem, portos de embarque e descarga, recebimento, entrega, transbordo
freightIncoterm, condição de pagamento e as linhas de custo
containersUm por contêiner, com as cargas que estão dentro dele
cargo_without_containerAs cargas que o documento não repartiu entre os contêineres
declared_totalsOs totais como o documento os imprime, sem recálculo. Vêm null quando o documento não imprime um total: a plataforma não soma por você e apresenta como declarado

A carga está dentro do contêiner

Esta é a diferença que mais economiza código do seu lado. Para saber o que vem no FCIU6596670, você lê o contêiner e lê o que está dentro. Não existe lista de vínculos para cruzar.

Um contêiner
{
  "container_number": "FCIU6596670",
  "seal_numbers": ["UL-2443323"],
  "container_type": "20GP",
  "size_in_feet": 20,
  "package_count": 18,
  "gross_weight":                { "value": 20291.664, "unit": "KG" },
  "tare_weight_empty_container": { "value": 2100,      "unit": "KG" },
  "cargo_weight_excluding_tare": { "value": 18191.664, "unit": "KG" },
  "measurement_volume":          { "value": 25,        "unit": "M3" },
  "cargo": [ ... ]
}

cargo_weight_excluding_tare é o bruto menos a tara, calculado aqui para você não refazer. Quando o documento não declara tara ele vem null, e nunca o bruto: armadores discordam sobre o que o peso bruto de um contêiner inclui, e escolher uma interpretação por você gravaria no seu sistema uma carga mais pesada do que é.

Quando a mesma carga está em dois contêineres

Acontece com frequência: 36 totes repartidos em 18 e 18. Uma carga e dois contêineres não cabem numa árvore sem repetir alguma coisa, então a carga aparece dentro dos dois — e três campos dizem isso em voz alta, para ninguém somar errado.

A mesma carga, vista de dentro do primeiro contêiner
{
  "cargo_id": 1,
  "description": "EVOTHERM P25, TE, 2100 LB, HAZARDOUS CHEMICALS...",
  "ncm_codes": [{ "code": "3824", "digit_count": 4 }],

  "package_count_in_this_container": 18,
  "gross_weight_in_this_container": { "value": 18191.664, "unit": "KG" },

  "also_loaded_in_containers": ["HLBU3614917"],
  "total_in_all_containers": {
    "package_count": 36,
    "gross_weight": { "value": 36383.328, "unit": "KG" }
  }
}
cargo_id é o mesmo nos dois contêineres. É por ele que você sabe que são a mesma carga, e não duas. Somar os nós sem olhar o id daria 72 volumes num embarque de 36.

Quando a carga cabe num contêiner só, also_loaded_in_containers vem vazio e a parte é igual ao total.

Carga que o documento não repartiu

Um BL com três contêineres e três cargas frequentemente não diz qual carga está em qual. Nesse caso a plataforma não distribui por conta própria: essas cargas saem em cargo_without_container, inteiras, e quem responde pela carga decide na conferência.

Distribuir por plausibilidade produziria um vínculo com cara de dado lido, e esse vínculo vira declaração aduaneira com o NCM no contêiner errado.

Carga perigosa

Quando o documento declara carga perigosa, cada carga traz a lista dangerous_goods, com uma entrada por declaração impressa: número ONU, nome, classe, grupo de embalagem e o que mais o emissor escreveu junto. Carga comum vem com a lista vazia.

Uma carga com dois produtos perigosos
{
  "cargo_id": 2,
  "description": "ALUMINIUM PROFILES AND ACCESSORIES",
  "hazardous_material_marked": true,
  "dangerous_goods": [
    {
      "un_number": "1219",
      "un_number_as_printed": "UN1219",
      "proper_shipping_name": "ISOPROPANOL",
      "technical_name": "ISOPROPANOL SOLUTION",
      "hazard_class": "3",
      "subsidiary_hazard_classes": [],
      "packing_group": "II",
      "flash_point": { "value": 12.0, "unit": "CEL", "method": "closed_cup", "as_printed": "12.0 C C.C." },
      "shipped_as_limited_quantity": true,
      "marine_pollutant": null,
      "ems": null,
      "package_count": 4,
      "package_type": "BOX",
      "emergency_contact": "+4917843374341"
    },
    { "un_number": "1133", "proper_shipping_name": "ADHESIVES", "hazard_class": "3", "packing_group": "II", ... }
  ]
}
CampoO que é
un_numberOs quatro dígitos do número ONU. null quando o impresso não tem exatamente quatro
un_number_as_printedO número como estava no documento, com prefixo e pontuação
proper_shipping_nameO nome apropriado para embarque, como impresso
technical_nameO nome técnico que o emissor pôs entre parênteses
hazard_classClasse ou divisão, sem o rótulo: CLASS 3 vem como 3
subsidiary_hazard_classesRiscos subsidiários declarados
packing_groupI, II ou III, sempre em romano: PG 2 vem como II
flash_pointPonto de fulgor, com unidade (CEL ou FAH) e método (closed_cup ou open_cup)
shipped_as_limited_quantitytrue quando declarado LTD QTY. null quando o documento não diz
marine_pollutanttrue quando declarado poluente marinho. null quando o documento não diz
emsO plano de emergência do IMDG, como impresso
package_countQuantos volumes esta declaração cobre, quando impresso junto dela
emergency_contactO contato de emergência da declaração

hazardous_material_marked é a coluna HM do formulário: true quando marcada, false quando existe e está em branco, null quando o formulário não tem a coluna.

A declaração vem como o documento a imprime. Classe, grupo ou nome que o documento não escreveu vêm null, mesmo quando o número ONU só admite um valor: completar pela lista da ONU faria uma declaração errada parecer certa. Declaração repetida no documento vem repetida.

Tudo separado, até o CEP

Seu sistema raramente quer o texto como está no papel. Às vezes quer só o CNPJ, às vezes só o CEP. Cada parte vem quebrada até o último pedaço:

O consignee
{
  "name": "INGEVITY QUIMICA LTD",
  "tax_ids": [
    { "type": "cnpj", "label": "CNPJ",
      "number": "30381107001160", "as_printed": "30.381.107/0011-60" }
  ],
  "address": {
    "street": "AV CONSTANTE PAVAN",
    "street_number": "4327",
    "complement": null,
    "district": "BETEL",
    "city": "PAULINIA",
    "state": "SP",
    "postal_code": { "as_printed": "13148-198", "digits_only": "13148198" },
    "country": "BRAZIL",
    "country_code": "BR"
  },
  "contact": { "phone": "+55 19 3514-1600", "email": null, "fax": null, "attention_to": null }
}

tax_ids é uma lista porque o mesmo bloco pode trazer CNPJ e inscrição estadual, ou um documento estrangeiro. number é o valor limpo, para comparar e gravar; as_printed é o que estava escrito, para conferir sem abrir o PDF.

Quantidade sempre vem com unidade

Peso, cubagem e medidas nunca vêm como número solto. Vêm como { value, unit }, com a unidade que o documento usou, sem conversão. Um BL em libras chega em libras: converter por você seria transformar em silêncio um número que vira declaração aduaneira.

"gross_weight":       { "value": 18450.5, "unit": "KGS" },
"measurement_volume": { "value": 58.2,    "unit": "CBM" }

Como a leitura foi feita

Página de origem de cada campo, endereço como estava impresso, base de cada vínculo, correções automáticas e o texto que não coube em campo nenhum: nada disso é o BL, então nada disso está dentro de data. Fica em extraction, e vem completo só quando pedido.

O que sempre vem
"extraction": {
  "contradictions_found": 0,
  "unmapped_text_blocks": 2,
  "auto_corrections_applied": 0,
  "url": "/v1/ebl/documents/{job_id}?include=extraction"
}

O resumo aparece sempre, mesmo sem pedir. Um documento com texto que não coube em campo nenhum precisa avisar que isso existe: a garantia de que nada se perde não pode depender de você adivinhar um parâmetro.

Pedindo o detalhe
GET /v1/ebl/documents/{job_id}?include=extraction
Dentro de extractionO que é
unmapped_textTodo texto que não virou campo, com a página. Nada é descartado
contradictions_in_documentOnde o documento se contradiz. O documento não é corrigido: quem decide é quem responde pela carga
auto_correctionsConsertos determinísticos aplicados sobre a leitura, como dígito verificador de contêiner
party_sourcesPágina e endereço como impresso, por parte
cargo_container_linksDe onde veio cada vínculo carga × contêiner, e de onde vieram os números repartidos
cargo_sourcesO cargo_id de cada carga, a página em que foi lida e a página de cada declaração de carga perigosa

Campo ausente e campo nulo

null significa que o documento não trazia aquilo. Não é zero, não é vazio, e nunca é um valor inventado para tapar a lacuna. Se o BL não declara o IMO do navio, vessel_imo_number vem null.

A chave continua presente de propósito: sem ela você não distingue "não consta no documento" de "o campo saiu da API". Se o tamanho da resposta importar mais que essa distinção, peça omit_null=true e as chaves nulas somem. Lista vazia e texto vazio continuam, porque já são um valor.

GET /v1/ebl/documents/{job_id}?omit_null=true

Versão do formato

Todo resultado carrega schema_version. Campo novo pode aparecer sem aviso, então trate desconhecido como ignorável. Campo existente não muda de significado nem some dentro da mesma versão.