# Criar produto e oferta

URL: https://staging.pagpolar.com/docs/guias/jornadas/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](/docs/guias/conceitos/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.

```mermaid
sequenceDiagram
  autonumber
  participant S as Seu servidor
  participant A as API PagPolar
  S->>A: POST /products com name e description
  A-->>S: 201 com data.id do produto
  S->>A: POST /offers com product_id, price, meios e parcelas
  A-->>S: 201 com data.id e data.identifier da oferta
  S->>A: GET /offers/{identifier}
  A-->>S: 200 com os dados da oferta
```

## Antes de começar

* Uma chave de API. Se ainda não tem, siga o [Início rápido](/docs/guias/inicio-rapido).
* Um token de acesso. Veja [Autenticação](/docs/guias/fundamentos/autenticacao#obter-o-token); 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 `.mjs` e rode com `node 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

1. **Crie o produto**

   Envie o nome do produto. A descrição é opcional.

   #### cURL

   ```bash
           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"
             }'
           ```

   #### Node.js

   ```js
           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):

       ```json
       {
         "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, vale `7`.

   > **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](/docs/guias/jornadas/vender-um-produto-fisico). 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`](/docs/referencia/produtos/create-product).

2. **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](/docs/guias/fundamentos/valores-datas-e-identificadores#valores-nas-respostas).

   #### cURL

   ```bash
           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
             }'
           ```

   #### Node.js

   ```js
           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](#meios-de-pagamento).                                                 |
       | `is_enabled_billet`            | Não         | `true`             | Aceita boleto. Veja o [valor mínimo](#meios-de-pagamento).                                              |
       | `is_enabled_credit_card`       | Não         | `true`             | Aceita cartão de crédito. Veja o [valor mínimo](#meios-de-pagamento).                                   |
       | `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](#parcela-minima). |
       | `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_card` e `billet`). 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](#parcela-minima). 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}`](/docs/referencia/ofertas/update-offer), 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):

       ```json
       {
         "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`](/docs/referencia/ofertas/create-offer).

3. **Consulte a oferta**

   Antes de vender, confira se a oferta está como você espera. Envie o código da oferta ou o `id`.

   #### cURL

   ```bash
           curl "https://api.pagpolar.com/v1/offers/<CODIGO_DA_OFERTA>" \
             -H "Authorization: Bearer <SEU_TOKEN_DE_ACESSO>"
           ```

   #### Node.js

   ```js
           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}`](/docs/referencia/ofertas/get-offer). Para ver todas as ofertas de um produto, use [`GET /offers/by-product/{id}`](/docs/referencia/ofertas/list-product-offers).

## 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](/docs/guias/fundamentos/erros#erros-comuns), 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](#parcela-minima). |
| 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.                                             |

## Próximos passos

- [Vender com PIX ou boleto](/docs/guias/jornadas/vender-com-pix-ou-boleto) — Use o código da oferta para cobrar por PIX ou boleto.
- [Vender com cartão de crédito](/docs/guias/jornadas/vender-com-cartao) — Cobre no cartão, parcelado ou não.
- [Vender com afiliado](/docs/guias/jornadas/vender-com-afiliado) — Credite a venda ao afiliado que trouxe o cliente.
- [Ofertas, planos e ofertas ocultas](/docs/guias/conceitos/ofertas-planos-e-ofertas-ocultas) — Informe a oferta na hora da venda, sem criar antes.
