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.
Cadastre o lojista, uma vez por loja
Leia o catálogo, uma vez (e quando mudar)
Mande o pedido a cada venda
order_id da 3Print.Receba as atualizações
Base URL
https://3print-srv-01.com.br/api/partner/v202Credenciais
O time da 3Print emite duas coisas para você, uma única vez:
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
X-3Print-Key | string | sim | A chave da API. Vai no cabeçalho de toda requisição. Começa com ptk_. |
segredo de assinatura | string | não | Usado 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:
X-3Print-Key: ptk_sua_chave_aqui
Content-Type: application/json
Accept: application/jsonRotaçã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.
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
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:
{
"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):
{
"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ção | O que acontece |
|---|---|
| Bloco presente, lojista ainda não existe | Criado junto com o pedido, na mesma transação |
| Bloco presente, lojista já existe | Usa o cadastro que está lá e ignora o bloco |
| Bloco ausente, lojista não existe | 404 — o pedido não entra |
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.POST /sellers.05Catálogo e gramática de SKU
Um SKU se lê da esquerda para a direita. Por exemplo Q1CM2030P:
| Trecho | Significa |
|---|---|
Q1 | Composição Avulso (Q2 = Duo, Q3 = Trio) |
CM | Canvas com moldura |
2030 | 20 × 30 cm |
P | Moldura preta |
São sete formatos aceitos. O catálogo devolve todos, junto com as composições, as cores, os tamanhos de tabela e o preço unitário de cada combinação:
curl "https://3print-srv-01.com.br/api/partner/v2/catalog" \
-H "X-3Print-Key: ptk_sua_chave_aqui"{
"success": true,
"data": {
"sku_grammar": {
"composition": [
{ "digit": "1", "name": "Avulso", "pieces": 1, "price_multiplier": 1 },
{ "digit": "2", "name": "Duo", "pieces": 2, "price_multiplier": 2 },
{ "digit": "3", "name": "Trio", "pieces": 3, "price_multiplier": 3 }
],
"colors": {
"P": "Preta", "B": "Branca", "N": "Natural",
"T": "Tabaco", "FJ": "Freijó"
},
"size_format": {
"standard": ["40x60", "50x70", "60x90", "80x120", "100x150",
"40x40", "50x50", "60x60", "80x80", "100x100"],
"custom": "Qualquer medida é aceita. Fora da tabela, o preço é calculado
por área e arredondado para o tier imediatamente maior."
},
"variants": [
{ "pattern": "Q{g}CM{tamanho}{cor}", "frame": "Canvas Com Moldura",
"accepts_color": true, "example": "Q1CM2030P" },
{ "pattern": "Q{g}C{tamanho}", "frame": "Canvas Sem Moldura",
"accepts_color": false, "example": "Q1C4060" },
{ "pattern": "Q{g}F{tamanho}SM", "frame": "Somente Impressão",
"accepts_color": false, "example": "Q1F5070SM" },
{ "pattern": "Q{g}F{tamanho}C{cor}SV", "frame": "Caixa",
"finish": "Sem Vidro", "example": "Q1F4060CPSV" }
]
},
"price_table": {
"currency": "BRL",
"basis": "Preço unitário da composição Avulso. Duo x2, Trio x3.",
"sizes": [
{ "size": "40x60", "variants": [
{ "frame": "Canvas Com Moldura", "finish": null, "unit_price": 0.00 }
]}
]
}
}
}06Simular o pedido
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.
{
"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
}/preview — o que passa aqui passa na criação, porque é o mesmo código.07Criar o pedido
{
"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
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
external_order_id | string | sim | O número do pedido na sua plataforma. É a chave de idempotência. |
external_seller_id | string | sim | A loja, como cadastrada em POST /sellers. |
shipping_cost | number | sim | Frete cobrado do cliente final. Entra no total do pedido. |
customer | object | sim | Cliente final e endereço de entrega. Todos os campos são obrigatórios, menos complement. |
items | array | sim | Pelo menos um item. |
Campos do item
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
sku | string | sim | O tipo físico do quadro. Ver catálogo. |
quantity | integer | sim | Quantas peças iguais desta mesma arte. |
print_files | string[] | sim | URLs da arte em alta, acessíveis sem autenticação. Pelo menos uma. |
external_item_id | string | não | Identificador da arte, único por linha. Leia o alerta abaixo. |
mockup | string | não | URL da imagem de capa. Aparece no painel, não é impressa. |
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
{
"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.
422 apontando qual item, corrige e reenvia.08Erros
| Código | Significa | O que fazer |
|---|---|---|
| 201 | Pedido criado | Guarde o order_id. |
| 200 | Pedido já existia | Reenvio idêntico. Traz duplicate: true e o mesmo order_id. Trate como sucesso — porque é. |
| 401 | Chave ausente, inválida ou revogada | Confira o cabeçalho X-3Print-Key. Se estiver certo, a chave foi revogada: fale com o suporte. |
| 404 | Lojista não cadastrado | O external_seller_id não existe nesta parceria. Cadastre em POST /sellers antes. |
| 409 | Mesmo id, conteúdo diferente | Dois pedidos seus receberam o mesmo número. A resposta traz o order_id do que já existe. |
| 422 | Payload ou SKU inválido | A resposta diz qual campo, e no caso de SKU traz também item_index. Nada foi gravado. |
| 5xx | Falha nossa | O único caso em que reenviar o mesmo pedido é a atitude certa. Use backoff exponencial. |
Exemplo de 422 por SKU:
{
"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.
{
"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.
$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.
| status | Significa |
|---|---|
processing | Aceito, na fila ou em produção |
ready_to_ship | Pronto para coleta |
shipped | Despachado — a etiqueta saiu |
in_transit | Coletado pela transportadora |
delivered | Entregue |
returned | Devolvido ou não entregue |
canceled | Cancelado |
order_id mais o status identificam o evento.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.
{
"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"
}Código de rastreio, transportadora e, quando existe página pública, a URL de acompanhamento.
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.
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
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.
12Limites e suporte
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
Tamanho do corpo | 1 MB | não | Pedido com muitos itens pode precisar ser dividido. |
Arquivos de impressão | URL pública | não | Precisam responder sem autenticação enquanto o pedido não for produzido. Nós baixamos e re-hospedamos, mas não imediatamente. |
Ambiente de teste | não há | não | Use /orders/preview. Pedido criado é produzido de verdade. |
Versionamento | /v2 | não | A 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_idjunto do seu número de pedido? - Verifica a assinatura dos callbacks?
- Manda
external_item_iddistinto quando duas artes compartilham o SKU?
external_order_id, o horário aproximado e o corpo da resposta que você recebeu.