Integre seu sistema com a API do Bridge Link para criar pedidos, consultar rastreamento e baixar etiquetas.
Siga estes passos para comecar a usar a API do Bridge Link em poucos minutos.
Entre em contato com a equipe Bridge Link para receber seu client_id e client_secret.
Use o endpoint POST /api/v1/oauth/token para obter seu idToken (valido por 24h).
Use o endpoint POST /api/v1/orders com o header Authorization: Bearer {token}.
Consulte o status e baixe as etiquetas usando os endpoints de consulta.
Ambiente de Producao: A URL base da API e https://bridgelink.com.br/api/v1
A API utiliza autenticacao OAuth2 com Bearer Token. Primeiro obtenha um token de acesso, depois use-o em todas as requisicoes.
curl -X POST "https://bridgelink.com.br/api/v1/oauth/token" \
-H "Content-Type: application/json" \
-d '{
"client_id": "bl_seu_client_id",
"client_secret": "seu_client_secret"
}'
{
"idToken": "eyJhbGciOiJIUzI1NiJ9.eyJjbGllbnRfaWQi...",
"expiresIn": "86400"
}
Token TTL: O token e valido por 24 horas (86400 segundos). Apos expirar, solicite um novo token.
Importante: Mantenha suas credenciais em seguranca. Nunca compartilhe ou exponha seu client_secret em codigo publico.
curl -X GET "https://bridgelink.com.br/api/v1/orders/ATN12345" \ -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..." \ -H "Content-Type: application/json"
Esta secao descreve a superficie de consumo multi-cliente usada por um parceiro/agregador. Clientes single-customer (um token = um cliente) nao precisam dela — seu token continua escopado ao seu unico cliente, exatamente como antes. Nada aqui altera o comportamento dos endpoints ja documentados.
Um token de parceiro le atraves de uma allowlist explicita de clientes (configurada pelo administrador). Com ele voce pode:
GET /api/v1/customers (paginado).GET /api/v1/customers/:id.customer_id em GET /api/v1/orders?customer_id=<uuid> estreita a busca a um unico cliente dentro da allowlist. Escopo e isolamento: o escopo parte sempre da allowlist do token — nunca de um id arbitrario que voce envie. Um customer_id (ou um :id de cliente) fora da allowlist (ou um UUID fabricado) retorna o mesmo 404 / codigo 5 que um recurso inexistente, sem qualquer sinal de enumeracao (nao existe um 403 "existe-mas-proibido" que revele a existencia de outro cliente).
A emissao de credenciais (criar o token de parceiro e definir a allowlist) e uma acao administrativa — nao e uma chamada de API. Voce consome sua credencial; nao a emite.
# Listar os clientes que o seu token de parceiro pode ler curl -X GET "https://bridgelink.com.br/api/v1/customers?per_page=50" \ -H "Authorization: Bearer SEU_TOKEN" # Puxar os pedidos de um cliente especifico da allowlist curl -X GET "https://bridgelink.com.br/api/v1/orders?customer_id=550e8400-e29b-41d4-a716-446655440000" \ -H "Authorization: Bearer SEU_TOKEN"
Os endpoints GET /customers, GET /customers/:id e o filtro customer_id estao detalhados nas secoes "Listar Clientes", "Consultar Cliente" e "Listar Pedidos" abaixo.
Crie um novo pedido/HAWB no sistema. O pedido sera processado e enviado para a transportadora automaticamente.
| Parametro | Tipo | Descricao |
|---|---|---|
| shipTo* | object | Dados do destinatario (name, phone, federalTaxId, address) |
| shipTo.address.correios* | object | Endereco no formato Correios (logradouro, numero, bairro, cep, cidade, uf) |
| packages* | array | Lista de pacotes com peso e nota fiscal |
| packages[].weightG(opcional) | number | Peso do pacote em gramas |
| packages[].documentTypes.invoice(opcional) | object | Dados da nota fiscal (key, number, totalValue) |
| contractId(opcional) | UUID | UUID de um contrato (loja/remetente) do seu proprio cliente (nivel raiz do payload). Carimba o pedido criado com aquele contrato. Aditivo: se ausente, a criacao e byte-identica a atual (ORDER-05) — nenhuma chave nova entra no payload nem na resposta. Um contractId malformado ou de outro tenant retorna 404 / codigo 5 byte-identico ao recurso inexistente (sem sinal de enumeracao, nunca um 403), antes da criacao. O id aceito e o UUID do contrato Bridge Link — nunca o client_id/contract_id nativo da Flash. |
{
"shipTo": {
"name": "Joao da Silva",
"phone": "21988887777",
"federalTaxId": "12345678901",
"address": {
"correios": {
"logradouro": "Av. Atlantica",
"numero": "500",
"complemento": "Apto 1001",
"bairro": "Copacabana",
"cep": "22010000",
"cidade": "Rio de Janeiro",
"uf": "RJ"
}
}
},
"packages": [
{
"weightG": 1500,
"documentTypes": {
"invoice": {
"key": "35240612345678000195550010000001011234567890",
"number": "12345",
"totalValue": 299.90
}
}
}
]
}
{
"packages": [
{
"trackingCode": "ATN123456789",
"loggiKey": "bf7a1c2d-3e4f-5678-9abc-def012345678",
"status": {
"code": 2,
"highLevelStatus": "IMPORTADO",
"description": "Pedido importado na transportadora",
"updatedTime": "2025-01-16T10:30:00Z"
},
"labelUrl": "https://bridgelink.com.br/api/v1/labels/ATN123456789",
"flashLabelUrl": "https://webservice.flashpegasus.com.br/..."
}
]
}
curl -X POST "https://bridgelink.com.br/api/v1/orders" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..." \
-H "Content-Type: application/json" \
-d '{
"shipTo": {
"name": "Joao da Silva",
"phone": "21988887777",
"federalTaxId": "12345678901",
"address": {
"correios": {
"logradouro": "Av. Atlantica",
"numero": "500",
"bairro": "Copacabana",
"cep": "22010000",
"cidade": "Rio de Janeiro",
"uf": "RJ"
}
}
},
"packages": [
{
"weightG": 1500,
"documentTypes": {
"invoice": {
"key": "35240612345678000195550010000001011234567890",
"number": "12345",
"totalValue": 299.90
}
}
}
]
}'
O POST /orders retorna dois formatos distintos de erro 422, ambos com codigo Loggi 9. Um cliente/SDK deve tratar os dois casos.
(a) Falha de credenciais/configuracao — o cliente nao tem credenciais da transportadora (Jall/Flash Courier) configuradas. O campo details vem vazio:
{
"code": 9,
"message": "Customer does not have Jall/Flash Courier credentials configured",
"details": []
}
(b) Falha de validacao do payload — o payload esta malformado ou faltam campos obrigatorios. A message e "Invalid payload" e details traz a lista de campos invalidos no formato fieldViolations (mesmo formato descrito na secao Formato de Resposta de Erro): details.fieldViolations[].field nomeia o campo ausente/invalido e description e a mensagem legivel:
{
"code": 9,
"message": "Invalid payload",
"details": [
{
"@type": "type.googleapis.com/google.rpc.BadRequest",
"fieldViolations": [
{ "field": "shipTo", "description": "shipTo is required" }
]
}
]
}
Retorna uma lista paginada e filtravel dos pedidos do proprio cliente, no envelope data + meta. A listagem e sempre escopada ao cliente do token — nunca expoe pedidos de outro cliente.
| Parametro | Tipo | Descricao |
|---|---|---|
| page(opcional) | inteiro | Pagina desejada (default: 1) |
| per_page(opcional) | inteiro | Itens por pagina (default: 50, maximo: 100 — valores acima sao limitados a 100) |
| status(opcional) | string | Chave do enum de status: erro, integrado, importado, rastreado, aguardando_nf, aguardando_centro_custo |
| source(opcional) | string | Chave do enum de origem do pedido |
| order_number(opcional) | string | Filtro exato pelo numero do pedido |
| invoice_number(opcional) | string | Filtro exato pelo numero da nota fiscal |
| tracking(opcional) | string | Filtro exato pelo codigo de rastreio |
| customer_id(opcional) | UUID | (Parceiro) Estreita a busca a um unico cliente dentro da allowlist do token. Um valor fora da allowlist (ou fabricado) retorna 404 / codigo 5 — nunca e silenciosamente ignorado nem amplia o escopo. |
| contract_id(opcional) | UUID | Estreita a busca aos pedidos de um contrato (loja/remetente) dentro do escopo do token. Aditivo: ausente nao altera a lista. Um valor fora do escopo (ou um UUID fabricado) retorna 404 / codigo 5 — byte-identico ao recurso inexistente, sem sinal de enumeracao. Um valor malformado (nao-UUID) falha alto em 400 / codigo 9 (campo contract_id). |
| created_from(opcional) | ISO-8601 | Data/hora inicial (inclusiva, fuso America/Sao_Paulo) |
| created_to(opcional) | ISO-8601 | Data/hora final (inclusiva, fuso America/Sao_Paulo) |
| created_after(opcional) | ISO-8601 | Alias de created_from (limite inferior inclusivo). Quando ambos sao enviados, o nome novo prevalece; a coluna e filtrada uma unica vez. |
| created_before(opcional) | ISO-8601 | Alias de created_to (limite superior inclusivo). Quando ambos sao enviados, o nome novo prevalece; a coluna e filtrada uma unica vez. |
| include_consumed(opcional) | booleano | Quando true, inclui na lista os pedidos ja consumidos (acknowledged). Por padrao a lista exclui os consumidos (pull-dedup). |
Um valor de enum invalido ou uma data malformada retorna 400 / codigo 9 (fail-loud), com o campo ofensor em details.fieldViolations. Parametros desconhecidos sao ignorados.
{
"data": [
{
"trackingCode": "ATN12345",
"status": {
"code": 2,
"highLevelStatus": "IMPORTADO",
"description": "Pedido importado na transportadora",
"updatedTime": "2024-12-17T10:30:00Z"
},
"orderNumber": "PED-001",
"invoiceNumber": "NF-001",
"source": "api",
"createdAt": "2024-12-17T10:30:00Z",
"customer": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "ATN Contact",
"cnpj": "12345678000190"
},
"contract": {
"id": "9c8b7a65-4d3e-2f1a-0b9c-8d7e6f5a4b3c",
"code": "LOJA-SP",
"name": "Loja Sao Paulo LTDA"
}
}
],
"meta": {
"page": 1,
"per_page": 50,
"total": 137,
"total_pages": 3
}
}
O bloco customer ({ id, name, cnpj }) e sempre presente na linha de lista e no detalhe — e apenas a identidade do cliente dono do pedido (nunca transportadora, plataforma ou credenciais). O id e o UUID do cliente e faz round-trip no filtro customer_id; o cnpj e apenas digitos. Os campos weight/value/itemsQuantity/obs/consumedAt ficam apenas no detalhe (GET /orders/:id).
O bloco contract ({ id, code, name }) e aditivo e condicional: aparece na linha de lista e no detalhe apenas quando o pedido tem contrato (omitido caso contrario — um pedido sem contrato fica byte-identico). O id e o UUID do contrato — a chave de enderecamento publica que faz round-trip no filtro contract_id e no contractId de criacao (os identificadores Flash nativos clientId/contractId da jallConnection seguem internos da transportadora). Fronteira de nao-vazamento: o bloco nunca carrega carrier_token, flash_client_id nem flash_contract_id.
Sobre o objeto status: aplica-se a mesma limitacao conhecida documentada na secao "Consultar Pedido" (o code: 0 de fallback vale igualmente aqui). Use o highLevelStatus como fonte de verdade.
curl -X GET "https://bridgelink.com.br/api/v1/orders?status=integrado&page=1&per_page=50" \ -H "Authorization: Bearer SEU_TOKEN"
Consulte os detalhes de um pedido existente. O segmento :id aceita o UUID do pedido OU o codigo de rastreio — um valor no formato UUID estrito (8-4-4-4-12) resolve por id; qualquer outro valor cai na busca por codigo de rastreio.
| Parametro | Tipo | Descricao |
|---|---|---|
| id* | string | UUID do pedido OU codigo de rastreamento (ex: ATN123456789). Um valor no formato UUID estrito resolve por id; qualquer outro valor resolve por codigo de rastreio. |
{
"packages": [
{
"trackingCode": "ATN12345",
"loggiKey": "550e8400-e29b-41d4-a716-446655440000",
"status": {
"code": 2,
"highLevelStatus": "IMPORTADO",
"description": "Pedido importado na transportadora",
"updatedTime": "2024-12-17T10:30:00Z"
},
"destination": {
"recipient": {
"name": "Joao da Silva",
"phone": "11999998888",
"federalTaxId": "12345678901"
},
"postalAddress": {
"postalCode": "01234567",
"administrativeArea": "SP",
"addressLines": [
"Rua das Flores, 123",
"Apto 101",
"Centro",
"Sao Paulo"
]
}
},
"invoice": {
"number": "NF-001",
"key": "35240612345678901234550010000000011000000001",
"series": "1",
"date": "2024-12-15"
},
"labelUrl": "https://bridgelink.com.br/api/v1/labels/ATN12345",
"flashLabelUrl": "https://webservice.flashpegasus.com.br/FlashPegasus/rest/padrao/etiqueta/HAWB12345.pdf",
"costCenter": {
"code": "CC01",
"name": "Centro de Custo SP"
},
"contract": {
"id": "9c8b7a65-4d3e-2f1a-0b9c-8d7e6f5a4b3c",
"code": "LOJA-SP",
"name": "Loja Sao Paulo LTDA"
},
"receiptUrl": "https://bridgelink.com.br/api/v1/delivery_receipts/ATN12345",
"createdAt": "2024-12-17T10:30:00Z",
"updatedAt": "2024-12-17T10:30:00Z",
"weight": 0.5,
"value": 150.0,
"itemsQuantity": 3,
"obs": "Entregar no periodo da manha",
"consumedAt": "2024-12-17T10:35:00Z",
"customer": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "ATN Contact",
"cnpj": "12345678000190"
}
}
]
}
| Campo | Tipo | Descricao |
|---|---|---|
| costCenter | object | Centro de custo resolvido para o pedido, exposto apenas como { code, name }. Omitido quando o pedido nao possui centro de custo. Os campos sensiveis (token/produto) nunca sao expostos. |
| contract | object | Contrato (loja/remetente) do pedido, bloco aditivo de identidade exposto apenas como { id, code, name } (name = razao_social, com fallback para nome_fantasia). Omitido quando o pedido nao tem contrato. O id e o UUID do contrato — a chave de enderecamento publica (round-trip no filtro contract_id e no contractId de criacao); os identificadores Flash nativos seguem internos da transportadora. Nunca carrega carrier_token, flash_client_id nem flash_contract_id. |
| receiptUrl | string | Link absoluto para o comprovante de entrega (GET /api/v1/delivery_receipts/:tracking), presente apenas quando ja existe um comprovante para este pedido. Omitido caso contrario. |
| invoice.date | ISO-8601 | Data de emissao da nota fiscal (YYYY-MM-DD), dentro do bloco invoice. null quando a data nao esta preenchida. |
| weight | number | Peso do pedido como armazenado. Omitido quando nao informado. |
| value | number | Valor do pedido como armazenado. Omitido quando nao informado. |
| itemsQuantity | inteiro | Quantidade de itens. Omitido quando nao informado. |
| obs | string | Observacoes livres do pedido. Omitido quando nao informado. |
| consumedAt | ISO-8601 | Instante em que o pedido foi marcado como consumido (acknowledged). Apenas no detalhe (nao aparece na linha de lista); omitido para pedidos ainda nao consumidos. Veja a secao "Acknowledge". |
| customer | object | Bloco de identidade do cliente dono do pedido, sempre presente, exposto apenas como { id, name, cnpj }. O id e o UUID do cliente (round-trip no filtro customer_id); o cnpj e apenas digitos. Nunca carrega transportadora, plataforma ou credenciais. |
status (limitacao conhecida)O objeto status e emitido na forma { code, highLevelStatus, description, updatedTime }. O highLevelStatus e a chave do enum de status em maiusculas (ex.: INTEGRADO, IMPORTADO, ERRO); o code e o codigo numerico Loggi correspondente.
| code | highLevelStatus | Descricao |
|---|---|---|
| 1 | INTEGRADO | Pedido integrado, aguardando importacao |
| 2 | IMPORTADO | Pedido importado na transportadora |
| 99 | ERRO | Erro no processamento |
| 0 | RASTREADO / AGUARDANDO_NF / AGUARDANDO_CENTRO_CUSTO | Status desconhecido (ver nota abaixo) |
Limitacao conhecida (status): os status rastreado, aguardando_nf e aguardando_centro_custo ainda nao possuem codigo numerico proprio e sao emitidos com code: 0 e description: "Status desconhecido" — embora o highLevelStatus (chave do enum em maiusculas) seja preenchido corretamente. Use o highLevelStatus como fonte de verdade do status do pedido; o code: 0 e um valor de fallback, nao um erro. Esse comportamento e pre-existente e mantido por compatibilidade. Nao confie em codigos numericos para esses tres status.
curl -X GET "https://bridgelink.com.br/api/v1/orders/ATN12345" \ -H "Authorization: Bearer SEU_TOKEN"
Marca um pedido como consumido pelo agregador (pull-dedup). Apos o acknowledge, o pedido sai da listagem padrao GET /orders (volta apenas com include_consumed=true). E a peca central do ciclo pull → acknowledge → re-poll.
| Parametro | Tipo | Descricao |
|---|---|---|
| id* | string | UUID do pedido OU codigo de rastreamento. Resolucao dupla, sempre dentro do escopo do token. |
{
"trackingCode": "ATN12345",
"consumedAt": "2024-12-17T10:35:00Z"
}
Idempotente (first-write-wins): a primeira chamada carimba consumedAt; as chamadas seguintes no mesmo pedido ecoam o mesmo instante, sem rebaixar nem rebatizar. Re-fazer o acknowledge e um no-op de sucesso. Resolve o pedido independentemente de ja estar consumido (nao depende de include_consumed).
Um :id fora do escopo (ou um UUID fabricado) retorna 404 / codigo 5 byte-identico ao Loggi (sem sinal de enumeracao). Token ausente/expirado retorna 401 / codigo 16.
curl -X POST "https://bridgelink.com.br/api/v1/orders/ATN12345/acknowledge" \ -H "Authorization: Bearer SEU_TOKEN"
Obtenha o historico completo de rastreamento de um pedido, incluindo todos os eventos e movimentacoes.
| Parametro | Tipo | Descricao |
|---|---|---|
| tracking* | string | Codigo de rastreamento do pacote |
{
"packages": [
{
"trackingCode": "ATN12345",
"loggiKey": "123456",
"status": {
"code": 10,
"highLevelStatus": "in_transit",
"description": "HAWB em transito para o destino",
"updatedTime": "2024-12-17T14:00:00Z"
},
"trackingHistory": [
{
"status": {
"code": 10,
"highLevelStatus": "in_transit",
"description": "HAWB em transito para o destino",
"updatedTime": "2024-12-17T14:00:00Z"
},
"location": {
"description": "CD Sao Paulo"
}
},
{
"status": {
"code": 5,
"highLevelStatus": "registered",
"description": "HAWB registrado no sistema",
"updatedTime": "2024-12-17T10:00:00Z"
},
"location": {
"description": "CD Rio de Janeiro"
}
}
],
"promisedDate": "2024-12-20T18:00:00Z",
"deliveryInformation": null
}
]
}
{
"packages": [
{
"trackingCode": "INT-EXEMPLO-0001",
"status": {
"code": 15,
"highLevelStatus": "out_for_delivery",
"description": "EM ROTA DE ENTREGA AO DESTINATARIO",
"updatedTime": "2024-12-17T13:45:00Z"
},
"trackingHistory": [
{
"status": {
"code": 15,
"highLevelStatus": "out_for_delivery",
"description": "EM ROTA DE ENTREGA AO DESTINATARIO",
"updatedTime": "2024-12-17T13:45:00Z"
},
"location": {
"description": "Base de Entrega - Sao Paulo/SP"
}
},
{
"status": {
"code": 5,
"highLevelStatus": "registered",
"description": "IMPORTACAO/CADASTRO DE ENTREGA",
"updatedTime": "2024-12-17T09:10:00Z"
},
"location": {
"description": "CD Origem - Guarulhos/SP"
}
}
],
"proofOfDeliveryUrl": "https://bridgelink.com.br/api/v1/comprovantes/INT-EXEMPLO-0001"
}
]
}
Quando entregue, deliveryInformation contem { receiverName, receiverDocument }. Codigos de status do rastreamento (carrier): 0 pending, 5 registered, 10 in_transit, 15 out_for_delivery, 20 delivered, 25 returned, 30 failed_delivery, 35 cancelled, 40 loss.
Interlog (v2.12): para pedidos de transportadora Interlog, o objeto status (e cada trackingHistory[].status) tem code/highLevelStatus/description derivados do StatusMapping (DE/PARA do codlog) e nunca sao null — cobrindo o conjunto completo de transportadora (incl. 30 failed_delivery, 35 cancelled, 40 loss). A resposta Interlog nao inclui loggiKey, promisedDate nem deliveryInformation (campos exclusivos da Flash).
proofOfDeliveryUrl (v2.16 — exclusivo Interlog): presente somente na resposta de pedidos Interlog — nunca nas respostas Flash ou Logan, e o envelope { "packages": [...] } permanece inalterado (o campo vive apenas dentro do ramo Interlog). Quando presente, e uma URL absoluta para GET /api/v1/comprovantes/:tracking (o proxy autenticado que devolve a imagem JPG do comprovante). O campo e incluido apenas quando ha um comprovante anexado ao pedido; caso contrario e omitido por completo (ausente — nunca null).
curl -X GET "https://bridgelink.com.br/api/v1/tracking/ATN12345" \ -H "Authorization: Bearer SEU_TOKEN"
Baixe as etiquetas de envio em formato PDF codificadas em Base64.
| Parametro | Tipo | Descricao |
|---|---|---|
| tracking* | string | Codigo de rastreamento do pacote |
{
"packages": [
{
"trackingCode": "ATN12345",
"loggiKey": "550e8400-e29b-41d4-a716-446655440000",
"label": {
"content": "JVBERi0xLjQKJeLjz9MKNCAwIG9iago8PC9...",
"format": "PDF",
"encoding": "base64"
},
"createdTime": "2024-12-17T10:30:00Z"
}
]
}
Dica: O campo content contem o PDF codificado em Base64. Para visualizar ou salvar, decodifique o conteudo Base64.
curl -s -X GET "https://bridgelink.com.br/api/v1/labels/ATN12345" \ -H "Authorization: Bearer SEU_TOKEN" \ | jq -r '.packages[0].label.content' \ | base64 -d > etiqueta.pdf
# Ruby
pdf_content = Base64.decode64(response['packages'][0]['label']['content'])
File.write('etiqueta.pdf', pdf_content)
# Python
import base64
pdf_content = base64.b64decode(response['packages'][0]['label']['content'])
with open('etiqueta.pdf', 'wb') as f:
f.write(pdf_content)
Retorna o perfil do proprio cliente autenticado (configuracao espelhada) e um resumo das integracoes. E um recurso singular auto-escopado: nao ha :id e nenhum parametro de cliente e consultado — a resposta e sempre o cliente dono do token.
{
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "ATN Contact",
"cnpj": "12345678000190",
"active": true,
"city": "Sao Paulo",
"uf": "SP",
"address": "Rua das Flores, 123",
"zipcode": "01234-567",
"whatsappPhone": "11999998888",
"traceable": true,
"deliveryReceipt": true,
"invoiceEmitter": false,
"carrier": "flash_courier",
"platform": "api",
"interlog": {
"ambiente": "producao",
"active": true,
"costCenters": [
{ "code": "CC01", "name": "Centro de Custo SP", "default": true },
{ "code": "CC02", "name": "Centro de Custo RJ", "default": false }
]
},
"contracts": [
{ "id": "9c8b7a65-4d3e-2f1a-0b9c-8d7e6f5a4b3c", "code": "LOJA-SP", "name": "Loja Sao Paulo LTDA", "cnpj": "12345678000190", "trackingPrefix": "SP" }
]
}
}
O campo id e o UUID do cliente (string), nunca um inteiro. O campo cnpj e retornado somente com digitos (normalizado pelo modelo, ex.: "12345678000190"), sem mascara. O bloco interlog aparece apenas para clientes com integracao Interlog configurada (omitido inteiramente para clientes Flash/Logan). Os centros de custo expoem somente { code, name, default }. Fronteira de nao-vazamento: este recurso (e o GET /customers/:id de parceiro) nunca expoem login, password, authorization, token, codproduto nem codgrupoproduto. Campos fixos com valor nulo sao serializados como null (nao omitidos).
O array contracts (lojas/remetentes do cliente) esta sempre presente — [] quando o cliente nao tem contrato. Cada item expoe apenas { id, code, name, cnpj, trackingPrefix } (name = razao_social, com fallback para nome_fantasia; trackingPrefix = prefix_tracking). O id e o UUID do contrato — a chave de enderecamento publica usada no filtro contract_id e no contractId de criacao. Fronteira de nao-vazamento: o item nunca carrega carrier_token, flash_client_id, flash_contract_id nem qualquer credencial.
curl -X GET "https://bridgelink.com.br/api/v1/customer" \ -H "Authorization: Bearer SEU_TOKEN"
(Endpoint de parceiro) Retorna a lista paginada dos clientes que o token de parceiro pode ler — a sua allowlist, ordenada por nome. Um token single-customer le apenas o seu proprio cliente. O escopo parte sempre de authorized_customer_ids — nunca de um id enviado pelo cliente.
| Parametro | Tipo | Descricao |
|---|---|---|
| page(opcional) | inteiro | Pagina desejada (default: 1) |
| per_page(opcional) | inteiro | Itens por pagina (default: 50, maximo: 100) |
{
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "ATN Contact",
"cnpj": "12345678000190",
"carrier": "flash_courier",
"platform": "api",
"active": true
}
],
"meta": {
"page": 1,
"per_page": 50,
"total": 4,
"total_pages": 1
}
}
A linha de resumo traz exatamente { id, name, cnpj, carrier, platform, active }. O id e o UUID do cliente (round-trip no filtro customer_id dos pedidos); carrier e platform sao as chaves de enum em string; active e a negacao de disabled. A lista e sempre escopada a allowlist do token.
curl -X GET "https://bridgelink.com.br/api/v1/customers?per_page=50" \ -H "Authorization: Bearer SEU_TOKEN"
(Endpoint de parceiro) Retorna o detalhe de um cliente dentro da allowlist do token, incluindo o remetente padrao (sender), o remetente estruturado da transportadora (carrierSender, apenas Logan) e a conexao de contrato Flash (jallConnection, apenas Flash). Um :id fora da allowlist (ou um UUID fabricado) retorna o mesmo 404 / codigo 5 que um recurso inexistente, sem qualquer sinal de enumeracao.
| Parametro | Tipo | Descricao |
|---|---|---|
| id* | UUID | UUID do cliente dentro da allowlist do token. |
{
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "ATN Contact",
"cnpj": "12345678000190",
"active": true,
"city": "Sao Paulo",
"uf": "SP",
"address": "Rua das Flores, 123",
"zipcode": "01234-567",
"whatsappPhone": "11999998888",
"traceable": true,
"deliveryReceipt": true,
"invoiceEmitter": false,
"carrier": "flash_courier",
"platform": "api",
"sender": {
"name": "Centro de Distribuicao SP",
"cnpj": "12345678000190",
"address": "Av. Logistica, 1000",
"city": "Sao Paulo",
"uf": "SP",
"zipcode": "04001-000"
},
"carrierSender": {
"name": "Remetente Logan",
"cnpj": "98765432000110",
"address": "Rua do Galpao",
"number": "500",
"neighborhood": "Distrito Industrial",
"city": "Cajamar",
"state": "SP",
"zipcode": "07750-000",
"ie": "111222333444"
},
"jallConnection": {
"senderLocationId": "1001",
"clientId": "FLASH-CLI-42",
"contractId": "CTR-2024",
"subsidiaryId": "FIL-01"
},
"contracts": [
{ "id": "9c8b7a65-4d3e-2f1a-0b9c-8d7e6f5a4b3c", "code": "LOJA-SP", "name": "Loja Sao Paulo LTDA", "cnpj": "12345678000190", "trackingPrefix": "SP" }
]
}
}
O bloco sender (6 chaves: name, cnpj, address, city, uf, zipcode) esta sempre presente. carrierSender (9 chaves, incluindo ie) aparece apenas para clientes Logan; jallConnection aparece apenas para clientes Flash e expoe exatamente as 4 chaves de contrato { senderLocationId, clientId, contractId, subsidiaryId }. Atencao: o contractId/clientId aqui sao os identificadores nativos da Flash (internos da transportadora) — nao sao a chave de enderecamento publica. A chave publica de contrato e o UUID do array contracts abaixo.
O array contracts (igual ao de GET /customer) esta sempre presente — [] quando o cliente nao tem contrato. Cada item expoe apenas { id, code, name, cnpj, trackingPrefix }; o id e o UUID do contrato (round-trip no filtro contract_id e no contractId de criacao). Nunca carrega carrier_token, flash_client_id nem flash_contract_id.
Fronteira de nao-vazamento: o detalhe nunca expoe login, password, authorization, token, codproduto nem codgrupoproduto — a whitelist enumerada de campos e a fronteira.
curl -X GET "https://bridgelink.com.br/api/v1/customers/550e8400-e29b-41d4-a716-446655440000" \ -H "Authorization: Bearer SEU_TOKEN"
Liste os comprovantes de entrega do proprio cliente (paginado + filtros) e consulte o detalhe curado de um comprovante pela chave de rastreio.
| Parametro | Tipo | Descricao |
|---|---|---|
| page(opcional) | inteiro | Pagina desejada (default: 1) |
| per_page(opcional) | inteiro | Itens por pagina (default: 50, maximo: 100) |
| status(opcional) | string | Chave do enum de status do comprovante (ex.: processando, aguardando_envio, entregue, concluido) |
| tracking(opcional) | string | Filtro exato pelo codigo de rastreio |
| created_from(opcional) | ISO-8601 | Data/hora inicial (inclusiva) |
| created_to(opcional) | ISO-8601 | Data/hora final (inclusiva) |
{
"data": [
{
"trackingCode": "ATN12345",
"status": "concluido",
"statusDescription": "Documento de entrega enviado ao servidor do cliente",
"invoiceNumber": "NF-001",
"createdAt": "2024-12-17T10:30:00Z"
}
],
"meta": {
"page": 1,
"per_page": 50,
"total": 12,
"total_pages": 1
}
}
Aqui o status e a chave bruta do enum do comprovante (string), acompanhada de statusDescription em pt-BR — diferente do objeto status { code, ... } dos pedidos.
curl -X GET "https://bridgelink.com.br/api/v1/delivery_receipts?status=concluido" \ -H "Authorization: Bearer SEU_TOKEN"
| Parametro | Tipo | Descricao |
|---|---|---|
| tracking* | string | Codigo de rastreamento do comprovante |
{
"data": {
"trackingCode": "ATN12345",
"status": "concluido",
"statusDescription": "Documento de entrega enviado ao servidor do cliente",
"invoiceNumber": "NF-001",
"createdAt": "2024-12-17T10:30:00Z",
"invoice": {
"number": "NF-001",
"series": "1",
"issueDate": "2024-12-15",
"volumeCount": "1",
"totalWeight": "0.5",
"invoiceValue": "150.00"
},
"recipient": {
"razaoSocial": "Cliente Final LTDA",
"city": "Sao Paulo",
"uf": "SP"
}
}
}
Os blocos invoice e recipient sao opcionais e omitidos quando nao ha dados. Nao ha campo de imagem/URL do comprovante — o status carrega o sinal do ciclo de vida da entrega. Um tracking de outro cliente ou inexistente retorna o mesmo 404 / codigo 5.
curl -X GET "https://bridgelink.com.br/api/v1/delivery_receipts/ATN12345" \ -H "Authorization: Bearer SEU_TOKEN"
Devolve a imagem JPG original do comprovante de entrega (foto/assinatura do recebimento) de um pedido Interlog, pela chave de rastreio. E o destino do campo proofOfDeliveryUrl da resposta de GET /api/v1/tracking — diferente de GET /api/v1/delivery_receipts/:tracking, que devolve o JSON curado do ciclo de vida (sem imagem). Requisicao autenticada via OAuth2 Bearer e escopada ao proprio cliente (tenant).
| Parametro | Tipo | Descricao |
|---|---|---|
| tracking* | string | Codigo de rastreamento do pedido |
Authorization: Bearer <idToken>
O corpo e a imagem binaria image/jpeg servida inline (Content-Type: image/jpeg, Content-Disposition: inline) — o JPG original completo do comprovante. Nao e um envelope JSON e nao e base64.
404 · code 5 — { "code": 5, "message": "Comprovante not found", "details": [] }: uma unica resposta indistinguivel para rastreio inexistente, rastreio de outro cliente, OU pedido que existe mas nao tem comprovante anexado (sem pista de enumeracao entre os tres casos).
500 · code 13 — { "code": 13, "message": "Comprovante retrieval failed", "details": [] }: falha ao ler o blob da imagem.
PII / seguranca: o comprovante e a imagem do recebimento do destinatario (dado pessoal). Por isso e servido apenas atraves deste endpoint autenticado e escopado por tenant — nunca como uma URL de blob direta /rails/active_storage/... (as rotas globais de blob do Active Storage nao sao escopadas por tenant). O campo proofOfDeliveryUrl aponta sempre para este proxy, nunca para um blob direto.
curl -X GET "https://bridgelink.com.br/api/v1/comprovantes/INT-EXEMPLO-0001" \ -H "Authorization: Bearer SEU_TOKEN" \ --output comprovante.jpg
A API utiliza codigos HTTP padrao para indicar o sucesso ou falha de uma requisicao. Todas as respostas de erro seguem o padrao Loggi { code, message, details }.
Esta API nao emite codigo 7 / PERMISSION_DENIED. Um recurso de outro cliente (ou inexistente) retorna sempre o mesmo 404 / codigo 5, sem revelar se o recurso existe em outro tenant.
{
"code": 5,
"message": "Resource not found",
"details": []
}
details)Para os erros 400 / 422 (codigo 9), o campo details traz a lista de campos invalidos no formato fieldViolations:
{
"code": 9,
"message": "Invalid filter",
"details": [
{
"@type": "type.googleapis.com/google.rpc.BadRequest",
"fieldViolations": [
{ "field": "status", "description": "Unrecognized status value" }
]
}
]
}
A API e protegida por rate limiting. Ao exceder o limite, a resposta e 429 com codigo Loggi 8 e o cabecalho Retry-After (segundos ate liberar).
| Superficie | Limite | Chave |
|---|---|---|
| /api/v1/* (endpoints autenticados) | 120 requisicoes/minuto | por cliente da API (JWT) |
| POST /api/v1/oauth/token | 10 requisicoes/minuto | por IP |
HTTP/1.1 429 Too Many Requests
Retry-After: 42
{
"code": 8,
"message": "Rate limit exceeded",
"details": []
}
Dica: Respeite o Retry-After antes de repetir a requisicao. Como o token dura 24 horas, solicite-o uma vez e reutilize-o — o limite por cliente (120/min) e mais generoso que o limite anonimo de oauth/token (10/min por IP). Sempre verifique o campo details para obter informacoes especificas sobre erros de validacao.
Contrato legivel por maquina: o contrato OpenAPI 3.0 desta API esta disponivel publicamente (sem token) em GET /api/v1/openapi.yaml — use-o para gerar um cliente/SDK automaticamente no seu ERP/WMS.
Alem de consultar a API por polling, voce pode receber os eventos por push: o Bridge Link envia uma requisicao POST assinada para uma URL sua (o endpoint receptor) assim que algo relevante acontece. Assim um parceiro/agregador (ex.: Engloba) recebe pedidos e atualizacoes de clientes sem precisar fazer polling.
O canal de webhooks e estritamente aditivo — ele entrega exatamente os mesmos recursos que a pull API ja expoe, com o mesmo escopo de allowlist. Voce pode usar webhooks, polling, ou os dois ao mesmo tempo.
A assinatura (cadastrar a URL receptora, os eventos desejados e gerar o segredo) e uma acao administrativa — nao e uma chamada de API. Fale com a equipe Bridge Link para habilitar o push e receber o segredo de assinatura (secret), revelado uma unica vez no cadastro.
Sao tres tipos de evento. Cada assinatura escolhe quais deseja receber.
| Evento | Quando dispara |
|---|---|
order.created |
Quando um pedido fica pronto pela primeira vez (integrado/importado), em qualquer uma das tres origens de criacao — pedido vindo da Tiny, pedido criado via API (POST /orders) ou importacao por SFTP — para um cliente dentro da allowlist do seu token. |
order.status_changed |
A cada transicao de status do pedido (incluindo as atualizacoes geradas no registro de rastreamento). |
customer.updated |
Quando um cliente (ou a sua configuracao de remetente / contrato CT-e) e criado ou atualizado. |
Todo webhook chega com o mesmo envelope versionado, com exatamente estas quatro chaves em camelCase: { event, eventId, occurredAt, data }.
{
"event": "order.status_changed",
"eventId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"occurredAt": "2026-06-20T19:12:27Z",
"data": { }
}
| Chave | Descricao |
|---|---|
event |
O tipo do evento — um de order.created, order.status_changed, customer.updated. |
eventId |
Chave de idempotencia (UUID). Definida uma vez por evento logico e reaproveitada em todas as retentativas — e tambem no reenvio (replay) feito pelo admin. Deduplique por eventId. |
occurredAt |
Instante do evento, em ISO8601 UTC. |
data |
O mesmo payload que a pull API retorna para aquele recurso (ver abaixo). |
O bloco data nao e um formato novo: e identico ao que voce ja recebe pela pull API — para order.created/order.status_changed e o detalhe de Consultar Pedido (GET /orders/:id); para customer.updated e o de Consultar Cliente (GET /customers/:id). Abaixo documentamos cada chave de cada payload, em todos os cenarios.
data de order.created e order.status_changedPara os dois eventos de pedido, data e o objeto abaixo. Modelo de presenca: as chaves de topo cujo valor seria nulo sao removidas (a chave nao aparece — nao vem como null): isso vale para invoice, weight, value, itemsQuantity, obs, flashLabelUrl, costCenter, contract, receiptUrl e consumedAt. Ja as chaves aninhadas (dentro de status, destination, recipient, etc.) podem vir como null.
| Campo | Tipo | Presenca | Descricao |
|---|---|---|---|
trackingCode | string | Sempre* | Codigo de rastreio (HAWB). *Ausente se o pedido ainda nao tiver tracking. |
loggiKey | string (UUID) | Sempre | ID interno do pedido no Bridge Link. |
status | objeto | Sempre | Bloco de status (4 chaves abaixo). |
status.code | inteiro | Sempre | Codigo numerico legado (Loggi). Varios status mapeiam para 0 — ver tabela de status. Use highLevelStatus como fonte de verdade. |
status.highLevelStatus | string | Sempre | Status canonico em MAIUSCULAS (ex.: INTEGRADO). E o nome do status interno em caixa alta. |
status.description | string | Sempre | Descricao em pt-BR (ver tabela de status). |
status.updatedTime | string (ISO8601) | Sempre | Quando o status mudou. |
destination | objeto | Sempre | Destinatario + endereco. |
destination.recipient.name | string | null | Sempre | Nome do destinatario. |
destination.recipient.phone | string | null | Sempre | Telefone do destinatario. |
destination.recipient.federalTaxId | string | null | Sempre | CPF ou CNPJ do destinatario (CPF tem precedencia quando ambos existem). |
destination.postalAddress.postalCode | string | null | Sempre | CEP do destinatario. |
destination.postalAddress.administrativeArea | string | null | Sempre | UF do destinatario. |
destination.postalAddress.addressLines | array de string | Sempre | Linhas do endereco: ["Rua, numero", complemento, bairro, cidade]. Partes em branco sao removidas — pode ser []. |
destination.postalAddress.street | string | null | Sempre | Aditivo (v2.11) — logradouro do destinatario, ao lado de addressLines (que continua). null quando em branco. |
destination.postalAddress.number | string | null | Sempre | Numero do endereco do destinatario. |
destination.postalAddress.complement | string | null | Sempre | Complemento do endereco do destinatario. |
destination.postalAddress.district | string | null | Sempre | Bairro do destinatario. |
destination.postalAddress.city | string | null | Sempre | Cidade do destinatario. |
destination.postalAddress.state | string | null | Sempre | UF do destinatario (espelha administrativeArea). |
invoice | objeto | Condicional | Bloco fiscal. Presente somente quando ha NF (number preenchido); ausente (a chave some) quando o pedido nao tem NF. |
invoice.number | string | Com invoice | Numero da nota fiscal. |
invoice.key | string | null | Com invoice | Chave de acesso (44 digitos). |
invoice.series | string | null | Com invoice | Serie da NF. |
invoice.date | string (ISO8601) | null | Com invoice | Data de emissao. |
customer | objeto | Sempre | Cliente dono do pedido (identidade apenas). |
customer.id | string (UUID) | Sempre | ID do cliente no Bridge Link. |
customer.name | string | Sempre | Razao social / nome do cliente. |
customer.cnpj | string | Sempre | CNPJ do cliente (somente digitos). |
sender | objeto | Sempre | Aditivo (v2.11) — remetente do pedido, por transportadora; irmao do bloco identitario customer (que nao muda). Conjunto de 11 chaves uniforme independente da transportadora: Logan vem de logan_senders (inclui ie); Flash/interlog vem das colunas planas do cliente, com ie/number/complement/district/phone = null. Um campo sem fonte serializa null (sem dado fabricado). |
sender.name | string | null | Sempre | Razao social / nome do remetente. |
sender.cnpj | string | null | Sempre | CNPJ do remetente. |
sender.ie | string | null | Sempre | Inscricao estadual do remetente (apenas Logan; null no Flash/interlog). |
sender.street | string | null | Sempre | Logradouro do remetente. |
sender.number | string | null | Sempre | Numero do endereco do remetente (null no Flash/interlog). |
sender.complement | string | null | Sempre | Complemento do endereco do remetente (null no Flash/interlog). |
sender.district | string | null | Sempre | Bairro do remetente (null no Flash/interlog). |
sender.city | string | null | Sempre | Cidade do remetente. |
sender.state | string | null | Sempre | UF do remetente. |
sender.postalCode | string | null | Sempre | CEP do remetente. |
sender.phone | string | null | Sempre | Telefone do remetente (null no Flash/interlog). |
weight | numero | Condicional | Peso. Ausente (chave some) quando nulo. |
value | numero | Condicional | Valor declarado. Ausente quando nulo. |
itemsQuantity | inteiro | Condicional | Quantidade de volumes/itens. Ausente quando nulo. |
obs | string | Condicional | Observacoes. Ausente quando nulo. |
labelUrl | string (URL) | Sempre | URL da etiqueta no Bridge Link (GET /api/v1/labels/:tracking). |
flashLabelUrl | string (URL) | Condicional | URL da etiqueta na Flash Pegasus. Presente somente quando o pedido tem etiqueta Flash gerada; ausente caso contrario. |
costCenter | objeto | Condicional | Centro de custo. Presente somente quando o pedido tem centro de custo (codigo e/ou nome); ausente caso contrario. |
costCenter.code | string | Com costCenter | Codigo do centro de custo (snapshot do pedido). |
costCenter.name | string | null | Com costCenter | Nome do centro de custo. |
contract | objeto | Condicional | Contrato (loja/remetente) do pedido — bloco aditivo de identidade. Presente somente quando o pedido tem contrato; ausente caso contrario. Nunca carrega carrier_token / flash_client_id / flash_contract_id. |
contract.id | string (UUID) | Com contract | UUID do contrato (round-trip no filtro contract_id). |
contract.code | string | null | Com contract | Codigo do contrato. |
contract.name | string | null | Com contract | razao_social, com fallback para nome_fantasia. |
receiptUrl | string (URL) | Condicional | URL do comprovante de entrega (GET /api/v1/delivery_receipts/:tracking). Presente somente quando existe comprovante para o tracking; ausente caso contrario. |
createdAt | string (ISO8601) | Sempre | Criacao do pedido. |
updatedAt | string (ISO8601) | Sempre | Ultima atualizacao do pedido. |
consumedAt | string (ISO8601) | Condicional | Quando o pedido foi marcado como consumido (acknowledge da pull-dedup). Ausente ate ser consumido. |
status.code / status.highLevelStatus / status.description)highLevelStatus | code | description |
|---|---|---|
INTEGRADO | 1 | Pedido integrado, aguardando importacao |
IMPORTADO | 2 | Pedido importado na transportadora |
RASTREADO | 0 | Status desconhecido |
AGUARDANDO_NF | 0 | Status desconhecido |
AGUARDANDO_CENTRO_CUSTO | 0 | Status desconhecido |
ERRO | 99 | Erro no processamento |
Use highLevelStatus como a fonte de verdade do status, nao code. O code e um numero legado (Loggi): varios status atuais (RASTREADO, AGUARDANDO_NF, AGUARDANDO_CENTRO_CUSTO) caem em 0, e os codigos 10 (em transito) e 20 (entregue) sao reservados/legados e nao sao emitidos pelos status atuais.
{
"event": "order.status_changed",
"eventId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"occurredAt": "2026-06-20T19:12:27Z",
"data": {
"trackingCode": "BL123456789BR",
"loggiKey": "0ae47987-0db1-4811-b131-06db580a25ab",
"status": {
"code": 2,
"highLevelStatus": "IMPORTADO",
"description": "Pedido importado na transportadora",
"updatedTime": "2026-06-20T16:12:27-03:00"
},
"destination": {
"recipient": { "name": "Maria Silva", "phone": "21999990000", "federalTaxId": "12345678900" },
"postalAddress": {
"postalCode": "20000000",
"administrativeArea": "RJ",
"addressLines": ["Rua Exemplo, 100", "Apto 201", "Centro", "Rio de Janeiro"],
"street": "Rua Exemplo",
"number": "100",
"complement": "Apto 201",
"district": "Centro",
"city": "Rio de Janeiro",
"state": "RJ"
}
},
"invoice": { "number": "12345", "key": "35250611222333000181550010000123451000123456", "series": "1", "date": "2026-06-20" },
"customer": { "id": "4ea8861a-cbe0-48be-b4b5-9def637603ef", "name": "CLIENTE EXEMPLO LTDA", "cnpj": "11222333000181" },
"sender": {
"name": "REMETENTE EXEMPLO LTDA",
"cnpj": "99888777000166",
"ie": "123456789",
"street": "Av Remetente",
"number": "500",
"complement": "Galpao 2",
"district": "Industrial",
"city": "Sao Paulo",
"state": "SP",
"postalCode": "01310100",
"phone": "1133334444"
},
"weight": 1.5,
"value": 199.9,
"itemsQuantity": 3,
"obs": "Entregar em horario comercial",
"labelUrl": "https://bridgelink.com.br/api/v1/labels/BL123456789BR",
"flashLabelUrl": "https://webservice.flashpegasus.com.br/FlashPegasus/rest/padrao/etiqueta/9988776655",
"costCenter": { "code": "CC-RJ-01", "name": "Filial Rio" },
"contract": { "id": "9c8b7a65-4d3e-2f1a-0b9c-8d7e6f5a4b3c", "code": "LOJA-RJ", "name": "Filial Rio LTDA" },
"receiptUrl": "https://bridgelink.com.br/api/v1/delivery_receipts/BL123456789BR",
"createdAt": "2026-06-20T15:40:00-03:00",
"updatedAt": "2026-06-20T16:12:27-03:00",
"consumedAt": "2026-06-20T16:15:00-03:00"
}
}
{
"event": "order.created",
"eventId": "7b2c9a14-3e6f-4d21-9c0a-1f8e7d6c5b4a",
"occurredAt": "2026-06-20T19:38:45Z",
"data": {
"trackingCode": "BL987654321BR",
"loggiKey": "1c2d3e4f-aaaa-bbbb-cccc-0123456789ab",
"status": {
"code": 1,
"highLevelStatus": "INTEGRADO",
"description": "Pedido integrado, aguardando importacao",
"updatedTime": "2026-06-20T16:38:44-03:00"
},
"destination": {
"recipient": { "name": "Joao Souza", "phone": null, "federalTaxId": null },
"postalAddress": {
"postalCode": "01310100",
"administrativeArea": "SP",
"addressLines": ["Av Paulista, 1000", "Bela Vista", "Sao Paulo"],
"street": "Av Paulista",
"number": "1000",
"complement": null,
"district": "Bela Vista",
"city": "Sao Paulo",
"state": "SP"
}
},
"customer": { "id": "4ea8861a-cbe0-48be-b4b5-9def637603ef", "name": "CLIENTE EXEMPLO LTDA", "cnpj": "11222333000181" },
"sender": {
"name": "CLIENTE EXEMPLO LTDA",
"cnpj": "11222333000181",
"ie": null,
"street": "Rua do Cliente",
"number": null,
"complement": null,
"district": null,
"city": "Sao Paulo",
"state": "SP",
"postalCode": "04567000",
"phone": null
},
"weight": 0.8,
"value": 50.0,
"itemsQuantity": 1,
"labelUrl": "https://bridgelink.com.br/api/v1/labels/BL987654321BR",
"createdAt": "2026-06-20T16:38:44-03:00",
"updatedAt": "2026-06-20T16:38:44-03:00"
}
}
No exemplo minimo, repare que invoice, obs, flashLabelUrl, costCenter, contract, receiptUrl e consumedAt nao aparecem (foram removidos por serem nulos), e os campos aninhados nulos (recipient.phone, recipient.federalTaxId, postalAddress.complement) vem como null. O bloco sender e um exemplo Flash: ie/number/complement/district/phone vem como null (sem coluna de origem), enquanto num cliente Logan esses campos viriam preenchidos a partir de logan_senders.
data de customer.updatedPara customer.updated, data e o objeto abaixo. Modelo de presenca (diferente do pedido): os campos fixos nunca somem — um valor nulo vem como null. Ja os blocos por transportadora (interlog, carrierSender, jallConnection) usam merge-when-present: a chave fica ausente quando nao se aplica (nunca null nem []). Qual bloco aparece depende do carrier. O array contracts nao e um bloco por transportadora: esta sempre presente ([] quando nao ha contrato), independente do carrier.
| Campo | Tipo | Presenca | Descricao |
|---|---|---|---|
id | string (UUID) | Sempre | ID do cliente. |
name | string | Sempre | Razao social / nome. |
cnpj | string | Sempre | CNPJ (somente digitos). |
active | boolean | Sempre | Cliente ativo (!disabled). |
city | string | null | Sempre | Cidade. |
uf | string | null | Sempre | UF. |
address | string | null | Sempre | Endereco. |
zipcode | string | null | Sempre | CEP. |
whatsappPhone | string | null | Sempre | WhatsApp de contato. |
traceable | boolean | null | Sempre | Habilita pagina de rastreio. |
deliveryReceipt | boolean | null | Sempre | Trata comprovantes de entrega. |
invoiceEmitter | boolean | null | Sempre | Emite NF. |
carrier | string (enum) | Sempre | Transportadora: flash_courier | logan | interlog. Define qual bloco condicional aparece. |
platform | string (enum) | Sempre | Plataforma de origem: tiny | server_info | vtex | bling | api. |
sender | objeto | Sempre | Remetente base (colunas do cliente). Chaves: name, cnpj, address, city, uf, zipcode (cada uma string | null). |
contracts | array | Sempre | Contratos (lojas/remetentes) do cliente — sempre presente ([] quando nao ha). Cada item: { id, code, name, cnpj, trackingPrefix } (id = UUID do contrato; name = razao_social→nome_fantasia). Nunca expoe carrier_token / flash_client_id / flash_contract_id. |
interlog | objeto | So quando carrier=interlog | Config Interlog. Chaves: ambiente (enum homologacao|producao), active (boolean), costCenters (array de {code, name, default}). Credenciais nunca expostas. |
carrierSender | objeto | So quando carrier=logan | Remetente Logan (CT-e). Chaves: name, cnpj, address, number, neighborhood, city, state, zipcode, ie. |
jallConnection | objeto | So quando carrier=flash_courier | Config de contrato CT-e da Flash. Apenas os 4 campos de contrato: senderLocationId, clientId, contractId, subsidiaryId (inteiros/string). Login/senha/token nunca expostos. |
{
"event": "customer.updated",
"eventId": "9f3b974c-649c-48ae-9af8-27cfdf4eb951",
"occurredAt": "2026-06-20T19:38:45Z",
"data": {
"id": "4ea8861a-cbe0-48be-b4b5-9def637603ef",
"name": "CLIENTE FLASH LTDA",
"cnpj": "11222333000181",
"active": true,
"city": "Rio de Janeiro",
"uf": "RJ",
"address": "Rua do Remetente, 50",
"zipcode": "20000000",
"whatsappPhone": null,
"traceable": true,
"deliveryReceipt": false,
"invoiceEmitter": true,
"carrier": "flash_courier",
"platform": "tiny",
"sender": { "name": "CLIENTE FLASH LTDA", "cnpj": "11222333000181", "address": "Rua do Remetente, 50", "city": "Rio de Janeiro", "uf": "RJ", "zipcode": "20000000" },
"contracts": [ { "id": "9c8b7a65-4d3e-2f1a-0b9c-8d7e6f5a4b3c", "code": "LOJA-RJ", "name": "Filial Rio LTDA", "cnpj": "11222333000181", "trackingPrefix": "RJ" } ],
"jallConnection": { "senderLocationId": 1234, "clientId": 5678, "contractId": 9012, "subsidiaryId": "SP01" }
}
}
{
"event": "customer.updated",
"eventId": "a1b2c3d4-e5f6-47a8-9b0c-1d2e3f4a5b6c",
"occurredAt": "2026-06-20T19:40:00Z",
"data": {
"id": "7c1f0e44-2b9a-4c33-8e77-aa11bb22cc33",
"name": "CLIENTE LOGAN SA",
"cnpj": "99888777000166",
"active": true,
"city": "Sao Paulo",
"uf": "SP",
"address": "Av Industrial, 2000",
"zipcode": "04001000",
"whatsappPhone": "11988887777",
"traceable": true,
"deliveryReceipt": true,
"invoiceEmitter": false,
"carrier": "logan",
"platform": "vtex",
"sender": { "name": "CLIENTE LOGAN SA", "cnpj": "99888777000166", "address": "Av Industrial, 2000", "city": "Sao Paulo", "uf": "SP", "zipcode": "04001000" },
"contracts": [ { "id": "1a2b3c4d-5e6f-4708-8a9b-0c1d2e3f4a5b", "code": "LOJA-SP", "name": "CLIENTE LOGAN SA", "cnpj": "99888777000166", "trackingPrefix": "SP" } ],
"carrierSender": { "name": "CLIENTE LOGAN SA", "cnpj": "99888777000166", "address": "Av Industrial", "number": "2000", "neighborhood": "Vila Industrial", "city": "Sao Paulo", "state": "SP", "zipcode": "04001000", "ie": "123456789012" }
}
}
{
"event": "customer.updated",
"eventId": "b0c1d2e3-f4a5-4607-8819-2a3b4c5d6e7f",
"occurredAt": "2026-06-20T19:42:00Z",
"data": {
"id": "33aa44bb-55cc-66dd-77ee-88ff99001122",
"name": "CLIENTE INTERLOG ME",
"cnpj": "55444333000122",
"active": true,
"city": "Niteroi",
"uf": "RJ",
"address": "Rua Central, 10",
"zipcode": "24000000",
"whatsappPhone": null,
"traceable": true,
"deliveryReceipt": false,
"invoiceEmitter": true,
"carrier": "interlog",
"platform": "bling",
"sender": { "name": "CLIENTE INTERLOG ME", "cnpj": "55444333000122", "address": "Rua Central, 10", "city": "Niteroi", "uf": "RJ", "zipcode": "24000000" },
"contracts": [ { "id": "2b3c4d5e-6f70-4819-9a0b-1c2d3e4f5a6b", "code": "PADRAO", "name": "CLIENTE INTERLOG ME", "cnpj": "55444333000122", "trackingPrefix": null } ],
"interlog": { "ambiente": "producao", "active": true, "costCenters": [ { "code": "PADRAO", "name": "Padrao", "default": true }, { "code": "SP", "name": "Sao Paulo", "default": false } ] }
}
}
Os tres blocos por transportadora sao mutuamente exclusivos na pratica: um cliente flash_courier traz jallConnection, um logan traz carrierSender, e um interlog traz interlog. O bloco sender (remetente base) vem sempre, para qualquer transportadora.
Cada POST de webhook traz Content-Type: application/json mais os quatro cabecalhos X-BridgeLink-*:
| Cabecalho | Conteudo |
|---|---|
X-BridgeLink-Signature |
A assinatura HMAC no formato sha256=<hex> (ver "Verificacao da assinatura"). |
X-BridgeLink-Timestamp |
O instante da assinatura em segundos Unix (string). Faz parte da string assinada (defesa contra replay). |
X-BridgeLink-Event |
O tipo do evento (ex.: order.created). |
X-BridgeLink-Delivery-Id |
O eventId desta entrega (igual ao eventId do envelope) — use-o para deduplicar. |
A assinatura e calculada sobre a string "#{timestamp}.#{raw_body}" — ou seja, o X-BridgeLink-Timestamp recebido, um ponto literal (.) e o corpo bruto exato da requisicao (os bytes recebidos, sem reserializar o JSON). O timestamp fica dentro da string assinada: ele nao pode ser alterado sem quebrar o HMAC.
Use sempre o corpo bruto (request.raw_post no Rails), nunca um JSON reparseado/reserializado, ou a assinatura nao vai bater.
Copie e cole — troque o SECRET pelo segredo do seu cadastro. O segredo abaixo e ficticio.
require 'openssl'
require 'active_support' # ActiveSupport::SecurityUtils.secure_compare
# PLACEHOLDER — substitua pelo segredo revelado no cadastro da sua assinatura.
SECRET = 'whsec_exemplo_NAO_REAL'
TOLERANCE = 300 # segundos (5 min)
def webhook_valido?(raw_body, signature_header, timestamp_header, secret = SECRET)
# 1) Rejeita timestamp velho (replay).
age = Time.now.to_i - Integer(timestamp_header, 10)
return false if age.abs > TOLERANCE
# 2) Recompute a assinatura sobre "#{timestamp}.#{raw_body}".
signed_string = "#{timestamp_header}.#{raw_body}"
expected = 'sha256=' + OpenSSL::HMAC.hexdigest('SHA256', secret, signed_string)
# 3) Comparacao de tempo constante (evita timing attack).
ActiveSupport::SecurityUtils.secure_compare(signature_header.to_s, expected)
rescue ArgumentError, TypeError
false # timestamp nao numerico => trate como invalido
end
# No seu controller (Rails): use request.raw_post (o corpo BRUTO, nao params).
# raw = request.raw_post
# sig = request.headers['X-BridgeLink-Signature']
# ts = request.headers['X-BridgeLink-Timestamp']
# head(:unauthorized) and return unless webhook_valido?(raw, sig, ts)
signed_string = timestamp_header + "." + raw_request_body
expected = "sha256=" + hmac_sha256_hexdigest(secret, signed_string)
valido = constant_time_equals(x_bridgelink_signature, expected)
AND abs(now_unix - timestamp_header) <= TOLERANCE
Atencao: os exemplos usam um segredo ficticio (whsec_exemplo_NAO_REAL). Nunca publique nem versione o seu segredo real — trate-o como uma senha.
2xx, a entrega e marcada como entregue e nao ha mais retentativas.polynomially_longer), ate um limite de tentativas (8). Esgotado o limite, a entrega vai para dead-letter (status exhausted) e nao e mais re-tentada automaticamente.eventId: toda retentativa reusa o mesmo eventId. Como a mesma entrega pode chegar mais de uma vez, o seu receptor deve deduplicar por eventId (= X-BridgeLink-Delivery-Id).eventId: o reenvio manual ("Reenviar") feito pela equipe re-entrega o payload original com o mesmo eventId — entao a sua dedupe por eventId tambem cobre o replay. Boas praticas do receptor: responda 2xx rapido (idealmente enfileire e processe de forma assincrona), valide a assinatura antes de processar e deduplique por eventId antes de aplicar qualquer efeito colateral.