> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ephra.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Catálogo

> Crie um produto, publique uma oferta e amarre um cupom de lançamento a ele — o caminho completo, com as respostas reais da API.

O catálogo é o que a sua empresa vende: **produtos**, as **ofertas** que dão preço a eles, e os **cupons** que descontam esse preço. São 38 operações; esta página percorre as cinco que qualquer integração usa primeiro.

<Info>
  Todos os exemplos assumem `$TOKEN` no ambiente. Se ainda não tem um, comece por [Primeiros passos](/guias/primeiros-passos).
</Info>

## A história

A Ana vende um curso de tráfego pago. Ela quer cadastrar o produto pela API, criar a oferta anual de R\$ 499 e deixar um cupom de 25% pronto para o lançamento de novembro. Quatro chamadas.

## 1. Descubra a categoria

Um produto nasce dentro de uma categoria, e o `categoryId` precisa existir. Liste antes de criar:

```bash theme={null}
curl -s "https://api.ephra.io/v1/product-categories?page=1&pageSize=5" \
  -H "Authorization: Bearer $TOKEN"
```

```json theme={null}
{
  "success": true,
  "data": [
    {
      "id": 1,
      "name": "Cursos e Treinamentos",
      "description": null,
      "productCount": 436,
      "createdAt": "2026-09-12T04:59:54.871Z",
      "updatedAt": "2026-09-12T04:59:54.871Z"
    }
  ],
  "pagination": { "page": 1, "pageSize": 5, "total": 1, "totalPages": 1 }
}
```

## 2. Crie o produto

`price` é o preço de vitrine, **em centavos**. `refundPeriodDays` é a garantia que aparece no checkout e que o [prazo de reembolso](/guias/reembolsos) vai respeitar.

```bash theme={null}
curl -s -X POST https://api.ephra.io/v1/products \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Curso de Tráfego Pago 2026",
    "description": "Do primeiro anúncio ao primeiro real faturado, em 8 semanas.",
    "categoryId": 1,
    "price": 49900,
    "productType": "digital",
    "billingType": "one_time",
    "deliveryType": "link",
    "salesPage": "https://exemplo.com.br/trafego-pago",
    "supportEmail": "suporte@exemplo.com.br",
    "refundPeriodDays": 7
  }'
```

```json theme={null}
{
  "success": true,
  "data": {
    "id": "cmty05wl0000apyg1bx1566ej",
    "name": "Curso de Tráfego Pago 2026",
    "description": "Do primeiro anúncio ao primeiro real faturado, em 8 semanas.",
    "status": "approved",
    "categoryId": 1,
    "price": 49900,
    "productType": "digital",
    "billingType": "one_time",
    "deliveryType": "link",
    "imageUrl": null,
    "salesPage": "https://exemplo.com.br/trafego-pago",
    "productorName": null,
    "supportEmail": "suporte@exemplo.com.br",
    "supportPhone": null,
    "refundPeriodDays": 7,
    "createdAt": "2026-09-12T06:27:03.358Z",
    "updatedAt": "2026-09-12T06:27:03.358Z"
  }
}
```

A resposta é `201`. Guarde o `data.id`: ele é o `productId` de quase tudo que vem depois.

<Note>
  Criar o produto pela API já monta um checkout padrão e uma oferta padrão junto. Você não precisa de `POST /v1/products/{productId}/publish` — essa rota só tira do rascunho os produtos criados no editor novo do painel, e responde `400` para os demais.
</Note>

## 3. Crie a oferta

A oferta é o que o comprador realmente compra: um preço, um conjunto de meios de pagamento e um `slug` público.

```bash theme={null}
curl -s -X POST https://api.ephra.io/v1/products/cmty05wl0000apyg1bx1566ej/offers \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Plano Anual — Curso de Tráfego Pago",
    "price": 49900,
    "paymentMethods": ["pix", "credit_card"],
    "compareAtPrice": 69900,
    "sku": "TRAFEGO-ANUAL"
  }'
```

```json theme={null}
{
  "success": true,
  "data": {
    "id": 540,
    "productId": "cmty05wl0000apyg1bx1566ej",
    "title": "Plano Anual — Curso de Tráfego Pago",
    "slug": "n8cp1gb",
    "price": 49900,
    "compareAtPrice": 69900,
    "sku": "TRAFEGO-ANUAL",
    "paymentMethods": ["pix", "credit_card"],
    "active": true,
    "isDefault": true,
    "promotional": false,
    "hiddenFromAffiliates": false,
    "sortOrder": 0,
    "expiresAt": null,
    "frequency": null,
    "renewalType": null,
    "maxCharges": null,
    "hasDifferentFirstPrice": false,
    "firstChargePrice": null,
    "admissionFeePrice": null,
    "createdAt": "2026-09-12T06:27:03.428Z",
    "updatedAt": "2026-09-12T06:27:03.428Z"
  }
}
```

`compareAtPrice` é o "de R$ 699 por R$ 499" do checkout — ele não cobra nada, só aparece riscado. O `id` da oferta é um **inteiro**, diferente do `id` do produto, que é uma string.

<Tip>
  Para assinatura, use `POST /v1/products/{productId}/offer-plans` em vez de `/offers`: ele aceita `frequency`, `renewalType`, `firstChargePrice` e taxa de adesão. A cobrança recorrente em si está em [Assinaturas](/guias/assinaturas).
</Tip>

## 4. Crie o cupom de lançamento

O cupom vive no nível da empresa e depois é amarrado a produtos ou ofertas. `appliesTo: "selected"` significa "só onde eu mandar".

```bash theme={null}
curl -s -X POST https://api.ephra.io/v1/coupons \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "LANCAMENTO25",
    "discountType": "percent",
    "discountPercent": 25,
    "appliesTo": "selected",
    "paymentMethods": ["pix", "credit_card"],
    "startAt": "2026-11-20T03:00:00.000Z",
    "expiresAt": "2026-12-01T02:59:59.000Z",
    "maxRedemptions": 500
  }'
```

```json theme={null}
{
  "success": true,
  "data": {
    "id": 46,
    "code": "LANCAMENTO25",
    "discountType": "percent",
    "discountPercent": 25,
    "discountAmountInCents": null,
    "appliesTo": "selected",
    "paymentMethods": ["pix", "credit_card"],
    "startAt": "2026-11-20T03:00:00.000Z",
    "expiresAt": "2026-12-01T02:59:59.000Z",
    "active": true,
    "maxRedemptions": 500,
    "autoApply": false,
    "triggerProductId": null,
    "triggerOfferId": null,
    "lookbackPeriod": null,
    "createdAt": "2026-09-12T06:27:03.523Z",
    "updatedAt": "2026-09-12T06:27:03.523Z"
  }
}
```

## 5. Amarre o cupom ao produto

```bash theme={null}
curl -s -X POST https://api.ephra.io/v1/coupons/46/products \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"productIds": ["cmty05wl0000apyg1bx1566ej"]}'
```

```json theme={null}
{
  "success": true,
  "data": {
    "couponId": 46,
    "productIds": ["cmty05wl0000apyg1bx1566ej"]
  }
}
```

Confira pelo outro lado — quais cupons valem para este produto:

```bash theme={null}
curl -s "https://api.ephra.io/v1/products/cmty05wl0000apyg1bx1566ej/coupons?page=1&pageSize=10" \
  -H "Authorization: Bearer $TOKEN"
```

```json theme={null}
{
  "success": true,
  "data": [
    {
      "id": 46,
      "code": "LANCAMENTO25",
      "discountType": "percent",
      "discountPercent": 25,
      "appliesTo": "selected",
      "paymentMethods": ["pix", "credit_card"],
      "startAt": "2026-11-20T03:00:00.000Z",
      "expiresAt": "2026-12-01T02:59:59.000Z",
      "active": true,
      "maxRedemptions": 500,
      "autoApply": false,
      "lookbackPeriod": null,
      "redemptionCount": 0,
      "createdAt": "2026-09-12T06:27:03.523Z",
      "updatedAt": "2026-09-12T06:27:03.523Z"
    }
  ],
  "pagination": { "page": 1, "pageSize": 10, "total": 1, "totalPages": 1 }
}
```

`redemptionCount` sobe a cada uso confirmado; quando alcança `maxRedemptions`, o cupom para de valer sozinho.

## Erros que você vai encontrar

<AccordionGroup>
  <Accordion title="422 — discountType: Required">
    `discountType` decide qual dos dois campos de valor o cupom usa: `percent` pede `discountPercent` (1 a 95), `fixed` pede `discountAmountInCents`. Mandar o valor sem o tipo reprova.
  </Accordion>

  <Accordion title="409 ao criar um cupom com código repetido">
    O par empresa + código é único, e a API recusa o segundo antes de gravar qualquer coisa:

    ```json theme={null}
    { "success": false, "message": "Já existe um cupom com o código LANCAMENTO25 nesta empresa. Escolha outro código." }
    ```

    Trate `409` como "já existe": liste com `GET /v1/coupons?code=LANCAMENTO25`, reutilize o cupom que voltar, ou escolha outro código. Renomear um cupom existente para um código já usado, com `PATCH /v1/coupons/{couponId}`, responde o mesmo `409`.
  </Accordion>

  <Accordion title="400 ao publicar um produto">
    ```json theme={null}
    { "success": false, "message": "Produto não encontrado ou não pertence à empresa" }
    ```

    Produtos criados pela API já nascem vendáveis. `POST /v1/products/{productId}/publish` só se aplica a rascunhos do editor novo.
  </Accordion>
</AccordionGroup>

## Todas as operações do domínio

<CardGroup cols={2}>
  <Card title="Produtos" icon="box" href="/api-reference/catálogo/listar-produtos">
    Listar, criar, consultar, atualizar, excluir, imagem, conteúdo e template de mensagens.
  </Card>

  <Card title="Ofertas" icon="tag" href="/api-reference/catálogo/listar-as-ofertas-de-um-produto">
    Ofertas avulsas, planos de assinatura e o grupo de ofertas ativo.
  </Card>

  <Card title="Cupons" icon="ticket" href="/api-reference/catálogo/listar-cupons">
    Cupons da empresa, vínculo com produtos e ofertas, ativação.
  </Card>

  <Card title="Order bumps e upsell" icon="cart-plus" href="/api-reference/catálogo/listar-order-bumps">
    O que sobe o ticket dentro e depois do checkout.
  </Card>
</CardGroup>
