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

# API de Campanhas

> Leia as campanhas de incentivo da sua empresa a partir do seu próprio backend — vitrine de prêmios, saldo do colaborador e o que ele já alcança, feita para telas iniciais de super app e painéis de parceiros.

# API de Campanhas

<Note>
  Os exemplos abaixo mostram o cabeçalho no formato de rede `Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE`. Para calcular a assinatura no seu código, use o helper `signedRequest` em [Autenticação](/pt/api/authentication).
</Note>

A API de Campanhas permite que o seu backend leia as **campanhas de incentivo** da sua empresa no SalesOS — quais estão vigentes, o que a vitrine oferece, quanto cada colaborador tem para gastar e quantos prêmios ele já alcança. Foi feita para **compor as suas próprias telas** antes de o usuário abrir o módulo do SalesOS.

É uma API **somente leitura, servidor a servidor**: você consulta colaboradores por CPF ou e-mail, em lote, e o SalesOS responde com as campanhas vigentes e o estado de cada pessoa nelas.

<Note>
  Uma campanha é **configurada pela sua empresa**: nome, vitrine, preços em pontos, categorias, regras de nível e até o nome da moeda. Esta API devolve o que estiver configurado — ela não conhece nenhuma campanha específica.
</Note>

## Como funciona

1. **Você recebe uma API Key** com o escopo `campaigns:read` (Admin > Integrações > API Keys)
2. **Seu backend consulta** o estado de um ou vários colaboradores (por CPF ou e-mail)
3. **O SalesOS responde** com as campanhas vigentes — os dados da campanha **uma vez**, e o estado de cada colaborador em seguida

<Note>
  Os metadados da campanha saem **uma vez no topo** da resposta, não repetidos por pessoa. Num lote de 500 colaboradores isso é a diferença entre uma resposta enxuta e uma de megabytes.
</Note>

<Warning>
  Este endpoint **não** resgata prêmios nem aceita termos. É leitura — combine com um deep link para o módulo do SalesOS na hora da ação.
</Warning>

***

## Autenticação

```
Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE
```

Veja a [página de Autenticação](/pt/api/authentication) para criar e gerenciar API Keys.

| Propriedade          | Detalhes                                                 |
| -------------------- | -------------------------------------------------------- |
| **Cabeçalho**        | `Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE` |
| **Escopo exigido**   | `campaigns:read`                                         |
| **Limite de uso**    | Configurável por chave (padrão: 1000 requisições/hora)   |
| **Formato da chave** | `sk_live_` (produção) ou `sk_test_` (testes)             |

***

## Ambientes

<Tabs>
  <Tab title="Produção">
    **URL base:** `https://api.play2sell.com`
  </Tab>

  <Tab title="Staging">
    **URL base:** `https://api-staging.play2sell.com`
  </Tab>
</Tabs>

***

## Referência do endpoint

```
POST https://api.play2sell.com/functions/v1/campaigns-partner-api
```

Duas ações no campo `action`: `campaign_status` e `campaign_catalog`.

Cada colaborador é identificado por **CPF** (qualquer formato — os dígitos são normalizados) ou **e-mail**. Quando os dois vêm, o CPF prevalece.

***

### Ação: campaign\_status

As campanhas vigentes da sua empresa e o estado de cada colaborador nelas.

#### Esquema da requisição

<ParamField body="action" type="string" required>
  Deve ser `"campaign_status"`
</ParamField>

<ParamField body="collaborators" type="array" required>
  Lista de referências de colaboradores (máximo 500)
</ParamField>

<ParamField body="collaborators[].cpf" type="string">
  CPF, em qualquer formato (`52998224725` ou `529.982.247-25`). Deve conter exatamente 11 dígitos.
</ParamField>

<ParamField body="collaborators[].email" type="string">
  E-mail — usado apenas quando o `cpf` está ausente
</ParamField>

<ParamField body="campaign_slug" type="string">
  Filtra uma campanha específica. Ausente = todas as vigentes.
</ParamField>

<ParamField body="locale" type="string">
  Idioma dos textos configuráveis. Ausente = o padrão da campanha.
</ParamField>

#### Exemplo

```bash theme={null}
curl -X POST https://api.play2sell.com/functions/v1/campaigns-partner-api \
  -H "Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "campaign_status",
    "collaborators": [{ "cpf": "529.982.247-25" }]
  }'
```

**Resposta (200):**

```json theme={null}
{
  "data": {
    "timezone": "America/Sao_Paulo",
    "reference_date": "2026-08-12",
    "campaigns": [
      {
        "id": "aaaaaaaa-0000-0000-0000-000000000001",
        "slug": "campanha-do-cliente",
        "name": "Campanha do Cliente",
        "tagline": "Sua vitrine de prêmios",
        "status": "active",
        "default_locale": "pt",
        "locales": ["pt"],
        "currency": { "label": "pontos" },
        "catalog": {
          "rewards": 22,
          "awarded_only": 2,
          "categories": 6,
          "cheapest_points": 200,
          "priciest_points": 22000
        }
      }
    ],
    "collaborators": [
      {
        "cpf": "52998224725",
        "found": true,
        "user_id": "8f14e45f-0000-0000-0000-000000000001",
        "name": "Maria Santos",
        "membership_status": "active",
        "balance": {
          "spendable": 1500,
          "source": "playcoin",
          "wallet_enabled": true
        },
        "score": { "xp": 1800, "level": 3, "note": "diverges_from_balance" },
        "campaigns": [
          {
            "campaign_id": "aaaaaaaa-0000-0000-0000-000000000001",
            "slug": "campanha-do-cliente",
            "eligible_rewards": 18,
            "affordable_rewards": 7,
            "next_reachable_points": 2000,
            "redemptions": { "open": 1, "fulfilled": 3 }
          }
        ]
      }
    ],
    "total": 1,
    "found": 1
  },
  "meta": {
    "request_id": "1f6c1b2e-0000-4a1b-8c2d-000000000001",
    "timestamp": "2026-08-12T13:45:00.000Z"
  }
}
```

#### Campos da campanha

| Campo                                         | Significado                                                                                          |
| --------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `slug` · `name` · `tagline`                   | Identificação e textos configurados pela sua empresa                                                 |
| `currency.label`                              | **O nome da moeda, como a sua empresa o configurou.** Renderize este valor — não fixe um nome no app |
| `catalog.rewards`                             | Prêmios que podem ser **resgatados com saldo**                                                       |
| `catalog.awarded_only`                        | Prêmios **concedidos** (por ranking ou gatilho), que não estão à venda                               |
| `catalog.categories`                          | Categorias distintas na vitrine                                                                      |
| `catalog.cheapest_points` / `priciest_points` | Faixa de preço da vitrine                                                                            |
| `terms`                                       | Presente só quando a campanha exige aceite, com a versão vigente                                     |

#### Campos do colaborador

| Campo                               | Significado                                                                      |
| ----------------------------------- | -------------------------------------------------------------------------------- |
| `balance.spendable`                 | **O saldo que dá para gastar agora**                                             |
| `balance.wallet_enabled`            | Se a carteira está habilitada para a sua empresa — **leia antes de exibir zero** |
| `score.xp` · `score.level`          | Pontuação e nível. O nível determina quais prêmios o colaborador alcança         |
| `score.note`                        | `diverges_from_balance` quando pontuação e saldo não coincidem                   |
| `campaigns[].eligible_rewards`      | Prêmios que ele **alcança** (regra de nível atendida)                            |
| `campaigns[].affordable_rewards`    | Prêmios que ele **consegue pagar** com o saldo atual                             |
| `campaigns[].next_reachable_points` | Quanto custa o próximo prêmio fora do alcance — o "falta X"                      |
| `campaigns[].terms_accepted`        | Presente só quando a campanha exige aceite                                       |
| `campaigns[].redemptions`           | Resgates em aberto e concluídos                                                  |

<Warning>
  **`spendable: 0` não significa "não tem pontos".** A carteira é habilitada por empresa; enquanto não estiver, o saldo vem zerado mesmo para quem tem pontuação. Sempre leia `wallet_enabled` antes de escrever "sem saldo" na tela — do contrário ela vai afirmar uma ausência que não existe.
</Warning>

<Tip>
  **Alcançar e poder pagar são contas diferentes.** Um prêmio pode estar liberado pelo nível e ainda assim custar mais do que o colaborador tem. Por isso `eligible_rewards` e `affordable_rewards` são campos separados.
</Tip>

***

### Ação: campaign\_catalog

O mesmo conteúdo com o recorte de catálogo. Como a vitrine cresce por colaborador, o **limite de lote cai para 100**.

```bash theme={null}
curl -X POST https://api.play2sell.com/functions/v1/campaigns-partner-api \
  -H "Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "campaign_catalog",
    "collaborators": [{ "cpf": "529.982.247-25" }],
    "campaign_slug": "campanha-do-cliente"
  }'
```

***

## Modo self (sessão federada)

Com um token de usuário federado, envie `Authorization: Bearer <jwt>` e **omita** `collaborators`. A resposta cobre apenas o próprio usuário do token.

<Warning>
  Enviar `collaborators` com um token de usuário é rejeitado com `SELF_MODE_NO_COLLABORATORS`. Consultas em lote exigem uma API Key de parceiro — do contrário, qualquer usuário autenticado poderia ler o saldo de outras pessoas por CPF.
</Warning>

***

## Tratamento de erros

```json theme={null}
{ "error": { "code": "ERROR_CODE", "message": "Descrição legível", "details": [] } }
```

<Warning>
  **São dois formatos, não um.** O bloco acima é o erro do *endpoint*. Falhas na **camada de autenticação** respondem antes de o endpoint rodar, com `error` como **string**:

  ```json theme={null}
  { "error": "Missing Authorization header", "code": "header_missing" }
  ```

  Código que assume `error.code` lê `undefined` em toda falha de autenticação. Ramifique pelo tipo — veja [Convenções da API](/pt/api/conventions#formatos-de-erro).
</Warning>

<AccordionGroup>
  <Accordion title="400 — VALIDATION_ERROR">
    Corpo inválido, lote acima do limite, CPF sem 11 dígitos, item sem `cpf` e sem `email`, ou `campaign_slug` malformado. A lista `details` indica o `index` do item.
  </Accordion>

  <Accordion title="400 — SELF_MODE_NO_COLLABORATORS">
    Uma lista `collaborators` foi enviada junto com um token de usuário.
  </Accordion>

  <Accordion title="401 — UNAUTHORIZED">
    API Key ausente, inválida ou expirada, ou assinatura que não confere.
  </Accordion>

  <Accordion title="403 — FORBIDDEN">
    API Key válida, mas sem o escopo `campaigns:read`.
  </Accordion>

  <Accordion title="405 — METHOD_NOT_ALLOWED">
    Apenas `POST` é aceito.
  </Accordion>

  <Accordion title="429 — RATE_LIMITED">
    Requisições demais nesta hora. Aguarde `retry_after` segundos.
  </Accordion>

  <Accordion title="500 — SERVER_ERROR">
    Erro interno. Tente de novo com espera progressiva (2s, 4s, 8s).
  </Accordion>
</AccordionGroup>

<Tip>
  **Nenhuma campanha vigente não é erro.** A resposta vem com `campaigns: []` e status 200 — a empresa pode simplesmente não ter campanha no ar.
</Tip>

***

## Limites de uso

| Limite                                         | Valor |
| ---------------------------------------------- | ----- |
| Requisições por hora (padrão)                  | 1000  |
| Máximo de colaboradores por `campaign_status`  | 500   |
| Máximo de colaboradores por `campaign_catalog` | 100   |

***

## Segurança

* Somente leitura: esta API nunca cria resgates, nem aceita termos, nem altera saldo
* Cada chave pertence a uma única empresa — um CPF de outra empresa responde `found: false`, e campanhas de outras empresas nunca aparecem
* As requisições são assinadas por HMAC (P2S-SIGN-V1) e registradas para auditoria; documentos nunca são gravados nos logs

<Warning>
  Nunca exponha sua API Key em código do lado do cliente. Esta API deve ser chamada apenas pelo seu servidor.
</Warning>

***

## Próximos passos

<CardGroup cols={2}>
  <Card title="API de Missões" icon="bullseye-arrow" href="/pt/api/integrations/missions">
    O progresso que gera os pontos gastos aqui
  </Card>

  <Card title="Autenticação" icon="key" href="/pt/api/authentication">
    Como criar e gerenciar API Keys
  </Card>
</CardGroup>
