Criar produto e oferta
Crie um produto, crie a oferta com preço, meios de pagamento e parcelas, e obtenha o código da oferta para vender.
Toda venda pela API aponta para uma oferta. A oferta pertence a um produto. Por isso, antes da primeira cobrança, você cria os dois.
Use este guia quando o preço é fixo e você quer reaproveitar a mesma oferta em muitas vendas. Se o preço é decidido na hora, como num orçamento, você pode informar a oferta direto na cobrança. Veja Ofertas, planos e ofertas ocultas.
Assinatura segue outro caminho
Para cobrança recorrente, crie um plano com POST /plans e a oferta de plano com POST /plans/{id}/offers. Este guia cobre só o produto avulso.
Visão geral
O diagrama mostra as três chamadas deste guia, na ordem. As mensagens 1 e 2 são o passo 1. As mensagens 3 e 4 são o passo 2. As mensagens 5 e 6 são o passo 3.
Antes de começar
- Uma chave de API. Se ainda não tem, siga o Início rápido.
- Um token de acesso. Veja Autenticação; os exemplos em Node.js assumem a variável
accessToken. - Um terminal com
curl, ou Node.js 18 ou mais novo. Salve os exemplos em Node.js em um arquivo.mjse rode comnode arquivo.mjs.
Estas chamadas não têm proteção contra repetição
POST /products e POST /offers não usam Idempotency-Key. Cada chamada cria um registro novo. Se você repetir a chamada, fica com dois produtos ou duas ofertas. Guarde o id retornado antes de tentar de novo.
Passo a passo
Crie o produto
Envie o nome do produto. A descrição é opcional.
curl -X POST "https://api.pagpolar.com/v1/products" \
-H "Authorization: Bearer <SEU_TOKEN_DE_ACESSO>" \
-H "Content-Type: application/json" \
-d '{
"name": "Curso de fotografia",
"description": "Curso online com 20 aulas"
}'const response = await fetch('https://api.pagpolar.com/v1/products', {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Curso de fotografia',
description: 'Curso online com 20 aulas',
}),
});
console.log(response.status, await response.json());| Campo | Obrigatório | O que é |
|---|---|---|
name | Sim | Nome do produto, até 255 caracteres. |
description | Não | Descrição, até 5000 caracteres. Aceita texto vazio ou null. |
A rota aceita só esses dois campos. Qualquer outro campo responde 400.
Resposta 201 (resumida):
{
"data": {
"id": "c4d5e6f7-a8b9-4c0d-8e2f-3a4b5c6d7e8f",
"name": "Curso de fotografia",
"description": "Curso online com 20 aulas",
"type": "DIGITAL",
"is_active": true,
"warranty_time": 7
}
}O que a API faz por você:
- O produto nasce digital (
type: DIGITAL) e ativo (is_active: true). warranty_timeé o prazo de garantia em dias. Vem da configuração da plataforma. Sem configuração, vale7.
Produto físico é cadastrado no painel
A API cria só produtos DIGITAL. Produto físico é cadastrado no painel, com peso, dimensões e frete — e aí vende pela API normalmente: veja Vender um produto físico. O envio e o código de rastreio continuam só no painel.
Guarde data.id. Ele é o <ID_DO_PRODUTO> do próximo passo. Contrato completo: POST /products.
Crie a oferta
A oferta define o preço, os meios de pagamento e o máximo de parcelas no cartão.
price vai em centavos, como número inteiro (9700 = R$ 97,00), e volta em reais na resposta (97). Veja Valores nas respostas.
curl -X POST "https://api.pagpolar.com/v1/offers" \
-H "Authorization: Bearer <SEU_TOKEN_DE_ACESSO>" \
-H "Content-Type: application/json" \
-d '{
"product_id": "<ID_DO_PRODUTO>",
"title": "Oferta principal",
"price": 9700,
"is_enabled_pix": true,
"is_enabled_billet": true,
"is_enabled_credit_card": true,
"max_credit_card_installments": 12
}'const response = await fetch('https://api.pagpolar.com/v1/offers', {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
product_id: '<ID_DO_PRODUTO>',
title: 'Oferta principal',
price: 9700,
is_enabled_pix: true,
is_enabled_billet: true,
is_enabled_credit_card: true,
max_credit_card_installments: 12,
}),
});
console.log(response.status, await response.json());| Campo | Obrigatório | Se você não enviar | O que é |
|---|---|---|---|
product_id | Sim | — | id do produto criado no passo 1. |
price | Sim | — | Preço em centavos, número inteiro a partir de 0. |
title | Não | Fica sem título | Nome da oferta, até 255 caracteres. |
is_enabled_pix | Não | true | Aceita PIX. Veja o valor mínimo. |
is_enabled_billet | Não | true | Aceita boleto. Veja o valor mínimo. |
is_enabled_credit_card | Não | true | Aceita cartão de crédito. Veja o valor mínimo. |
max_credit_card_installments | Não | 12 | Máximo de parcelas no cartão. Número inteiro a partir de 1. Veja a parcela mínima. |
is_active | Não | true | Oferta ativa. Uma oferta inativa não vende. |
Meios de pagamento e valor mínimo
Os três meios de pagamento começam ligados. Cada um tem um valor mínimo, conferido ao salvar a oferta:
| Meio | Valor mínimo | Em price (centavos) |
|---|---|---|
| PIX | R$ 5,00 | 500 |
| Cartão de crédito | R$ 5,00 | 500 |
| Boleto | R$ 10,00 | 1000 |
- Abaixo do mínimo, a API desliga o meio, mesmo que você envie
true. - A partir do mínimo, o meio fica ligado, a não ser que você envie
false. - A resposta mostra o resultado em
payment_methods(pix,credit_cardebillet). Confira sempre.
Exemplos, com max_credit_card_installments: 1 e sem enviar is_enabled_*:
price | Meios ligados em payment_methods |
|---|---|
300 (R$ 3,00) | Nenhum meio passa do mínimo. Na oferta avulsa, a criação responde 400 pela parcela mínima. Numa oferta de plano, que não tem parcela mínima, a oferta é criada com os três meios desligados. |
700 (R$ 7,00) | PIX e cartão. O boleto fica desligado. |
1500 (R$ 15,00) | PIX, cartão e boleto. |
Ao editar a oferta com PATCH /offers/{id}, a regra roda de novo:
| Você envia | O que acontece com os meios |
|---|---|
price | Os três meios são recalculados com o preço novo. Um meio desligado só por causa do preço volta a ligar se o preço chegar ao mínimo. Para manter um meio desligado, envie false na mesma chamada. |
Só is_enabled_*, sem price | Só os campos enviados são recalculados, com o preço atual da oferta. |
Nem price nem is_enabled_* | Os meios não mudam. |
A regra da parcela mínima
A API converte price para reais, dividindo por 100, e divide o resultado por max_credit_card_installments. O que sobra é o valor de cada parcela. Por padrão, cada parcela precisa valer pelo menos R$ 5,00.
Ao contrário do valor mínimo do meio, que só desliga o meio, a parcela mínima recusa a oferta: abaixo dela, a resposta é 400, e a mensagem mostra o valor da parcela e o mínimo em vigor. A regra roda ao criar e ao editar a oferta, mesmo com o cartão desligado.
price | max_credit_card_installments | Parcela | Resultado |
|---|---|---|---|
9700 | 12 | R$ 8,08 | Criada |
6000 | 12 | R$ 5,00 | Criada |
4990 | não enviado (vale 12) | R$ 4,16 | 400 |
4990 | 9 | R$ 5,54 | Criada |
1000 | 12 | R$ 0,83 | 400 |
0 | qualquer | R$ 0,00 | 400 |
Sem max_credit_card_installments, a conta usa 12 parcelas: todo price abaixo de 6000 recusa a criação. Para uma oferta barata, envie max_credit_card_installments: 1. Mesmo assim, um price abaixo de 500 não passa.
Resposta 201 (resumida):
{
"data": {
"id": "d5e6f7a8-b9c0-4d1e-8f3a-4b5c6d7e8f9a",
"identifier": "PPP1234567890",
"title": "Oferta principal",
"price": 97,
"is_active": true,
"product_id": "c4d5e6f7-a8b9-4c0d-8e2f-3a4b5c6d7e8f",
"payment_methods": {
"pix": true,
"credit_card": true,
"billet": true
},
"max_credit_card_installments": 12
}
}| Campo | O que fazer com ele |
|---|---|
identifier | É o código da oferta: PPP seguido de 10 dígitos. Guarde. É o <CODIGO_DA_OFERTA> que você envia em offer_identifier nas cobranças. |
id | Id interno da oferta (uuid). Use para editar a oferta com PATCH /offers/{id}. |
payment_methods | Os meios de pagamento ligados. billet é o boleto. |
Contrato completo: POST /offers.
Consulte a oferta
Antes de vender, confira se a oferta está como você espera. Envie o código da oferta ou o id.
curl "https://api.pagpolar.com/v1/offers/<CODIGO_DA_OFERTA>" \
-H "Authorization: Bearer <SEU_TOKEN_DE_ACESSO>"const response = await fetch('https://api.pagpolar.com/v1/offers/<CODIGO_DA_OFERTA>', {
headers: { Authorization: `Bearer ${accessToken}` },
});
console.log(response.status, await response.json());O caminho aceita três formatos:
| Você envia | Como a API procura |
|---|---|
| Um uuid | Pelo id da oferta. |
O código, com ou sem o prefixo, como PPP1234567890 ou 1234567890 | Pelo identifier. |
| Um link que termina com o código | Usa só os números do último pedaço do link. |
A resposta 200 traz os mesmos campos da criação.
Confira três coisas antes de vender:
| Campo | O que precisa estar certo |
|---|---|
is_active | true. A consulta também devolve ofertas inativas, mas uma cobrança com oferta inativa responde 409 com Oferta inativa. |
payment_methods | O meio que você vai cobrar está true. |
max_credit_card_installments | Cobre o número de parcelas que você vai oferecer no cartão. |
Contrato completo: GET /offers/{identifier}. Para ver todas as ofertas de um produto, use GET /offers/by-product/{id}.
Confira no painel
O produto e a oferta criados pela API aparecem no painel da sua conta.
Em Meus produtos → Produtos, cada produto aparece com o tipo, o status e a quantidade de ofertas:

Ao abrir o produto, a aba Ofertas e Configurações lista as ofertas com o código (o mesmo usado em offer_identifier), o preço, os meios de pagamento e as parcelas:

Eventos de webhook deste fluxo
Nenhum. Criar ou consultar produto e oferta não envia webhook. Os eventos começam quando você cria uma cobrança.
Quando algo dá errado
Além dos erros comuns a todas as rotas, este fluxo pode responder:
| Passo | Situação | Resposta | Como resolver |
|---|---|---|---|
| 1 | Faltou name. | 400 invalid_request com O campo nome é obrigatório | Envie name. |
| 1 | name com mais de 255 caracteres. | 400 invalid_request com O campo nome deve ter no máximo 20 caracteres | O limite real é 255. Encurte o nome. |
| 1 | Campo que a rota não aceita, como type ou price. | 400 invalid_request com O campo type não e permitido | Envie só name e description. O preço vai na oferta. |
| 2 | Faltou product_id ou price. | 400 invalid_request com O campo product_id é obrigatório ou O campo price é obrigatório | Envie os dois campos. |
| 2 | product_id não é um uuid. | 400 invalid_request com O campo product_id deve ser um UUID válido | Use o data.id do passo 1, não o nome do produto. |
| 2 | price com texto que não é número. | 400 invalid_request com O campo price deve ser um número | Envie um número em centavos, como 9700. |
| 2 | price com casas decimais, como 49.9. | 400 invalid_request | price é em centavos e só aceita inteiro. Envie 4990. |
| 2 | price negativo, ou max_credit_card_installments igual a 0 ou com casas decimais. | 400 invalid_request com message vazia | Envie price inteiro a partir de 0 e parcelas como número inteiro a partir de 1. |
| 2 | O produto não existe, foi removido ou é de outra conta. | 404 not_found com Produto não encontrado | Confira o product_id. A chave precisa ser da mesma conta que criou o produto. |
| 2 | Parcela abaixo do mínimo. | 400 invalid_request com O valor da parcela (R$ 4.16) fica abaixo do mínimo permitido (R$ 5.00). Reduza o número de parcelas ou aumente o preço da oferta. | Diminua max_credit_card_installments ou aumente price. Veja a regra da parcela mínima. |
| 3 | Código errado, oferta removida, de outra conta ou oferta oculta. | 404 not_found com Oferta não encontrada | Use o identifier do passo 2. Oferta oculta não abre por código. |