API de Parceiros — Encaminhei
Integre cotação, emissão de etiquetas, rastreamento e webhooks de status diretamente no seu sistema. API REST com autenticação OAuth2.
1. Visão geral
Todas as chamadas usam HTTPS e trafegam JSON. A URL base da API é:
O fluxo típico de uma integração é:
- Autenticar com
client_id+client_secrete obter um access token. - Cotar o frete para um trecho/pacote e escolher uma oferta.
- Emitir o envio a partir da oferta escolhida e receber a URL da etiqueta.
- Acompanhar o status por webhook (recomendado) ou consultando o rastreio.
Autorização nas chamadas
Exceto o endpoint de token, toda chamada exige o header:
Authorization: Bearer <access_token>
2. Autenticação (OAuth2)
A API usa o fluxo OAuth2 client_credentials. Troque suas credenciais por um access token JWT de curta duração (1 hora). Reemita o token quando expirar.
Corpo application/x-www-form-urlencoded:
grant_type=client_credentials
client_id=SEU_CLIENT_ID
client_secret=SEU_CLIENT_SECRET
scope=cotacao:read envio:write envio:read rastreio:read (opcional)
Exemplo:
curl -X POST https://parceiros.encaminhei.com/api/v2/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=SEU_CLIENT_ID" \
-d "client_secret=SEU_CLIENT_SECRET"
Resposta 200:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "cotacao:read envio:write envio:read rastreio:read"
}
Scopes
O token só pode acessar os recursos cobertos pelos scopes do seu cliente. Se você omitir scope, recebe todos os do cliente; se pedir, deve ser um subconjunto.
| Scope | Permite |
|---|---|
| cotacao:read | Cotar frete |
| envio:write | Emitir e cancelar envios |
| envio:read | Consultar e listar envios |
| rastreio:read | Consultar o rastreio |
| webhook:manage | Gerenciar as inscrições de webhook |
{ "error": "...", "error_description": "..." }.3. Cotação cotacao:read
Retorna as ofertas de frete disponíveis para o trecho e pacote informados. Cada oferta traz um ofertaId (um GUID): a oferta fica gravada no nosso lado, com preço, prazo, pacote e CEPs, e você a usa na emissão para garantir o preço cotado.
{
"remetente": "01310100",
"destino": "20040002",
"peso": 1.5,
"altura": 10,
"largura": 15,
"comprimento": 20,
"valorSegurado": 80
}
Resposta 200 — lista de ofertas:
[
{
"ofertaId": "0b6f1c1e6d0e4f5e9d3b2f7a8c9e1a42",
"expiraEm": "2026-10-30T18:00:00+00:00",
"transportadoraId": "3f2a1c8e-....",
"transportadora": "Correios",
"servicoCodigo": 1,
"servico": "SEDEX",
"preco": 45.88,
"precoReferencia": 58.90,
"prazoMinimo": 1,
"prazoMaximo": 1,
"aviso": "Acima de R$ 1.500,00, só com nota fiscal",
"seguroAte": null
},
{
"ofertaId": "7c2d4e9a1b3f4c5d8e6a0f1b2c3d4e5f",
"expiraEm": "2026-10-30T18:00:00+00:00",
"transportadoraId": "3f2a1c8e-....",
"transportadora": "Correios",
"servicoCodigo": 2,
"servico": "PAC",
"preco": 29.53,
"precoReferencia": 38.40,
"prazoMinimo": 5,
"prazoMaximo": 5,
"aviso": null,
"seguroAte": null
}
]
| Campo | Tipo | Descrição |
|---|---|---|
ofertaId | string | Identificador da oferta gravada (price-lock): 32 caracteres hexadecimais, sem hífens. Trate-o como texto opaco e envie-o na emissão exatamente como recebeu. Cada oferta vale para uma única emissão. |
expiraEm | data ISO 8601 | Até quando o ofertaId vale, com o fuso (+00:00, UTC). |
transportadoraId | uuid | Identificador da transportadora — estável, ao contrário do nome. |
transportadora | string | Nome de exibição (ex.: Correios). |
servicoCodigo | int | Código da modalidade. Use-o para casar serviços entre cotações, não o nome. |
servico | string | Nome de exibição do serviço (ex.: SEDEX). |
preco | decimal | O que você paga. É este valor que a emissão cobra. |
precoReferencia | decimal | Valor de referência sugerido, baseado na tabela Platinum dos Correios. Depende de acordo comercial — ver a nota abaixo. |
prazoMinimo | int | Dias úteis, mínimo. |
prazoMaximo | int | Dias úteis, máximo. |
aviso | string ou null | Aviso sobre a oferta, quando há. Ex.: "Acima de R$ 1.500,00, só com nota fiscal" — com esse valor declarado, este serviço só emite com NF-e (com declaração de conteúdo a emissão é recusada). |
seguroAte | decimal ou null | Teto do seguro quando o serviço o limita: com nota fiscal acima do máximo do serviço, o envio sai com seguro até este valor (não pelo valor total). Nulo quando não limita. Quando vier preenchido, a emissão exige "aceiteSeguroLimitado": true. |
precoReferencia. É um valor de referência
sugerido, calculado a partir da tabela Platinum dos Correios — não é
uma cobrança, nem um preço que definimos para o seu cliente. O que você cobra de quem compra
de você é decisão sua; este campo existe para lhe dar um parâmetro de mercado ao lado do
preco, que é o que você paga.
Ele depende de acordo comercial e não está habilitado para todas as contas. Sem esse acordo,
precoReferencia vem igual ao preco.
O campo está sempre presente na resposta — ele não some quando não há
referência ativa, para não quebrar quem já integrou.
Não existe
ofertaId do preço de referência, e isso é deliberado:
a emissão é sempre pela tabela base, no valor de preco, que vem da oferta gravada.
O valor de referência é só informativo e não pode ser usado para emitir.
ofertaId vale por 60 minutos a partir da cotação, e expiraEm traz o instante exato. Depois disso, a emissão devolve 400 oferta_invalida: basta cotar de novo. Dentro do prazo, o preço cobrado é exatamente o da oferta: a emissão não recota e não aplica tolerância de preço.4. Emissão de envio envio:write
Emite a etiqueta em uma única chamada, a partir do ofertaId. Toda transportadora ativa para a sua conta pode ser emitida pela API v2, e não só JadLog e Correios. É obrigatório enviar o header Idempotency-Key (um UUID seu por tentativa) — assim, um retry da mesma requisição não duplica a etiqueta nem o débito.
Headers:
Authorization: Bearer <access_token>
Content-Type: application/json
Idempotency-Key: 870fb130-3211-42fe-abc4-fb380361918b
O pacote, o preço e o prazo vêm da oferta. Os campos peso, altura, largura, comprimento e valorSegurado não são mais obrigatórios: omita-os. Se você enviar algum, ele precisa ser igual ao cotado (peso com 3 casas decimais, os demais com 2); senão a emissão é recusada com 400 pacote_divergente_da_oferta. O CEP do remetente e do destinatário também precisa ser o cotado (400 cep_divergente_da_oferta).
Corpo:
{
"ofertaId": "0b6f1c1e6d0e4f5e9d3b2f7a8c9e1a42",
"clienteReferencia": "PEDIDO-1234",
"remetente": {
"nome": "Minha Loja", "cpfCnpj": "11222333000181",
"telefone": "11999999999", "email": "[email protected]",
"logradouro": "Av Paulista", "numero": "1000", "complemento": "cj 1",
"bairro": "Bela Vista", "cidade": "Sao Paulo", "uf": "SP",
"cep": "01310100", "inscricaoEstadual": "ISENTO"
},
"destinatario": {
"nome": "Cliente", "cpfCnpj": "11144477735",
"telefone": "21988887777", "email": "[email protected]",
"logradouro": "Rua do Mercado", "numero": "50", "complemento": "",
"bairro": "Centro", "cidade": "Rio de Janeiro", "uf": "RJ", "cep": "20040002"
},
"declaracaoConteudo": [
{ "item": "Produto X", "quantidade": 1, "valor": 80 }
]
}
Resposta 200:
{
"envioId": 221910605028,
"status": "emitido",
"preco": 45.88,
"etiquetaUrl": "https://www.encaminhei.com/ParceiroImprimirEtiqueta/eyJuIjoyMjE5MTA2MDUwMjgsInQiOiJldHEiLCJlIjoxNzkxMDAwMDAwfQ.f3Xc2_Kq9m1sYd0aVb7NQe8Lp5RtZ6HgWjKcU4nMxYs",
"rastreioUrl": "https://www.encaminhei.com/ParceiroRastreio/eyJuIjoyMjE5MTA2MDUwMjgsInQiOiJyc3QiLCJlIjoxNzkxMDAwMDAwfQ.Lm4Kd8_Vp2wRt6yUi9OaSf1BnZx3CqEjHgTvMlYkNbA"
}
Status da emissão (síncrono x assíncrono)
O campo status da resposta indica se a etiqueta já está pronta:
| status | Significa |
|---|---|
emitido | Etiqueta pronta para impressão imediata (transportadoras síncronas, ex.: Correios, JadLog). A etiquetaUrl já funciona. |
emissao_em_andamento | Envio aceito; a etiqueta está sendo produzida de forma assíncrona (ex.: Loggi). Aguarde alguns instantes e consulte GET /api/v2/envios/{id}, ou acompanhe pelos webhooks de status. |
envio.emitido sinaliza que o envio foi aceito e registrado (dispara em ambos os casos). Para transportadoras assíncronas, use a evolução do status (consulta/webhooks envio.postado, etc.) para saber quando a etiqueta/objeto avançou.Documento fiscal: Nota Fiscal ou Declaração de Conteúdo
Cada envio leva um documento fiscal — você escolhe qual informar no corpo da emissão:
| Para enviar com… | Informe |
|---|---|
| Nota Fiscal | notaFiscal (número) e, opcionalmente, chaveAcessoNF (44 dígitos). Não envie declaracaoConteudo. |
| Declaração de Conteúdo | declaracaoConteudo — lista de itens (item, quantidade, valor). Não envie notaFiscal. |
A regra é simples: se houver itens em declaracaoConteudo, o envio é tratado como Declaração de Conteúdo; caso contrário, como Nota Fiscal. Exemplo com NF:
{
"ofertaId": "...",
"clienteReferencia": "PEDIDO-1234",
"notaFiscal": 12345,
"chaveAcessoNF": "35260612345678000190550010000123451000123456",
"remetente": { "...": "..." },
"destinatario": { "...": "..." }
}
declaracaoConteudo é recusada com 400 dce_indisponivel_contribuinte quando a inscricaoEstadual do remetente está preenchida e não é ISENTO, ou quando o CNPJ do remetente já foi identificado como contribuinte em uma emissão anterior. Nesse caso, envie o pedido com a NF-e (notaFiscal / chaveAcessoNF). A regra vale igual no sandbox.daceUrl — link para baixar o DACE. O documento é gerado sob demanda no primeiro acesso ao link (ou na 1ª impressão da etiqueta). Para Correios, a DCe é tratada via pré-postagem (sem daceUrl próprio); para Nota Fiscal, não há DCe.Seguro limitado
Se a oferta veio com seguroAte preenchido, envie "aceiteSeguroLimitado": true no corpo da emissão: é a sua ciência (e a do seu cliente) de que o seguro vai até esse valor. Sem o aceite, a emissão é recusada com 400 aceite_seguro_limitado_obrigatorio, sem débito.
Idempotência
Reenviar a mesma Idempotency-Key com o mesmo corpo retorna a resposta original (mesmo envioId), sem emitir de novo. Reusar a chave com um corpo diferente retorna 409.
400 aceite_seguro_limitado_obrigatorio é a resposta daquela Idempotency-Key: reenviar com a mesma chave devolve o mesmo erro, e com o corpo corrigido devolve 409 idempotency_key_conflito. Para reenviar com o aceite, use uma nova Idempotency-Key.Uso único da oferta
Cada ofertaId serve para um envio. Uma falha na emissão não consome a oferta: você pode tentar de novo com ela, mas com uma nova Idempotency-Key — o erro fica gravado sob a chave usada e seria devolvido de novo (ver Idempotência). Já uma segunda emissão com a mesma oferta (e outra Idempotency-Key) é recusada com 409 oferta_ja_utilizada; cote de novo. Repetir a mesma Idempotency-Key de uma emissão que deu certo continua devolvendo a resposta original.
Se a conexão cair antes da resposta, repita com a mesma chave. Se ela continuar respondendo 409 requisicao_em_andamento, tente com uma nova chave e a mesma oferta: como a oferta é de uso único, isso nunca gera um segundo envio — se a primeira tentativa chegou a emitir, a resposta é 409 oferta_ja_utilizada e o envio aparece em GET /api/v2/envios. Se a oferta já tiver vencido (expiraEm), a resposta é 400 oferta_invalida — mesmo que a primeira tentativa tenha emitido: antes de cotar e emitir de novo, confira em GET /api/v2/envios se o envio (pelo seu clienteReferencia) já existe.
Erros comuns
| HTTP | codigo | Significado |
|---|---|---|
| 402 | credito_insuficiente | Sem limite/saldo para o débito |
| 400 | dce_indisponivel_contribuinte | O remetente é contribuinte de ICMS e não pode usar a declaração de conteúdo. Reenvie com a nota fiscal (NF-e) e uma nova Idempotency-Key |
| 400 | documento_fiscal_obrigatorio | Faltou a chave/número da nota fiscal ou a declaração de conteúdo (ou vieram os dois, nos Correios). Informe um deles e reenvie com uma nova Idempotency-Key |
| 409 | oferta_ja_utilizada | A oferta já virou um envio (uso único). Cote de novo |
| 400 | pacote_divergente_da_oferta | Peso, dimensões ou valor segurado enviados diferem do cotado. Omita esses campos ou cote de novo |
| 400 | cep_divergente_da_oferta | O CEP de origem ou de destino difere do cotado. Cote de novo |
| 400 | exigencia_da_transportadora | O envio não atende uma exigência da transportadora (telefone, e-mail, NF-e ou valor declarado). A mensagem diz qual |
| 409 | idempotency_key_conflito | Idempotency-Key reusada com corpo diferente |
| 400 | oferta_invalida | ofertaId inexistente, expirado, de outro ambiente ou que não pode ser usado nesta emissão. Cote de novo |
| 400 | aceite_seguro_limitado_obrigatorio | A oferta limita o seguro (seguroAte) e o corpo veio sem "aceiteSeguroLimitado": true. Reenvie com o aceite e uma nova Idempotency-Key |
5. Consulta e listagem envio:read
Detalhe de um envio
{
"envioId": 221910605028,
"status": "EtiquetaLiberada",
"codigoRastreio": "AD611719409BR",
"transportadoraId": "....",
"servicoCodigo": 1,
"servico": "SEDEX",
"valor": 45.88,
"clienteReferencia": "PEDIDO-1234",
"dataCriacao": "2026-06-28T22:10:00",
"etiquetaUrl": "https://www.encaminhei.com/ParceiroImprimirEtiqueta/eyJuIjoyMjE5MTA2MDUwMjgsInQiOiJldHEiLCJlIjoxNzkxMDAwMDAwfQ.f3Xc2_Kq9m1sYd0aVb7NQe8Lp5RtZ6HgWjKcU4nMxYs",
"rastreioUrl": "https://www.encaminhei.com/ParceiroRastreio/eyJuIjoyMjE5MTA2MDUwMjgsInQiOiJyc3QiLCJlIjoxNzkxMDAwMDAwfQ.Lm4Kd8_Vp2wRt6yUi9OaSf1BnZx3CqEjHgTvMlYkNbA",
"dceStatus": "Autorizada",
"daceUrl": "https://www.encaminhei.com/ParceiroDace/eyJuIjoyMjE5MTA2MDUwMjgsInQiOiJkY2UiLCJlIjoxNzkxMDAwMDAwfQ.Qw7Ye1_Rt4uIo2pAs5DfGh8JkLz3XcVbNm6TyUiOpQr"
}
Os campos dceStatus e daceUrl aparecem apenas para envios com declaração de conteúdo em transportadora não-Correios (DCe SEFAZ-direta). dceStatus pode ser Pendente, Autorizada, Rejeitada, ContingenciaOffline etc. — fica Pendente/ausente até a DCe ser emitida (no 1º acesso ao DACE ou à etiqueta).
Listagem paginada
Paginação por cursor (mais recentes primeiro). Use o proximoCursor retornado para buscar a próxima página; quando vier null, não há mais.
{
"itens": [ { "envioId": 221910605028, "status": "...", ... } ],
"proximoCursor": 221910605008
}
limit aceita de 1 a 100 (padrão 20). Um envio de outro parceiro retorna 404 — você só enxerga os seus.6. Rastreio rastreio:read
Lista os eventos de rastreio do envio (vazio enquanto não houver movimentação da transportadora).
[
{ "codigo": 3, "status": "Postado", "descricao": "Objeto postado",
"local": "Sao Paulo/SP", "data": "2026-06-28T09:00:00" },
{ "codigo": 4, "status": "Entregue", "descricao": "Objeto entregue ao destinatario",
"local": "Rio de Janeiro/RJ", "data": "2026-06-29T14:20:00" }
]
7. Cancelamento envio:write
Cancela um envio ainda não postado. Resposta 200:
{ "status": "cancelado" }
Se o envio já foi postado/entregue ou já está cancelado, retorna 400 com codigo = cancelamento_invalido e a mensagem do motivo.
8. Webhooks (notificações de status) webhook:manage
Em vez de ficar consultando o rastreio, registre uma URL e a Encaminhei envia um POST assinado a cada mudança de status do envio.
Registrar / atualizar a inscrição
{
"url": "https://seu-sistema.com/webhooks/encaminhei",
"eventos": ["envio.emitido", "envio.postado", "envio.entregue", "envio.cancelado"]
}
Resposta 201 — o secret é mostrado uma única vez; guarde-o para verificar a assinatura:
{
"id": "2c41c649-c53c-433d-b0a7-f6790290abbb",
"url": "https://seu-sistema.com/webhooks/encaminhei",
"eventos": ["envio.emitido","envio.postado","envio.entregue","envio.cancelado"],
"secret": "whsec_0c9900fd820e76309ebc39dbb80b78565e4c7a5968b8af5f83e4a753fd965ef1",
"ativo": true
}
POST de novo substitui a inscrição atual (nova URL/eventos e um novo secret) — não cria uma segunda.Consultar a inscrição
Retorna a inscrição atual (sem o segredo). 404 se não houver inscrição ativa.
{
"id": "2c41c649-c53c-433d-b0a7-f6790290abbb",
"url": "https://seu-sistema.com/webhooks/encaminhei",
"eventos": ["envio.emitido","envio.postado","envio.entregue","envio.cancelado"],
"ativo": true,
"criadoEm": "2026-06-28T20:00:00",
"atualizadoEm": null
}
Desativar a inscrição
Desativa a inscrição (para de receber eventos). Responde 204; 404 se não houver inscrição ativa. Para voltar a receber, faça um novo POST.
Eventos disponíveis
| Evento | Quando | Quem emite em produção |
|---|---|---|
envio.emitido | Etiqueta emitida | Emissão pela API v2 |
envio.saiu_para_coleta | Coletador a caminho do remetente | Ainda não emitido em produção (depende da coleta pela API, prevista para o futuro) |
envio.coletado | Coletado pela transportadora | Transição de status para Coletado |
envio.postado | Objeto postado | Transição de status para Postado |
envio.em_transito | Objeto em trânsito | Ainda não emitido em produção |
envio.tentativa_entrega | Tentativa de entrega sem sucesso (pode se repetir; veja data.detalhe) | Ainda não emitido em produção |
envio.entregue | Entregue ao destinatário | Transição de status para Entregue |
envio.devolvido | Devolvido ao remetente | Transição de status para Devolvido |
envio.cancelado | Envio cancelado | Cancelamento do envio |
envio.sinistro | Extravio / avaria / roubo | Transição de status para Sinistro |
Corpo entregue
{
"event": "envio.entregue",
"event_id": "96f49e90-4965-4b1c-9989-9a5fccb68a1b",
"webhook_version": 1,
"occurred_at": "2026-06-29T14:20:05.123Z",
"data": {
"envio_id": 221910605028,
"status": "Entregue",
"codigo_rastreio": "AD611719409BR",
"transportadora_id": "....",
"cliente_referencia": "PEDIDO-1234",
"valor": 45.88,
"detalhe": null
}
}
data.detalhe é um texto livre opcional (ex.: "destinatário ausente" em envio.tentativa_entrega); vem null quando não há detalhe. É um campo aditivo: webhook_version continua 1.
Headers de cada entrega
| Header | Descrição |
|---|---|
X-Encaminhei-Signature | sha256=<hmac hex> — assinatura do corpo |
X-Encaminhei-Event-Id | UUID estável do evento (use para deduplicar) |
X-Encaminhei-Event | Nome do evento |
X-Encaminhei-Webhook-Version | Versão do contrato |
X-Encaminhei-Ambiente | sandbox ou producao — ambiente de origem do evento |
Verificação da assinatura
Calcule HMAC-SHA256 do corpo bruto usando o seu secret e compare (em tempo constante) com o valor após sha256= no header. Rejeite a requisição se não bater.
Node.js
const crypto = require("crypto");
function verificar(rawBody, header, secret) {
const esperado = "sha256=" +
crypto.createHmac("sha256", secret).update(rawBody, "utf8").digest("hex");
return crypto.timingSafeEqual(Buffer.from(header), Buffer.from(esperado));
}
C#
using System.Security.Cryptography;
bool Verificar(string corpoBruto, string header, string secret)
{
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(corpoBruto));
var esperado = "sha256=" + Convert.ToHexString(hash).ToLowerInvariant();
return CryptographicOperations.FixedTimeEquals(
Encoding.UTF8.GetBytes(esperado), Encoding.UTF8.GetBytes(header));
}
Confirmação, retry e idempotência
- Responda
2xxrapidamente (timeout de 10s) para confirmar o recebimento. - Em falha (não-2xx ou timeout), reenviamos com backoff: 1m, 5m, 15m, 1h, 6h, 24h. Após esgotar, a entrega é marcada como falha.
- Erros permanentes (ex.:
404/410) não são retentados. - O mesmo evento pode ser reentregue — deduplique pelo
X-Encaminhei-Event-Id.
9. Tratamento de erros
Fora o endpoint de token (formato OAuth2), todos os erros usam um envelope consistente:
{
"codigo": "url_invalida",
"mensagem": "A URL do webhook deve usar HTTPS.",
"detalhes": []
}
| HTTP | Significado |
|---|---|
| 400 | Requisição inválida (ver codigo/mensagem) |
| 401 | Token ausente, inválido ou expirado |
| 402 | Crédito insuficiente |
| 403 | Token sem o scope necessário |
| 404 | Recurso não encontrado (ou não pertence a você) |
| 409 | Conflito (idempotência / oferta já utilizada) |
| 500 | Erro interno |
API v1 (obsoleta)
10. Sandbox (ambiente de testes)
O sandbox é uma cópia do contrato da API para você integrar e testar sem emitir etiqueta real e sem ser cobrado. Tudo nele é fictício: nenhuma transportadora é acionada e nada vai para a sua fatura.
As rotas são as mesmas de produção (/v2/..., por exemplo /api/v2/cotacoes), com o mesmo formato de resposta, os mesmos códigos de erro e o mesmo webhook. Para ir a produção, troque só o host e a credencial.
Como obter a credencial
Qualquer conta de pessoa jurídica pode gerar a credencial de sandbox, mesmo pré-paga ou sem verificação. Para usar a API em produção, a conta precisa estar verificada e ser pós-paga; fale com o atendimento da Encaminhei. Para gerar a credencial de sandbox: no painel, abra Integração via API, aba Sandbox, e crie a credencial. O client_secret aparece uma única vez. A credencial de sandbox só funciona no host de sandbox, e a de produção só funciona no de produção; usar uma no host da outra retorna 401. Autentique-se em https://sandbox.encaminhei.com/api/v2/oauth/token, como na seção 2.
O que é fictício
- Preços e prazos não são reais. As transportadoras são simuladas, com valores fixos e redondos. Não use o sandbox para calibrar a sua margem.
- Saldo fictício. A sua conta de sandbox começa com R$ 10.000,00. Cada emissão debita desse saldo e o cancelamento estorna. Quando o saldo acaba, a emissão retorna
402comcredito_insuficiente, igual a produção. Na aba Sandbox do painel, Restaurar saldo devolve o valor inicial. - Etiqueta e DACE de exemplo. Os PDFs de etiqueta e DACE são gerados na hora e levam a marca SANDBOX — SEM VALOR em todas as páginas. Não servem para postar nada.
- Números de envio a partir de
900000000000000, e códigos de rastreio no formatoSBXseguido de 11 dígitos. Assim um envio de teste nunca se confunde com um real.
Provocando falhas
Para testar como o seu sistema reage a erros, use estes valores no CEP de destino da cotação (com ou sem hífen). Eles só valem no sandbox; em produção o mesmo CEP é tratado normalmente.
| Valor | Resultado |
|---|---|
CEP de destino 00000001 | Nenhuma transportadora atende o trecho: a cotação retorna a lista de ofertas vazia. |
CEP de destino 00000002 | A cotação é normal, mas o ofertaId já nasce expirado; a emissão com ele retorna 400 oferta_invalida (ofertaId inválido ou expirado). |
| Saldo fictício esgotado | A emissão retorna 402 credito_insuficiente. Depois de gastar o saldo com emissões de teste, restaure-o no painel. |
Simulador de eventos
Em produção o status do envio muda quando a transportadora informa. No sandbox ninguém informa nada, então quem avança o envio é você, no simulador da aba Sandbox do painel:
- Avança o status, um evento por vez, seguindo a ordem permitida para o envio (por exemplo, não é possível entregar um envio cancelado). Há também a opção de rodar até o fim.
- Cada evento dispara um webhook assinado exatamente como o de produção (mesmo corpo, mesmos headers, mesma verificação de assinatura da seção 8) para a URL do webhook de sandbox que você cadastrou. O webhook de sandbox é separado do de produção, com URL e
secretpróprios. - Mostra o log das entregas de webhook de cada envio, com o resultado de cada tentativa, e permite reenviar uma entrega.
- Copia o
curldo webhook, já com a assinatura calculada, para você repetir a chamada contra o seu sistema, e ocurlda consulta do envio. - Voltar para aqui: rebobina o mesmo envio até um evento anterior da linha do tempo, para você repetir um cenário sem emitir outro. Voltar não dispara webhook; ao avançar de novo, os eventos novos disparam.
O cancelamento pelo simulador segue a mesma regra de status e o mesmo estorno do cancelamento pela API.
Diferenças do rastreio
O GET /api/v2/envios/{id}/rastreio do sandbox tem o mesmo formato de produção, com duas diferenças:
- Logo após a emissão, a lista já traz um item
envio.emitido. Em produção ela fica vazia até a transportadora informar a primeira movimentação. - O campo
statustraz o nome do evento do simulador (por exemplo,envio.postado). Em produção ele traz o status informado pela transportadora. Para decidir o estado do envio, use ocodigo, que segue a mesma regra nos dois ambientes.
Eventos de webhook novos
Três eventos do catálogo da seção 8 só chegam hoje pelo simulador do sandbox, porque produção ainda não os emite: envio.saiu_para_coleta, envio.em_transito e envio.tentativa_entrega. Assine-os no webhook de sandbox para já deixar o seu sistema pronto para eles.
Retenção
Dúvidas de integração? Fale com o time da Encaminhei.
