Assinar um plano

Crie um plano e uma oferta de plano, assine um cliente no cartão e acompanhe a confirmação e as renovações.

Use este guia para cobrar um cliente de forma recorrente, a cada semana, mês ou ano.

Pela API, a assinatura é sempre no cartão de crédito. Assinaturas em PIX ou boleto só nascem no checkout da PagPolar. Veja Ciclo de vida da assinatura.

Para uma cobrança única, use Vender com cartão de crédito.

Visão geral

O diagrama mostra o caminho completo, da criação do plano até a renovação.

Antes de começar

Para testar sem cobrança real, use a chave de Homologação. Veja Ambiente de testes. Com a chave de Produção, o cartão é cobrado a cada ciclo: cancele a assinatura no final do teste.

Passo a passo

Crie o plano

O plano é o produto de assinatura. Ele guarda só o nome e a descrição.

curl -X POST "https://api.pagpolar.com/v1/plans" \
  -H "Authorization: Bearer <SEU_TOKEN_DE_ACESSO>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Clube de exemplo",
    "description": "Plano criado pelo guia de assinatura"
  }'
const response = await fetch('https://api.pagpolar.com/v1/plans', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${accessToken}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    name: 'Clube de exemplo',
    description: 'Plano criado pelo guia de assinatura',
  }),
});

console.log(response.status, await response.json());
CampoObrigatórioO que é
nameSimNome do plano, até 255 caracteres.
descriptionNãoDescrição do plano, até 5000 caracteres.

Resposta 201 (resumida):

{
  "data": {
    "id": "b7e1c2d3-4f5a-4b6c-8d7e-9f0a1b2c3d4e",
    "name": "Clube de exemplo",
    "type": "SUBSCRIPTION",
    "is_active": true
  }
}

Guarde data.id. Ele é o <ID_DO_PLANO> do próximo passo. Contrato completo: POST /plans.

Crie a oferta de plano

A oferta de plano define o preço e de quanto em quanto tempo o cliente é cobrado. price é em centavos: 1990 = R$ 19,90.

curl -X POST "https://api.pagpolar.com/v1/plans/<ID_DO_PLANO>/offers" \
  -H "Authorization: Bearer <SEU_TOKEN_DE_ACESSO>" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Mensal",
    "price": 1990,
    "cycle": "MONTHLY",
    "cycle_interval": 1,
    "is_enabled_credit_card": true,
    "max_credit_card_installments": 1
  }'
const response = await fetch('https://api.pagpolar.com/v1/plans/<ID_DO_PLANO>/offers', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${accessToken}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    title: 'Mensal',
    price: 1990,
    cycle: 'MONTHLY',
    cycle_interval: 1,
    is_enabled_credit_card: true,
    max_credit_card_installments: 1,
  }),
});

console.log(response.status, await response.json());
CampoObrigatórioO que é
priceSimValor de cada ciclo, em centavos, número inteiro a partir de 0.
cycleSimUnidade do ciclo: WEEKLY, MONTHLY ou YEARLY.
cycle_intervalNãoA cada quantas unidades de cycle o cliente é cobrado. Número inteiro, mínimo 1. MONTHLY com 3 cobra a cada 3 meses.
titleNãoNome da oferta, até 255 caracteres.
is_enabled_credit_cardNãoCartão na oferta. Começa ligado. A assinatura pela API precisa dele ligado.
max_credit_card_installmentsNãoSó aceita 1. Sem o campo, a API usa 1.
is_activeNãoSem o campo, a oferta nasce ativa.

Abaixo de R$ 5,00, a oferta de plano fica sem cartão

Ao salvar, a API desliga o cartão quando price fica abaixo de 500 (R$ 5,00), mesmo com is_enabled_credit_card: true. Com o cartão desligado, a assinatura pela API responde 409 com Método de pagamento CREDIT_CARD não habilitado para esta oferta. Confira payment_methods na resposta. Na edição com PATCH /plan-offers/{id}, a mesma regra recalcula os meios. Veja Meios de pagamento e valor mínimo.

Envie cycle_interval

A API não define um valor padrão para cycle_interval. Sem o campo, a oferta fica com cycle_interval: null. Envie 1 para cobrar a cada semana, mês ou ano.

Resposta 201 (resumida):

{
  "data": {
    "id": "c8f2d3e4-5a6b-4c7d-9e8f-0a1b2c3d4e5f",
    "identifier": "PPP1234567890",
    "title": "Mensal",
    "price": 19.9,
    "product_id": "b7e1c2d3-4f5a-4b6c-8d7e-9f0a1b2c3d4e",
    "payment_methods": {
      "credit_card": true
    },
    "max_credit_card_installments": 1,
    "cycle": "MONTHLY",
    "cycle_interval": 1
  }
}

Na resposta, price volta em reais: 19.9 é R$ 19,90. Veja Valores nas respostas.

Guarde data.identifier. Ele é o <CODIGO_DA_OFERTA> do próximo passo. O data.id também funciona no lugar do código. Contrato completo: POST /plans/{id}/offers.

Assine o cliente

Envie o código da oferta de plano na URL e os dados do cliente e do cartão no corpo. Envie também o header Idempotency-Key, um valor único para esta assinatura. Veja Idempotência.

curl -X POST "https://api.pagpolar.com/v1/plans/offer/<CODIGO_DA_OFERTA>/subscribe" \
  -H "Authorization: Bearer <SEU_TOKEN_DE_ACESSO>" \
  -H "Idempotency-Key: 8d2f4a6b-1c3e-4f5a-9b7c-2d4e6f8a0b1c" \
  -H "Content-Type: application/json" \
  -d '{
    "installments": 1,
    "external_reference": "ASSINATURA-0001",
    "customer": {
      "name": "Maria Silva",
      "email": "cliente@exemplo.com",
      "document": "<CPF_DO_CLIENTE>",
      "phone": "<TELEFONE_DO_CLIENTE>"
    },
    "credit_card": {
      "holder_name": "MARIA SILVA",
      "holder_document": "<CPF_DO_TITULAR>",
      "number": "<NUMERO_DO_CARTAO>",
      "expiration_month": 12,
      "expiration_year": 2030,
      "cvv": "<CVV>"
    }
  }'
import { randomUUID } from 'node:crypto';

const response = await fetch(
  'https://api.pagpolar.com/v1/plans/offer/<CODIGO_DA_OFERTA>/subscribe',
  {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${accessToken}`,
      'Idempotency-Key': randomUUID(),
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      installments: 1,
      external_reference: 'ASSINATURA-0001',
      customer: {
        name: 'Maria Silva',
        email: 'cliente@exemplo.com',
        document: '<CPF_DO_CLIENTE>',
        phone: '<TELEFONE_DO_CLIENTE>',
      },
      credit_card: {
        holder_name: 'MARIA SILVA',
        holder_document: '<CPF_DO_TITULAR>',
        number: '<NUMERO_DO_CARTAO>',
        expiration_month: 12,
        expiration_year: 2030,
        cvv: '<CVV>',
      },
    }),
  },
);

console.log(response.status, await response.json());
CampoObrigatórioO que é
installmentsSimNúmero de parcelas. Envie 1: a oferta de plano criada pela API aceita no máximo 1.
external_referenceNãoCódigo da assinatura no seu sistema, até 255 caracteres. Fica gravado na venda do primeiro ciclo.

Dados do cliente:

CampoObrigatórioO que é
customer.nameSimNome do cliente, até 255 caracteres.
customer.emailSimE-mail válido do cliente.
customer.documentSimCPF ou CNPJ do cliente, com ou sem pontuação. Veja o formato em Documento e telefone.
customer.phoneSimTelefone do cliente com DDD, com ou sem pontuação. Veja o formato em Documento e telefone.

Dados do cartão:

CampoObrigatórioO que é
credit_card.holder_nameSimNome impresso no cartão.
credit_card.holder_documentSimCPF ou CNPJ do titular do cartão, com ou sem pontuação. Veja o formato em Documento e telefone.
credit_card.numberSimNúmero do cartão. A API confere se o número é válido antes de enviar.
credit_card.expiration_monthSimMês de validade, número de 1 a 12.
credit_card.expiration_yearSimAno de validade com 4 dígitos, número.
credit_card.cvvSimCódigo de segurança, texto com 3 ou 4 caracteres.

No ambiente de testes, use os cartões de Comprar no ambiente de testes.

Campos opcionais:

CampoObrigatórioO que é
addressNãoEndereço do cliente. Se enviar, street, number, neighborhood, city, state e postal_code são obrigatórios. complement é opcional.
affiliate_identifierNãoCódigo do afiliado que indicou a venda. Veja Vender com afiliado.
buyer_ipNãoIP do cliente. Se não enviar, a API usa o IP de quem chamou, ou seja, o do seu servidor.
buyer_user_agentNãoNavegador do cliente, até 512 caracteres. Se não enviar, a API usa o header User-Agent da sua requisição.

Resposta 201 (resumida):

{
  "data": {
    "id": "e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b",
    "status": "DRAFT",
    "payment_method": "CREDIT_CARD",
    "start_at": "2026-09-15T13:00:00.000Z",
    "end_at": null,
    "next_billing_at": null,
    "canceled_at": null,
    "cycle_limit": null,
    "created_at": "2026-09-15T13:00:00.000Z"
  }
}

Guarde data.id, o id da assinatura, junto do cliente. Você usa esse valor para consultar e cancelar.

A resposta também traz customer, offer e product. Contrato completo: POST /plans/offer/{id}/subscribe.

201 não quer dizer cartão aprovado

A PagPolar só envia a assinatura ao gateway depois de responder. Por isso a resposta é sempre 201 com status: DRAFT, mesmo quando o cartão vai ser recusado. Não libere o acesso ainda. O resultado chega no próximo passo.

Se a requisição não tiver resposta, repita com a mesma Idempotency-Key. Veja O que acontece ao repetir. Para achar a venda do primeiro ciclo pela sua referência, use GET /sales?external_reference=ASSINATURA-0001.

Espere a confirmação do gateway

A sua URL recebe os eventos abaixo. Depois dos dois primeiros, a PagPolar cria a assinatura no gateway e chega só um dos dois últimos.

EventoQuando é enviadoO que fazer
TRANSACTION_CREATEDLogo depois da criação, com a venda do primeiro ciclo (transaction.cycle: 1).Registre a venda.
SUBSCRIPTION_CREATEDLogo depois da criação, com a assinatura em DRAFT.Registre a assinatura. Não libere o acesso.
SUBSCRIPTION_CONFIRMEDO gateway aceitou a assinatura. O status continua DRAFT e external_id vem preenchido.Guarde external_id se precisar. Ainda não libere o acesso.
SUBSCRIPTION_FAILEDO gateway recusou a criação. Status FAILED. A venda do primeiro ciclo também fica FAILED, sem evento de venda próprio.Não libere o acesso. Peça outro cartão ao cliente e crie uma assinatura nova, com outra Idempotency-Key.

Exemplo de SUBSCRIPTION_CONFIRMED (resumido):

{
  "id": "3c4d5e6f-7a8b-4c9d-0e1f-2a3b4c5d6e7f",
  "event": "SUBSCRIPTION_CONFIRMED",
  "creation_date": "2026-09-15T13:00:20.000Z",
  "version": "1.0.0",
  "data": {
    "subscription": {
      "id": "e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b",
      "external_id": "sub_abc123",
      "status": "DRAFT",
      "payment_method": "CREDIT_CARD"
    },
    "source": {
      "channel": "API",
      "api_credential_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7"
    }
  }
}

O webhook da credencial também recebe as assinaturas vendidas no checkout, com data.source.channel: CHECKOUT. Veja Canal da venda.

Um SUBSCRIPTION_CONFIRMED pode chegar antes do SUBSCRIPTION_CREATED: use o status que veio no evento. Veja Processar eventos sem duplicar.

Libere o acesso quando a assinatura ficar ACTIVE

A assinatura fica ACTIVE quando o gateway avisa que a primeira cobrança foi paga.

Nenhum evento de assinatura avisa essa ativação. Para confirmar, consulte a assinatura:

curl "https://api.pagpolar.com/v1/subscriptions/e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b" \
  -H "Authorization: Bearer <SEU_TOKEN_DE_ACESSO>"
const response = await fetch(
  'https://api.pagpolar.com/v1/subscriptions/e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b',
  { headers: { Authorization: `Bearer ${accessToken}` } },
);

console.log(response.status, await response.json());

Resposta 200 (resumida):

{
  "data": {
    "id": "e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b",
    "status": "ACTIVE",
    "payment_method": "CREDIT_CARD",
    "next_billing_at": "2026-10-15T13:00:00.000Z"
  }
}

Com ACTIVE, libere o acesso. O que fazer em cada status está em Ciclo de vida.

Consulte com moderação: a consulta conta no limite geral da credencial. Veja Limites de requisição. Contrato completo: GET /subscriptions/{id}.

Acompanhe as renovações

A cada ciclo, o gateway cobra o cartão sozinho. Você não precisa chamar a API.

Quando a cobrança de um ciclo, a partir do segundo, é paga, chega SUBSCRIPTION_RENEWED:

  • a assinatura continua ACTIVE;
  • subscription.next_billing_at traz a data da próxima cobrança;
  • cada ciclo gera uma venda nova, com id próprio.

Mantenha o acesso e atualize a data da próxima cobrança no seu sistema.

Se o gateway informar uma cobrança pendente, a assinatura fica PROCESSING, sem evento próprio. Quando a cobrança é paga, ela volta para ACTIVE.

Limite de ciclos

cycle_limit mostra quantos ciclos a assinatura cobra no máximo. null quando não há limite. A criação de oferta de plano pela API não aceita esse limite.

Ciclo de vida

A tabela resume os status que aparecem neste guia. Todos os status e transições estão em Ciclo de vida da assinatura.

StatusSignificadoO que fazer
DRAFTAssinatura registrada. O gateway ainda não confirmou ou a primeira cobrança ainda não foi paga.Não libere o acesso. Consulte de novo mais tarde.
FAILEDO gateway recusou a criação.Não libere o acesso. Crie uma assinatura nova.
ACTIVEA cobrança do ciclo foi paga.Libere ou mantenha o acesso. next_billing_at mostra a data da próxima cobrança.
PROCESSINGO gateway informou uma cobrança pendente.Espere.

Eventos de webhook deste fluxo

Os eventos da criação estão em Espere a confirmação do gateway. Nas renovações chega SUBSCRIPTION_RENEWED: veja Acompanhe as renovações.

Quando algo dá errado

Além dos erros comuns a todas as rotas, este fluxo pode responder:

PassoSituaçãoRespostaComo resolver
1 e 2Campo obrigatório ausente, ou cycle fora da lista, como cycle: DAILY.400 invalid_request. A message traz o campo, como O campo price é obrigatório ou Para o campo cycle os valores permitidos são [WEEKLY,MONTHLY,YEARLY]. Sem name no passo 1, a mensagem é O campo nome é obrigatório.Corrija o campo indicado em message.
2price com casas decimais, como 19.9.400 invalid_requestprice é em centavos e só aceita inteiro. Envie 1990.
2price negativo ou cycle_interval menor que 1, como 0.400 invalid_request com message vaziaEnvie price em centavos a partir de 0 e cycle_interval inteiro a partir de 1.
2<ID_DO_PLANO> errado, de outra conta ou de um produto que não é plano.404 com Plano não encontradoUse o data.id do passo 1.
2max_credit_card_installments maior que 1.400 com Ofertas de plano não podem ser parceladas no cartão de crédito (máximo de 1x)Envie 1 ou não envie o campo.
3Dados do cartão inválidos, como número de cartão inválido, mês 13 ou ano fora de 2000 a 2100.400 invalid_request, em geral com message vaziaConfira os campos na tabela do passo 3.
3Código da oferta errado, de outra conta, de uma oferta oculta ou de uma oferta que não é de plano.404 com Oferta de plano não encontradaUse o data.identifier do passo 2.
3Oferta de plano desativada.409 com Oferta inativaAtive a oferta ou use outra.
3Oferta de plano com data de expiração vencida.409 com Oferta expiradaUse outra oferta.
3Cartão desligado na oferta de plano.409 com Método de pagamento CREDIT_CARD não habilitado para esta ofertaLigue is_enabled_credit_card na oferta e confira o valor mínimo.
3installments maior que o máximo da oferta.400 com Número de parcelas acima do permitido para esta oferta (máximo 1)Envie installments: 1.
4O gateway recusou a assinatura.SUBSCRIPTION_FAILED, status FAILEDPeça outro cartão e crie uma assinatura nova com outra Idempotency-Key.
5Id da assinatura errado ou de outra conta.404 com Assinatura não encontradaUse o data.id do passo 3.

Confira no painel

O plano aparece em Meus produtos → Assinaturas. Na aba Ofertas e Configurações ficam as ofertas de plano, com o código, o preço por ciclo, os meios de pagamento e as parcelas:

Tela do plano no painel, aba Ofertas e Configurações, com as ofertas de plano, o código de cada uma, o preço por ciclo e as parcelas

Cada cobrança da assinatura vira uma venda em Vendas → Minhas vendas. Esta é a venda do primeiro ciclo, paga no cartão:

Tela Detalhes da venda com a venda do primeiro ciclo de uma assinatura paga no cartão de crédito

Próximos passos