Criar Assinatura
POST /v1/subscriptions
Cria uma assinatura recorrente para um cliente. A precificação vem das variantes referenciadas em
items (modelo Selectwin-strict — veja Visão Geral).
Requisição
curl -X POST "https://api.selectwin.io/v1/subscriptions" \
-H "SelectKey: sk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: 2dfc6a63-5df5-4900-baca-e49d91ef393e" \
-d '{
"customer": {
"firstName": "João",
"lastName": "Silva",
"email": "joao@example.com",
"document": { "type": "cpf", "number": "12345678901" },
"telephone": { "countryCode": "55", "areaCode": "11", "number": "999999999" }
},
"payment": {
"method": "credit",
"card": { "id": "card_abc123" },
"installments": 1
},
"items": [
{ "variantId": "var_abc123", "quantity": 1 }
],
"billing": {
"frequency": "monthly",
"frequencyCount": 1,
"exactDay": 10,
"startDate": "2026-08-01",
"endDate": null,
"freeTrial": { "enabled": true, "days": 7 },
"address": { "id": "addr_x" }
},
"discounts": [{ "code": "WELCOME5" }],
"externalReference": "SUB-1001",
"metadata": { "source": "checkout" }
}'Plano de N ciclos (cycles da variante)
Uma variante recorrente pode ser vendida com prazo — o 12× que aparece no catálogo e no portal
(contractedCycles). Desde 16/set esse número é cumprido: até então ele era exibido e a assinatura
renovava para sempre, cobrando o 13º.
- O término é a data da N-ésima cobrança. O motor encerra quando o próximo vencimento passa do
endDate, então o ciclo N ainda é cobrado e o N+1 não existe. - O trial não conta como ciclo: N ciclos são N cobranças, e o período de teste não cobra. A contagem começa na primeira cobrança de verdade.
- Carrinho com planos de prazos diferentes vale o MENOR. Um item de 12× junto de um de 24× encerra em 12 — seguir cobrando um contrato que já se cumpriu seria cobrar por nada.
billing.endDatedo corpo vence sempre. Quem o manda está declarando o prazo, e o catálogo não sobrepõe decisão do chamador.- ⚠️ Editar
cyclesna variante não mexe em assinatura já vendida. O prazo é congelado no dia da compra: o comprador contratou o que a tela mostrou.
Campos principais
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
customer | object | Sim | Cliente por id ou inline (firstName + lastName + email obrigatórios no caminho inline; document/telephone/gender/birthdate/additionalEmails opcionais) |
items[] | array | Sim | 1 a 20 itens, cada um referenciando uma variante recorrente |
items[].variantId | string | Sim | ID da variante (var_...; também aceita o prefixo legado prv_...) |
items[].quantity | integer | Não | Quantidade, 1–999 999 (default 1) — o mesmo teto de adicionar/alterar um item. O preço vem da variante |
items[].description | string | Não | Anotação do item (máx. 2048) |
items[].externalReference | string | Não | Sua referência do item |
items[].metadata | object | Não | Metadados do item |
payment.method | enum | Não | credit, pix ou billet (default credit) |
payment.card | object | Sim* | Cartão por id, por token do navegador (Tokenização no navegador) ou inline. *Obrigatório para credit (cobrado automaticamente a cada ciclo) |
payment.installments | integer | Não | Parcelas por cobrança, 1–21 (default 1) |
payment.pix.expiresInMinutes | integer | Não | Expiração do PIX por ciclo (15–525600) |
payment.billet.expiresInDays | integer | Não | Expiração do boleto por ciclo (1–365) |
billing | object | Não | Agenda de cobrança + endereço do pagador (espelha o bloco billing da resposta). Se omitido, usa os defaults de intervalo |
billing.frequency | enum | Não | daily, weekly, monthly, yearly (default monthly) |
billing.frequencyCount | integer | Não | Multiplicador da frequência, 1–365 (default 1). Ex.: billing.frequency=monthly + billing.frequencyCount=3 = a cada 3 meses |
billing.exactDay | integer | Não | Dia fixo do mês para a cobrança (1–31) |
billing.startDate | string | Não | Data de início (YYYY-MM-DD; default: hoje). A 1ª cobrança vence nesta data, então ela não pode ser anterior a hoje (dia de Brasília) → 422 subscriptionStartDateInPast |
billing.endDate | string | Não | Data de término (YYYY-MM-DD). Omitindo, e se a variante do catálogo declarar um plano de N ciclos (productVariants.cycles, o "12×" que a loja exibe), o término é derivado: a data da N-ésima cobrança — ver abaixo |
billing.freeTrial | object | Não | Período de teste: { "enabled": true, "days": 7 } |
billing.address | object | Não* | Endereço de cobrança (id ou inline). *Obrigatório para billet |
autoRenew | boolean | Não | Renovação automática (default true) — na raiz, não é agenda |
discounts | array | Não | Cupons por código ({ code }), máx. 20. Resolvidos e travados na criação; cada ciclo re-aplica o cupom conforme seu escopo (recurring reaplica a cada ciclo até o limite; firstCharge só no 1º ciclo). Mesmas regras de pilha da cobrança avulsa: código repetido → 422 couponAlreadyApplied; cupom isCumulative:false junto de outro → 422 couponNotCumulative. Aqui pesa mais, porque a pilha é congelada e reaplicada em todo ciclo |
splits[] | array | Não | Divisão de receita por ciclo: { recipient, type, value } (máx. 20) — veja Splits |
name | string | Não | Nome de exibição da assinatura |
description | string | Não | Descrição livre (aceita, mas não persistida na leitura) |
externalReference | string | Não | Sua referência externa |
metadata | object | Não | Pares chave-valor livres. As chaves pixAutomatic, marketplaceApplicationFee, recurringPaymentWindow, sessionId e integrationId são da plataforma e são descartadas se vierem no corpo (o Pix Automático se pede por payment.pix.automatic) |
shipping | object | Não | Dados de entrega (assinaturas com produto físico) |
callback.webhookUrl | string (url) | Não | Webhook específico desta assinatura (callback.active default true) |
onBehalfOf | string | Não | publicId de uma sub-conta sua: cria a assinatura na sub-conta e cobra a taxa de aplicação da plataforma a cada ciclo (marketplace) |
Resposta 201 Created
{
"id": "subs_01hqzvabc",
"status": "active",
"currency": "BRL",
"method": "credit",
"type": "recurring",
"currentCycle": {
"id": "scy_01hqzvabc",
"cycle": 1,
"status": "paid",
"startDate": "2026-04-01T00:00:00.000Z",
"endDate": "2026-05-01T00:00:00.000Z",
"dueDate": "2026-04-01T00:00:00.000Z",
"billedAt": "2026-04-01T10:15:00.000Z",
"updatedAt": "2026-04-01T10:15:00.000Z",
"createdAt": "2026-04-01T00:00:00.000Z"
},
"customer": {
"id": "cus_01hqzvabc",
"firstName": "João",
"lastName": "Silva",
"email": "joao@example.com",
"birthdate": null,
"gender": null,
"document": { "type": "cpf", "number": "12345678901" },
"telephone": { "countryCode": "55", "areaCode": "11", "number": "999999999", "line": "5511999999999" },
"available": true,
"delinquent": false,
"externalReference": null,
"additionalEmails": null,
"metadata": null,
"updatedAt": "2026-04-01T00:00:00.000Z",
"createdAt": "2026-04-01T00:00:00.000Z"
},
"billing": {
"frequency": "monthly",
"frequencyCount": 1,
"endDate": null,
"exactDay": null,
"freeTrialDays": null,
"autoRenew": true,
"address": {
"street": "Avenida Paulista",
"number": "1000",
"complement": null,
"district": "Bela Vista",
"city": "São Paulo",
"state": "SP",
"country": "BR",
"postcode": "01310100",
"line": "Avenida Paulista, 1000, Bela Vista, São Paulo - SP, 01310100, BR",
"fingerprint": null,
"line1": "Avenida Paulista, 1000",
"line2": null,
"line3": "Bela Vista"
}
},
"canceledAt": null,
"cancellation": null,
"discount": { "value": 500, "type": "flat", "percentageOfAmount": null },
"discounts": [
{ "source": "coupon", "couponId": 91, "code": "WELCOME5", "type": "flat", "value": 500, "appliedAmount": 500 }
],
"shippable": false,
"shipping": null,
"spplited": false,
"splits": null,
"items": [
{
"id": "sit_01hqzvabc",
"name": "Premium Plan",
"description": null,
"enabled": true,
"pricingSchema": "recurring",
"unitPrice": 9900,
"quantity": 1,
"currency": "BRL",
"images": null,
"metadata": null,
"externalReference": null,
"updatedAt": "2026-04-01T00:00:00.000Z",
"createdAt": "2026-04-01T00:00:00.000Z"
}
],
"callback": null,
"geolocation": null,
"externalReference": "SUB-1001",
"metadata": { "source": "checkout" },
"updatedAt": "2026-04-01T10:15:00.000Z",
"createdAt": "2026-04-01T00:00:00.000Z",
"merchant": { "name": "Seller Name", "merchantId": "bus_1234567890", "isSubAccount": false },
"_links": {
"self": { "href": "https://api.selectwin.io/v1/subscriptions/subs_01hqzvabc", "method": "GET", "description": "Read a subscription." }
}
}Erros comuns
error.code | HTTP | Quando |
|---|---|---|
variantNotFound | 404 | items[].variantId não existe |
variantNotRecurring | 422 | A variante não está precificada como recorrente |
subscriptionStartDateInPast | 422 | billing.startDate anterior a hoje (dia de Brasília); error.params[0] traz { startDate, today } |
customerNotFound | 404 | customer.id inexistente |
cardNotFound | 404 | Cartão (payment.card.id) inexistente |
addressNotFound | 404 | Endereço de cobrança inexistente |
couponNotFound | 404 | Código de cupom inválido |
duplicateSplitRecipient | 422 | Dois splits para o mesmo destinatário |
splitRecipientNotFound | 404 | Destinatário de split inexistente |
onBehalfOfInvalid | 422 | onBehalfOf não referencia uma sub-conta sua / marketplace não habilitado |
fraudBlocked | 403 | O antifraude recusou a criação — o mesmo motor da cobrança avulsa, com o contexto desta requisição (IP, cabeçalho X-Device-Fingerprint) e o do comprador (documento, e-mail, cartão). Uma decisão de revisão manual também recusa: a assinatura não fica parada esperando um analista. Nada é criado e nenhum ciclo é cobrado. Os ciclos seguintes não passam de novo pelo antifraude |
Catálogo completo: Códigos de Erro.
Atualizado em 17 de outubro de 2026