Pular para o conteúdo

Produtos

O produto é o catálogo puro: nome + preço mensal, fixo ou variável. Período, parcelamento e desconto não moram aqui — são decisões de cada plano. Modelo completo em Conceitos.

Rota O que faz
POST /v1/products Cria um produto, com preço fixo ou variável
GET /v1/products Lista o catálogo, paginado
GET /v1/products/{productId} Um produto pelo id
PATCH /v1/products/{productId} Edita nome, descrição ou preço
POST /v1/products/{productId}/archive Bloqueia o produto em planos novos
POST /v1/products/{productId}/unarchive Volta a aceitar o produto em planos novos
POST /v1/products/{productId}/pricing-test Chamada real de teste no seu endpoint de preço

Toda rota de produto devolve este objeto:

Campo Tipo Descrição
id string (uuid) Identificador do produto no motor
name string Nome do produto — é o que o assinante lê na fatura
description string | null Descrição interna, opcional
externalRef string | null O seu id para este produto — devolvido nos webhooks e no endpoint de preço
pricingMode enum Como o preço é definido — ver valores. Imutável após a criação
monthlyPriceCents integer | null Preço mensal em centavos (24990 = R$ 249,90). null em produto variável
endpointUrl string | null URL do seu endpoint de preço. null em produto fixo
capCents integer | null Teto por cobrança em centavos — resposta do endpoint acima disso é falha. null em produto fixo
archivedAt datetime | null Quando foi arquivado — null = ativo. Arquivado não entra em plano novo
createdAt datetime Quando foi criado
Valor Significado
fixed Preço de tabela: o valor mensal está em monthlyPriceCents
endpoint Preço variável: a cada ciclo o motor pergunta o valor ao seu sistema — veja o guia do endpoint de preço

O preço é sempre o mensal; quem multiplica é o periodMonths do plano. Um plano semestral cobra 6 × o mensal numa fatura só.

POST /v1/products

Headers:

Header Obrigatório Descrição
Idempotency-Key Sim Chave única sua — repetir devolve a mesma resposta

Corpo:

Campo Tipo Obrigatório Descrição
name string Sim 1–120 caracteres. É o que o assinante lê na fatura
description string Não Até 500 caracteres. Descrição interna
externalRef string Não 1–120 caracteres. O seu id — devolvido em webhooks e no endpoint de preço
pricing object Sim Uma das duas formas abaixo. O modo escolhido é imutável

pricing com preço fixo:

Campo Tipo Obrigatório Descrição
pricing.mode fixed Sim Literal "fixed"
pricing.monthlyPriceCents integer Sim Preço mensal em centavos, ≥ 0

pricing com preço variável:

Campo Tipo Obrigatório Descrição
pricing.mode endpoint Sim Literal "endpoint"
pricing.url string Sim URL https do seu endpoint de preço
pricing.capCents integer Sim Teto por cobrança em centavos, ≥ 0 — a proteção contra um bug seu virar uma cobrança absurda
Janela do terminal
# preço fixo
curl -X POST $MOTOR/products -H "$AUTH" -H "Idempotency-Key: prod-caixa-1" \
-H "Content-Type: application/json" -d '{
"name": "Caixa do Clube",
"description": "6 garrafas · seleção do sommelier",
"pricing": { "mode": "fixed", "monthlyPriceCents": 24990 }
}'
# preço variável
curl -X POST $MOTOR/products -H "$AUTH" -H "Idempotency-Key: prod-garrafas-1" \
-H "Content-Type: application/json" -d '{
"name": "Garrafas extras",
"externalRef": "garrafas-extras",
"pricing": { "mode": "endpoint", "url": "https://seusistema.com.br/pricing", "capCents": 100000 }
}'

Resposta 201 — o objeto Produto:

{
"id": "0199c1a2-…",
"name": "Caixa do Clube",
"description": "6 garrafas · seleção do sommelier",
"externalRef": null,
"pricingMode": "fixed",
"monthlyPriceCents": 24990,
"endpointUrl": null,
"capCents": null,
"archivedAt": null,
"createdAt": "2026-08-29T18:12:03.000Z"
}

Se o seu gateway tem catálogo de produtos (Vindi, Stripe), o motor cria o espelho lá automaticamente, em background — a criação aqui nunca espera chamada a terceiro.

Erros: 400 (payload inválido ou Idempotency-Key ausente).

GET /v1/products

Query (paginação padrão):

Parâmetro Tipo Padrão Descrição
page integer 1 Página, começando em 1
pageSize integer 25 Itens por página, máximo 100

Resposta 200: { data: [Produto…], pagination }, mais recentes primeiro. Inclui os arquivados (com archivedAt preenchido).

GET /v1/products/{productId}
Parâmetro Em Descrição
productId path Id do produto

Resposta 200: o objeto Produto. Erros: 404.

PATCH /v1/products/{productId}

Todos os campos são opcionais — envie só o que muda:

Campo Tipo Vale para Descrição
name string ambos os modos 1–120 caracteres
description string ambos os modos Até 500 caracteres
monthlyPriceCents integer só produto fixed Novo preço mensal em centavos, ≥ 0
endpointUrl string só produto endpoint Nova URL https do endpoint
capCents integer só produto endpoint Novo teto por cobrança, ≥ 0

Duas regras:

  • Preço novo vale a partir das próximas faturas — nada retroativo é reprocessado.
  • O modo de preço é imutável. Enviar campo do outro modo (ex.: monthlyPriceCents num produto variável) devolve 409 — para trocar o modo, crie outro produto.

Resposta 200: o objeto Produto atualizado. Erros: 404, 409.

POST /v1/products/{productId}/archive
POST /v1/products/{productId}/unarchive

Arquivar bloqueia o produto em planos novos; planos e assinaturas existentes seguem faturando normalmente. Produto nunca é apagado. Reativar volta a aceitá-lo em planos novos.

Resposta 200: o objeto Produto, com archivedAt preenchido (ou null de volta). Erros: 404.

POST /v1/products/{productId}/pricing-test

Só para produto de preço variável. Dispara uma chamada real ao seu endpoint com mode: "test" e um payload de exemplo, e devolve o resultado cru — ninguém descobre integração quebrada na primeira fatura. O contrato da chamada está no guia.

Resposta 200:

Campo Tipo Descrição
outcome enum Resultado da chamada — ver valores
amountCents integer | null Valor devolvido pelo seu endpoint, quando ok
description string | null Descrição devolvida, se houver
responseStatus integer | null HTTP status da resposta — null se nem respondeu
latencyMs integer Latência da chamada
responseBody string | null Corpo cru da resposta — é com isso que seu time debuga

O mesmo enum aparece no histórico pricingAttempts das faturas:

Valor Significado
ok Resposta válida — o valor seria aceito
skip Seu endpoint respondeu { "skip": true } — o item não seria cobrado neste ciclo
timeout Sem resposta em 10 segundos
http_error Resposta com status fora de 2xx
invalid_response 2xx, mas corpo fora do contrato (ex.: amount float ou ausente)
over_cap Valor acima do capCents do produto — tratado como falha por proteção

Erros: 404 (produto não existe ou não é de preço variável).