{"openapi":"3.0.0","paths":{"/health":{"get":{"description":"Responde enquanto o processo está vivo. É o que o load balancer usa para decidir reiniciar a task.","operationId":"HealthController_check","parameters":[],"responses":{"200":{"description":"Processo vivo","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"description":"Indicador estático de liveness","type":"string","enum":["ok"]}},"required":["status"]}}}}},"summary":"Liveness","tags":["health"]}},"/health/ready":{"get":{"description":"Verifica as dependências (banco). É o que decide se a task recebe tráfego — falha aqui tira a instância do balanceamento sem matá-la.","operationId":"HealthController_ready","parameters":[],"responses":{"200":{"description":"Pronta para receber tráfego","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"description":"Pronta para tráfego ou degradada","type":"string","enum":["ok","degraded"]},"checks":{"description":"Resultado de cada dependência verificada","type":"object","properties":{"database":{"description":"Conexão com o Postgres respondendo","type":"boolean"}},"required":["database"]}},"required":["status","checks"]}}}},"503":{"description":"Degradada, não deve receber tráfego","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"description":"Pronta para tráfego ou degradada","type":"string","enum":["ok","degraded"]},"checks":{"description":"Resultado de cada dependência verificada","type":"object","properties":{"database":{"description":"Conexão com o Postgres respondendo","type":"boolean"}},"required":["database"]}},"required":["status","checks"]}}}}},"summary":"Readiness","tags":["health"]}},"/v1/products":{"post":{"description":"Cria um produto do catálogo com preço MENSAL fixo ou variável via endpoint. O tipo de preço é imutável depois de criado.","operationId":"ProductsController_create","parameters":[{"name":"Idempotency-Key","in":"header","description":"Chave única sua — repetir devolve a mesma resposta","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"Nome do produto — é o que o assinante lê na fatura","type":"string","minLength":1,"maxLength":120},"description":{"description":"Descrição interna, opcional","type":"string","maxLength":500,"nullable":true},"externalRef":{"description":"Seu identificador para este produto — devolvido nos webhooks e no endpoint de preço","type":"string","minLength":1,"maxLength":120,"nullable":true},"pricing":{"description":"Como o preço deste produto é definido. O modo é imutável depois de criado","oneOf":[{"type":"object","properties":{"mode":{"description":"Preço fixo: o valor mensal está na tabela","type":"string","enum":["fixed"]},"monthlyPriceCents":{"description":"Preço MENSAL em centavos — o período do plano multiplica","type":"integer","minimum":0,"exclusiveMinimum":false}},"required":["mode","monthlyPriceCents"]},{"type":"object","properties":{"mode":{"description":"Preço variável: o motor pergunta o valor ao seu endpoint a cada ciclo","type":"string","enum":["endpoint"]},"url":{"description":"URL https do seu endpoint de preço","type":"string","format":"uri"},"capCents":{"description":"Teto por cobrança em centavos — resposta acima disso é tratada como falha","type":"integer","minimum":0,"exclusiveMinimum":true}},"required":["mode","url","capCents"]}]}},"required":["name","pricing"]}}}},"responses":{"201":{"description":"Produto criado","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"description":"Identificador do produto","type":"string","format":"uuid"},"name":{"description":"Nome do produto","type":"string"},"description":{"description":"Descrição interna","type":"string","nullable":true},"externalRef":{"description":"Seu identificador para este produto","type":"string","nullable":true},"pricingMode":{"description":"fixed (preço mensal na tabela) ou endpoint (seu sistema responde)","type":"string","enum":["fixed","endpoint"],"x-enumNames":["fixed","endpoint"]},"monthlyPriceCents":{"description":"Valor em centavos, sempre inteiro (24990 = R$ 249,90)","type":"integer","minimum":0,"exclusiveMinimum":false,"nullable":true},"endpointUrl":{"description":"URL do endpoint de preço — nulo em produto fixo","type":"string","nullable":true},"capCents":{"description":"Teto por cobrança em centavos — nulo em produto fixo","type":"integer","nullable":true},"archivedAt":{"description":"Quando foi arquivado — arquivado não entra em plano novo","type":"string","format":"date-time","nullable":true},"createdAt":{"description":"Quando foi criado","type":"string","format":"date-time"}},"required":["id","name","description","externalRef","pricingMode","monthlyPriceCents","endpointUrl","capCents","archivedAt","createdAt"]}}}},"400":{"description":"Payload inválido ou Idempotency-Key ausente","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}}},"security":[{"bearer":[]}],"summary":"Criar produto","tags":["Produtos"]},"get":{"description":"Catálogo completo, paginado, mais recentes primeiro.","operationId":"ProductsController_list","parameters":[{"name":"page","required":false,"in":"query","description":"Página, começando em 1","schema":{"description":"Página, começando em 1","type":"integer","minimum":1,"exclusiveMinimum":false,"default":1}},{"name":"pageSize","required":false,"in":"query","description":"Itens por página (máximo 100)","schema":{"description":"Itens por página (máximo 100)","type":"integer","minimum":1,"exclusiveMinimum":false,"maximum":100,"exclusiveMaximum":false,"default":25}}],"responses":{"200":{"description":"Página de produtos","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"description":"Records of the current page","type":"array","items":{"type":"object","properties":{"id":{"description":"Identificador do produto","type":"string","format":"uuid"},"name":{"description":"Nome do produto","type":"string"},"description":{"description":"Descrição interna","type":"string","nullable":true},"externalRef":{"description":"Seu identificador para este produto","type":"string","nullable":true},"pricingMode":{"description":"fixed (preço mensal na tabela) ou endpoint (seu sistema responde)","type":"string","enum":["fixed","endpoint"],"x-enumNames":["fixed","endpoint"]},"monthlyPriceCents":{"description":"Valor em centavos, sempre inteiro (24990 = R$ 249,90)","type":"integer","minimum":0,"exclusiveMinimum":false,"nullable":true},"endpointUrl":{"description":"URL do endpoint de preço — nulo em produto fixo","type":"string","nullable":true},"capCents":{"description":"Teto por cobrança em centavos — nulo em produto fixo","type":"integer","nullable":true},"archivedAt":{"description":"Quando foi arquivado — arquivado não entra em plano novo","type":"string","format":"date-time","nullable":true},"createdAt":{"description":"Quando foi criado","type":"string","format":"date-time"}},"required":["id","name","description","externalRef","pricingMode","monthlyPriceCents","endpointUrl","capCents","archivedAt","createdAt"]}},"pagination":{"type":"object","properties":{"page":{"description":"Current page (1-based)","type":"integer"},"pageSize":{"description":"Records per page","type":"integer"},"totalRecords":{"description":"Total records matching the filters","type":"integer"},"totalPages":{"description":"Total pages for the current pageSize","type":"integer"}},"required":["page","pageSize","totalRecords","totalPages"]}},"required":["data","pagination"]}}}}},"security":[{"bearer":[]}],"summary":"Listar produtos","tags":["Produtos"]}},"/v1/products/{productId}":{"get":{"description":"Um produto do catálogo pelo id.","operationId":"ProductsController_get","parameters":[{"name":"productId","required":true,"in":"path","description":"Id do produto","schema":{"type":"string"}}],"responses":{"200":{"description":"Produto","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"description":"Identificador do produto","type":"string","format":"uuid"},"name":{"description":"Nome do produto","type":"string"},"description":{"description":"Descrição interna","type":"string","nullable":true},"externalRef":{"description":"Seu identificador para este produto","type":"string","nullable":true},"pricingMode":{"description":"fixed (preço mensal na tabela) ou endpoint (seu sistema responde)","type":"string","enum":["fixed","endpoint"],"x-enumNames":["fixed","endpoint"]},"monthlyPriceCents":{"description":"Valor em centavos, sempre inteiro (24990 = R$ 249,90)","type":"integer","minimum":0,"exclusiveMinimum":false,"nullable":true},"endpointUrl":{"description":"URL do endpoint de preço — nulo em produto fixo","type":"string","nullable":true},"capCents":{"description":"Teto por cobrança em centavos — nulo em produto fixo","type":"integer","nullable":true},"archivedAt":{"description":"Quando foi arquivado — arquivado não entra em plano novo","type":"string","format":"date-time","nullable":true},"createdAt":{"description":"Quando foi criado","type":"string","format":"date-time"}},"required":["id","name","description","externalRef","pricingMode","monthlyPriceCents","endpointUrl","capCents","archivedAt","createdAt"]}}}},"404":{"description":"Produto não encontrado","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}}},"security":[{"bearer":[]}],"summary":"Detalhar produto","tags":["Produtos"]},"patch":{"description":"Preço novo vale a partir das próximas faturas. Campos do outro modo de preço são recusados com 409.","operationId":"ProductsController_patch","parameters":[{"name":"productId","required":true,"in":"path","description":"Id do produto","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"Novo nome","type":"string","minLength":1,"maxLength":120},"description":{"description":"Nova descrição","type":"string","maxLength":500,"nullable":true},"monthlyPriceCents":{"description":"Valor em centavos, sempre inteiro (24990 = R$ 249,90)","type":"integer","minimum":0,"exclusiveMinimum":false},"endpointUrl":{"description":"Nova URL do endpoint (só produto variável)","type":"string","format":"uri"},"capCents":{"description":"Novo teto por cobrança (só produto variável)","type":"integer","minimum":0,"exclusiveMinimum":true}}}}}},"responses":{"200":{"description":"Produto atualizado","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"description":"Identificador do produto","type":"string","format":"uuid"},"name":{"description":"Nome do produto","type":"string"},"description":{"description":"Descrição interna","type":"string","nullable":true},"externalRef":{"description":"Seu identificador para este produto","type":"string","nullable":true},"pricingMode":{"description":"fixed (preço mensal na tabela) ou endpoint (seu sistema responde)","type":"string","enum":["fixed","endpoint"],"x-enumNames":["fixed","endpoint"]},"monthlyPriceCents":{"description":"Valor em centavos, sempre inteiro (24990 = R$ 249,90)","type":"integer","minimum":0,"exclusiveMinimum":false,"nullable":true},"endpointUrl":{"description":"URL do endpoint de preço — nulo em produto fixo","type":"string","nullable":true},"capCents":{"description":"Teto por cobrança em centavos — nulo em produto fixo","type":"integer","nullable":true},"archivedAt":{"description":"Quando foi arquivado — arquivado não entra em plano novo","type":"string","format":"date-time","nullable":true},"createdAt":{"description":"Quando foi criado","type":"string","format":"date-time"}},"required":["id","name","description","externalRef","pricingMode","monthlyPriceCents","endpointUrl","capCents","archivedAt","createdAt"]}}}},"409":{"description":"Tentativa de mexer no modo de preço, que é imutável","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}}},"security":[{"bearer":[]}],"summary":"Editar produto","tags":["Produtos"]}},"/v1/products/{productId}/archive":{"post":{"description":"Bloqueia planos novos com este produto; assinaturas existentes seguem faturando. Produto nunca é apagado.","operationId":"ProductsController_archive","parameters":[{"name":"productId","required":true,"in":"path","description":"Id do produto","schema":{"type":"string"}}],"responses":{"200":{"description":"Produto arquivado","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"description":"Identificador do produto","type":"string","format":"uuid"},"name":{"description":"Nome do produto","type":"string"},"description":{"description":"Descrição interna","type":"string","nullable":true},"externalRef":{"description":"Seu identificador para este produto","type":"string","nullable":true},"pricingMode":{"description":"fixed (preço mensal na tabela) ou endpoint (seu sistema responde)","type":"string","enum":["fixed","endpoint"],"x-enumNames":["fixed","endpoint"]},"monthlyPriceCents":{"description":"Valor em centavos, sempre inteiro (24990 = R$ 249,90)","type":"integer","minimum":0,"exclusiveMinimum":false,"nullable":true},"endpointUrl":{"description":"URL do endpoint de preço — nulo em produto fixo","type":"string","nullable":true},"capCents":{"description":"Teto por cobrança em centavos — nulo em produto fixo","type":"integer","nullable":true},"archivedAt":{"description":"Quando foi arquivado — arquivado não entra em plano novo","type":"string","format":"date-time","nullable":true},"createdAt":{"description":"Quando foi criado","type":"string","format":"date-time"}},"required":["id","name","description","externalRef","pricingMode","monthlyPriceCents","endpointUrl","capCents","archivedAt","createdAt"]}}}},"404":{"description":"Produto não encontrado","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}}},"security":[{"bearer":[]}],"summary":"Arquivar produto","tags":["Produtos"]}},"/v1/products/{productId}/unarchive":{"post":{"description":"Volta a aceitar o produto em planos novos.","operationId":"ProductsController_unarchive","parameters":[{"name":"productId","required":true,"in":"path","description":"Id do produto","schema":{"type":"string"}}],"responses":{"200":{"description":"Produto reativado","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"description":"Identificador do produto","type":"string","format":"uuid"},"name":{"description":"Nome do produto","type":"string"},"description":{"description":"Descrição interna","type":"string","nullable":true},"externalRef":{"description":"Seu identificador para este produto","type":"string","nullable":true},"pricingMode":{"description":"fixed (preço mensal na tabela) ou endpoint (seu sistema responde)","type":"string","enum":["fixed","endpoint"],"x-enumNames":["fixed","endpoint"]},"monthlyPriceCents":{"description":"Valor em centavos, sempre inteiro (24990 = R$ 249,90)","type":"integer","minimum":0,"exclusiveMinimum":false,"nullable":true},"endpointUrl":{"description":"URL do endpoint de preço — nulo em produto fixo","type":"string","nullable":true},"capCents":{"description":"Teto por cobrança em centavos — nulo em produto fixo","type":"integer","nullable":true},"archivedAt":{"description":"Quando foi arquivado — arquivado não entra em plano novo","type":"string","format":"date-time","nullable":true},"createdAt":{"description":"Quando foi criado","type":"string","format":"date-time"}},"required":["id","name","description","externalRef","pricingMode","monthlyPriceCents","endpointUrl","capCents","archivedAt","createdAt"]}}}},"404":{"description":"Produto não encontrado","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}}},"security":[{"bearer":[]}],"summary":"Reativar produto","tags":["Produtos"]}},"/v1/products/{productId}/pricing-test":{"post":{"description":"Dispara uma chamada real com mode: test e o payload de exemplo, e devolve o resultado cru — status, latência e validação do formato. Ninguém descobre integração quebrada na primeira fatura.","operationId":"ProductsController_pricingTest","parameters":[{"name":"productId","required":true,"in":"path","description":"Id do produto de preço variável","schema":{"type":"string"}}],"responses":{"200":{"description":"Resultado cru da chamada de teste","content":{"application/json":{"schema":{"type":"object","properties":{"outcome":{"description":"Resultado da chamada de teste","type":"string","enum":["ok","skip","timeout","http_error","invalid_response","over_cap"]},"amountCents":{"description":"Valor devolvido, quando ok","type":"integer","nullable":true},"description":{"description":"Descrição devolvida","type":"string","nullable":true},"responseStatus":{"description":"HTTP status","type":"integer","nullable":true},"latencyMs":{"description":"Latência","type":"integer"},"responseBody":{"description":"Corpo cru da resposta","type":"string","nullable":true}},"required":["outcome","amountCents","description","responseStatus","latencyMs","responseBody"]}}}},"404":{"description":"Produto não encontrado","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}}},"security":[{"bearer":[]}],"summary":"Testar o endpoint de preço","tags":["Produtos"]}},"/v1/plans":{"post":{"description":"A oferta completa num POST: período, método padrão, parcelamento máximo (independente do período), fidelidade e os produtos com suas réguas de desconto por faixa de ciclo — a primeira regra que casa com o ciclo ganha.","operationId":"PlansController_create","parameters":[{"name":"Idempotency-Key","in":"header","description":"Chave única sua — repetir devolve a mesma resposta","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"Nome do plano","type":"string","minLength":1,"maxLength":120},"periodMonths":{"description":"Meses por ciclo (1, 3, 6, 12) — cada ciclo cobra período × o mensal de cada produto","oneOf":[{"type":"number","minimum":1,"maximum":1},{"type":"number","minimum":3,"maximum":3},{"type":"number","minimum":6,"maximum":6},{"type":"number","minimum":12,"maximum":12}]},"defaultPaymentMethod":{"description":"Método padrão de quem assinar — cada assinatura pode sobrescrever","type":"string","enum":["card","pix","boleto"],"x-enumNames":["card","pix","boleto"]},"maxInstallments":{"description":"Parcelamento máximo no cartão (1 = à vista). Independente do período: semestral à vista é válido","type":"integer","minimum":1,"exclusiveMinimum":false,"maximum":12,"exclusiveMaximum":false,"default":1},"expirationDays":{"description":"Dias entre a geração (chargeDay) e o vencimento da cobrança no gateway — vale para cartão, boleto e pix","type":"integer","minimum":0,"exclusiveMinimum":false,"maximum":90,"exclusiveMaximum":false,"default":0},"commitmentCycles":{"description":"Fidelidade em ciclos — só registro; a tratativa de quebra é do lojista","type":"integer","minimum":1,"exclusiveMinimum":false,"nullable":true},"items":{"description":"Produtos do plano","type":"array","minItems":1,"maxItems":20,"items":{"type":"object","properties":{"productId":{"description":"Produto do catálogo","type":"string","format":"uuid"},"discounts":{"description":"Régua de desconto deste produto. Quando duas regras pegam o mesmo ciclo, a primeira da lista ganha","type":"array","maxItems":20,"items":{"type":"object","properties":{"percent":{"description":"Percentual de desconto (1 a 100)","type":"integer","minimum":1,"exclusiveMinimum":false,"maximum":100,"exclusiveMaximum":false},"fromCycle":{"description":"Primeiro ciclo em que a regra vale (ciclo = fatura)","type":"integer","minimum":1,"exclusiveMinimum":false},"toCycle":{"description":"Último ciclo em que a regra vale — omita para desconto sem fim","type":"integer","minimum":1,"exclusiveMinimum":false,"nullable":true}},"required":["percent","fromCycle"]},"default":[]}},"required":["productId"]}}},"required":["name","periodMonths","defaultPaymentMethod","items"]}}}},"responses":{"201":{"description":"Plano criado","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"description":"Identificador do plano","type":"string","format":"uuid"},"name":{"description":"Nome do plano","type":"string"},"version":{"description":"Versão vigente da oferta — toda edição cria uma versão nova; assinantes ficam pinados na deles","type":"integer"},"periodMonths":{"description":"Meses por ciclo","type":"integer"},"defaultPaymentMethod":{"description":"Método de pagamento: card, pix ou boleto. Pix e boleto são sempre à vista","type":"string","enum":["card","pix","boleto"],"x-enumNames":["card","pix","boleto"]},"maxInstallments":{"description":"Parcelamento máximo no cartão (1 = à vista)","type":"integer"},"expirationDays":{"description":"Dias entre a geração (chargeDay) e o vencimento da cobrança no gateway","type":"integer"},"commitmentCycles":{"description":"Fidelidade em ciclos — só registro","type":"integer","nullable":true},"archivedAt":{"description":"Quando foi arquivado — arquivado não aceita assinatura nova","type":"string","format":"date-time","nullable":true},"createdAt":{"description":"Quando foi criado","type":"string","format":"date-time"},"items":{"description":"Produtos e réguas de desconto do plano","type":"array","items":{"type":"object","properties":{"productId":{"description":"Produto do catálogo","type":"string","format":"uuid"},"position":{"description":"Ordem do item no plano","type":"integer"},"discounts":{"description":"Régua de desconto do produto, na ordem de prioridade","type":"array","items":{"type":"object","properties":{"percent":{"description":"Percentual de desconto (1 a 100)","type":"integer","minimum":1,"exclusiveMinimum":false,"maximum":100,"exclusiveMaximum":false},"fromCycle":{"description":"Primeiro ciclo em que a regra vale (ciclo = fatura)","type":"integer","minimum":1,"exclusiveMinimum":false},"toCycle":{"description":"Último ciclo em que a regra vale — omita para desconto sem fim","type":"integer","minimum":1,"exclusiveMinimum":false,"nullable":true}},"required":["percent","fromCycle"]}}},"required":["productId","position","discounts"]}}},"required":["id","name","version","periodMonths","defaultPaymentMethod","maxInstallments","expirationDays","commitmentCycles","archivedAt","createdAt","items"]}}}},"409":{"description":"Produto arquivado no plano","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}},"422":{"description":"Desconto em produto variável — o valor já vem do seu endpoint","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}}},"security":[{"bearer":[]}],"summary":"Criar plano","tags":["Planos"]},"get":{"description":"As ofertas, paginadas, mais recentes primeiro.","operationId":"PlansController_list","parameters":[{"name":"page","required":false,"in":"query","description":"Página, começando em 1","schema":{"description":"Página, começando em 1","type":"integer","minimum":1,"exclusiveMinimum":false,"default":1}},{"name":"pageSize","required":false,"in":"query","description":"Itens por página (máximo 100)","schema":{"description":"Itens por página (máximo 100)","type":"integer","minimum":1,"exclusiveMinimum":false,"maximum":100,"exclusiveMaximum":false,"default":25}}],"responses":{"200":{"description":"Página de planos","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"description":"Records of the current page","type":"array","items":{"type":"object","properties":{"id":{"description":"Identificador do plano","type":"string","format":"uuid"},"name":{"description":"Nome do plano","type":"string"},"version":{"description":"Versão vigente da oferta — toda edição cria uma versão nova; assinantes ficam pinados na deles","type":"integer"},"periodMonths":{"description":"Meses por ciclo","type":"integer"},"defaultPaymentMethod":{"description":"Método de pagamento: card, pix ou boleto. Pix e boleto são sempre à vista","type":"string","enum":["card","pix","boleto"],"x-enumNames":["card","pix","boleto"]},"maxInstallments":{"description":"Parcelamento máximo no cartão (1 = à vista)","type":"integer"},"expirationDays":{"description":"Dias entre a geração (chargeDay) e o vencimento da cobrança no gateway","type":"integer"},"commitmentCycles":{"description":"Fidelidade em ciclos — só registro","type":"integer","nullable":true},"archivedAt":{"description":"Quando foi arquivado — arquivado não aceita assinatura nova","type":"string","format":"date-time","nullable":true},"createdAt":{"description":"Quando foi criado","type":"string","format":"date-time"}},"required":["id","name","version","periodMonths","defaultPaymentMethod","maxInstallments","expirationDays","commitmentCycles","archivedAt","createdAt"]}},"pagination":{"type":"object","properties":{"page":{"description":"Current page (1-based)","type":"integer"},"pageSize":{"description":"Records per page","type":"integer"},"totalRecords":{"description":"Total records matching the filters","type":"integer"},"totalPages":{"description":"Total pages for the current pageSize","type":"integer"}},"required":["page","pageSize","totalRecords","totalPages"]}},"required":["data","pagination"]}}}}},"security":[{"bearer":[]}],"summary":"Listar planos","tags":["Planos"]}},"/v1/plans/{planId}":{"put":{"description":"A oferta completa de novo: cria a próxima versão base. Assinantes existentes seguem pinados na versão deles — só quem assinar daqui em diante pega a nova. Propagar para quem já assina é uma operação separada e explícita.","operationId":"PlansController_releaseNewVersion","parameters":[{"name":"planId","required":true,"in":"path","description":"Id do plano","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"Nome do plano","type":"string","minLength":1,"maxLength":120},"periodMonths":{"description":"Meses por ciclo (1, 3, 6, 12) — cada ciclo cobra período × o mensal de cada produto","oneOf":[{"type":"number","minimum":1,"maximum":1},{"type":"number","minimum":3,"maximum":3},{"type":"number","minimum":6,"maximum":6},{"type":"number","minimum":12,"maximum":12}]},"defaultPaymentMethod":{"description":"Método padrão de quem assinar — cada assinatura pode sobrescrever","type":"string","enum":["card","pix","boleto"],"x-enumNames":["card","pix","boleto"]},"maxInstallments":{"description":"Parcelamento máximo no cartão (1 = à vista). Independente do período: semestral à vista é válido","type":"integer","minimum":1,"exclusiveMinimum":false,"maximum":12,"exclusiveMaximum":false,"default":1},"expirationDays":{"description":"Dias entre a geração (chargeDay) e o vencimento da cobrança no gateway — vale para cartão, boleto e pix","type":"integer","minimum":0,"exclusiveMinimum":false,"maximum":90,"exclusiveMaximum":false,"default":0},"commitmentCycles":{"description":"Fidelidade em ciclos — só registro; a tratativa de quebra é do lojista","type":"integer","minimum":1,"exclusiveMinimum":false,"nullable":true},"items":{"description":"Produtos do plano","type":"array","minItems":1,"maxItems":20,"items":{"type":"object","properties":{"productId":{"description":"Produto do catálogo","type":"string","format":"uuid"},"discounts":{"description":"Régua de desconto deste produto. Quando duas regras pegam o mesmo ciclo, a primeira da lista ganha","type":"array","maxItems":20,"items":{"type":"object","properties":{"percent":{"description":"Percentual de desconto (1 a 100)","type":"integer","minimum":1,"exclusiveMinimum":false,"maximum":100,"exclusiveMaximum":false},"fromCycle":{"description":"Primeiro ciclo em que a regra vale (ciclo = fatura)","type":"integer","minimum":1,"exclusiveMinimum":false},"toCycle":{"description":"Último ciclo em que a regra vale — omita para desconto sem fim","type":"integer","minimum":1,"exclusiveMinimum":false,"nullable":true}},"required":["percent","fromCycle"]},"default":[]}},"required":["productId"]}}},"required":["name","periodMonths","defaultPaymentMethod","items"]}}}},"responses":{"200":{"description":"Plano na nova versão","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"description":"Identificador do plano","type":"string","format":"uuid"},"name":{"description":"Nome do plano","type":"string"},"version":{"description":"Versão vigente da oferta — toda edição cria uma versão nova; assinantes ficam pinados na deles","type":"integer"},"periodMonths":{"description":"Meses por ciclo","type":"integer"},"defaultPaymentMethod":{"description":"Método de pagamento: card, pix ou boleto. Pix e boleto são sempre à vista","type":"string","enum":["card","pix","boleto"],"x-enumNames":["card","pix","boleto"]},"maxInstallments":{"description":"Parcelamento máximo no cartão (1 = à vista)","type":"integer"},"expirationDays":{"description":"Dias entre a geração (chargeDay) e o vencimento da cobrança no gateway","type":"integer"},"commitmentCycles":{"description":"Fidelidade em ciclos — só registro","type":"integer","nullable":true},"archivedAt":{"description":"Quando foi arquivado — arquivado não aceita assinatura nova","type":"string","format":"date-time","nullable":true},"createdAt":{"description":"Quando foi criado","type":"string","format":"date-time"},"items":{"description":"Produtos e réguas de desconto do plano","type":"array","items":{"type":"object","properties":{"productId":{"description":"Produto do catálogo","type":"string","format":"uuid"},"position":{"description":"Ordem do item no plano","type":"integer"},"discounts":{"description":"Régua de desconto do produto, na ordem de prioridade","type":"array","items":{"type":"object","properties":{"percent":{"description":"Percentual de desconto (1 a 100)","type":"integer","minimum":1,"exclusiveMinimum":false,"maximum":100,"exclusiveMaximum":false},"fromCycle":{"description":"Primeiro ciclo em que a regra vale (ciclo = fatura)","type":"integer","minimum":1,"exclusiveMinimum":false},"toCycle":{"description":"Último ciclo em que a regra vale — omita para desconto sem fim","type":"integer","minimum":1,"exclusiveMinimum":false,"nullable":true}},"required":["percent","fromCycle"]}}},"required":["productId","position","discounts"]}}},"required":["id","name","version","periodMonths","defaultPaymentMethod","maxInstallments","expirationDays","commitmentCycles","archivedAt","createdAt","items"]}}}},"404":{"description":"Plano não encontrado","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}}},"security":[{"bearer":[]}],"summary":"Publicar uma nova versão do plano","tags":["Planos"]},"get":{"description":"O plano com itens e réguas de desconto.","operationId":"PlansController_get","parameters":[{"name":"planId","required":true,"in":"path","description":"Id do plano","schema":{"type":"string"}}],"responses":{"200":{"description":"Plano","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"description":"Identificador do plano","type":"string","format":"uuid"},"name":{"description":"Nome do plano","type":"string"},"version":{"description":"Versão vigente da oferta — toda edição cria uma versão nova; assinantes ficam pinados na deles","type":"integer"},"periodMonths":{"description":"Meses por ciclo","type":"integer"},"defaultPaymentMethod":{"description":"Método de pagamento: card, pix ou boleto. Pix e boleto são sempre à vista","type":"string","enum":["card","pix","boleto"],"x-enumNames":["card","pix","boleto"]},"maxInstallments":{"description":"Parcelamento máximo no cartão (1 = à vista)","type":"integer"},"expirationDays":{"description":"Dias entre a geração (chargeDay) e o vencimento da cobrança no gateway","type":"integer"},"commitmentCycles":{"description":"Fidelidade em ciclos — só registro","type":"integer","nullable":true},"archivedAt":{"description":"Quando foi arquivado — arquivado não aceita assinatura nova","type":"string","format":"date-time","nullable":true},"createdAt":{"description":"Quando foi criado","type":"string","format":"date-time"},"items":{"description":"Produtos e réguas de desconto do plano","type":"array","items":{"type":"object","properties":{"productId":{"description":"Produto do catálogo","type":"string","format":"uuid"},"position":{"description":"Ordem do item no plano","type":"integer"},"discounts":{"description":"Régua de desconto do produto, na ordem de prioridade","type":"array","items":{"type":"object","properties":{"percent":{"description":"Percentual de desconto (1 a 100)","type":"integer","minimum":1,"exclusiveMinimum":false,"maximum":100,"exclusiveMaximum":false},"fromCycle":{"description":"Primeiro ciclo em que a regra vale (ciclo = fatura)","type":"integer","minimum":1,"exclusiveMinimum":false},"toCycle":{"description":"Último ciclo em que a regra vale — omita para desconto sem fim","type":"integer","minimum":1,"exclusiveMinimum":false,"nullable":true}},"required":["percent","fromCycle"]}}},"required":["productId","position","discounts"]}}},"required":["id","name","version","periodMonths","defaultPaymentMethod","maxInstallments","expirationDays","commitmentCycles","archivedAt","createdAt","items"]}}}},"404":{"description":"Plano não encontrado","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}}},"security":[{"bearer":[]}],"summary":"Detalhar plano","tags":["Planos"]}},"/v1/plans/{planId}/archive":{"post":{"description":"Sem assinaturas novas; as existentes seguem faturando.","operationId":"PlansController_archive","parameters":[{"name":"planId","required":true,"in":"path","description":"Id do plano","schema":{"type":"string"}}],"responses":{"200":{"description":"Plano arquivado","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"description":"Identificador do plano","type":"string","format":"uuid"},"name":{"description":"Nome do plano","type":"string"},"version":{"description":"Versão vigente da oferta — toda edição cria uma versão nova; assinantes ficam pinados na deles","type":"integer"},"periodMonths":{"description":"Meses por ciclo","type":"integer"},"defaultPaymentMethod":{"description":"Método de pagamento: card, pix ou boleto. Pix e boleto são sempre à vista","type":"string","enum":["card","pix","boleto"],"x-enumNames":["card","pix","boleto"]},"maxInstallments":{"description":"Parcelamento máximo no cartão (1 = à vista)","type":"integer"},"expirationDays":{"description":"Dias entre a geração (chargeDay) e o vencimento da cobrança no gateway","type":"integer"},"commitmentCycles":{"description":"Fidelidade em ciclos — só registro","type":"integer","nullable":true},"archivedAt":{"description":"Quando foi arquivado — arquivado não aceita assinatura nova","type":"string","format":"date-time","nullable":true},"createdAt":{"description":"Quando foi criado","type":"string","format":"date-time"}},"required":["id","name","version","periodMonths","defaultPaymentMethod","maxInstallments","expirationDays","commitmentCycles","archivedAt","createdAt"]}}}},"404":{"description":"Plano não encontrado","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}}},"security":[{"bearer":[]}],"summary":"Arquivar plano","tags":["Planos"]}},"/v1/subscriptions":{"post":{"description":"Cria (ou reaproveita, pelo externalRef) o cliente final e o vincula ao plano. chargeDay omitido assume o dia da criação travado em 28; paymentMethod omitido herda o padrão do plano. A conexão de gateway é congelada aqui: com uma ativa ela é usada, com mais de uma o gatewayConnectionId é obrigatório. A primeira fatura nasce no próximo ciclo de geração.","operationId":"SubscriptionsController_create","parameters":[{"name":"Idempotency-Key","in":"header","description":"Chave única sua — repetir devolve a mesma resposta","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"planId":{"description":"Plano a atribuir","type":"string","format":"uuid"},"customer":{"description":"Cliente final — criado ou reaproveitado pelo externalRef","type":"object","properties":{"externalRef":{"description":"O identificador do cliente NO SEU SISTEMA — é ele que devolvemos em webhooks e no endpoint de preço","type":"string","minLength":1,"maxLength":120},"name":{"description":"Nome do cliente final","type":"string","minLength":1,"maxLength":200},"email":{"description":"E-mail do cliente final","type":"string","format":"email"},"document":{"description":"CPF/CNPJ — alguns gateways exigem para emitir","type":"string","minLength":11,"maxLength":18,"nullable":true},"phone":{"description":"Telefone com DDD, só dígitos (ex.: 5511999998888) — a pagar.me exige para emitir pix","type":"string","pattern":"^\\+?\\d{10,13}$","nullable":true},"address":{"description":"Endereço do cliente — a pagar.me exige para emitir boleto","type":"object","properties":{"line":{"description":"Logradouro com número (ex.: Rua das Videiras, 52)","type":"string","minLength":1,"maxLength":200},"zipCode":{"description":"CEP, 8 dígitos","type":"string","pattern":"^\\d{8}$"},"city":{"description":"Cidade","type":"string","minLength":1,"maxLength":100},"state":{"description":"UF (ex.: SP)","type":"string"}},"required":["line","zipCode","city","state"],"nullable":true}},"required":["externalRef","name","email"]},"paymentMethod":{"description":"Método de pagamento: card, pix ou boleto. Pix e boleto são sempre à vista","type":"string","enum":["card","pix","boleto"],"x-enumNames":["card","pix","boleto"]},"chargeDay":{"description":"Dia da cobrança (1 a 28). Omitido: o dia da criação, travado em 28","type":"integer","minimum":1,"exclusiveMinimum":false,"maximum":28,"exclusiveMaximum":false},"firstChargeDate":{"description":"Data da primeira cobrança (YYYY-MM-DD), hoje ou futura. Omitida: cobramos assim que a folga de geração permite. Sem chargeDay junto, o dia dela vira a âncora dos próximos ciclos","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"gatewayConnectionId":{"description":"Em qual gateway esta assinatura fatura — obrigatório quando a conta tem mais de uma conexão ativa. As opções estão em GET /v1/integration; com uma conexão só, omita que ela é usada","type":"string","format":"uuid"},"expirationDays":{"description":"Override do prazo de vencimento desta assinatura — omitido, herda o expirationDays do plano","type":"integer","minimum":0,"exclusiveMinimum":false,"maximum":90,"exclusiveMaximum":false}},"required":["planId","customer"]}}}},"responses":{"201":{"description":"Assinatura criada","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"description":"Identificador da assinatura","type":"string","format":"uuid"},"planId":{"description":"Plano atribuído","type":"string","format":"uuid"},"planVersion":{"description":"Versão do plano pinada na adesão — edições do plano base não mexem aqui","type":"integer"},"customized":{"description":"true quando esta assinatura roda uma versão própria (condição negociada)","type":"boolean"},"customer":{"description":"Cliente final","type":"object","properties":{"id":{"description":"Nosso identificador do cliente","type":"string","format":"uuid"},"externalRef":{"description":"O seu identificador do cliente","type":"string"},"name":{"description":"Nome","type":"string"},"email":{"description":"E-mail","type":"string"}},"required":["id","externalRef","name","email"]},"status":{"description":"Estado da assinatura","type":"string","enum":["active","canceled"]},"chargeDay":{"description":"Dia do vencimento das faturas (1 a 28)","type":"integer"},"paymentMethod":{"description":"Método efetivo desta assinatura","type":"string","enum":["card","pix","boleto"],"x-enumNames":["card","pix","boleto"]},"paymentMethodSource":{"description":"De onde veio o método: padrão do plano ou override na criação","type":"string","enum":["plan_default","override"]},"expirationDays":{"description":"Prazo de vencimento efetivo desta assinatura, em dias após a geração","type":"integer"},"expirationDaysSource":{"description":"De onde veio o prazo: padrão do plano ou override na criação","type":"string","enum":["plan_default","override"]},"gatewayConnectionId":{"description":"Conexão de gateway pela qual esta assinatura fatura — congelada na criação; nula se não havia gateway","type":"string","format":"uuid","nullable":true},"cycleCount":{"description":"Faturas já geradas para esta assinatura","type":"integer"},"commitmentCycles":{"description":"Fidelidade congelada na adesão — só registro","type":"integer","nullable":true},"nextDueDate":{"description":"Vencimento da próxima fatura (YYYY-MM-DD)","type":"string"},"startedAt":{"description":"Quando a assinatura começou","type":"string","format":"date-time"},"canceledAt":{"description":"Quando foi cancelada","type":"string","format":"date-time","nullable":true},"card":{"description":"Cartão guardado para esta assinatura na conexão atual — só bandeira, finais e validade. Nulo quando não há cartão ou quando a assinatura trocou de gateway depois do cadastro","type":"object","properties":{"brand":{"description":"Bandeira normalizada (visa, master, elo, amex, hipercard…); nula quando o gateway não informa","type":"string","nullable":true},"lastFourDigits":{"description":"Os quatro últimos dígitos do cartão — o resto nunca é guardado","type":"string"},"expiryMonth":{"description":"Mês de validade (1 a 12)","type":"integer"},"expiryYear":{"description":"Ano de validade, com quatro dígitos","type":"integer"}},"required":["brand","lastFourDigits","expiryMonth","expiryYear"],"nullable":true}},"required":["id","planId","planVersion","customized","customer","status","chargeDay","paymentMethod","paymentMethodSource","expirationDays","expirationDaysSource","gatewayConnectionId","cycleCount","commitmentCycles","nextDueDate","startedAt","canceledAt","card"]}}}},"404":{"description":"Plano não encontrado","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}},"409":{"description":"Plano arquivado não aceita assinatura nova","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}},"422":{"description":"Mais de um gateway ativo sem gatewayConnectionId, id de conexão inválido, ou método/parcelamento não suportado","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}}},"security":[{"bearer":[]}],"summary":"Vincular um cliente a um plano","tags":["Assinaturas"]},"get":{"description":"Assinaturas com cliente, plano e ciclo, paginadas.","operationId":"SubscriptionsController_list","parameters":[{"name":"page","required":false,"in":"query","description":"Página, começando em 1","schema":{"description":"Página, começando em 1","type":"integer","minimum":1,"exclusiveMinimum":false,"default":1}},{"name":"pageSize","required":false,"in":"query","description":"Itens por página (máximo 100)","schema":{"description":"Itens por página (máximo 100)","type":"integer","minimum":1,"exclusiveMinimum":false,"maximum":100,"exclusiveMaximum":false,"default":25}}],"responses":{"200":{"description":"Página de assinaturas","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"description":"Records of the current page","type":"array","items":{"type":"object","properties":{"id":{"description":"Identificador da assinatura","type":"string","format":"uuid"},"planId":{"description":"Plano atribuído","type":"string","format":"uuid"},"planVersion":{"description":"Versão do plano pinada na adesão — edições do plano base não mexem aqui","type":"integer"},"customized":{"description":"true quando esta assinatura roda uma versão própria (condição negociada)","type":"boolean"},"customer":{"description":"Cliente final","type":"object","properties":{"id":{"description":"Nosso identificador do cliente","type":"string","format":"uuid"},"externalRef":{"description":"O seu identificador do cliente","type":"string"},"name":{"description":"Nome","type":"string"},"email":{"description":"E-mail","type":"string"}},"required":["id","externalRef","name","email"]},"status":{"description":"Estado da assinatura","type":"string","enum":["active","canceled"]},"chargeDay":{"description":"Dia do vencimento das faturas (1 a 28)","type":"integer"},"paymentMethod":{"description":"Método efetivo desta assinatura","type":"string","enum":["card","pix","boleto"],"x-enumNames":["card","pix","boleto"]},"paymentMethodSource":{"description":"De onde veio o método: padrão do plano ou override na criação","type":"string","enum":["plan_default","override"]},"expirationDays":{"description":"Prazo de vencimento efetivo desta assinatura, em dias após a geração","type":"integer"},"expirationDaysSource":{"description":"De onde veio o prazo: padrão do plano ou override na criação","type":"string","enum":["plan_default","override"]},"gatewayConnectionId":{"description":"Conexão de gateway pela qual esta assinatura fatura — congelada na criação; nula se não havia gateway","type":"string","format":"uuid","nullable":true},"cycleCount":{"description":"Faturas já geradas para esta assinatura","type":"integer"},"commitmentCycles":{"description":"Fidelidade congelada na adesão — só registro","type":"integer","nullable":true},"nextDueDate":{"description":"Vencimento da próxima fatura (YYYY-MM-DD)","type":"string"},"startedAt":{"description":"Quando a assinatura começou","type":"string","format":"date-time"},"canceledAt":{"description":"Quando foi cancelada","type":"string","format":"date-time","nullable":true},"card":{"description":"Cartão guardado para esta assinatura na conexão atual — só bandeira, finais e validade. Nulo quando não há cartão ou quando a assinatura trocou de gateway depois do cadastro","type":"object","properties":{"brand":{"description":"Bandeira normalizada (visa, master, elo, amex, hipercard…); nula quando o gateway não informa","type":"string","nullable":true},"lastFourDigits":{"description":"Os quatro últimos dígitos do cartão — o resto nunca é guardado","type":"string"},"expiryMonth":{"description":"Mês de validade (1 a 12)","type":"integer"},"expiryYear":{"description":"Ano de validade, com quatro dígitos","type":"integer"}},"required":["brand","lastFourDigits","expiryMonth","expiryYear"],"nullable":true}},"required":["id","planId","planVersion","customized","customer","status","chargeDay","paymentMethod","paymentMethodSource","expirationDays","expirationDaysSource","gatewayConnectionId","cycleCount","commitmentCycles","nextDueDate","startedAt","canceledAt","card"]}},"pagination":{"type":"object","properties":{"page":{"description":"Current page (1-based)","type":"integer"},"pageSize":{"description":"Records per page","type":"integer"},"totalRecords":{"description":"Total records matching the filters","type":"integer"},"totalPages":{"description":"Total pages for the current pageSize","type":"integer"}},"required":["page","pageSize","totalRecords","totalPages"]}},"required":["data","pagination"]}}}}},"security":[{"bearer":[]}],"summary":"Listar assinaturas","tags":["Assinaturas"]}},"/v1/subscriptions/{subscriptionId}/preview":{"get":{"description":"Roda a mesma decisão pura da geração — régua do ciclo seguinte, itens e parcelamento — e devolve a fatura simulada. Zero efeitos: nada é gravado, emitido ou consumido. Item variável aparece como pending com a nota de como o pull resolveria o valor.","operationId":"SubscriptionsController_previewNext","parameters":[{"name":"subscriptionId","required":true,"in":"path","description":"Assinatura a simular","schema":{"type":"string"}}],"responses":{"200":{"description":"A próxima fatura, simulada","content":{"application/json":{"schema":{"type":"object","properties":{"subscriptionId":{"description":"Assinatura simulada","type":"string","format":"uuid"},"cycle":{"description":"O ciclo que seria cobrado (o próximo)","type":"integer"},"dueDate":{"description":"Data do ciclo da próxima fatura (YYYY-MM-DD) — quando ela será gerada","type":"string"},"paymentDueDate":{"description":"Vencimento real da cobrança (YYYY-MM-DD) — o que o gateway confirmou; sem confirmação, dueDate + expirationDays","type":"string"},"expirationDays":{"description":"Prazo de vencimento efetivo, em dias","type":"integer"},"paymentMethod":{"description":"Método efetivo: o override da assinatura ou o padrão do plano","type":"string"},"installments":{"description":"Parcelas no cartão — nulo quando à vista ou método não-cartão","type":"integer","nullable":true},"fixedTotalCents":{"description":"Soma dos itens de preço fixo, em centavos — itens variáveis entram como zero","type":"integer"},"hasPendingItems":{"description":"true quando há item variável: o total final depende do seu endpoint de preço","type":"boolean"},"items":{"description":"Itens exatamente como a geração real os montaria","type":"array","items":{"type":"object","properties":{"productId":{"description":"Produto do item","type":"string","format":"uuid"},"productName":{"description":"Nome que iria para a fatura","type":"string"},"pricingSource":{"description":"table (preço fixo) ou endpoint (variável)","type":"string"},"pricingStatus":{"description":"priced (valor fechado) ou pending (aguarda o pull)","type":"string"},"monthlyPriceCents":{"description":"Preço mensal de tabela — nulo em item variável","type":"integer","nullable":true},"months":{"description":"Meses do período multiplicando o mensal","type":"integer"},"discountPercent":{"description":"Desconto da régua que casa com este ciclo","type":"number","nullable":true},"amountCents":{"description":"Valor do item — nulo em item variável","type":"integer","nullable":true},"note":{"description":"Como o valor variável seria resolvido na geração real","type":"string","nullable":true}},"required":["productId","productName","pricingSource","pricingStatus","monthlyPriceCents","months","discountPercent","amountCents","note"]}}},"required":["subscriptionId","cycle","dueDate","paymentDueDate","expirationDays","paymentMethod","installments","fixedTotalCents","hasPendingItems","items"]}}}},"404":{"description":"Assinatura não encontrada","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}},"409":{"description":"Assinatura cancelada não tem próxima fatura","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"],"example":{"type":"engine.subscription_already_canceled","title":"Conflito","status":409}}}}}},"security":[{"bearer":[]}],"summary":"Simular a próxima fatura (dry-run)","tags":["Assinaturas"]}},"/v1/subscriptions/{subscriptionId}":{"get":{"description":"Uma assinatura pelo id.","operationId":"SubscriptionsController_get","parameters":[{"name":"subscriptionId","required":true,"in":"path","description":"Id da assinatura","schema":{"type":"string"}}],"responses":{"200":{"description":"Assinatura","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"description":"Identificador da assinatura","type":"string","format":"uuid"},"planId":{"description":"Plano atribuído","type":"string","format":"uuid"},"planVersion":{"description":"Versão do plano pinada na adesão — edições do plano base não mexem aqui","type":"integer"},"customized":{"description":"true quando esta assinatura roda uma versão própria (condição negociada)","type":"boolean"},"customer":{"description":"Cliente final","type":"object","properties":{"id":{"description":"Nosso identificador do cliente","type":"string","format":"uuid"},"externalRef":{"description":"O seu identificador do cliente","type":"string"},"name":{"description":"Nome","type":"string"},"email":{"description":"E-mail","type":"string"}},"required":["id","externalRef","name","email"]},"status":{"description":"Estado da assinatura","type":"string","enum":["active","canceled"]},"chargeDay":{"description":"Dia do vencimento das faturas (1 a 28)","type":"integer"},"paymentMethod":{"description":"Método efetivo desta assinatura","type":"string","enum":["card","pix","boleto"],"x-enumNames":["card","pix","boleto"]},"paymentMethodSource":{"description":"De onde veio o método: padrão do plano ou override na criação","type":"string","enum":["plan_default","override"]},"expirationDays":{"description":"Prazo de vencimento efetivo desta assinatura, em dias após a geração","type":"integer"},"expirationDaysSource":{"description":"De onde veio o prazo: padrão do plano ou override na criação","type":"string","enum":["plan_default","override"]},"gatewayConnectionId":{"description":"Conexão de gateway pela qual esta assinatura fatura — congelada na criação; nula se não havia gateway","type":"string","format":"uuid","nullable":true},"cycleCount":{"description":"Faturas já geradas para esta assinatura","type":"integer"},"commitmentCycles":{"description":"Fidelidade congelada na adesão — só registro","type":"integer","nullable":true},"nextDueDate":{"description":"Vencimento da próxima fatura (YYYY-MM-DD)","type":"string"},"startedAt":{"description":"Quando a assinatura começou","type":"string","format":"date-time"},"canceledAt":{"description":"Quando foi cancelada","type":"string","format":"date-time","nullable":true},"card":{"description":"Cartão guardado para esta assinatura na conexão atual — só bandeira, finais e validade. Nulo quando não há cartão ou quando a assinatura trocou de gateway depois do cadastro","type":"object","properties":{"brand":{"description":"Bandeira normalizada (visa, master, elo, amex, hipercard…); nula quando o gateway não informa","type":"string","nullable":true},"lastFourDigits":{"description":"Os quatro últimos dígitos do cartão — o resto nunca é guardado","type":"string"},"expiryMonth":{"description":"Mês de validade (1 a 12)","type":"integer"},"expiryYear":{"description":"Ano de validade, com quatro dígitos","type":"integer"}},"required":["brand","lastFourDigits","expiryMonth","expiryYear"],"nullable":true}},"required":["id","planId","planVersion","customized","customer","status","chargeDay","paymentMethod","paymentMethodSource","expirationDays","expirationDaysSource","gatewayConnectionId","cycleCount","commitmentCycles","nextDueDate","startedAt","canceledAt","card"]}}}},"404":{"description":"Assinatura não encontrada","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}}},"security":[{"bearer":[]}],"summary":"Detalhar assinatura","tags":["Assinaturas"]}},"/v1/subscriptions/{subscriptionId}/customize":{"post":{"description":"Clona o plano numa versão própria (custom) com os termos informados e re-pina só esta assinatura — o plano base e os demais assinantes não mudam. Vale a partir do próximo ciclo.","operationId":"SubscriptionsController_customize","parameters":[{"name":"subscriptionId","required":true,"in":"path","description":"Id da assinatura","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"periodMonths":{"description":"Meses por ciclo desta assinatura","oneOf":[{"type":"number","minimum":1,"maximum":1},{"type":"number","minimum":3,"maximum":3},{"type":"number","minimum":6,"maximum":6},{"type":"number","minimum":12,"maximum":12}]},"defaultPaymentMethod":{"description":"Método padrão desta versão","type":"string","enum":["card","pix","boleto"],"x-enumNames":["card","pix","boleto"]},"maxInstallments":{"description":"Parcelamento máximo no cartão (1 = à vista)","type":"integer","minimum":1,"exclusiveMinimum":false,"maximum":12,"exclusiveMaximum":false,"default":1},"expirationDays":{"description":"Dias entre a geração e o vencimento da cobrança desta assinatura","type":"integer","minimum":0,"exclusiveMinimum":false,"maximum":90,"exclusiveMaximum":false,"default":0},"commitmentCycles":{"description":"Fidelidade em ciclos — só registro","type":"integer","minimum":1,"exclusiveMinimum":false,"nullable":true},"items":{"description":"Itens e réguas desta condição individual","type":"array","minItems":1,"maxItems":20,"items":{"type":"object","properties":{"productId":{"description":"Produto do catálogo","type":"string","format":"uuid"},"discounts":{"description":"Régua de desconto deste produto. Quando duas regras pegam o mesmo ciclo, a primeira da lista ganha","type":"array","maxItems":20,"items":{"type":"object","properties":{"percent":{"description":"Percentual de desconto (1 a 100)","type":"integer","minimum":1,"exclusiveMinimum":false,"maximum":100,"exclusiveMaximum":false},"fromCycle":{"description":"Primeiro ciclo em que a regra vale (ciclo = fatura)","type":"integer","minimum":1,"exclusiveMinimum":false},"toCycle":{"description":"Último ciclo em que a regra vale — omita para desconto sem fim","type":"integer","minimum":1,"exclusiveMinimum":false,"nullable":true}},"required":["percent","fromCycle"]},"default":[]}},"required":["productId"]}}},"required":["periodMonths","defaultPaymentMethod","items"]}}}},"responses":{"200":{"description":"Assinatura na versão própria","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"description":"Identificador da assinatura","type":"string","format":"uuid"},"planId":{"description":"Plano atribuído","type":"string","format":"uuid"},"planVersion":{"description":"Versão do plano pinada na adesão — edições do plano base não mexem aqui","type":"integer"},"customized":{"description":"true quando esta assinatura roda uma versão própria (condição negociada)","type":"boolean"},"customer":{"description":"Cliente final","type":"object","properties":{"id":{"description":"Nosso identificador do cliente","type":"string","format":"uuid"},"externalRef":{"description":"O seu identificador do cliente","type":"string"},"name":{"description":"Nome","type":"string"},"email":{"description":"E-mail","type":"string"}},"required":["id","externalRef","name","email"]},"status":{"description":"Estado da assinatura","type":"string","enum":["active","canceled"]},"chargeDay":{"description":"Dia do vencimento das faturas (1 a 28)","type":"integer"},"paymentMethod":{"description":"Método efetivo desta assinatura","type":"string","enum":["card","pix","boleto"],"x-enumNames":["card","pix","boleto"]},"paymentMethodSource":{"description":"De onde veio o método: padrão do plano ou override na criação","type":"string","enum":["plan_default","override"]},"expirationDays":{"description":"Prazo de vencimento efetivo desta assinatura, em dias após a geração","type":"integer"},"expirationDaysSource":{"description":"De onde veio o prazo: padrão do plano ou override na criação","type":"string","enum":["plan_default","override"]},"gatewayConnectionId":{"description":"Conexão de gateway pela qual esta assinatura fatura — congelada na criação; nula se não havia gateway","type":"string","format":"uuid","nullable":true},"cycleCount":{"description":"Faturas já geradas para esta assinatura","type":"integer"},"commitmentCycles":{"description":"Fidelidade congelada na adesão — só registro","type":"integer","nullable":true},"nextDueDate":{"description":"Vencimento da próxima fatura (YYYY-MM-DD)","type":"string"},"startedAt":{"description":"Quando a assinatura começou","type":"string","format":"date-time"},"canceledAt":{"description":"Quando foi cancelada","type":"string","format":"date-time","nullable":true},"card":{"description":"Cartão guardado para esta assinatura na conexão atual — só bandeira, finais e validade. Nulo quando não há cartão ou quando a assinatura trocou de gateway depois do cadastro","type":"object","properties":{"brand":{"description":"Bandeira normalizada (visa, master, elo, amex, hipercard…); nula quando o gateway não informa","type":"string","nullable":true},"lastFourDigits":{"description":"Os quatro últimos dígitos do cartão — o resto nunca é guardado","type":"string"},"expiryMonth":{"description":"Mês de validade (1 a 12)","type":"integer"},"expiryYear":{"description":"Ano de validade, com quatro dígitos","type":"integer"}},"required":["brand","lastFourDigits","expiryMonth","expiryYear"],"nullable":true}},"required":["id","planId","planVersion","customized","customer","status","chargeDay","paymentMethod","paymentMethodSource","expirationDays","expirationDaysSource","gatewayConnectionId","cycleCount","commitmentCycles","nextDueDate","startedAt","canceledAt","card"]}}}},"404":{"description":"Assinatura não encontrada","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}}},"security":[{"bearer":[]}],"summary":"Condição individual para esta assinatura","tags":["Assinaturas"]}},"/v1/subscriptions/{subscriptionId}/gateway":{"post":{"description":"Re-pina a assinatura em outra conexão, valendo do próximo ciclo — faturas já emitidas ficam onde nasceram, ciclos e fidelidade seguem intactos. Sem externalCustomerId, criamos o cliente no gateway novo com os dados que guardamos; com ele, apontamos o espelho para o cliente importado pela portabilidade de cofre (caso cartão).","operationId":"SubscriptionsController_changeGateway","parameters":[{"name":"subscriptionId","required":true,"in":"path","description":"Id da assinatura","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"gatewayConnectionId":{"description":"Conexão nova — as opções estão em GET /v1/integration","type":"string","format":"uuid"},"externalCustomerId":{"description":"De-para opcional: o id deste cliente no gateway novo (cliente importado pela portabilidade de cofre)","type":"string","minLength":1,"maxLength":120}},"required":["gatewayConnectionId"]}}}},"responses":{"200":{"description":"Assinatura na conexão nova","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"description":"Identificador da assinatura","type":"string","format":"uuid"},"planId":{"description":"Plano atribuído","type":"string","format":"uuid"},"planVersion":{"description":"Versão do plano pinada na adesão — edições do plano base não mexem aqui","type":"integer"},"customized":{"description":"true quando esta assinatura roda uma versão própria (condição negociada)","type":"boolean"},"customer":{"description":"Cliente final","type":"object","properties":{"id":{"description":"Nosso identificador do cliente","type":"string","format":"uuid"},"externalRef":{"description":"O seu identificador do cliente","type":"string"},"name":{"description":"Nome","type":"string"},"email":{"description":"E-mail","type":"string"}},"required":["id","externalRef","name","email"]},"status":{"description":"Estado da assinatura","type":"string","enum":["active","canceled"]},"chargeDay":{"description":"Dia do vencimento das faturas (1 a 28)","type":"integer"},"paymentMethod":{"description":"Método efetivo desta assinatura","type":"string","enum":["card","pix","boleto"],"x-enumNames":["card","pix","boleto"]},"paymentMethodSource":{"description":"De onde veio o método: padrão do plano ou override na criação","type":"string","enum":["plan_default","override"]},"expirationDays":{"description":"Prazo de vencimento efetivo desta assinatura, em dias após a geração","type":"integer"},"expirationDaysSource":{"description":"De onde veio o prazo: padrão do plano ou override na criação","type":"string","enum":["plan_default","override"]},"gatewayConnectionId":{"description":"Conexão de gateway pela qual esta assinatura fatura — congelada na criação; nula se não havia gateway","type":"string","format":"uuid","nullable":true},"cycleCount":{"description":"Faturas já geradas para esta assinatura","type":"integer"},"commitmentCycles":{"description":"Fidelidade congelada na adesão — só registro","type":"integer","nullable":true},"nextDueDate":{"description":"Vencimento da próxima fatura (YYYY-MM-DD)","type":"string"},"startedAt":{"description":"Quando a assinatura começou","type":"string","format":"date-time"},"canceledAt":{"description":"Quando foi cancelada","type":"string","format":"date-time","nullable":true},"card":{"description":"Cartão guardado para esta assinatura na conexão atual — só bandeira, finais e validade. Nulo quando não há cartão ou quando a assinatura trocou de gateway depois do cadastro","type":"object","properties":{"brand":{"description":"Bandeira normalizada (visa, master, elo, amex, hipercard…); nula quando o gateway não informa","type":"string","nullable":true},"lastFourDigits":{"description":"Os quatro últimos dígitos do cartão — o resto nunca é guardado","type":"string"},"expiryMonth":{"description":"Mês de validade (1 a 12)","type":"integer"},"expiryYear":{"description":"Ano de validade, com quatro dígitos","type":"integer"}},"required":["brand","lastFourDigits","expiryMonth","expiryYear"],"nullable":true}},"required":["id","planId","planVersion","customized","customer","status","chargeDay","paymentMethod","paymentMethodSource","expirationDays","expirationDaysSource","gatewayConnectionId","cycleCount","commitmentCycles","nextDueDate","startedAt","canceledAt","card"]}}}},"404":{"description":"Assinatura não encontrada","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}},"409":{"description":"Assinatura cancelada não muda de gateway","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}},"422":{"description":"Conexão inexistente/inativa, ou método/parcelamento não suportado nela","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}}},"security":[{"bearer":[]}],"summary":"Trocar o gateway desta assinatura","tags":["Assinaturas"]}},"/v1/subscriptions/{subscriptionId}/cancel":{"post":{"description":"Encerra a assinatura e devolve o estado da fidelidade — o motor registra o compromisso, a tratativa de quebra é sua.","operationId":"SubscriptionsController_cancel","parameters":[{"name":"subscriptionId","required":true,"in":"path","description":"Id da assinatura","schema":{"type":"string"}}],"responses":{"200":{"description":"Assinatura cancelada, com o estado da fidelidade","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"description":"Identificador da assinatura","type":"string","format":"uuid"},"planId":{"description":"Plano atribuído","type":"string","format":"uuid"},"planVersion":{"description":"Versão do plano pinada na adesão — edições do plano base não mexem aqui","type":"integer"},"customized":{"description":"true quando esta assinatura roda uma versão própria (condição negociada)","type":"boolean"},"customer":{"description":"Cliente final","type":"object","properties":{"id":{"description":"Nosso identificador do cliente","type":"string","format":"uuid"},"externalRef":{"description":"O seu identificador do cliente","type":"string"},"name":{"description":"Nome","type":"string"},"email":{"description":"E-mail","type":"string"}},"required":["id","externalRef","name","email"]},"status":{"description":"Estado da assinatura","type":"string","enum":["active","canceled"]},"chargeDay":{"description":"Dia do vencimento das faturas (1 a 28)","type":"integer"},"paymentMethod":{"description":"Método efetivo desta assinatura","type":"string","enum":["card","pix","boleto"],"x-enumNames":["card","pix","boleto"]},"paymentMethodSource":{"description":"De onde veio o método: padrão do plano ou override na criação","type":"string","enum":["plan_default","override"]},"expirationDays":{"description":"Prazo de vencimento efetivo desta assinatura, em dias após a geração","type":"integer"},"expirationDaysSource":{"description":"De onde veio o prazo: padrão do plano ou override na criação","type":"string","enum":["plan_default","override"]},"gatewayConnectionId":{"description":"Conexão de gateway pela qual esta assinatura fatura — congelada na criação; nula se não havia gateway","type":"string","format":"uuid","nullable":true},"cycleCount":{"description":"Faturas já geradas para esta assinatura","type":"integer"},"commitmentCycles":{"description":"Fidelidade congelada na adesão — só registro","type":"integer","nullable":true},"nextDueDate":{"description":"Vencimento da próxima fatura (YYYY-MM-DD)","type":"string"},"startedAt":{"description":"Quando a assinatura começou","type":"string","format":"date-time"},"canceledAt":{"description":"Quando foi cancelada","type":"string","format":"date-time","nullable":true},"card":{"description":"Cartão guardado para esta assinatura na conexão atual — só bandeira, finais e validade. Nulo quando não há cartão ou quando a assinatura trocou de gateway depois do cadastro","type":"object","properties":{"brand":{"description":"Bandeira normalizada (visa, master, elo, amex, hipercard…); nula quando o gateway não informa","type":"string","nullable":true},"lastFourDigits":{"description":"Os quatro últimos dígitos do cartão — o resto nunca é guardado","type":"string"},"expiryMonth":{"description":"Mês de validade (1 a 12)","type":"integer"},"expiryYear":{"description":"Ano de validade, com quatro dígitos","type":"integer"}},"required":["brand","lastFourDigits","expiryMonth","expiryYear"],"nullable":true},"commitment":{"description":"Estado da fidelidade no cancelamento — o motor registra, não julga","type":"object","properties":{"cycles":{"description":"Ciclos de fidelidade combinados na adesão","type":"integer","nullable":true},"completed":{"description":"Ciclos efetivamente faturados até o cancelamento","type":"integer"},"fulfilled":{"description":"true quando o compromisso foi cumprido — a tratativa de quebra é sua","type":"boolean"}},"required":["cycles","completed","fulfilled"]}},"required":["id","planId","planVersion","customized","customer","status","chargeDay","paymentMethod","paymentMethodSource","expirationDays","expirationDaysSource","gatewayConnectionId","cycleCount","commitmentCycles","nextDueDate","startedAt","canceledAt","card","commitment"]}}}},"409":{"description":"Assinatura já cancelada","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}}},"security":[{"bearer":[]}],"summary":"Cancelar assinatura","tags":["Assinaturas"]}},"/v1/subscriptions/{subscriptionId}/card-capture-sessions":{"post":{"description":"Gera um link de uso único, válido por 15 minutos, para uma página hospedada pela Revtriever onde o pagador digita o cartão — o número nunca passa pelo seu servidor nem pelo seu front. Redirecione o pagador para captureUrl (não use iframe). O token aparece só nesta resposta (guardamos apenas o hash) e aceita até 3 tentativas. Com returnUrl, a página devolve o pagador para ela com ?card=saved ou ?card=canceled; a origem precisa estar cadastrada em PUT /v1/integration/card-capture-return-origins. Exige assinatura ativa numa conexão que guarda cartão pelo motor (hoje, Asaas). Com o cartão salvo, a assinatura passa a usar cartão a partir da próxima fatura gerada, cada fatura é cobrada no cartão no dia do vencimento e o evento subscription.card_saved é emitido.","operationId":"CardCaptureSessionsController_create","parameters":[{"name":"subscriptionId","required":true,"in":"path","description":"Assinatura que vai receber o cartão","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","description":"Chave única sua — repetir devolve o mesmo token","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"returnUrl":{"description":"URL https para onde a página devolve o pagador, acrescida de ?card=saved ou ?card=canceled. A origem precisa estar em PUT /v1/integration/card-capture-return-origins. Sem ela, a página só mostra a confirmação","type":"string","maxLength":2000}},"default":{}}}}},"responses":{"201":{"description":"Sessão aberta","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"description":"Token de uso único da sessão. Aparece só nesta resposta — guardamos apenas o hash. Entregue ao navegador do pagador","type":"string"},"expiresAt":{"description":"Até quando o token aceita o cartão","type":"string","format":"date-time"},"captureUrl":{"description":"Página da Revtriever onde o pagador digita o cartão. Redirecione o pagador para ela (não abra em iframe). Já contém o token — trate como segredo de curta duração e envie só ao titular do cartão","type":"string","format":"uri"},"returnUrl":{"description":"Para onde a página devolve o pagador ao terminar, com ?card=saved ou ?card=canceled","type":"string","nullable":true}},"required":["token","expiresAt","captureUrl","returnUrl"]}}}},"400":{"description":"Header Idempotency-Key ausente","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"],"example":{"type":"engine.idempotency_key_required","title":"EngineIdempotencyKeyRequiredError","status":400}}}}},"404":{"description":"Assinatura não encontrada nesta conta","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"],"example":{"type":"engine.subscription_not_found","title":"EngineSubscriptionNotFoundError","status":404,"meta":{"subscriptionId":"0199b2c4-7d1e-7a3b-9c2d-4e5f6a7b8c9d"}}}}}},"409":{"description":"Assinatura cancelada não recebe cartão","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"],"example":{"type":"engine.subscription_already_canceled","title":"EngineSubscriptionAlreadyCanceledError","status":409}}}}},"422":{"description":"A conexão da assinatura não guarda cartão pelo motor, ou a returnUrl não é https com origem cadastrada","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"],"example":{"type":"engine.card_tokenization_unsupported","title":"EngineCardTokenizationUnsupportedError","status":422,"meta":{"provider":"vindi"}}}}}}},"security":[{"bearer":[]}],"summary":"Abrir uma sessão de captura de cartão","tags":["Assinaturas"]}},"/public/card-capture/{token}":{"get":{"description":"Chamado pela página hospedada de cadastro de cartão, sem API key, para se apresentar: nome e marca da empresa, para onde voltar e até quando o link vale. Não gasta tentativa. Token desconhecido, vencido, já usado ou esgotado recebem a mesma resposta 410. Aceita CORS apenas das origens configuradas.","operationId":"PublicCardCaptureController_view","parameters":[{"name":"token","required":true,"in":"path","description":"Token da sessão de captura, o final de captureUrl","schema":{"type":"string"}}],"responses":{"200":{"description":"O que a página mostra","content":{"application/json":{"schema":{"type":"object","properties":{"merchant":{"description":"Quem está pedindo o cartão, para o pagador reconhecer a página","type":"object","properties":{"name":{"description":"Nome da empresa, o mesmo que assina os e-mails","type":"string"},"brandColor":{"description":"Cor da marca, quando cadastrada","type":"string","nullable":true},"logoUrl":{"description":"Logo da empresa, quando cadastrada","type":"string","nullable":true}},"required":["name","brandColor","logoUrl"]},"returnUrl":{"description":"Para onde voltar ao terminar; nulo quando o integrador não informou","type":"string","nullable":true},"expiresAt":{"description":"Até quando o link aceita o cartão","type":"string","format":"date-time"}},"required":["merchant","returnUrl","expiresAt"]}}}},"410":{"description":"Sessão indisponível: token desconhecido, vencido, já usado ou com as tentativas esgotadas","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"],"example":{"type":"engine.card_capture_unavailable","title":"EngineCardCaptureUnavailableError","status":410}}}}},"429":{"description":"Tentativas demais deste IP ou para este token — veja o header Retry-After","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"],"example":{"type":"engine.rate_limited","title":"EngineRateLimitedError","status":429,"meta":{"retryAfterSeconds":540}}}}}}},"summary":"Apresentar a página de cadastro de cartão","tags":["Captura de cartão"]},"post":{"description":"Chamado pelo navegador do pagador, sem API key: o token da sessão é a autorização. O cartão é tokenizado na conexão de gateway da assinatura e só o token do gateway fica guardado, cifrado; número e CVV nunca são gravados nem registrados. Sucesso consome a sessão e a assinatura passa a cobrar no cartão. Token desconhecido, vencido, já usado ou esgotado recebem a mesma resposta 410. Aceita CORS apenas das origens configuradas.","operationId":"PublicCardCaptureController_capture","parameters":[{"name":"token","required":true,"in":"path","description":"Token da sessão de captura, o final de captureUrl","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"card":{"description":"Dados do cartão — trafegam só até o gateway e nunca são guardados nem registrados","type":"object","properties":{"holderName":{"description":"Nome impresso no cartão","type":"string","minLength":2,"maxLength":100},"number":{"description":"Número do cartão; espaços e hífens são ignorados","type":"string"},"expiryMonth":{"description":"Mês de validade (1 a 12)","type":"integer","minimum":1,"exclusiveMinimum":false,"maximum":12,"exclusiveMaximum":false},"expiryYear":{"description":"Ano de validade com quatro dígitos","type":"integer","minimum":2000,"exclusiveMinimum":false,"maximum":2100,"exclusiveMaximum":false},"ccv":{"description":"Código de segurança (3 ou 4 dígitos)","type":"string","pattern":"^\\d{3,4}$"}},"required":["holderName","number","expiryMonth","expiryYear","ccv"]},"holder":{"description":"Dados do titular que o gateway exige para análise antifraude","type":"object","properties":{"name":{"description":"Nome do titular","type":"string","minLength":2,"maxLength":100},"email":{"description":"E-mail do titular","type":"string","format":"email","maxLength":200},"cpfCnpj":{"description":"CPF ou CNPJ (numérico ou alfanumérico) do titular; pontuação é ignorada e letras viram maiúsculas","type":"string"},"postalCode":{"description":"CEP do titular; o hífen é ignorado","type":"string"},"addressNumber":{"description":"Número do endereço do titular","type":"string","minLength":1,"maxLength":20},"phone":{"description":"Telefone do titular com DDD","type":"string"}},"required":["name","email","cpfCnpj","postalCode","addressNumber","phone"]}},"required":["card","holder"]}}}},"responses":{"200":{"description":"Cartão salvo na assinatura","content":{"application/json":{"schema":{"type":"object","properties":{"brand":{"description":"Bandeira normalizada (visa, master, elo, amex, hipercard…); nula quando o gateway não informa","type":"string","nullable":true},"lastFourDigits":{"description":"Os quatro últimos dígitos do cartão — o resto nunca é guardado","type":"string"},"expiryMonth":{"description":"Mês de validade (1 a 12)","type":"integer"},"expiryYear":{"description":"Ano de validade, com quatro dígitos","type":"integer"}},"required":["brand","lastFourDigits","expiryMonth","expiryYear"]}}}},"400":{"description":"Corpo inválido — os campos com problema vêm em meta, nunca os valores","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"],"example":{"type":"validation_failed","title":"ZodValidationException","status":400}}}}},"410":{"description":"Sessão indisponível: token desconhecido, vencido, já usado ou com as tentativas esgotadas","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"],"example":{"type":"engine.card_capture_unavailable","title":"EngineCardCaptureUnavailableError","status":410}}}}},"422":{"description":"O gateway não aceitou o cartão — a mensagem é genérica de propósito","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"],"example":{"type":"engine.card_declined","title":"EngineCardDeclinedError","status":422,"meta":{"attemptsRemaining":2}}}}}},"429":{"description":"Tentativas demais deste IP ou para este token — veja o header Retry-After","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"],"example":{"type":"engine.rate_limited","title":"EngineRateLimitedError","status":429,"meta":{"retryAfterSeconds":540}}}}}},"502":{"description":"O gateway não respondeu; a tentativa não é descontada","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"],"example":{"type":"engine.card_capture_gateway_unavailable","title":"EngineCardCaptureGatewayUnavailableError","status":502}}}}}},"summary":"Enviar o cartão do pagador","tags":["Captura de cartão"]}},"/v1/customers":{"get":{"description":"Clientes finais da conta, paginados, do mais recente ao mais antigo. Com externalRef, devolve só o cliente com aquele id do seu sistema — é o jeito de achar o id do motor sem guardá-lo.","operationId":"CustomersController_list","parameters":[{"name":"page","required":false,"in":"query","description":"Página, começando em 1","schema":{"description":"Página, começando em 1","type":"integer","minimum":1,"exclusiveMinimum":false,"default":1}},{"name":"pageSize","required":false,"in":"query","description":"Itens por página (máximo 100)","schema":{"description":"Itens por página (máximo 100)","type":"integer","minimum":1,"exclusiveMinimum":false,"maximum":100,"exclusiveMaximum":false,"default":25}},{"name":"externalRef","required":false,"in":"query","description":"Filtra pelo SEU id do cliente — casamento exato","schema":{"description":"Filtra pelo SEU id do cliente — casamento exato","type":"string","minLength":1,"maxLength":120}}],"responses":{"200":{"description":"Página de clientes","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"description":"Records of the current page","type":"array","items":{"type":"object","properties":{"id":{"description":"Identificador do cliente no motor","type":"string","format":"uuid"},"externalRef":{"description":"O identificador do cliente NO SEU SISTEMA — imutável, é a chave de reaproveitamento","type":"string"},"name":{"description":"Nome do cliente final","type":"string"},"email":{"description":"E-mail do cliente final — é para ele que vão avisos de fatura e a nota fiscal","type":"string"},"document":{"description":"CPF/CNPJ — null quando nunca informado","type":"string","nullable":true},"phone":{"description":"Telefone com DDD, só dígitos — null quando nunca informado","type":"string","nullable":true},"address":{"description":"Endereço — null quando nunca informado","type":"object","properties":{"line":{"description":"Logradouro com número (ex.: Rua das Videiras, 52)","type":"string","minLength":1,"maxLength":200},"zipCode":{"description":"CEP, 8 dígitos","type":"string","pattern":"^\\d{8}$"},"city":{"description":"Cidade","type":"string","minLength":1,"maxLength":100},"state":{"description":"UF (ex.: SP)","type":"string"}},"required":["line","zipCode","city","state"],"nullable":true},"createdAt":{"description":"Quando o cliente foi criado no motor","type":"string","format":"date-time"},"updatedAt":{"description":"Última alteração dos dados","type":"string","format":"date-time"}},"required":["id","externalRef","name","email","document","phone","address","createdAt","updatedAt"]}},"pagination":{"type":"object","properties":{"page":{"description":"Current page (1-based)","type":"integer"},"pageSize":{"description":"Records per page","type":"integer"},"totalRecords":{"description":"Total records matching the filters","type":"integer"},"totalPages":{"description":"Total pages for the current pageSize","type":"integer"}},"required":["page","pageSize","totalRecords","totalPages"]}},"required":["data","pagination"]}}}}},"security":[{"bearer":[]}],"summary":"Listar clientes","tags":["Clientes"]}},"/v1/customers/{customerId}":{"get":{"description":"Um cliente pelo id do motor, com documento, telefone e endereço.","operationId":"CustomersController_get","parameters":[{"name":"customerId","required":true,"in":"path","description":"Id do cliente no motor","schema":{"type":"string"}}],"responses":{"200":{"description":"Cliente","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"description":"Identificador do cliente no motor","type":"string","format":"uuid"},"externalRef":{"description":"O identificador do cliente NO SEU SISTEMA — imutável, é a chave de reaproveitamento","type":"string"},"name":{"description":"Nome do cliente final","type":"string"},"email":{"description":"E-mail do cliente final — é para ele que vão avisos de fatura e a nota fiscal","type":"string"},"document":{"description":"CPF/CNPJ — null quando nunca informado","type":"string","nullable":true},"phone":{"description":"Telefone com DDD, só dígitos — null quando nunca informado","type":"string","nullable":true},"address":{"description":"Endereço — null quando nunca informado","type":"object","properties":{"line":{"description":"Logradouro com número (ex.: Rua das Videiras, 52)","type":"string","minLength":1,"maxLength":200},"zipCode":{"description":"CEP, 8 dígitos","type":"string","pattern":"^\\d{8}$"},"city":{"description":"Cidade","type":"string","minLength":1,"maxLength":100},"state":{"description":"UF (ex.: SP)","type":"string"}},"required":["line","zipCode","city","state"],"nullable":true},"createdAt":{"description":"Quando o cliente foi criado no motor","type":"string","format":"date-time"},"updatedAt":{"description":"Última alteração dos dados","type":"string","format":"date-time"}},"required":["id","externalRef","name","email","document","phone","address","createdAt","updatedAt"]}}}},"404":{"description":"Cliente não encontrado","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}}},"security":[{"bearer":[]}],"summary":"Detalhar cliente","tags":["Clientes"]},"patch":{"description":"Altera nome, e-mail, CPF/CNPJ, telefone ou endereço — só os campos enviados mudam; null apaga. O externalRef é imutável. A alteração vale para as próximas faturas, notas fiscais e avisos, e é replicada ao cliente espelhado em cada gateway que aceita atualização (assíncrono).","operationId":"CustomersController_patch","parameters":[{"name":"customerId","required":true,"in":"path","description":"Id do cliente no motor","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"Nome do cliente final","type":"string","minLength":1,"maxLength":200},"email":{"description":"E-mail do cliente final","type":"string","format":"email"},"document":{"description":"CPF/CNPJ — alguns gateways exigem para emitir","type":"string","minLength":11,"maxLength":18,"nullable":true},"phone":{"description":"Telefone com DDD, só dígitos (ex.: 5511999998888) — a pagar.me exige para emitir pix","type":"string","pattern":"^\\+?\\d{10,13}$","nullable":true},"address":{"description":"Novo endereço completo — null apaga","type":"object","properties":{"line":{"description":"Logradouro com número (ex.: Rua das Videiras, 52)","type":"string","minLength":1,"maxLength":200},"zipCode":{"description":"CEP, 8 dígitos","type":"string","pattern":"^\\d{8}$"},"city":{"description":"Cidade","type":"string","minLength":1,"maxLength":100},"state":{"description":"UF (ex.: SP)","type":"string"}},"required":["line","zipCode","city","state"],"nullable":true}}}}}},"responses":{"200":{"description":"Cliente atualizado","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"description":"Identificador do cliente no motor","type":"string","format":"uuid"},"externalRef":{"description":"O identificador do cliente NO SEU SISTEMA — imutável, é a chave de reaproveitamento","type":"string"},"name":{"description":"Nome do cliente final","type":"string"},"email":{"description":"E-mail do cliente final — é para ele que vão avisos de fatura e a nota fiscal","type":"string"},"document":{"description":"CPF/CNPJ — null quando nunca informado","type":"string","nullable":true},"phone":{"description":"Telefone com DDD, só dígitos — null quando nunca informado","type":"string","nullable":true},"address":{"description":"Endereço — null quando nunca informado","type":"object","properties":{"line":{"description":"Logradouro com número (ex.: Rua das Videiras, 52)","type":"string","minLength":1,"maxLength":200},"zipCode":{"description":"CEP, 8 dígitos","type":"string","pattern":"^\\d{8}$"},"city":{"description":"Cidade","type":"string","minLength":1,"maxLength":100},"state":{"description":"UF (ex.: SP)","type":"string"}},"required":["line","zipCode","city","state"],"nullable":true},"createdAt":{"description":"Quando o cliente foi criado no motor","type":"string","format":"date-time"},"updatedAt":{"description":"Última alteração dos dados","type":"string","format":"date-time"}},"required":["id","externalRef","name","email","document","phone","address","createdAt","updatedAt"]}}}},"400":{"description":"Corpo sem nenhum campo, ou campo inválido","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}},"404":{"description":"Cliente não encontrado","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}}},"security":[{"bearer":[]}],"summary":"Editar cliente","tags":["Clientes"]}},"/v1/invoices":{"get":{"description":"Todas as faturas do motor, paginadas. Use status=awaiting_value para ver as retidas aguardando valor.","operationId":"InvoicesController_list","parameters":[{"name":"page","required":false,"in":"query","description":"Página, começando em 1","schema":{"description":"Página, começando em 1","type":"integer","minimum":1,"exclusiveMinimum":false,"default":1}},{"name":"pageSize","required":false,"in":"query","description":"Itens por página (máximo 100)","schema":{"description":"Itens por página (máximo 100)","type":"integer","minimum":1,"exclusiveMinimum":false,"maximum":100,"exclusiveMaximum":false,"default":25}},{"name":"status","required":false,"in":"query","description":"Filtra por estado — awaiting_value lista as retidas","schema":{"description":"Filtra por estado — awaiting_value lista as retidas","type":"string","enum":["draft","awaiting_value","ready","issued","paid","overdue","canceled"]}}],"responses":{"200":{"description":"Página de faturas","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"description":"Records of the current page","type":"array","items":{"type":"object","properties":{"id":{"description":"Identificador da fatura","type":"string","format":"uuid"},"subscriptionId":{"description":"Assinatura dona da fatura","type":"string","format":"uuid"},"cycle":{"description":"Ciclo da assinatura que esta fatura representa","type":"integer"},"status":{"description":"Estado: awaiting_value é a fatura retida aguardando valor do seu endpoint. overdue não é final: a fatura vencida segue sendo conciliada com o gateway por 90 dias após o vencimento e vira paid (evento invoice.paid) se o pagamento atrasado cair","type":"string","enum":["draft","awaiting_value","ready","issued","paid","overdue","canceled"]},"dueDate":{"description":"Data do ciclo (YYYY-MM-DD) — o chargeDay da assinatura, quando a fatura é gerada","type":"string"},"paymentDueDate":{"description":"Vencimento real da cobrança (YYYY-MM-DD) — o que o gateway confirmou; sem confirmação, dueDate + expirationDays","type":"string"},"expirationDays":{"description":"Prazo congelado na geração, em dias","type":"integer"},"paymentMethod":{"description":"Método de pagamento desta fatura","type":"string"},"installments":{"description":"Parcelas no cartão — nulo quando à vista ou método sem parcela","type":"integer","nullable":true},"totalCents":{"description":"Total em centavos — nulo enquanto houver item aguardando valor","type":"integer","nullable":true},"gatewayInvoiceId":{"description":"Id da cobrança no seu gateway, após a emissão","type":"string","nullable":true},"issuedAt":{"description":"Quando foi emitida no gateway","type":"string","format":"date-time","nullable":true},"paidAt":{"description":"Quando foi paga","type":"string","format":"date-time","nullable":true},"createdAt":{"description":"Quando foi gerada","type":"string","format":"date-time"}},"required":["id","subscriptionId","cycle","status","dueDate","paymentDueDate","expirationDays","paymentMethod","installments","totalCents","gatewayInvoiceId","issuedAt","paidAt","createdAt"]}},"pagination":{"type":"object","properties":{"page":{"description":"Current page (1-based)","type":"integer"},"pageSize":{"description":"Records per page","type":"integer"},"totalRecords":{"description":"Total records matching the filters","type":"integer"},"totalPages":{"description":"Total pages for the current pageSize","type":"integer"}},"required":["page","pageSize","totalRecords","totalPages"]}},"required":["data","pagination"]}}}}},"security":[{"bearer":[]}],"summary":"Listar faturas","tags":["Faturas"]}},"/v1/invoices/{invoiceId}":{"get":{"description":"A fatura com itens (snapshot de preço e desconto) e o histórico cru das chamadas ao seu endpoint de preço.","operationId":"InvoicesController_get","parameters":[{"name":"invoiceId","required":true,"in":"path","description":"Id da fatura","schema":{"type":"string"}}],"responses":{"200":{"description":"Fatura completa","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"description":"Identificador da fatura","type":"string","format":"uuid"},"subscriptionId":{"description":"Assinatura dona da fatura","type":"string","format":"uuid"},"cycle":{"description":"Ciclo da assinatura que esta fatura representa","type":"integer"},"status":{"description":"Estado: awaiting_value é a fatura retida aguardando valor do seu endpoint. overdue não é final: a fatura vencida segue sendo conciliada com o gateway por 90 dias após o vencimento e vira paid (evento invoice.paid) se o pagamento atrasado cair","type":"string","enum":["draft","awaiting_value","ready","issued","paid","overdue","canceled"]},"dueDate":{"description":"Data do ciclo (YYYY-MM-DD) — o chargeDay da assinatura, quando a fatura é gerada","type":"string"},"paymentDueDate":{"description":"Vencimento real da cobrança (YYYY-MM-DD) — o que o gateway confirmou; sem confirmação, dueDate + expirationDays","type":"string"},"expirationDays":{"description":"Prazo congelado na geração, em dias","type":"integer"},"paymentMethod":{"description":"Método de pagamento desta fatura","type":"string"},"installments":{"description":"Parcelas no cartão — nulo quando à vista ou método sem parcela","type":"integer","nullable":true},"totalCents":{"description":"Total em centavos — nulo enquanto houver item aguardando valor","type":"integer","nullable":true},"gatewayInvoiceId":{"description":"Id da cobrança no seu gateway, após a emissão","type":"string","nullable":true},"issuedAt":{"description":"Quando foi emitida no gateway","type":"string","format":"date-time","nullable":true},"paidAt":{"description":"Quando foi paga","type":"string","format":"date-time","nullable":true},"createdAt":{"description":"Quando foi gerada","type":"string","format":"date-time"},"items":{"description":"Itens da fatura, com snapshot de preço e desconto","type":"array","items":{"type":"object","properties":{"id":{"description":"Identificador do item","type":"string","format":"uuid"},"productId":{"description":"Produto do catálogo","type":"string","format":"uuid"},"productName":{"description":"Nome congelado na geração","type":"string"},"pricingSource":{"description":"De onde o valor veio","type":"string","enum":["table","endpoint","manual"]},"pricingStatus":{"description":"pending = aguardando valor","type":"string","enum":["pending","priced","skipped"]},"months":{"description":"Meses cobertos pelo ciclo","type":"integer"},"monthlyPriceCents":{"description":"Preço mensal congelado — nulo em item variável","type":"integer","nullable":true},"discountPercent":{"description":"Desconto aplicado pela régua neste ciclo","type":"integer","nullable":true},"amountCents":{"description":"Valor final do item — congelado, auditável","type":"integer","nullable":true},"description":{"description":"A linha que o assinante lê","type":"string","nullable":true}},"required":["id","productId","productName","pricingSource","pricingStatus","months","monthlyPriceCents","discountPercent","amountCents","description"]}},"pricingAttempts":{"description":"Histórico cru das chamadas ao seu endpoint de preço","type":"array","items":{"type":"object","properties":{"attempt":{"description":"Número da tentativa","type":"integer"},"url":{"description":"URL chamada","type":"string"},"outcome":{"description":"Resultado da chamada ao seu endpoint","type":"string","enum":["ok","skip","timeout","http_error","invalid_response","over_cap"]},"responseStatus":{"description":"HTTP status devolvido, quando houve resposta","type":"integer","nullable":true},"latencyMs":{"description":"Latência da chamada","type":"integer","nullable":true},"responseBody":{"description":"Corpo cru da resposta — é com isso que seu time debuga","type":"string","nullable":true},"createdAt":{"description":"Quando a tentativa aconteceu","type":"string","format":"date-time"}},"required":["attempt","url","outcome","responseStatus","latencyMs","responseBody","createdAt"]}}},"required":["id","subscriptionId","cycle","status","dueDate","paymentDueDate","expirationDays","paymentMethod","installments","totalCents","gatewayInvoiceId","issuedAt","paidAt","createdAt","items","pricingAttempts"]}}}},"404":{"description":"Fatura não encontrada","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}}},"security":[{"bearer":[]}],"summary":"Detalhar fatura","tags":["Faturas"]}},"/v1/invoices/{invoiceId}/payment-link":{"get":{"description":"O link para mostrar no \"pagar\" da sua tela. Com a cobrança já na régua, é a página de pagamento da Revtriever, onde o pagador escolhe cartão, Pix ou boleto (a mesma do smart retries); antes disso, o link do gateway. Chamar de novo devolve o mesmo link. Só fatura emitida ou vencida, com cobrança em aberto, tem link.","operationId":"InvoicesController_paymentLink","parameters":[{"name":"invoiceId","required":true,"in":"path","description":"Id da fatura","schema":{"type":"string"}}],"responses":{"200":{"description":"Link de pagamento","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"description":"Link para o pagador quitar a fatura. Com a régua conhecendo a cobrança, é a página de pagamento da Revtriever (cartão, Pix e boleto, a mesma do smart retries); antes disso, o link do próprio gateway. Trate como segredo: quem tem o link paga a fatura — não registre em log nem exponha fora da sessão do dono da fatura.","type":"string","format":"uri"},"kind":{"description":"payment_page = página da Revtriever com troca de método; gateway = página do gateway, só no método da cobrança","type":"string","enum":["payment_page","gateway"]}},"required":["url","kind"]}}}},"400":{"description":"invoiceId não é um UUID","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}},"404":{"description":"Fatura não encontrada","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}},"409":{"description":"Fatura sem cobrança em aberto (rascunho, paga, cancelada ou ainda não emitida)","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"],"example":{"type":"engine.invoice_not_payable","title":"EngineInvoiceNotPayableError","status":409}}}}}},"security":[{"bearer":[]}],"summary":"Link de pagamento da fatura","tags":["Faturas"]}},"/v1/invoices/{invoiceId}/retry-pricing":{"post":{"description":"Re-dispara a chamada ao seu endpoint para todo item pendente desta fatura, fora do cronograma de backoff.","operationId":"InvoicesController_retryPricing","parameters":[{"name":"invoiceId","required":true,"in":"path","description":"Id da fatura","schema":{"type":"string"}}],"responses":{"200":{"description":"Itens re-disparados","content":{"application/json":{"schema":{"type":"object","properties":{"retried":{"description":"Quantos itens pendentes foram re-disparados agora","type":"integer"}},"required":["retried"]}}}},"404":{"description":"Fatura não encontrada","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}}},"security":[{"bearer":[]}],"summary":"Retentar o pricing agora","tags":["Faturas"]}},"/v1/invoices/{invoiceId}/items/{itemId}/value":{"post":{"description":"Destrava um item aguardando valor com o montante informado (pricingSource: manual). Quando todos os itens fecham, a fatura vai a ready e é emitida.","operationId":"InvoicesController_manualValue","parameters":[{"name":"invoiceId","required":true,"in":"path","description":"Id da fatura","schema":{"type":"string"}},{"name":"itemId","required":true,"in":"path","description":"Id do item aguardando valor","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"amount":{"description":"Valor do item em centavos — destrava a fatura retida","type":"integer","minimum":0,"exclusiveMinimum":false},"description":{"description":"Linha da fatura, opcional","type":"string","maxLength":300,"nullable":true}},"required":["amount"]}}}},"responses":{"200":{"description":"Fatura após o destravamento","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"description":"Identificador da fatura","type":"string","format":"uuid"},"subscriptionId":{"description":"Assinatura dona da fatura","type":"string","format":"uuid"},"cycle":{"description":"Ciclo da assinatura que esta fatura representa","type":"integer"},"status":{"description":"Estado: awaiting_value é a fatura retida aguardando valor do seu endpoint. overdue não é final: a fatura vencida segue sendo conciliada com o gateway por 90 dias após o vencimento e vira paid (evento invoice.paid) se o pagamento atrasado cair","type":"string","enum":["draft","awaiting_value","ready","issued","paid","overdue","canceled"]},"dueDate":{"description":"Data do ciclo (YYYY-MM-DD) — o chargeDay da assinatura, quando a fatura é gerada","type":"string"},"paymentDueDate":{"description":"Vencimento real da cobrança (YYYY-MM-DD) — o que o gateway confirmou; sem confirmação, dueDate + expirationDays","type":"string"},"expirationDays":{"description":"Prazo congelado na geração, em dias","type":"integer"},"paymentMethod":{"description":"Método de pagamento desta fatura","type":"string"},"installments":{"description":"Parcelas no cartão — nulo quando à vista ou método sem parcela","type":"integer","nullable":true},"totalCents":{"description":"Total em centavos — nulo enquanto houver item aguardando valor","type":"integer","nullable":true},"gatewayInvoiceId":{"description":"Id da cobrança no seu gateway, após a emissão","type":"string","nullable":true},"issuedAt":{"description":"Quando foi emitida no gateway","type":"string","format":"date-time","nullable":true},"paidAt":{"description":"Quando foi paga","type":"string","format":"date-time","nullable":true},"createdAt":{"description":"Quando foi gerada","type":"string","format":"date-time"},"items":{"description":"Itens da fatura, com snapshot de preço e desconto","type":"array","items":{"type":"object","properties":{"id":{"description":"Identificador do item","type":"string","format":"uuid"},"productId":{"description":"Produto do catálogo","type":"string","format":"uuid"},"productName":{"description":"Nome congelado na geração","type":"string"},"pricingSource":{"description":"De onde o valor veio","type":"string","enum":["table","endpoint","manual"]},"pricingStatus":{"description":"pending = aguardando valor","type":"string","enum":["pending","priced","skipped"]},"months":{"description":"Meses cobertos pelo ciclo","type":"integer"},"monthlyPriceCents":{"description":"Preço mensal congelado — nulo em item variável","type":"integer","nullable":true},"discountPercent":{"description":"Desconto aplicado pela régua neste ciclo","type":"integer","nullable":true},"amountCents":{"description":"Valor final do item — congelado, auditável","type":"integer","nullable":true},"description":{"description":"A linha que o assinante lê","type":"string","nullable":true}},"required":["id","productId","productName","pricingSource","pricingStatus","months","monthlyPriceCents","discountPercent","amountCents","description"]}},"pricingAttempts":{"description":"Histórico cru das chamadas ao seu endpoint de preço","type":"array","items":{"type":"object","properties":{"attempt":{"description":"Número da tentativa","type":"integer"},"url":{"description":"URL chamada","type":"string"},"outcome":{"description":"Resultado da chamada ao seu endpoint","type":"string","enum":["ok","skip","timeout","http_error","invalid_response","over_cap"]},"responseStatus":{"description":"HTTP status devolvido, quando houve resposta","type":"integer","nullable":true},"latencyMs":{"description":"Latência da chamada","type":"integer","nullable":true},"responseBody":{"description":"Corpo cru da resposta — é com isso que seu time debuga","type":"string","nullable":true},"createdAt":{"description":"Quando a tentativa aconteceu","type":"string","format":"date-time"}},"required":["attempt","url","outcome","responseStatus","latencyMs","responseBody","createdAt"]}}},"required":["id","subscriptionId","cycle","status","dueDate","paymentDueDate","expirationDays","paymentMethod","installments","totalCents","gatewayInvoiceId","issuedAt","paidAt","createdAt","items","pricingAttempts"]}}}},"409":{"description":"Item não está aguardando valor","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}}},"security":[{"bearer":[]}],"summary":"Informar valor manualmente","tags":["Faturas"]}},"/v1/integration":{"get":{"description":"Secret vigente (últimos 4), janela de graça de uma rotação em curso e o webhook de alertas.","operationId":"IntegrationController_status","parameters":[],"responses":{"200":{"description":"Estado da integração","content":{"application/json":{"schema":{"type":"object","properties":{"secretLastFour":{"description":"Últimos 4 caracteres do secret de integração vigente","type":"string"},"previousSecretExpiresAt":{"description":"Até quando o secret anterior ainda é aceito (janela de graça da rotação)","type":"string","format":"date-time","nullable":true},"alertWebhookUrl":{"description":"URL que recebe os alertas e eventos, se configurada","type":"string","nullable":true},"cardCaptureReturnOrigins":{"description":"Origens para onde a página de cadastro de cartão pode devolver o pagador (returnUrl da sessão de captura). Vazio = nenhuma returnUrl é aceita","type":"array","items":{"type":"string"}},"gateways":{"description":"Conexões ativas pelas quais o motor consegue emitir — com mais de uma, toda assinatura nova precisa escolher","type":"array","items":{"type":"object","properties":{"connectionId":{"description":"Id da conexão — o gatewayConnectionId aceito na criação de assinatura","type":"string","format":"uuid"},"provider":{"description":"Gateway (vindi, stripe, malga, asaas…)","type":"string"},"environment":{"description":"sandbox ou production","type":"string"},"issuableMethods":{"description":"Métodos que este gateway emite (card, pix, boleto)","type":"array","items":{"type":"string"}},"maxCardInstallments":{"description":"Parcelamento máximo no cartão","type":"integer"}},"required":["connectionId","provider","environment","issuableMethods","maxCardInstallments"]}}},"required":["secretLastFour","previousSecretExpiresAt","alertWebhookUrl","cardCaptureReturnOrigins","gateways"]}}}}},"security":[{"bearer":[]}],"summary":"Estado da integração","tags":["Integração"]}},"/v1/integration/migration-map":{"get":{"description":"Para a portabilidade de cofre numa migração de gateway: cada cliente com espelho na conexão de origem, com o id dele lá e o id na conexão de destino (nulo enquanto o espelho novo não existe). Anexe no ticket de portabilidade.","operationId":"IntegrationController_migrationMap","parameters":[{"name":"from","required":true,"in":"query","description":"Conexão de origem (a antiga)","schema":{"description":"Conexão de origem (a antiga)","type":"string","format":"uuid"}},{"name":"to","required":true,"in":"query","description":"Conexão de destino (a nova)","schema":{"description":"Conexão de destino (a nova)","type":"string","format":"uuid"}},{"name":"page","required":false,"in":"query","description":"Página, começando em 1","schema":{"description":"Página, começando em 1","type":"integer","minimum":1,"exclusiveMinimum":false,"default":1}},{"name":"pageSize","required":false,"in":"query","description":"Itens por página (máximo 100)","schema":{"description":"Itens por página (máximo 100)","type":"integer","minimum":1,"exclusiveMinimum":false,"maximum":100,"exclusiveMaximum":false,"default":25}}],"responses":{"200":{"description":"Página do de-para","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"description":"Records of the current page","type":"array","items":{"type":"object","properties":{"customerExternalRef":{"description":"O SEU id do cliente","type":"string"},"code":{"description":"Código do cliente nos gateways (rev-…) — o mesmo nas duas pontas quando nós criamos","type":"string"},"fromExternalCustomerId":{"description":"Id do cliente no gateway de origem","type":"string"},"toExternalCustomerId":{"description":"Id do cliente no gateway de destino — nulo enquanto o espelho não existe lá","type":"string","nullable":true}},"required":["customerExternalRef","code","fromExternalCustomerId","toExternalCustomerId"]}},"pagination":{"type":"object","properties":{"page":{"description":"Current page (1-based)","type":"integer"},"pageSize":{"description":"Records per page","type":"integer"},"totalRecords":{"description":"Total records matching the filters","type":"integer"},"totalPages":{"description":"Total pages for the current pageSize","type":"integer"}},"required":["page","pageSize","totalRecords","totalPages"]}},"required":["data","pagination"]}}}}},"security":[{"bearer":[]}],"summary":"De-para de clientes entre duas conexões","tags":["Integração"]}},"/v1/integration/routing":{"get":{"description":"Por qual conexão cada método sai quando a assinatura não tem pino explícito. Método sem regra segue a precedência padrão.","operationId":"IntegrationController_routing","parameters":[],"responses":{"200":{"description":"Regras vigentes","content":{"application/json":{"schema":{"type":"object","properties":{"rules":{"description":"Regras de roteamento por método — método sem regra segue a precedência padrão (pino da assinatura, conexão única)","type":"array","items":{"type":"object","properties":{"method":{"description":"Método de pagamento que a regra roteia","type":"string","enum":["card","pix","boleto"]},"connectionId":{"description":"Conexão de gateway por onde o método sai","type":"string","format":"uuid"},"provider":{"description":"Provedor da conexão roteada","type":"string"}},"required":["method","connectionId","provider"]}}},"required":["rules"]}}}}},"security":[{"bearer":[]}],"summary":"Regras de roteamento por método","tags":["Integração"]},"put":{"description":"Aponta o método para uma conexão que o emite; connectionId null remove a regra. A capacidade é validada aqui e novamente na emissão. Precedência: pino da assinatura > regra > conexão única.","operationId":"IntegrationController_setRouting","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"method":{"description":"Método a rotear","type":"string","enum":["card","pix","boleto"]},"connectionId":{"description":"Conexão que passa a emitir o método — null remove a regra e o método volta à precedência padrão","type":"string","format":"uuid","nullable":true}},"required":["method","connectionId"]}}}},"responses":{"200":{"description":"Regras após a mudança","content":{"application/json":{"schema":{"type":"object","properties":{"rules":{"description":"Regras de roteamento por método — método sem regra segue a precedência padrão (pino da assinatura, conexão única)","type":"array","items":{"type":"object","properties":{"method":{"description":"Método de pagamento que a regra roteia","type":"string","enum":["card","pix","boleto"]},"connectionId":{"description":"Conexão de gateway por onde o método sai","type":"string","format":"uuid"},"provider":{"description":"Provedor da conexão roteada","type":"string"}},"required":["method","connectionId","provider"]}}},"required":["rules"]}}}},"404":{"description":"Conexão inexistente ou inativa","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}},"422":{"description":"A conexão não emite esse método","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}}},"security":[{"bearer":[]}],"summary":"Definir o roteamento de um método","tags":["Integração"]}},"/v1/integration/secret/rotate":{"post":{"description":"O novo secret assina na hora; o anterior segue aceito por 24 horas — rotação sem downtime.","operationId":"IntegrationController_rotateSecret","parameters":[],"responses":{"200":{"description":"Novo secret em claro — guarde agora","content":{"application/json":{"schema":{"type":"object","properties":{"secret":{"description":"O novo secret em claro — o anterior segue aceito por 24 horas","type":"string"},"previousSecretExpiresAt":{"description":"Quando o secret anterior deixa de ser aceito","type":"string","format":"date-time"}},"required":["secret","previousSecretExpiresAt"]}}}}},"security":[{"bearer":[]}],"summary":"Gerar novo secret de integração","tags":["Integração"]}},"/v1/integration/alert-webhook":{"put":{"description":"Para onde avisamos na hora: endpoint de preço falhando, fatura retida, cobrança recusada. null desliga.","operationId":"IntegrationController_setAlertWebhook","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"description":"URL https do seu webhook — null desliga","type":"string","format":"uri","nullable":true}},"required":["url"]}}}},"responses":{"200":{"description":"Estado atualizado","content":{"application/json":{"schema":{"type":"object","properties":{"secretLastFour":{"description":"Últimos 4 caracteres do secret de integração vigente","type":"string"},"previousSecretExpiresAt":{"description":"Até quando o secret anterior ainda é aceito (janela de graça da rotação)","type":"string","format":"date-time","nullable":true},"alertWebhookUrl":{"description":"URL que recebe os alertas e eventos, se configurada","type":"string","nullable":true},"cardCaptureReturnOrigins":{"description":"Origens para onde a página de cadastro de cartão pode devolver o pagador (returnUrl da sessão de captura). Vazio = nenhuma returnUrl é aceita","type":"array","items":{"type":"string"}},"gateways":{"description":"Conexões ativas pelas quais o motor consegue emitir — com mais de uma, toda assinatura nova precisa escolher","type":"array","items":{"type":"object","properties":{"connectionId":{"description":"Id da conexão — o gatewayConnectionId aceito na criação de assinatura","type":"string","format":"uuid"},"provider":{"description":"Gateway (vindi, stripe, malga, asaas…)","type":"string"},"environment":{"description":"sandbox ou production","type":"string"},"issuableMethods":{"description":"Métodos que este gateway emite (card, pix, boleto)","type":"array","items":{"type":"string"}},"maxCardInstallments":{"description":"Parcelamento máximo no cartão","type":"integer"}},"required":["connectionId","provider","environment","issuableMethods","maxCardInstallments"]}}},"required":["secretLastFour","previousSecretExpiresAt","alertWebhookUrl","cardCaptureReturnOrigins","gateways"]}}}}},"security":[{"bearer":[]}],"summary":"Configurar o webhook de alertas","tags":["Integração"]}},"/v1/integration/card-capture-return-origins":{"put":{"description":"A página hospedada de cadastro de cartão só devolve o pagador para uma returnUrl cuja origem esteja nesta lista. Cada item é uma origem exata em https (sem caminho, query ou curinga). A lista enviada substitui a anterior.","operationId":"IntegrationController_setCardCaptureReturnOrigins","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"origins":{"description":"Origens exatas (esquema + host + porta, sem caminho), em https — ex.: https://app.suaempresa.com.br. Substitui a lista inteira; [] remove todas. Até 10","type":"array","maxItems":10,"items":{"type":"string","maxLength":200}}},"required":["origins"]}}}},"responses":{"200":{"description":"Estado atualizado","content":{"application/json":{"schema":{"type":"object","properties":{"secretLastFour":{"description":"Últimos 4 caracteres do secret de integração vigente","type":"string"},"previousSecretExpiresAt":{"description":"Até quando o secret anterior ainda é aceito (janela de graça da rotação)","type":"string","format":"date-time","nullable":true},"alertWebhookUrl":{"description":"URL que recebe os alertas e eventos, se configurada","type":"string","nullable":true},"cardCaptureReturnOrigins":{"description":"Origens para onde a página de cadastro de cartão pode devolver o pagador (returnUrl da sessão de captura). Vazio = nenhuma returnUrl é aceita","type":"array","items":{"type":"string"}},"gateways":{"description":"Conexões ativas pelas quais o motor consegue emitir — com mais de uma, toda assinatura nova precisa escolher","type":"array","items":{"type":"object","properties":{"connectionId":{"description":"Id da conexão — o gatewayConnectionId aceito na criação de assinatura","type":"string","format":"uuid"},"provider":{"description":"Gateway (vindi, stripe, malga, asaas…)","type":"string"},"environment":{"description":"sandbox ou production","type":"string"},"issuableMethods":{"description":"Métodos que este gateway emite (card, pix, boleto)","type":"array","items":{"type":"string"}},"maxCardInstallments":{"description":"Parcelamento máximo no cartão","type":"integer"}},"required":["connectionId","provider","environment","issuableMethods","maxCardInstallments"]}}},"required":["secretLastFour","previousSecretExpiresAt","alertWebhookUrl","cardCaptureReturnOrigins","gateways"]}}}},"422":{"description":"Algum item não é uma origem https exata","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"],"example":{"type":"engine.card_capture_return_origin_invalid","title":"EngineCardCaptureReturnOriginInvalidError","status":422,"meta":{"origin":"https://app.getcart.com.br/conta"}}}}}}},"security":[{"bearer":[]}],"summary":"Definir as origens de volta da captura de cartão","tags":["Integração"]}},"/v1/integration/api-keys":{"post":{"description":"A chave em claro aparece UMA vez na resposta.","operationId":"IntegrationController_createApiKey","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"Nome da chave (ex.: producao, staging)","type":"string","minLength":1,"maxLength":120},"scopes":{"description":"Escopos pedidos; só entram os de produto contratado, e a chave é recusada se nenhum entrar","type":"array","minItems":1,"items":{"description":"engine é o motor, flows a régua editável, fiscal a nota, negotiation a política de negociação e suas ferramentas","type":"string","enum":["engine","flows","fiscal","negotiation"]},"default":["engine","flows"]}},"required":["name"]}}}},"responses":{"201":{"description":"Chave criada","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"description":"Identificador da chave","type":"string","format":"uuid"},"name":{"description":"Nome dado à chave","type":"string"},"scopes":{"description":"O que esta chave pode tocar — a mesma chave serve todos os produtos","type":"array","items":{"description":"engine é o motor, flows a régua editável, fiscal a nota, negotiation a política de negociação e suas ferramentas","type":"string","enum":["engine","flows","fiscal","negotiation"]}},"lastUsedAt":{"description":"Último uso","type":"string","format":"date-time","nullable":true},"createdAt":{"description":"Quando foi criada","type":"string","format":"date-time"},"plainKey":{"description":"A chave em claro — mostrada UMA vez, guarde agora","type":"string"}},"required":["id","name","scopes","lastUsedAt","createdAt","plainKey"]}}}}},"security":[{"bearer":[]}],"summary":"Criar API key","tags":["Integração"]},"get":{"description":"Sem o valor em claro — só nome, uso e criação.","operationId":"IntegrationController_listApiKeys","parameters":[],"responses":{"200":{"description":"Chaves ativas","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"description":"Identificador da chave","type":"string","format":"uuid"},"name":{"description":"Nome dado à chave","type":"string"},"scopes":{"description":"O que esta chave pode tocar — a mesma chave serve todos os produtos","type":"array","items":{"description":"engine é o motor, flows a régua editável, fiscal a nota, negotiation a política de negociação e suas ferramentas","type":"string","enum":["engine","flows","fiscal","negotiation"]}},"lastUsedAt":{"description":"Último uso","type":"string","format":"date-time","nullable":true},"createdAt":{"description":"Quando foi criada","type":"string","format":"date-time"}},"required":["id","name","scopes","lastUsedAt","createdAt"]}}}}}},"security":[{"bearer":[]}],"summary":"Listar API keys ativas","tags":["Integração"]}},"/v1/integration/api-keys/{apiKeyId}":{"delete":{"description":"A chave para de autenticar imediatamente.","operationId":"IntegrationController_revokeApiKey","parameters":[{"name":"apiKeyId","required":true,"in":"path","description":"Id da chave","schema":{"type":"string"}}],"responses":{"200":{"description":"Chave revogada","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"description":"Identificador da chave","type":"string","format":"uuid"},"name":{"description":"Nome dado à chave","type":"string"},"scopes":{"description":"O que esta chave pode tocar — a mesma chave serve todos os produtos","type":"array","items":{"description":"engine é o motor, flows a régua editável, fiscal a nota, negotiation a política de negociação e suas ferramentas","type":"string","enum":["engine","flows","fiscal","negotiation"]}},"lastUsedAt":{"description":"Último uso","type":"string","format":"date-time","nullable":true},"createdAt":{"description":"Quando foi criada","type":"string","format":"date-time"}},"required":["id","name","scopes","lastUsedAt","createdAt"]}}}},"401":{"description":"Chave não encontrada ou já revogada","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"]}}}}},"security":[{"bearer":[]}],"summary":"Revogar API key","tags":["Integração"]}},"/v1/events":{"get":{"description":"Tudo que o motor conta pra você vira evento persistido — este endpoint devolve o histórico com cursor, inclusive o que o seu webhook perdeu. O corpo de cada evento é idêntico ao entregue no webhook.","operationId":"EventsController_list","parameters":[{"name":"cursor","required":false,"in":"query","description":"Id do último evento da página anterior — devolvemos os seguintes","schema":{"description":"Id do último evento da página anterior — devolvemos os seguintes","type":"string","format":"uuid"}},{"name":"type","required":false,"in":"query","description":"Filtra por tipo de evento (ex.: invoice.paid, invoice.overdue, charge.failed, subscription.card_saved)","schema":{"description":"Filtra por tipo de evento (ex.: invoice.paid, invoice.overdue, charge.failed, subscription.card_saved)","type":"string"}},{"name":"from","required":false,"in":"query","description":"Só eventos a partir deste instante","schema":{"description":"Só eventos a partir deste instante","type":"string","format":"date-time"}},{"name":"to","required":false,"in":"query","description":"Só eventos até este instante","schema":{"description":"Só eventos até este instante","type":"string","format":"date-time"}},{"name":"limit","required":false,"in":"query","description":"Tamanho da página (padrão 50)","schema":{"description":"Tamanho da página (padrão 50)","type":"integer","minimum":1,"exclusiveMinimum":false,"maximum":200,"exclusiveMaximum":false}}],"responses":{"200":{"description":"Página de eventos, mais antiga primeiro","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"description":"Eventos em ordem de ocorrência","type":"array","items":{"type":"object","properties":{"id":{"description":"Identificador do evento — também é o cursor","type":"string","format":"uuid"},"type":{"description":"Tipo do evento — transições (invoice.issued, invoice.paid, invoice.overdue, invoice.held, pricing.failing, pricing.recovered, subscription.canceled, subscription.card_saved, charge.failed) e ciclo de vida dos recursos (product.created, product.updated, plan.created, plan.updated, customer.created, customer.updated, subscription.created, subscription.updated, invoice.created, api_key.created, api_key.revoked)","type":"string"},"occurredAt":{"description":"Quando o evento aconteceu","type":"string","format":"date-time"},"data":{"description":"Payload do evento, o mesmo corpo entregue no webhook","type":"object","additionalProperties":{}},"delivery":{"description":"Situação da entrega no seu webhook","type":"object","properties":{"status":{"description":"pending, delivered, failed ou skipped (sem webhook cadastrado)","type":"string"},"attempts":{"description":"Tentativas de entrega já feitas","type":"integer"},"deliveredAt":{"description":"Quando o webhook aceitou","type":"string","format":"date-time","nullable":true},"lastError":{"description":"Último erro de entrega, sem tradução","type":"string","nullable":true}},"required":["status","attempts","deliveredAt","lastError"]}},"required":["id","type","occurredAt","data","delivery"]}},"nextCursor":{"description":"Cursor da próxima página — nulo quando acabou","type":"string","format":"uuid","nullable":true}},"required":["data","nextCursor"]}}}}},"security":[{"bearer":[]}],"summary":"Listar eventos (replay completo)","tags":["Eventos"]}},"/v1/integration/alert-webhook/test":{"post":{"description":"Envia um evento de exemplo assinado para o webhook cadastrado e devolve o resultado cru — status, corpo e latência.","operationId":"EventsController_testAlertWebhook","parameters":[],"responses":{"200":{"description":"Resultado cru da entrega de teste","content":{"application/json":{"schema":{"type":"object","properties":{"delivered":{"description":"Se o seu endpoint aceitou (2xx)","type":"boolean"},"responseStatus":{"description":"Status HTTP devolvido — nulo quando nem respondeu","type":"integer","nullable":true},"responseBody":{"description":"Corpo cru da resposta, truncado","type":"string","nullable":true},"latencyMs":{"description":"Tempo de resposta em milissegundos","type":"integer"},"error":{"description":"Erro de rede/timeout, sem tradução","type":"string","nullable":true}},"required":["delivered","responseStatus","responseBody","latencyMs","error"]}}}},"409":{"description":"Nenhum webhook de alertas cadastrado","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"description":"Stable machine-readable error code (e.g. identity.email_already_in_use)","type":"string"},"title":{"description":"Error class name","type":"string"},"status":{"description":"HTTP status code","type":"integer"},"detail":{"description":"Human-readable message, safe to display to end users","type":"string"},"requestId":{"description":"Correlation id — send it to support to locate the full trail","type":"string","nullable":true},"meta":{"description":"Structured details safe for the frontend (field errors, ids)","type":"object","additionalProperties":{}}},"required":["type","title","status","detail","requestId"],"example":{"type":"engine.integration_settings_missing","title":"Conflito","status":409}}}}}},"security":[{"bearer":[]}],"summary":"Testar o webhook de alertas","tags":["Eventos","Integração"]}}},"info":{"title":"Revtriever Motor de Cobrança","description":"API pública do motor de cobrança por assinatura. Autentique com `Authorization: Bearer rk_…` (API key). Todo POST que cria recurso exige o header `Idempotency-Key`. Valores monetários são sempre centavos inteiros. Erros seguem problem+json: `{ type, title, status, detail, requestId, meta? }`.","version":"1.0.0","contact":{}},"tags":[{"name":"Produtos","description":"Catálogo: preço mensal fixo ou variável via endpoint, espelhado no gateway"},{"name":"Planos","description":"A oferta: período, método padrão, parcelamento e a régua de descontos — versionada"},{"name":"Assinaturas","description":"O vínculo cliente↔plano: criação, customização por assinante, preview e cancelamento"},{"name":"Faturas","description":"O que o ciclo gerou: acompanhamento e os destravamentos da fatura retida"},{"name":"Eventos","description":"Tudo que o motor conta, com replay por cursor"},{"name":"Integração","description":"Secret HMAC, webhook de alertas e API keys"},{"name":"Captura de cartão","description":"Rota pública chamada pelo navegador do pagador com o token da sessão — sem API key"}],"servers":[],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http"}},"schemas":{}}}