Integrações & Desenvolvedores

API de integração para parceiros

Você é dono da venda: catálogo, vitrine, pagamento e pré-venda. A 3Print é dona da produção: impressão, montagem, embalagem, nota fiscal e transportadora. Esta API é o contrato entre os dois lados — você manda o pedido, a gente devolve o andamento.

Isto é para você?

A mesma API atende três situações bem diferentes. Acha a sua antes de seguir — a terceira economiza semanas de trabalho.

Tenho vários lojistas

Marketplace, plataforma de criadores, agência que atende várias marcas. Cada vendedor seu é um lojista aqui, e o external_seller_id é o identificador dele do seu lado. Leia tudo, na ordem.

Tenho uma loja só, com site próprio

Você é um lojista único. Cadastre um, guarde o external_seller_id numa constante e repita em todo pedido. Depois disso pode pular direto para Catálogo e SKU — o resto do conceito de lojista não muda nada para você.

Uso Nuvemshop, Shopify, Bling ou CartPanda

Não use esta API. Já existe integração pronta para essas plataformas: você conecta a loja pelo painel e os pedidos entram sozinhos, com rastreio voltando automaticamente. Construir contra esta API seria refazer o que já está feito. Veja Integrações.

01Como funciona

É a mesma esteira que processa os pedidos da Nuvemshop, da Shopify e dos marketplaces — exposta para quem tem plataforma própria. Do seu lado são quatro chamadas, e só a terceira acontece a cada venda.

1

Cadastre o lojista, uma vez por loja

Cada loja da sua plataforma vira um lojista na 3Print. É o que amarra o pedido à origem certa e o que separa os seus dados dos de outros parceiros.
2

Leia o catálogo, uma vez (e quando mudar)

O catálogo devolve a gramática de SKU e o preço unitário de cada combinação. É o de/para entre o seu produto e o que a fábrica sabe produzir.
3

Mande o pedido a cada venda

Um POST com cliente, endereço e itens. A resposta traz o order_id da 3Print.
4

Receba as atualizações

Cadastre uma URL de callback e a gente avisa a cada mudança de status, com assinatura. Sem callback, dá para consultar por polling.

Base URL

HTTP
https://3print-srv-01.com.br/api/partner/v2
Não existe ambiente de homologação. Pedido criado aqui entra na esteira física e é produzido. Para desenvolver e validar a integração sem gerar produção, use o endpoint de simulação: ele valida o payload inteiro e devolve os totais sem gravar nada.

02Credenciais

O time da 3Print emite duas coisas para você, uma única vez:

CampoTipoObrig.Descrição
X-3Print-KeystringsimA chave da API. Vai no cabeçalho de toda requisição. Começa com ptk_.
segredo de assinaturastringnãoUsado só para verificar os callbacks que a gente manda. Nunca vai numa requisição sua. Necessário apenas se você usar callback.

Cabeçalhos de toda chamada:

HTTP
X-3Print-Key:  ptk_sua_chave_aqui
Content-Type:  application/json
Accept:        application/json

Rotação e revogação

Se a chave vazar (log, repositório, ex-colaborador), avise o suporte. A chave antiga para de funcionar na hora e passa a receber 401. Rotacionar troca também o segredo de assinatura — se você usa callback, atualize os dois no mesmo deploy.

Só do seu back-end. A chave dá acesso a criar pedidos e ler dados de clientes finais. Nunca em código de front-end, repositório público ou log de aplicação.

03Convenções

Valores e datas

Valores em reais, número com até 2 casas e ponto decimal (129.90, nunca 129,90). Datas em ISO 8601 com offset (2026-09-03T14:11:48-03:00). Tudo em UTF-8.

Identificadores

Os campos com prefixo external_ são seus — você escolhe o formato. O order_id é da 3Print, inteiro, e vem na resposta da criação. Guarde os dois: os endpoints de leitura aceitam qualquer um.

Idempotência

A chave de idempotência é o external_order_id, dentro do lojista. Reenviar o mesmo pedido — timeout, retry, duplo clique — devolve 200 com duplicate: true e o mesmo order_id. Nada é duplicado e nada é reproduzido.

Reenviar o mesmo id com conteúdo diferente devolve 409. Isso quase sempre significa que dois pedidos da sua plataforma receberam o mesmo número.

Quando reenviar

4xx é com você, 5xx é com a gente. Resposta na faixa 4xx significa que alguma coisa no pedido precisa mudar — reenviar igual vai dar o mesmo erro. Só reenvie em 5xx ou timeout, e aí pode reenviar à vontade: a idempotência protege.

04Cadastrar o lojista

Cada loja da sua plataforma é um lojista aqui. O external_seller_id é o identificador dela do seu lado, e é o que você repete em todo pedido. Se você tem uma loja só, cadastre um e use sempre o mesmo.

Há dois caminhos, e você escolhe o que encaixa melhor no seu fluxo:

POST/sellers
JSON
{
  "external_seller_id": "77001",
  "name": "Loja do Vale",
  "email": "contato@lojadovale.com.br",
  "document": "12345678000199",
  "phone": "17997765724",
  "address": {
    "street": "Rua Bernardino de Campos",
    "number": "1200",
    "complement": "Sala 4",
    "district": "Centro",
    "city": "São José do Rio Preto",
    "state": "SP",
    "zipcode": "15015000"
  }
}

Responde 201 quando cadastra e 200 com duplicate: true quando o lojista já existe — recadastrar não é erro, então dá para chamar sem checar antes.

Ou: cadastre junto com o primeiro pedido

Se preferir não gerenciar lojista em separado, mande um bloco seller dentro do próprio pedido. Mesmos campos de cima, sem o external_seller_id (que já vai na raiz do pedido):

JSON
{
  "external_order_id": "PED-2026-000871",
  "external_seller_id": "77001",

  "seller": {
    "name": "Loja do Vale",
    "email": "contato@lojadovale.com.br",
    "document": "12345678000199",
    "phone": "17997765724",
    "address": { "...": "igual ao POST /sellers" }
  },

  "customer": { "...": "..." },
  "items": [ "..." ]
}
SituaçãoO que acontece
Bloco presente, lojista ainda não existeCriado junto com o pedido, na mesma transação
Bloco presente, lojista já existeUsa o cadastro que está lá e ignora o bloco
Bloco ausente, lojista não existe404 — o pedido não entra
Criar exige mandar os dados — só o id nunca cria nada. É de propósito: se fosse automático, um external_seller_id digitado errado criaria um lojista vazio e o pedido entraria nele. Você só descobriria semanas depois, com pedidos espalhados por lojistas que não existem. Com a regra acima, o typo devolve 404 na hora.
O bloco não atualiza cadastro existente. Mandar dados diferentes para um lojista que já existe não muda nada — o endereço dele não pode mudar sozinho a cada venda. Para atualizar, use POST /sellers.

06Simular o pedido

POST/orders/preview

Mesmo corpo do endpoint de criação, mesma validação, mesmos cálculos — só que nada é gravado e nenhuma produção é acionada. Use durante o desenvolvimento e, em produção, para mostrar o custo antes de fechar a venda.

JSON
{
  "success": true,
  "valid": true,
  "external_order_id": "PED-2026-000871",
  "seller": { "external_seller_id": "77001", "name": "Loja do Vale" },
  "items": [
    {
      "sku": "Q1CM2030P",
      "external_item_id": "a7f3-arte-01",
      "description": "Quadro Avulso Canvas 20x30 Canvas Com Moldura Preta -",
      "quantity": 2,
      "unit_price": 0.00,
      "line_total": 0.00
    }
  ],
  "subtotal": 0.00,
  "shipping_cost": 24.90,
  "total": 24.90
}
É o substituto do ambiente de homologação. Rode a sua suíte de integração inteira contra o /preview — o que passa aqui passa na criação, porque é o mesmo código.

07Criar o pedido

POST/orders
JSON
{
  "external_order_id": "PED-2026-000871",
  "external_seller_id": "77001",
  "shipping_cost": 24.90,

  "customer": {
    "name": "Marina Prado",
    "document": "12345678909",
    "email": "marina@exemplo.com.br",
    "phone": "17997765724",
    "zipcode": "15015000",
    "street": "Rua Bernardino de Campos",
    "number": "1200",
    "complement": "Sala 4",
    "district": "Centro",
    "city": "São José do Rio Preto",
    "state": "SP"
  },

  "items": [
    {
      "external_item_id": "a7f3-arte-01",
      "sku": "Q1CM2030P",
      "quantity": 2,
      "print_files": ["https://cdn.suaplataforma.com/arte-01.tif"],
      "mockup": "https://cdn.suaplataforma.com/mockup-01.jpg"
    }
  ]
}

Campos do pedido

CampoTipoObrig.Descrição
external_order_idstringsimO número do pedido na sua plataforma. É a chave de idempotência.
external_seller_idstringsimA loja, como cadastrada em POST /sellers.
shipping_costnumbersimFrete cobrado do cliente final. Entra no total do pedido.
customerobjectsimCliente final e endereço de entrega. Todos os campos são obrigatórios, menos complement.
itemsarraysimPelo menos um item.

Campos do item

CampoTipoObrig.Descrição
skustringsimO tipo físico do quadro. Ver catálogo.
quantityintegersimQuantas peças iguais desta mesma arte.
print_filesstring[]simURLs da arte em alta, acessíveis sem autenticação. Pelo menos uma.
external_item_idstringnãoIdentificador da arte, único por linha. Leia o alerta abaixo.
mockupstringnãoURL da imagem de capa. Aparece no painel, não é impressa.
Duas artes diferentes com o mesmo SKU precisam de external_item_id distintos. O SKU descreve só o tipo físico e se repete entre itens. Sem essa chave, um pedido com três canvas 20×30 pretos e três artes diferentes vira um produto só — e duas das artes são descartadas silenciosamente. Se você manda uma arte por SKU, pode omitir o campo.

Resposta

JSON
{
  "success": true,
  "duplicate": false,
  "order_id": 2474,
  "external_order_id": "PED-2026-000871",
  "subtotal": 0.00,
  "shipping_cost": 24.90,
  "total": 24.90,
  "status": "Pendente"
}

201 quando cria, 200 quando é reenvio do mesmo pedido. Guarde o order_id.

Ou o pedido inteiro entra, ou nada entra. Todos os SKUs são resolvidos antes de qualquer gravação. Um item inválido no meio da lista não deixa pedido pela metade — você recebe 422 apontando qual item, corrige e reenvia.

08Erros

CódigoSignificaO que fazer
201Pedido criadoGuarde o order_id.
200Pedido já existiaReenvio idêntico. Traz duplicate: true e o mesmo order_id. Trate como sucesso — porque é.
401Chave ausente, inválida ou revogadaConfira o cabeçalho X-3Print-Key. Se estiver certo, a chave foi revogada: fale com o suporte.
404Lojista não cadastradoO external_seller_id não existe nesta parceria. Cadastre em POST /sellers antes.
409Mesmo id, conteúdo diferenteDois pedidos seus receberam o mesmo número. A resposta traz o order_id do que já existe.
422Payload ou SKU inválidoA resposta diz qual campo, e no caso de SKU traz também item_index. Nada foi gravado.
5xxFalha nossaO único caso em que reenviar o mesmo pedido é a atitude certa. Use backoff exponencial.

Exemplo de 422 por SKU:

JSON
{
  "success": false,
  "message": "Invalid SKU 'Q1XX9999' on item #1: SKU inválido ou fora do padrão
               esperado. See GET /api/partner/v2/catalog for the accepted SKU grammar.",
  "sku": "Q1XX9999",
  "item_index": 1
}

09Receber as atualizações

Cadastre uma URL com o suporte e a 3Print manda um POST a cada mudança relevante do pedido. É o caminho recomendado: sem ele, você precisa consultar em loop para saber que um pedido foi despachado.

JSON
{
  "event": "order.status_changed",
  "sent_at": "2026-09-03T14:11:48-03:00",
  "order": {
    "order_id": 2474,
    "external_order_id": "PED-2026-000871",
    "status": "shipped",
    "status_label": "Enviado",
    "tracking_code": "BR123456789BR",
    "carrier": "Correios",
    "updated_at": "2026-09-03T14:11:47-03:00"
  }
}

Verifique a assinatura

Todo callback vai assinado no cabeçalho Signature: HMAC-SHA256 do corpo bruto com o seu segredo. Confira antes de processar — é o que garante que o aviso veio da 3Print.

PHP
$esperado = hash_hmac('sha256', $corpoBruto, $seuSegredo);

if (! hash_equals($esperado, $request->header('Signature'))) {
    abort(401);
}

Status possíveis

O campo status usa um vocabulário fixo — o mesmo do endpoint de consulta, para você não precisar de dois dicionários. O status_label traz o texto interno da fábrica e pode mudar; não construa lógica em cima dele.

statusSignifica
processingAceito, na fila ou em produção
ready_to_shipPronto para coleta
shippedDespachado — a etiqueta saiu
in_transitColetado pela transportadora
deliveredEntregue
returnedDevolvido ou não entregue
canceledCancelado
Responda 2xx rápido e processe depois. Callback que falha é reenviado com backoff exponencial, então processamento lento vira reentrega. E trate o mesmo evento chegando duas vezes: o order_id mais o status identificam o evento.
Pedido despachado normalmente gera dois avisos: um em shipped e outro quando o código de rastreio fica disponível. Nem sempre eles saem juntos.

10Consultar o pedido

Todos aceitam o order_id da 3Print ou o seu external_order_id. Você só enxerga os seus pedidos.

GET/orders/{id}/status
JSON
{
  "success": true,
  "order_id": 2474,
  "external_order_id": "PED-2026-000871",
  "status": "shipped",
  "status_label": "Enviado",
  "status_3print": "Coletado",
  "approved_at": "2026-09-01T09:22:10-03:00",
  "tracking_code": "BR123456789BR"
}
GET/orders/{id}/tracking

Código de rastreio, transportadora e, quando existe página pública, a URL de acompanhamento.

GET/orders/{id}/invoice

Dados da NF-e: número, série, chave de acesso e link do DANFE. A nota é emitida quando o pedido entra em despacho — antes disso os campos vêm nulos.

Se o mesmo external_order_id existir em duas lojas suas, a consulta por ele devolve 409 com a lista de candidatos, em vez de arriscar mostrar o rastreio do pedido errado ao cliente final. Consulte pelo order_id para desempatar.

11Cotação de frete

POST/shipping/quote

Cotação antes de fechar a venda, a partir do CEP de destino e dos itens do carrinho. Devolve as opções disponíveis com prazo e valor.

Carrinho acima de 50 kg não recebe opções. A resposta volta bem-sucedida mas com a lista vazia — trate esse caso no seu checkout em vez de assumir que sempre vem pelo menos uma opção.

12Limites e suporte

CampoTipoObrig.Descrição
Tamanho do corpo1 MBnãoPedido com muitos itens pode precisar ser dividido.
Arquivos de impressãoURL públicanãoPrecisam responder sem autenticação enquanto o pedido não for produzido. Nós baixamos e re-hospedamos, mas não imediatamente.
Ambiente de testenão hánãoUse /orders/preview. Pedido criado é produzido de verdade.
Versionamento/v2nãoA versão está na URL. Existe uma v1 anterior, sem prefixo, que continua no ar para quem já a usa — se você está integrando agora, é a v2. Mudança incompatível viraria /v3, com a anterior mantida e aviso por e-mail antes.

Antes de subir para produção

  • Rodou a integração inteira contra /orders/preview?
  • Trata 4xx sem reenviar e 5xx com backoff?
  • Guarda o order_id junto do seu número de pedido?
  • Verifica a assinatura dos callbacks?
  • Manda external_item_id distinto quando duas artes compartilham o SKU?
Suporte técnico da integração: WhatsApp (17) 99776-5724. Ao relatar um problema, mande o external_order_id, o horário aproximado e o corpo da resposta que você recebeu.