Pular para o conteúdo

Assinaturas

A assinatura vincula um cliente final a um plano. A partir daí o motor gera as faturas sozinho, a cada ciclo. Modelo completo em Conceitos.

Rota O que faz
POST /v1/subscriptions Vincula um cliente (criado ou reaproveitado) ao plano
GET /v1/subscriptions Lista as assinaturas, paginadas
GET /v1/subscriptions/{subscriptionId} Uma assinatura pelo id
GET /v1/subscriptions/{subscriptionId}/preview Dry-run da próxima fatura
POST /v1/subscriptions/{subscriptionId}/customize Condição negociada só para esta assinatura
POST /v1/subscriptions/{subscriptionId}/cancel Encerra, devolvendo o estado da fidelidade
Campo Tipo Descrição
id string (uuid) Identificador da assinatura
planId string (uuid) Plano atribuído
planVersion integer Versão do plano pinada na adesão — edições do plano base não mexem aqui
customized boolean true quando a assinatura roda uma versão própria
customer object Cliente final — ver abaixo
status enum active ou canceled — ver valores
chargeDay integer Dia do vencimento das faturas, 1 a 28
paymentMethod enum Método efetivo desta assinatura: card, pix ou boleto
paymentMethodSource enum De onde o método veio — ver valores
gatewayConnectionId uuid | null Conexão de gateway pela qual esta assinatura fatura — congelada na criação. null em assinatura anterior ao recurso (emite pelo comportamento antigo)
cycleCount integer Faturas já geradas para esta assinatura (incrementa na geração, não no pagamento)
commitmentCycles integer | null Fidelidade congelada na adesão — mudar o plano depois não mexe aqui. Só registro
nextDueDate string (date) Vencimento da próxima fatura, YYYY-MM-DD
startedAt datetime Quando a assinatura começou
canceledAt datetime | null Quando foi cancelada — null enquanto ativa

O objeto customer:

Campo Tipo Descrição
customer.id string (uuid) O identificador do cliente no motor
customer.externalRef string O identificador do cliente no seu sistema — é ele que devolvemos em webhooks e no endpoint de preço
customer.name string Nome do cliente final
customer.email string E-mail do cliente final
Valor Significado
active Faturando — uma fatura nova nasce a cada ciclo
canceled Encerrada — nenhuma fatura nova; a do ciclo corrente segue o fluxo
Valor Significado
plan_default Herdou o defaultPaymentMethod do plano
override Método foi sobrescrito na criação da assinatura
POST /v1/subscriptions

Cria (ou reaproveita, pelo externalRef) o cliente final e o vincula ao plano. A primeira fatura nasce no próximo ciclo de geração.

Headers:

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

Corpo:

Campo Tipo Obrigatório Descrição
planId uuid Sim Plano a atribuir — arquivado devolve 409
customer object Sim Cliente final, inline
customer.externalRef string Sim 1–120. O id do cliente no seu sistema — se já existe um cliente com este ref, ele é reusado
customer.name string Sim 1–200 caracteres
customer.email string Sim E-mail válido
customer.document string Não CPF/CNPJ (11–18 caracteres) — alguns gateways exigem para emitir
paymentMethod enum Não card, pix ou boleto — sobrescreve o padrão do plano só nesta assinatura. Omitido, herda o plano
chargeDay integer Não 1–28. Dia do vencimento. Omitido: o dia da criação, travado em 28 (criou dia 29/30/31 → vira 28)
firstChargeDate string Não YYYY-MM-DD, hoje ou futura — adia a primeira fatura. Sem chargeDay junto, o dia dela vira a âncora dos próximos ciclos
gatewayConnectionId uuid Depende Em qual gateway esta assinatura fatura — ver a regra
Janela do terminal
curl -X POST $MOTOR/subscriptions -H "$AUTH" -H "Idempotency-Key: sub-4512-1" \
-H "Content-Type: application/json" -d '{
"planId": "0199c1a8-…",
"customer": {
"externalRef": "cliente-4512",
"name": "Ana Beatriz Sales",
"email": "[email protected]"
},
"chargeDay": 10
}'

Resposta 201 — o objeto Assinatura:

{
"id": "0199c1b0-…",
"planId": "0199c1a8-…",
"planVersion": 1,
"customized": false,
"customer": {
"id": "",
"externalRef": "cliente-4512",
"name": "Ana Beatriz Sales",
"email": "[email protected]"
},
"status": "active",
"chargeDay": 10,
"paymentMethod": "card",
"paymentMethodSource": "plan_default",
"gatewayConnectionId": "0199d1…",
"cycleCount": 0,
"commitmentCycles": 2,
"nextDueDate": "2026-09-10",
"startedAt": "2026-08-29T18:20:11.000Z",
"canceledAt": null
}

A conexão de gateway é congelada na criação — é ela que emite todas as faturas desta assinatura:

  • Uma conexão emissível ativa: omita gatewayConnectionId — ela é usada sozinha.

  • Duas ou mais: o campo é obrigatório. Omitido, a resposta é 422 engine.gateway_choice_required, com as opções no meta:

    {
    "type": "engine.gateway_choice_required",
    "status": 422,
    "detail": "Esta conta tem mais de um gateway ativo — informe gatewayConnectionId na criação da assinatura. As opções estão em GET /v1/integration.",
    "meta": {
    "options": [
    { "connectionId": "0199d1…", "provider": "vindi" },
    { "connectionId": "0199d2…", "provider": "stripe" }
    ]
    }
    }
  • Os ids vivem em GET /v1/integration, no array gateways[] — junto com os métodos e o parcelamento que cada conexão emite.

  • Id inexistente ou de conexão inativa: 422 engine.gateway_connection_not_found.

  • Conexão que não emite cobrança (ex.: gateway só de Recuperação) não conta como opção nem força escolha.

Método e parcelamento são validados contra a conexão pinada — o mesmo vale para o customize.

Erros: 400 · 404 (plano não existe) · 409 (plano arquivado) · 422 (engine.gateway_choice_required, engine.gateway_connection_not_found, ou método/parcelamento que a conexão pinada não suporta).

GET /v1/subscriptions

Paginação padrão (page, pageSize). Resposta 200: { data: [Assinatura…], pagination }, com cliente, plano e ciclo em cada item.

GET /v1/subscriptions/{subscriptionId}

Resposta 200: o objeto Assinatura. Erros: 404.

GET /v1/subscriptions/{subscriptionId}/preview

Roda a mesma decisão pura da geração real — régua do ciclo seguinte, itens e parcelamento — e devolve a fatura simulada. Zero efeitos: nada é gravado, emitido ou consumido. Use para conferir régua e parcelas antes do primeiro ciclo, ou depois de um customize.

Resposta 200:

Campo Tipo Descrição
subscriptionId string (uuid) Assinatura simulada
cycle integer O ciclo que seria cobrado (o próximo)
dueDate string (date) Vencimento da fatura simulada
paymentMethod string Método efetivo: o override da assinatura ou o padrão do plano
installments integer | null Parcelas no cartão — null quando à vista ou método não-cartão
fixedTotalCents integer Soma dos itens de preço fixo, em centavos — itens variáveis entram como zero
hasPendingItems boolean true quando há item variável: o total final depende do seu endpoint de preço
items[] array Itens exatamente como a geração real os montaria

Cada elemento de items[]:

Campo Tipo Descrição
productId string (uuid) Produto do item
productName string Nome que iria para a fatura
pricingSource string table (preço fixo) ou endpoint (variável)
pricingStatus string priced (valor fechado) ou pending (aguarda o pull)
monthlyPriceCents integer | null Preço mensal de tabela — null em item variável
months integer Meses do período multiplicando o mensal
discountPercent number | null Desconto da régua que casa com este ciclo — null = sem desconto
amountCents integer | null Valor do item — null em item variável
note string | null Como o valor variável seria resolvido na geração real

Erros: 404 · 409 (assinatura cancelada não tem próxima fatura).

POST /v1/subscriptions/{subscriptionId}/customize

Clona o plano numa versão própria desta assinatura, com os termos informados, e re-pina só ela — o plano base e os demais assinantes não mudam. Vale a partir do próximo ciclo. Depois disso, customized: true.

O corpo é a condição completa (mesma semântica de criar plano, sem name):

Campo Tipo Obrigatório Descrição
periodMonths integer Sim 1, 3, 6 ou 12
defaultPaymentMethod enum Sim card, pix ou boleto
maxInstallments integer Não 1–12, padrão 1
commitmentCycles integer Não ≥ 1 — só registro
items[] array Sim Itens e réguas, com a mesma forma e regras de resolução do plano

Resposta 200: o objeto Assinatura já na versão própria. Erros: 404, e os mesmos 409/422 de validação de plano.

POST /v1/subscriptions/{subscriptionId}/cancel

Encerra de vez: a fatura do ciclo corrente segue o fluxo normal, nenhuma nova é gerada. Não há pausa nem reativação.

Resposta 200: o objeto Assinatura com status: "canceled" e canceledAt, mais o bloco commitment com o estado da fidelidade — o motor registra, não julga:

Campo Tipo Descrição
commitment.cycles integer | null Ciclos de fidelidade combinados na adesão — null = sem fidelidade
commitment.completed integer Ciclos efetivamente faturados até o cancelamento
commitment.fulfilled boolean true quando o compromisso foi cumprido — a tratativa de quebra é sua

Erros: 404 · 409 (já cancelada).