API da PIED — REST e Webhook¶
Status: Aprovado · Responsável: Gustavo Madruga · Atualizado em: 2026-07-11
Referência técnica dos dois canais de entrada do maxsul-pied. Contrato validado contra o
ambiente real em 2026-06-17.
1. Acesso¶
- Base URL v2:
https://backend-pied-prod.piedadmin.com.br/api/v2(produtos, pedidos). - Base URL v1:
https://backend-pied-prod.piedadmin.com.br/api/v1(clientes/companies). - Autenticação: header
Authorization: Bearer <token>. O token de produção é provido por variável de ambiente do Coolify (PIED_TOKEN), nunca no código; o poll nasce desligado até ele existir. - Envelope de resposta:
{ "error": null, "data": { "items": [ … ], "totalItems": N } }. - Paginação: rota
/{recurso}/{página}/{limite}, 1-based, limite máx. 50. Página além do fim retorna HTTP 200 comitemsvazio (condição de parada);204= nenhum registro. - Limite de uso: dois tetos por hora (nº de chamadas e tempo de processamento). Estourar →
429comhardLimit/timeLimitno corpo → backoff; emtimeLimit, esperar a virada da janela.
2. REST¶
2.1 Produtos — GET /api/v2/equipments/{página}/{limite}¶
Varredura completa (sem delta). Upsert por productCode.
{ "productCode": "MOD-550", "name": "Painel Solar 550W Monocristalino",
"manufacturer": "Canadian Solar", "model": "CS6W-550MS", "type": "module",
"baseprice": 780.00, "inventory": { "current": 320, "reserved": 12, "minimum": 50 } }
2.2 Clientes — GET /api/v1/companies/{página}/{limite} (v1)¶
Mesmo objeto company do pedido (cnpj/cpf, companyName, mainContact, address) + stateInscription.
Varredura completa. Use para clientes que ainda não geraram pedido (os demais vêm embutidos nos pedidos).
2.3 Pedidos — GET /api/v2/requests/order/{página}/{limite}[?lastUpdateAfter=AAAA-MM-DD]¶
Cliente (company) e itens (products) vêm embutidos. Orçamentos usam
GET /api/v2/requests/budget/… com o mesmo formato (uso interno — não vão ao X-Adm).
{
"id": "6f3a9c12", "code": "200000123", "type": "order",
"kind": "Kit Personalizado", "totalPower": 5.5,
"dealStatus": "Novo", "originalValue": 25000, "discount": 5, "finalValue": 23750,
"company": {
"cnpj": "12.345.678/0001-09", "companyName": "Solar Sul Ltda",
"mainContact": { "name": "João", "surname": "Silva", "email": "joao@solarsul.com", "cellphone": "5642999990000" },
"address": { "CEP": "84010-000", "state": "PR", "city": "Ponta Grossa",
"neighborhood": "Centro", "patio": "Rua das Acácias", "number": "100", "complement": "" }
},
"products": [ { "productCode": "MOD-550", "type": "module", "quantity": 10,
"singlePrice": 780, "totalPrice": 7800, "center": { "code": "CD001" } } ],
"payment": { "type": "credito", "status": "received", "paidAt": "2026-08-18T16:00:38.620Z",
"transactionId": "fb35…", "transactionStatus": "approved", "links": [],
"condition": { "name": "30/60 dias", "quantityOfInstallments": 2 } },
"freight": { "type": "CIF", "price": 350 },
"lastUpdate": "2026-06-15T19:12:24.391Z"
}
Pagamento — um status macro por pedido. O payment é um objeto único (não um array): um
status, um paidAt, uma condition. Verificado em 1409 objetos capturados. Valores de status:
notRequested · requested · partial · received · cancelled.
paidAt= quando entrou o primeiro dinheiro; permanece preenchido mesmo apóscancelled(é histórico) — para saber o estado atual olhestatus, nãopaidAt.- O status individualizado por parcela/forma (o que a tela "Pagamento" da PIED mostra por linha)
não vem embutido: só via
payment.links[](GET /api/v2/payments/{id}, um por lançamento — esparso, presente em ~4% dos pedidos). A integração não consome esse sub-recurso hoje. - O valor da NF independe do pagamento:
finalValue/originalValue/invoice.serviceTotal/ produtos são idênticos empartialereceived(ver decisão 0010).
2.4 Incremental¶
?lastUpdateAfter=AAAA-MM-DDsó tem efeito em pedidos (produtos/clientes são varridos por inteiro). Granularidade de dia → a janela do último dia é reprocessada de propósito (dedup é da fase 2). O cursor vive empied_cursor(Modelagem §2.1).- A documentação da PIED cita um delta implícito por
apiSent/apiSentUpdatedsem parâmetros — não confirmado no ambiente real; o mecanismo confiável é olastUpdateAfter.
2.5 Erros¶
401/403 aborta a entidade; 429/5xx/timeout → retentativa com espera progressiva; resposta
não-JSON → falha controlada.
3. Webhook (PIED → nós)¶
POST HTTPS para a URL configurada, corpo { "event": …, "data": { … } }. O data é completo
(mesmo formato do pedido REST — em geral dispensa segunda consulta).
Eventos: budget.created · budget.updated · order.created · order.updated. Empresas também
disparam evento, mas sem nome publicado (a confirmar). Não há webhook de produto.
{ "event": "order.created",
"data": { "id": "6f3a9c12", "code": "200000123", "type": "order", "kind": "Kit Personalizado",
"totalPower": 5.5, "dealStatus": "Novo", "finalValue": 23750,
"company": { "cnpj": "12.345.678/0001-09", "companyName": "Solar Sul Ltda", "…": "…" },
"products": [ { "productCode": "MOD-550", "quantity": 10, "singlePrice": 780 } ],
"payment": { "type": "credito", "status": "received" },
"lastUpdate": "2026-06-15T19:12:24.391Z" } }
Resposta: 200 OK rápido (só confirma o recebimento; processamento assíncrono).
3.1 Lacunas (NÃO disparam webhook)¶
Alteração de responsável, edição de frete, campos customizados, anotações, upload de arquivos. Por isso o webhook não é espelho fiel — o que falta é coberto pela reconciliação REST (§2.3). É por isso que o poll REST é obrigatório, não rede de segurança.
3.2 Segurança (a confirmar com a PIED)¶
- Sem HMAC/assinatura documentada → segredo compartilhado (header
X-Pied-Secret, a confirmar) + IP allowlist no Traefik + HTTPS. - Política de reenvio não documentada → assumir "pode perder evento"; a reconciliação REST cobre.
3.3 Cadastro do webhook (manual, painel da PIED)¶
Configurações → API e Webhooks → aba Webhooks → Adicionar: endpoint
https://pied.maxsul.xadm.biz/webhook/pied, gatilhos budget.*/order.*, segredo. Testar com
webhook.site/ngrok antes de apontar para produção. Ver Configuração.