> ## 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.

# Autenticação

> Autentique-se na API de Integração SalesOS usando API Keys com escopo por tenant.

# Autenticação

<Tip>
  Teste requisições assinadas no navegador no [Sandbox da API](https://play2sellsa.github.io/api-sandbox/) — cole sua API key e secret, e o playground assina as requisições automaticamente.
</Tip>

A API de Integração SalesOS usa o esquema de requisições assinadas **P2S-SIGN-V1** no header `Authorization`. As API Keys têm escopo em um único tenant, são hasheadas com bcrypt e suportam rate limiting e listas de IPs permitidos.

| Esquema         | Quando usar                                                   | Formato                                   |
| --------------- | ------------------------------------------------------------- | ----------------------------------------- |
| **P2S-SIGN-V1** | Chamadas server-to-server — assinadas com o secret da API key | `P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE` |

## Ambientes

<Tabs>
  <Tab title="Production">
    **URL Base:** `https://api.play2sell.com`

    **Dashboard:** `https://dashboard.play2sell.com`

    **App:** `https://app.play2sell.com`
  </Tab>

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

    **Dashboard:** `https://dashboard-staging.play2sell.com`

    **App:** `https://app-staging.play2sell.com`
  </Tab>
</Tabs>

## Início Rápido

### 1. Crie uma API Key

Acesse **Integracoes > API Keys** no Dashboard SalesOS:

1. Clique em **Criar API Key**
2. Nomeie sua chave (ex.: "Sincronização CRM Noturna", "Integração Formulário Website")
3. Selecione o escopo: `default:sync`
4. Clique em **Criar**
5. **Copie os dois valores imediatamente** — só são exibidos uma vez:
   * **API Key** — identificador público, ex. `sk_live_a1b2c3d4...`
   * **API Key Secret** — usado para assinar requisições, nunca é enviado pela rede

### 2. Assine e Envie uma Requisição

Para chamadas server-to-server, monte o header `Authorization` como `P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE`. A `SIGNATURE` é o HMAC-SHA256 hex de uma cadeia de chaves derivada em 5 passos:

1. `k1  = HMAC_SHA256(key=API_KEY_SECRET, msg=API_KEY)`
2. `k2  = HMAC_SHA256(key=k1, msg=TIMESTAMP)`
3. `k3  = HMAC_SHA256(key=k2, msg=METHOD)`
4. `k4  = HMAC_SHA256(key=k3, msg=PATH)`
5. `SIG = HMAC_SHA256_HEX(key=k4, msg=PAYLOAD_SHA256_HEX)`

`TIMESTAMP` é Unix epoch em segundos, válido por 30 segundos. `PAYLOAD_SHA256_HEX` é o SHA-256 hex em minúsculas do corpo bruto da requisição (use o digest da string vazia `e3b0c4...b855` quando não houver body).

<Tabs>
  <Tab title="Node.js">
    ```js theme={null}
    import crypto from 'node:crypto';

    const API_KEY = process.env.SALESOS_API_KEY;
    const SECRET  = process.env.SALESOS_API_SECRET;

    const hmac = (key, msg) =>
      crypto.createHmac('sha256', key).update(msg).digest();

    async function signedRequest(method, path, body) {
      const ts          = Math.floor(Date.now() / 1000).toString();
      const bodyStr     = body ? JSON.stringify(body) : '';
      const payloadHash = crypto.createHash('sha256').update(bodyStr).digest('hex');

      const k1  = hmac(SECRET, API_KEY);
      const k2  = hmac(k1, ts);
      const k3  = hmac(k2, method);
      const k4  = hmac(k3, path);
      const sig = hmac(k4, payloadHash).toString('hex');

      const res = await fetch(`https://api.play2sell.com${path}`, {
        method,
        headers: {
          Authorization: `P2S-SIGN-V1 ${API_KEY}:${ts}:${sig}`,
          'Content-Type': 'application/json',
        },
        body: bodyStr || undefined,
      });
      return res.json();
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import hashlib, hmac, json, os, time, requests

    API_KEY = os.environ['SALESOS_API_KEY']
    SECRET  = os.environ['SALESOS_API_SECRET']

    def hmac_raw(key: bytes, msg: str) -> bytes:
        return hmac.new(key, msg.encode(), hashlib.sha256).digest()

    def signed_request(method: str, path: str, body=None):
        ts           = str(int(time.time()))
        body_str     = json.dumps(body) if body else ''
        payload_hash = hashlib.sha256(body_str.encode()).hexdigest()

        k1  = hmac_raw(SECRET.encode(), API_KEY)
        k2  = hmac_raw(k1, ts)
        k3  = hmac_raw(k2, method)
        k4  = hmac_raw(k3, path)
        sig = hmac_raw(k4, payload_hash).hex()

        return requests.request(
            method, f'https://api.play2sell.com{path}',
            headers={
                'Authorization': f'P2S-SIGN-V1 {API_KEY}:{ts}:{sig}',
                'Content-Type': 'application/json',
            },
            data=body_str or None,
        ).json()
    ```
  </Tab>

  <Tab title="Bash">
    ```bash theme={null}
    API_KEY="sk_live_..."
    SECRET="..."
    METHOD="POST"
    PATH_="/functions/v1/default-integration/sync_collaborators"
    BODY='{"action":"sync_collaborators","collaborators":[{"external_id":"emp-101","name":"Maria Santos","email":"maria@yourcompany.com"}]}'
    TS=$(date +%s)

    PAYLOAD=$(printf '%s' "$BODY"        | openssl dgst -sha256 -hex                                  | awk '{print $2}')
    K1=$(printf '%s'      "$API_KEY"     | openssl dgst -sha256 -hmac "$SECRET" -hex                  | awk '{print $2}')
    K2=$(printf '%s'      "$TS"          | openssl dgst -sha256 -mac HMAC -macopt "hexkey:$K1" -hex   | awk '{print $2}')
    K3=$(printf '%s'      "$METHOD"      | openssl dgst -sha256 -mac HMAC -macopt "hexkey:$K2" -hex   | awk '{print $2}')
    K4=$(printf '%s'      "$PATH_"       | openssl dgst -sha256 -mac HMAC -macopt "hexkey:$K3" -hex   | awk '{print $2}')
    SIG=$(printf '%s'     "$PAYLOAD"     | openssl dgst -sha256 -mac HMAC -macopt "hexkey:$K4" -hex   | awk '{print $2}')

    curl -X "$METHOD" "https://api.play2sell.com$PATH_" \
      -H "Authorization: P2S-SIGN-V1 $API_KEY:$TS:$SIG" \
      -H "Content-Type: application/json" \
      -d "$BODY"
    ```
  </Tab>
</Tabs>

<Tip>
  Não quer escrever o código de assinatura ainda? O [Sandbox da API](https://play2sellsa.github.io/api-sandbox/) assina as requisições para você no navegador — cole sua API key e secret, depois clique em *Try it out*.
</Tip>

### 3. Verifique a Resposta

**Sucesso (200):**

```json theme={null}
{
  "data": { "created": 1, "existing": 0, "errors": [], "total": 1 },
  "meta": { "request_id": "f47ac10b-...", "timestamp": "2026-03-17T10:30:00.000Z" }
}
```

**Assinatura inválida ou timestamp expirado (401):**

```json theme={null}
{
  "error": { "code": "UNAUTHORIZED", "message": "Invalid signature for P2S-SIGN-V1 request" }
}
```

***

## Propriedades da API Key

| Propriedade                 | Detalhes                                                            |
| --------------------------- | ------------------------------------------------------------------- |
| **Prefixo**                 | `sk_live_` (produção) ou `sk_test_` (testes)                        |
| **Escopo**                  | `default:sync` — habilita `sync_collaborators` e `sync_activities`  |
| **Rate limit**              | Configurável por chave (padrão: 1000 requisições/hora)              |
| **Expiração**               | Opcional — defina uma data de expiração ou deixe como sem expiração |
| **Lista de IPs permitidos** | Opcional — restrinja a endereços IP específicos                     |
| **Armazenamento**           | Hasheada com bcrypt — a chave em texto puro nunca é armazenada      |

***

## Formatos de Chave

O SalesOS usa dois prefixos de chave para distinguir ambientes:

| Prefixo    | Ambiente | Caso de uso                                                          |
| ---------- | -------- | -------------------------------------------------------------------- |
| `sk_live_` | Produção | Dados reais, missões reais, pontos reais                             |
| `sk_test_` | Testes   | Seguro para usar durante o desenvolvimento — sem impacto na produção |

<Tip>
  Use chaves `sk_test_` durante o desenvolvimento e testes de integração. Mude para `sk_live_` quando for para produção.
</Tip>

***

## Erros de Autenticação

| Status HTTP | Código do Erro | Significado                              | O que fazer                                                        |
| ----------- | -------------- | ---------------------------------------- | ------------------------------------------------------------------ |
| 401         | `UNAUTHORIZED` | Chave ausente, inválida ou expirada      | Verifique o header `Authorization`. Confirme a chave no Dashboard. |
| 403         | `FORBIDDEN`    | Chave válida mas sem o escopo necessário | Edite a chave e adicione o escopo `default:sync`                   |
| 429         | `RATE_LIMITED` | Muitas requisições nesta hora            | Aguarde `retry_after` segundos e tente novamente                   |

### Exemplo: Header Authorization ausente

```bash theme={null}
curl -X POST https://api.play2sell.com/functions/v1/default-integration \
  -H "Content-Type: application/json" \
  -d '{"action": "sync_collaborators", "collaborators": [...]}'
```

```json theme={null}
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Missing or malformed Authorization header"
  }
}
```

### Exemplo: Assinatura inválida

Uma assinatura que não bate com o que o servidor recalcula — geralmente causada por mudança no body após assinar, divergência na canonicalização do path, ou chave desatualizada:

```json theme={null}
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid signature for P2S-SIGN-V1 request"
  }
}
```

### Exemplo: Timestamp fora da janela de 30s

```json theme={null}
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Request timestamp is outside the 30-second validity window"
  }
}
```

### Exemplo: Chave sem o escopo necessário

Se sua chave possui apenas `leads:read` mas o endpoint requer `default:sync`:

```json theme={null}
{
  "error": {
    "code": "FORBIDDEN",
    "message": "API key missing required scopes: default:sync"
  }
}
```

### Exemplo: Rate limit excedido

```json theme={null}
{
  "error": {
    "code": "RATE_LIMITED",
    "message": "Rate limit exceeded",
    "retry_after": 1847
  }
}
```

O campo `retry_after` indica quantos segundos aguardar. A janela de rate limit é reiniciada a cada hora.

***

## Rate Limits

Cada API key possui um contador de rate limit independente que é reiniciado a cada hora:

| Configuração         | Padrão | Faixa       |
| -------------------- | ------ | ----------- |
| Requisições por hora | 1000   | 1 — 100.000 |

**Como funciona:**

1. Cada requisição bem-sucedida incrementa o contador
2. Quando o contador atinge o limite, requisições seguintes retornam `429`
3. O contador é reiniciado para 0 uma hora após a primeira requisição na janela

**Tratando rate limits no código:**

```javascript theme={null}
// signedRequest() é o helper do Início Rápido acima.
async function callWithRetry(method, path, body) {
  const response = await signedRequest(method, path, body);

  if (response.status === 429) {
    const { error } = await response.json();
    const waitSeconds = error.retry_after || 60;
    console.log(`Rate limited. Retrying in ${waitSeconds}s...`);
    await new Promise(r => setTimeout(r, waitSeconds * 1000));
    return callWithRetry(method, path, body); // retry
  }

  return response.json();
}
```

***

## Boas Práticas de Segurança

<Warning>
  **Nunca exponha API keys em código client-side.** JavaScript no navegador, aplicativos mobile e repositórios públicos podem vazar sua chave. Sempre chame a API SalesOS a partir do seu servidor backend.
</Warning>

* **Use variáveis de ambiente** — Armazene `SALESOS_API_KEY` em variáveis de ambiente ou em um gerenciador de segredos, nunca no código-fonte
* **Rotacione as chaves periodicamente** — Crie uma nova chave, atualize sua integração e depois revogue a antiga
* **Use listas de IPs permitidos** — Se sua integração roda a partir de IPs fixos, restrinja a chave apenas a esses IPs
* **Monitore o uso** — Verifique os logs de uso da API no Dashboard para padrões inesperados
* **Use `sk_test_` para desenvolvimento** — Chaves de teste isolam seu ambiente de desenvolvimento da produção
* **Revogue chaves comprometidas imediatamente** — Acesse Dashboard > Admin > API Keys > Revogar

### Exemplo de rotação de chaves

```bash theme={null}
# 1. Crie uma nova chave no Dashboard → copie ambos os valores:
#    - SALESOS_API_KEY=sk_live_NEW_KEY
#    - SALESOS_API_SECRET=NEW_SECRET
# 2. Atualize ambas as variáveis de ambiente no seu deployment.
# 3. Verifique se funciona usando o helper de requisição assinada do Início
#    Rápido (Node, Python ou Bash). Exemplo de payload:
#    { "action": "sync_collaborators",
#      "collaborators": [{"external_id":"test","name":"Test","email":"test@co.com"}] }
# 4. Revogue a chave antiga no Dashboard.
```

***

## Proximos Passos

<CardGroup cols={2}>
  <Card title="Integracao Padrao" icon="plug" href="/pt/api/integrations/default">
    Comece a enviar atividades para o SalesOS
  </Card>

  <Card title="API Keys" icon="key" href="/pt/api/integrations/api-keys">
    Gerencie chaves programaticamente
  </Card>
</CardGroup>
