Fluxo de pedidos para ERP
O padrão no Brasil: a loja gera o pedido, o ERP importa, emite a NF-e e devolve a nota e o rastreio. Este guia mostra o caminho completo com os endpoints padrão, o mesmo que os conectores Bling e Tiny e o TF ERP seguem.
Visão geral
- 1
order.paidwebhook — ou polling da fila pendente - 2
GET /busca o pedidoorders/ {order_id} - pedido pronto para NF-e
- 3
POST /confirma a importaçãoorders/ {order_id}/ acknowledge - emite a NF-e na SEFAZ
- 4
POST /envia a NF-e (só se o emissor for o ERP)orders/ {order_id}/ invoices a loja publicainvoice.authorized - 5
POST /informa transportadora e rastreioorders/ {order_id}/ fulfillments a loja publicafulfillment.shipped - 6
POST /informa a entregaorders/ {order_id}/ fulfillments/ delivered a loja publicafulfillment.delivered
Fluxo de pedidos para ERP
Saiba que há pedido novo
Assine o webhook order.paid para reagir em segundos. Como webhooks podem atrasar, faça também um polling periódico (a cada 5 a 15 minutos) da fila de pedidos pagos que este app ainda não confirmou:
curl "https://api.tfshop.cloud/v1/orders?export_status=pending&limit=50" \
-H "Authorization: Bearer $TFSHOP_TOKEN"export_status=pending, e pedidos de teste ficam de fora. O webhook traz só ids e estados; os dados vêm do GET.Busque o pedido
GET /orders/{order_id} devolve tudo que a NF-e precisa. Use o id do pedido como chave única no ERP e o number como número de exibição. Dados do comprador exigem orders.pii:read.
curl https://api.tfshop.cloud/v1/orders/o_1011 \
-H "Authorization: Bearer $TFSHOP_TOKEN"Confirme a importação
Depois de gravar o pedido no ERP, chame acknowledge com o número do pedido no seu sistema. O pedido sai da fila pending deste app, o lojista vê que ele foi importado, e chamadas repetidas devolvem o mesmo resultado.
curl -X POST https://api.tfshop.cloud/v1/orders/o_1011/acknowledge \
-H "Authorization: Bearer $TFSHOP_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6c1f2a9e-5b7d-4e3a-9f10-2d8c4b6a7e51" \
-d '{ "external_id": "10293" }'Envie a NF-e (só se o emissor for o ERP)
Cada loja tem um único emissor de NF-e, escolhido pelo lojista em Aplicativos e API: o TF Fiscal ou o ERP. Quando é o ERP, envie a nota depois de autorizada na SEFAZ:
curl -X POST https://api.tfshop.cloud/v1/orders/o_1011/invoices \
-H "Authorization: Bearer $TFSHOP_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 0e4b8d7c-1a2f-4c3e-8b5d-9f6a7e2c1d40" \
-d '{
"access_key": "35261011222333000181550010000010421304812756",
"number": 1042,
"series": 1,
"issued_at": "2026-10-29T14:20:00Z",
"xml_url": "https://erp.exemplo.com.br/nfe/35261011222333000181550010000010421304812756.xml",
"danfe_url": "https://erp.exemplo.com.br/nfe/35261011222333000181550010000010421304812756.pdf"
}'Validação da chave de acesso
- 44 caracteres, com o CNPJ do emitente numérico ou alfanumérico.
- Dígito verificador (módulo 11) correto.
- Modelo
55(NF-e) nas posições 21 e 22. - CNPJ do emitente (posições 7 a 20) igual ao CNPJ da loja.
- Idempotente pela chave: reenviar a mesma chave devolve a mesma nota, sem duplicar.
- Recusada com
invoice_issuer_mismatchquando a loja emite pelo TF Fiscal, e comtest_orderpara pedido de teste com tokentfs_live_.
Composição da chave de acesso
35261011222333000181550010000010421304812756
| Parte | Posições | Valor |
|---|---|---|
UF do emitente (35 = SP) | 1–2 | 35 |
| Ano e mês de emissão | 3–6 | 2610 |
| CNPJ do emitente | 7–20 | 11222333000181 |
Modelo (55 = NF-e) | 21–22 | 55 |
| Série | 23–25 | 001 |
| Número da nota | 26–34 | 000001042 |
| Tipo de emissão | 35 | 1 |
| Código numérico | 36–43 | 30481275 |
| Dígito verificador | 44 | 6 |
A loja publica invoice.authorized, também quando a nota vem do TF Fiscal, para que nenhum outro sistema emita de novo. Se a nota for cancelada na SEFAZ, informe com POST /orders/{order_id}/invoices/{access_key}/cancel.
Informe o envio
Com a mercadoria postada, envie transportadora, serviço e código de rastreio. O envio exige um documento fiscal no pedido: a NF-e autorizada ou, para quem não emite NF-e, uma DC-e (Declaração de Conteúdo eletrônica). Sem ele, a resposta é fiscal_document_required.
curl -X POST https://api.tfshop.cloud/v1/orders/o_1011/fulfillments \
-H "Authorization: Bearer $TFSHOP_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 9b3e1f60-7c2d-4a8e-b5f4-0d1c2e3f4a5b" \
-d '{
"carrier": "Correios",
"service": "SEDEX",
"tracking_code": "AB123456785BR",
"shipped_at": "2026-10-29T17:40:00Z"
}'O comprador recebe o e-mail com o link de rastreio e a loja publica fulfillment.shipped.
Marque como entregue
Quando a transportadora confirmar a entrega, chame o endpoint abaixo. A loja publica fulfillment.delivered.
curl -X POST https://api.tfshop.cloud/v1/orders/o_1011/fulfillments/delivered \
-H "Authorization: Bearer $TFSHOP_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "delivered_at": "2026-10-31T15:12:00Z" }'Cancelamentos e reembolsos
order.cancelled: cancele o pedido no ERP. Se já houver NF-e, o emissor a cancela na SEFAZ no prazo legal e informa a loja.refund.completed: o reembolso foi feito pela loja, na conta C6 do lojista. O ERP não executa reembolsos.- O ERP pode cancelar um pedido não pago, ou pedir o cancelamento de um pago, com
POST /orders/{order_id}/cancel; quem decide é o lojista.
O pedido pronto para NF-e
Os campos que o ERP usa para montar a NF-e já vêm calculados pela loja, para que todos os ERPs cheguem ao mesmo total, centavo por centavo.
| Campo | Na NF-e | Descrição |
|---|---|---|
buyer.type · buyer.document | dest · CPF / CNPJ | Somente dígitos (o CNPJ pode ser alfanumérico). Pessoa física (PF) ou jurídica (PJ). Exige orders.pii:read. |
buyer.ie_indicator · buyer.state_registration | indIEDest · IE | Indicador da IE do destinatário (indIEDest): 1 contribuinte, 2 isento, 9 não contribuinte. Pessoa física é sempre 9; a IE vem em state_registration quando informada. |
shipping_address.ibge_code | cMun | Código IBGE do município (7 dígitos), obtido a partir do CEP. |
lines[].discount_allocated | vDesc | Desconto do pedido (cupom, Pix) rateado por item. A soma dos itens é igual a discount. |
lines[].freight_allocated | vFrete | Frete rateado por item. A soma dos itens é igual a shipping_total. |
lines[].ncm · cest · origin | NCM · CEST · orig | Apenas sugestão (o NCM é opcional na loja). O ERP usa o próprio cadastro fiscal. |
payment.nfe_payment_code | tPag | Meio de pagamento já no código da NF-e: 17 Pix, 03 cartão de crédito, 15 boleto. |
payment.card_brand · payment.nfe_card_brand | tBand | Bandeira do cartão, também no código da NF-e (01 Visa, 02 Mastercard, 06 Elo…). |
payment.acquirer_cnpj | card · CNPJ | CNPJ da instituição de pagamento (grupo do cartão na NF-e). |
payment.authorization_code | cAut | Código de autorização do cartão; no Pix, o id fim a fim vem em pix_end_to_end_id. |
fiscal_hints.ind_pres | indPres | 2: operação não presencial, pela internet. |
fiscal_hints.ind_intermed | indIntermed | 0: venda em site próprio. A TF é só o software da loja e o dinheiro vai direto para a conta C6 do lojista, então não há intermediador (não informe infIntermed). |
fiscal_hints.ind_final | indFinal | 1 consumidor final; 0 quando um comprador de atacado compra para revender. |
test | — | Pedido de teste: nunca emita NF-e. |
ind_intermed = 0 para venda em loja própria segue a NT 2020.006. Confirme com o contador do lojista as regras de cada operação.GET /orders/{order_id} (com orders.pii:read){
"id": "o_1011",
"number": 1011,
"created_at": "2026-10-26T12:00:00.000Z",
"updated_at": "2026-10-26T12:00:00.000Z",
"status": "paid",
"payment_status": "confirmed",
"fulfillment_status": "unfulfilled",
"invoice_status": "pending",
"test": false,
"channel": "online_store",
"currency": "BRL",
"subtotal": 19990,
"discount": 0,
"shipping_total": 0,
"total": 19990,
"refunded": 0,
"buyer": {
"type": "PF",
"name": "Juliana Martins",
"document": "12345678909",
"document_type": "cpf",
"email": "juliana@exemplo.com.br",
"phone": "+5519981234567",
"state_registration": null,
"ie_indicator": 9,
"masked": false
},
"shipping_address": {
"zip": "13010111",
"street": "Rua Barão de Jaguara",
"number": "100",
"complement": null,
"district": "Centro",
"city": "Campinas",
"state": "SP",
"country": "BR",
"ibge_code": "3509502"
},
"billing_address": {
"zip": "13010111",
"street": "Rua Barão de Jaguara",
"number": "100",
"complement": null,
"district": "Centro",
"city": "Campinas",
"state": "SP",
"country": "BR",
"ibge_code": "3509502"
},
"lines": [
{
"line_id": "o_1011:1",
"product_id": "p_pb_20k",
"variant_id": "p_pb_20k_v1",
"sku": "VLT-PB20K",
"title": "Power Bank 20.000 mAh 22,5W",
"variant_title": "Padrão",
"quantity": 1,
"unit_price": 19990,
"list_price": 19990,
"discount_allocated": 0,
"freight_allocated": 0,
"total": 19990,
"ncm": "85076000",
"cest": null,
"origin": 2,
"gtin": "7891000079190"
}
],
"shipping": {
"carrier": "Correios",
"service": "SEDEX",
"price": 0,
"business_days": 2,
"tracking_code": null,
"tracking_url": null
},
"payment": {
"method": "credit_card",
"status": "confirmed",
"paid_at": "2026-10-26T12:00:00.000Z",
"installments": 2,
"nfe_payment_code": "03",
"card_brand": "visa",
"nfe_card_brand": "01",
"card_last4": "8407",
"acquirer_cnpj": "31872495000172",
"authorization_code": "482913",
"pix_end_to_end_id": null,
"provider": "c6",
"provider_payment_id": "c6_card_1011"
},
"fiscal_hints": {
"ind_pres": 2,
"ind_intermed": 0,
"ind_final": 1
},
"invoice": null,
"export": {
"acknowledged_at": "2026-10-26T12:05:00.000Z",
"external_id": "11011"
}
}Esquema completo do pedido
| Campo | Tipo | Descrição |
|---|---|---|
| idObrigatório | string | Order id (immutable). Use it as the unique key in your system. |
| numberObrigatório | integer | Order number shown to the buyer and the merchant (#1011). |
| created_atObrigatório | string (date-time) | Creation time. |
| updated_atObrigatório | string (date-time) | Last change (latest timeline entry). Use with updated_since. |
| statusObrigatório | string | Order status. Valores: pending_paymentpaidfulfillingfulfilledcancelledexpiredreturn_requestedrefundedpartially_refunded |
| payment_statusObrigatório | string | Payment status (see Payment.status). |
| fulfillment_statusObrigatório | string | Fulfillment status. Valores: unfulfilledlabel_purchasedshippeddeliveredreturned |
| invoice_statusObrigatório | string | NF-e status. Valores: not_requiredpendingauthorizederrorcancelled |
| testObrigatório | boolean | Test order placed by the merchant: never issue an NF-e for it. |
| channelObrigatório | string | Sales channel. Valores: online_storewhatsappmanualvendedormercado_livreshopeetiktok_shop |
| currencyObrigatório | string | Always BRL. Valores: BRL |
| subtotalObrigatório | integer | Sum of the lines (unit_price × quantity). In centavos. ≥ 0 |
| discountObrigatório | integer | Coupon + Pix discount. In centavos. ≥ 0 |
| shipping_totalObrigatório | integer | Freight charged (same as shipping.price). In centavos. ≥ 0 |
| totalObrigatório | integer | subtotal − discount + shipping_total. In centavos. ≥ 0 |
| refundedObrigatório | integer | Refunded so far. In centavos. ≥ 0 |
| buyerObrigatório | → Buyer | NF-e recipient (dest). |
| shipping_addressObrigatório | → Address | Brazilian address. Without orders.pii:read, street, number, complement and district are masked and the CEP keeps only its first 5 digits. |
| billing_addressObrigatório | → Address | Same as the shipping address in v1 (the checkout has a single address). |
| linesObrigatório | → OrderLine[] | Order lines. |
| shippingObrigatório | → OrderShipping | |
| paymentObrigatório | → Payment | Money goes straight to the merchant's own C6 account; refunds are made by the store. |
| fiscal_hintsObrigatório | → FiscalHints | |
| invoiceObrigatório | → Invoice | null | Current NF-e, or null. |
| exportObrigatório | → OrderExportStatus | null | This app's acknowledgement, or null when not yet imported. |