CarrierAPI — Plataforma de envios multitransportadora self-hosted

Uma única API REST para etiquetas de envio, tarifas, tracking e envio em lote em várias transportadoras — self-hosted, como alternativa a uma ferramenta SaaS como o SendCloud, com controlo total sobre os contratos próprios com as transportadoras.

Envios multitransportadora
FastAPI / Python
Sync NetSuite & Shopify
Self-hosted

Função & âmbito

Uma empresa de e-commerce com ERP próprio (NetSuite) expedia através de uma ferramenta SaaS, com os custos e o controlo limitado sobre os contratos com transportadoras que isso implica. O CarrierAPI é o substituto self-hosted: uma API que cria etiquetas, compara tarifas, segue expedições e processa lotes em várias transportadoras, com os mesmos pontos de ligação da ferramenta anterior, para que a mudança a partir do NetSuite pudesse ser uma substituição direta em vez de uma reconstrução.

  • Uma arquitetura de adaptadores por transportadora (PostNL, DHL Express e UPS como conjunto base; DHL Parcel, FedEx, GLS e o próprio SendCloud como adaptadores adicionais), pelo que acrescentar uma transportadora não exige alterações no resto do sistema.
  • Substituição direta da ferramenta SaaS anterior dentro do NetSuite: mesmos nomes de campos personalizados, alterações mínimas à integração SuiteScript existente.
  • Ligação direta aos registos Item Fulfillment do NetSuite, mais sincronização com o Shopify e uma integração com a Amazon a par das transportadoras.
  • Duas formas de gerir as credenciais das transportadoras: pela interface (guardadas em base de dados, a via preferida) ou por variáveis de ambiente (caminho legado para instalações existentes).

Impacto

  • Custos de envio e contratos com transportadoras de novo na própria mão, em vez de ficarem presos às tarifas e condições de uma camada SaaS intermédia.
  • Corrigido o reconhecimento de moradas para uma classe de moradas antes mal interpretadas (a extração do número de porta apanhava por engano o último número em vez do primeiro depois do nome da rua — errado, por exemplo, em moradas de parques de férias com número de porta e número de chalé).
  • A reconciliação de faturas com a UPS continuou a funcionar depois de a UPS ter mudado duas vezes o formato de exportação das suas faturas, ao basear a deteção de colunas no conteúdo em vez de posições fixas, verificada contra o total que a própria fatura declara.
  • Fechada uma fuga de sessão em que utilizadores eliminados mantinham acesso até 24 horas, porque só eram verificadas a assinatura e a validade do token — não se a conta ainda existia.

Arquitetura

Cada transportadora é um adaptador atrás de uma interface partilhada (criar e cancelar expedições, consultar tarifas, tracking, serviços disponíveis) — ligar uma nova transportadora não mexe no resto do sistema. O backend é FastAPI (Python, async) com PostgreSQL como base de dados principal e um worker de segundo plano em Redis (arq) para o processamento em lote de etiquetas, para que criar centenas de etiquetas de uma vez não bloqueie a API. As etiquetas ficam guardadas em MinIO (compatível com S3), para que o armazenamento não fique preso a um único fornecedor cloud. O frontend é um dashboard Next.js/React para gerir expedições, definições de transportadoras e credenciais. Tudo corre self-hosted via Docker Compose atrás do Traefik, implantado com Coolify.

Escolha do modelo

Sem IA/LLM — este é deliberadamente um sistema determinístico: etiquetas, tarifas e tracking têm de ser previsíveis e auditáveis, não gerados. A decisão central aqui foi self-hosted versus SaaS: uma ferramenta SaaS monta-se mais depressa, mas uma plataforma self-hosted dá controlo sobre os contratos com transportadoras, elimina qualquer taxa por etiqueta ou por expedição a uma camada intermédia e permite construir funcionalidades (como a integração direta com o NetSuite) exatamente à medida, em vez de esperar pelo que um fornecedor SaaS suporta ou não.

Custos & latência

  • Infraestrutura self-hosted (Docker Compose, PostgreSQL, Redis, MinIO) em vez de uma taxa SaaS recorrente por expedição.
  • A geração de etiquetas em lote corre de forma assíncrona numa fila Redis (arq), pelo que criar centenas de etiquetas de uma vez mantém a API responsiva em vez de deixar quem pediu à espera.
  • Reconciliação de faturas verificada com faturas reais da UPS: entre outras, uma fatura de 1.275 linhas de 4.855,72 € e uma exportação de 2.275 linhas, ambas processadas corretamente após a correção na deteção de colunas.

Lições aprendidas

A análise de moradas neerlandesas falhava numa classe específica de moradas (parques de férias com número de porta e um número de chalé separado, como «Hessenweg 83 41») porque o parser lia o último número da linha da rua como número de porta em vez do primeiro depois do nome da rua. Resolvido com um parser partilhado, verificado contra um caso real. A importação de faturas da UPS quebrou duas vezes porque a UPS alterou o formato de exportação (número e ordem das colunas mudaram); a abordagem de posições fixas foi substituída por deteção de colunas baseada em conteúdo, cujo resultado é cruzado com o total declarado pela própria fatura. Os avisos de validação de morada começaram por bloquear totalmente a criação de uma expedição; isso foi reduzido a bloquear apenas a compra automática da etiqueta, para que uma expedição assinalada continue visível e possa ser repetida em vez de ficar presa. E uma vulnerabilidade de sessão: utilizadores eliminados mantinham acesso até 24 horas porque os tokens só eram verificados na assinatura e validade, não na existência da conta — resolvido verificando em cada pedido que a conta ainda existe.