> ## 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 Check-in

> Leia o estado de check-in (presença no plantão) do seu time no SalesOS a partir do seu próprio backend — feita para Homes de superapps e dashboards de parceiros.

# API de Check-in

<Note>
  Os exemplos abaixo mostram o header no formato de transmissão `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 Check-in permite que o seu backend leia o **estado de presença no plantão** dos seus vendedores no SalesOS — fizeram check-in hoje, onde, em qual turno e até quando ele vale. Ela foi criada para **compor as suas próprias telas** (por exemplo, um widget na Home de um superapp) antes mesmo de o usuário abrir o módulo SalesOS.

É uma API **somente leitura, servidor-a-servidor**: você consulta colaboradores por CPF ou email, em lote, e o SalesOS responde com o estado de hoje calculado no fuso horário da sua empresa.

## Como funciona

1. **Você obtém uma API Key** com o scope `checkin:read` (Admin > Integrações > API Keys)
2. **Seu backend consulta** o estado de check-in de um ou vários colaboradores (por CPF ou email)
3. **O SalesOS responde** com o último check-in de hoje por colaborador — e, opcionalmente, contagens de engajamento semana/mês

<Note>
  O check-in continua sendo **executado** dentro do app SalesOS (a validação de GPS acontece no contexto do usuário). Esta API é para **ler** o estado de presença — combine-a com um deep link para o módulo SalesOS na ação "Fazer check-in".
</Note>

***

## Autenticação

Todas as requisições exigem uma API Key no header `Authorization`:

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

Veja a página de [Autenticação](/pt/api/authentication) para detalhes sobre criação e gestão de API Keys.

| Propriedade        | Detalhes                                                 |
| ------------------ | -------------------------------------------------------- |
| **Header**         | `Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE` |
| **Scope exigido**  | `checkin:read`                                           |
| **Rate limit**     | Configurável por key (padrão: 1000 requisições/hora)     |
| **Formato da key** | `sk_live_` (produção) ou `sk_test_` (teste)              |

***

## Ambientes

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

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

***

## Referência do Endpoint

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

O endpoint aceita duas actions pelo campo `action`: `checkin_status` e `checkin_engagement`.

Cada colaborador é identificado por **CPF** (qualquer formato — os dígitos são normalizados) ou **email**. Quando os dois são enviados, o CPF prevalece.

***

### Action: checkin\_status

Último check-in de hoje por colaborador. "Hoje" é calculado no fuso horário da sua empresa (retornado como `timezone` / `reference_date` na resposta) — nunca em UTC.

#### Schema da requisição

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

<ParamField body="collaborators" type="array" required>
  Array de referências de colaborador (máx. 500)
</ParamField>

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

<ParamField body="collaborators[].email" type="string">
  Email — usado apenas quando `cpf` não é enviado
</ParamField>

#### Exemplo

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

**Resposta (200):**

```json theme={null}
{
  "data": {
    "timezone": "America/Sao_Paulo",
    "reference_date": "2026-07-19",
    "collaborators": [
      {
        "cpf": "52998224725",
        "found": true,
        "user_id": "8f14e45f-...",
        "name": "Maria Santos",
        "membership_status": "active",
        "checked_in_today": true,
        "on_duty_now": true,
        "latest_checkin": {
          "status": "active",
          "mode": "geo",
          "shift": "afternoon",
          "checked_in_at": "2026-07-19T16:03:21+00:00",
          "shift_expires_at": "2026-07-19T21:00:00+00:00",
          "location_id": "c81e728d-...",
          "location_name": "Plantão Morumbi"
        },
        "org_unit_id": "a87ff679-..."
      },
      {
        "email": "joao@suaempresa.com",
        "found": true,
        "user_id": "45c48cce-...",
        "name": "Joao Silva",
        "membership_status": "active",
        "checked_in_today": false,
        "on_duty_now": false
      }
    ],
    "total": 2,
    "found": 2
  },
  "meta": { "request_id": "a1b2c3d4-...", "timestamp": "2026-07-19T18:00:00.000Z" }
}
```

#### Campos da resposta

<ResponseField name="found" type="boolean">
  Se o CPF/email correspondeu a um colaborador **da sua empresa**. Colaborador desconhecido vem como `found: false` — não é erro, e o array mantém a mesma ordem da requisição.
</ResponseField>

<ResponseField name="checked_in_today" type="boolean">
  Se o colaborador tem ao menos um check-in não-rejeitado hoje (fuso da empresa).
</ResponseField>

<ResponseField name="on_duty_now" type="boolean">
  `true` quando o último check-in está ativo/aprovado e o turno ainda não expirou — ou seja, o colaborador está em plantão agora.
</ResponseField>

<ResponseField name="latest_checkin" type="object">
  Check-in mais recente de hoje. Omitido quando não há nenhum. `mode` é `geo` (presencial com GPS), `home` (home office) ou `offline`. `status` pode ser `active`, `approved`, `pending` (aguardando aprovação do líder), `expired` (turno encerrado normalmente) ou `checkout`.
</ResponseField>

<ResponseField name="membership_status" type="string">
  Vínculo do colaborador com a sua empresa (`active`, `inactive`, ...). Um colaborador pode ser encontrado e estar inativo.
</ResponseField>

***

### Action: checkin\_engagement

Tudo que `checkin_status` retorna, mais contagens de engajamento semana/mês por colaborador. Lotes limitados a 100.

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

<ParamField body="collaborators" type="array" required>
  Array de referências de colaborador (máx. 100)
</ParamField>

#### Exemplo

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

Cada colaborador encontrado ganha um objeto `engagement`:

```json theme={null}
{
  "engagement": {
    "week":  { "checkins": 9,  "presence_days": 5 },
    "month": { "checkins": 34, "presence_days": 17 }
  }
}
```

* `checkins` — check-ins na semana/mês corrente (fuso da empresa; `expired` conta — é o fim normal de um turno)
* `presence_days` — dias distintos com ao menos um check-in

***

## Modo self (sessão federada)

Se o seu app já possui uma **sessão federada do SalesOS** — o JWT devolvido por um login customizado, como a federation do Superapp — o mesmo endpoint responde pelo próprio usuário do token, sem API key:

```bash theme={null}
curl -X POST https://api.play2sell.com/functions/v1/checkin-partner-api \
  -H "Authorization: Bearer SESSION_JWT" \
  -H "Content-Type: application/json" \
  -d '{ "action": "checkin_status" }'
```

* A identidade vem **exclusivamente** do token verificado — **não** envie `collaborators`. Requisição com esse campo é rejeitada com `SELF_MODE_NO_COLLABORATORS`: token de usuário nunca consulta outras pessoas.
* O escopo de empresa vem da sessão; a resposta tem exatamente o mesmo formato, com um único item em `collaborators`.
* `checkin_engagement` funciona da mesma forma (`{ "action": "checkin_engagement" }`).

<Tip>
  Use o modo self em telas renderizadas **dentro** do app autenticado; use a API key (S2S) quando o seu backend compõe telas **antes** de o usuário ter sessão no SalesOS — como um widget na Home de um superapp.
</Tip>

***

## Tratamento de Erros

Todos os erros seguem a estrutura compartilhada:

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

<AccordionGroup>
  <Accordion title="400 — VALIDATION_ERROR">
    Body inválido, lote acima do limite, CPF sem 11 dígitos, ou item sem `cpf` nem `email`. O array `details` aponta o `index` do item.
  </Accordion>

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

  <Accordion title="403 — FORBIDDEN">
    API key válida, mas sem o scope `checkin:read`.
  </Accordion>

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

  <Accordion title="429 — RATE_LIMITED">
    Muitas requisições nesta hora. Aguarde `retry_after` segundos e tente de novo.
  </Accordion>

  <Accordion title="500 — SERVER_ERROR">
    Erro interno. Faça retry com backoff exponencial (2s, 4s, 8s).
  </Accordion>
</AccordionGroup>

<Tip>
  **Colaborador desconhecido não é erro.** A requisição responde 200 com `found: false` naquele item — um CPF errado nunca quebra a renderização da Home inteira.
</Tip>

***

## Rate Limits

| Limite                                      | Valor |
| ------------------------------------------- | ----- |
| Requisições por hora (padrão)               | 1000  |
| Máx. colaboradores por `checkin_status`     | 500   |
| Máx. colaboradores por `checkin_engagement` | 100   |

***

## Segurança

* Somente leitura: esta API nunca cria nem altera check-ins
* Cada key é limitada a uma única empresa — CPF de outra empresa responde `found: false`
* Requisições assinadas com HMAC (P2S-SIGN-V1) e logadas para auditoria; documentos nunca são escritos em logs

<Warning>
  Nunca exponha a sua API key em código client-side. Esta API deve ser chamada apenas do seu backend.
</Warning>

***

## Próximos passos

<CardGroup cols={2}>
  <Card title="Autenticação" icon="key" href="/pt/api/authentication">
    Aprenda a criar e gerenciar API Keys
  </Card>

  <Card title="Suporte" icon="headset" href="mailto:suporte@play2sell.com">
    Precisa de ajuda? Fale com nosso time
  </Card>
</CardGroup>
