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

> Lee el estado de check-in (presencia en el turno) de tu equipo en SalesOS desde tu propio backend — hecha para Homes de superapps y dashboards de socios.

# API de Check-in

<Note>
  Los ejemplos muestran el header en formato de transmisión `Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE`. Para calcular la firma en tu código, usa el helper `signedRequest` en [Autenticación](/es/api/authentication).
</Note>

La API de Check-in permite que tu backend lea el **estado de presencia en el turno** de tus vendedores en SalesOS — si hicieron check-in hoy, dónde, en qué turno y hasta cuándo es válido. Fue creada para **componer tus propias pantallas** (por ejemplo, un widget en la Home de un superapp) antes de que el usuario abra el módulo SalesOS.

Es una API **de solo lectura, servidor-a-servidor**: consultas colaboradores por CPF o email, en lote, y SalesOS responde con el estado de hoy calculado en la zona horaria de tu empresa.

## Cómo funciona

1. **Obtienes una API Key** con el scope `checkin:read` (Admin > Integraciones > API Keys)
2. **Tu backend consulta** el estado de check-in de uno o varios colaboradores (por CPF o email)
3. **SalesOS responde** con el último check-in de hoy por colaborador — y, opcionalmente, conteos de engagement semana/mes

<Note>
  El check-in se sigue **ejecutando** dentro de la app SalesOS (la validación de GPS ocurre en el contexto del usuario). Esta API es para **leer** el estado de presencia — combínala con un deep link al módulo SalesOS en la acción "Hacer check-in".
</Note>

***

## Autenticación

Todas las solicitudes requieren una API Key en el header `Authorization`:

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

Consulta la página de [Autenticación](/es/api/authentication) para detalles sobre creación y gestión de API Keys.

| Propiedad             | Detalles                                                  |
| --------------------- | --------------------------------------------------------- |
| **Header**            | `Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE`  |
| **Scope requerido**   | `checkin:read`                                            |
| **Rate limit**        | Configurable por key (por defecto: 1000 solicitudes/hora) |
| **Formato de la key** | `sk_live_` (producción) o `sk_test_` (pruebas)            |

***

## Ambientes

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

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

***

## Referencia del Endpoint

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

El endpoint acepta dos actions mediante el campo `action`: `checkin_status` y `checkin_engagement`.

Cada colaborador se identifica por **CPF** (cualquier formato — los dígitos se normalizan) o **email**. Cuando se envían ambos, el CPF prevalece.

***

### Action: checkin\_status

Último check-in de hoy por colaborador. "Hoy" se calcula en la zona horaria de tu empresa (devuelta como `timezone` / `reference_date` en la respuesta) — nunca en UTC.

#### Schema de la solicitud

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

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

<ParamField body="collaborators[].cpf" type="string">
  CPF, cualquier formato (`52998224725` o `529.982.247-25`). Debe contener exactamente 11 dígitos.
</ParamField>

<ParamField body="collaborators[].email" type="string">
  Email — usado solo cuando `cpf` no se envía
</ParamField>

#### Ejemplo

```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@tuempresa.com" }
    ]
  }'
```

**Respuesta (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@tuempresa.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 de la respuesta

<ResponseField name="found" type="boolean">
  Si el CPF/email correspondió a un colaborador **de tu empresa**. Un colaborador desconocido viene como `found: false` — no es un error, y el array mantiene el mismo orden de la solicitud.
</ResponseField>

<ResponseField name="checked_in_today" type="boolean">
  Si el colaborador tiene al menos un check-in no rechazado hoy (zona horaria de la empresa).
</ResponseField>

<ResponseField name="on_duty_now" type="boolean">
  `true` cuando el último check-in está activo/aprobado y el turno aún no expiró — es decir, el colaborador está de turno ahora.
</ResponseField>

<ResponseField name="latest_checkin" type="object">
  Check-in más reciente de hoy. Omitido cuando no hay ninguno. `mode` es `geo` (presencial con GPS), `home` (home office) u `offline`. `status` puede ser `active`, `approved`, `pending` (esperando aprobación del líder), `expired` (turno finalizado normalmente) o `checkout`.
</ResponseField>

<ResponseField name="membership_status" type="string">
  Vínculo del colaborador con tu empresa (`active`, `inactive`, ...). Un colaborador puede ser encontrado y estar inactivo.
</ResponseField>

***

### Action: checkin\_engagement

Todo lo que devuelve `checkin_status`, más conteos de engagement semana/mes por colaborador. Lotes limitados a 100.

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

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

#### Ejemplo

```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 gana un objeto `engagement`:

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

* `checkins` — check-ins en la semana/mes corriente (zona horaria de la empresa; `expired` cuenta — es el fin normal de un turno)
* `presence_days` — días distintos con al menos un check-in

***

## Modo self (sesión federada)

Si tu app ya posee una **sesión federada de SalesOS** — el JWT devuelto por un login personalizado, como la federación del Superapp — el mismo endpoint responde por el propio usuario del token, sin 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" }'
```

* La identidad viene **exclusivamente** del token verificado — **no** envíes `collaborators`. Una solicitud con ese campo se rechaza con `SELF_MODE_NO_COLLABORATORS`: un token de usuario nunca consulta a otras personas.
* El alcance de empresa viene de la sesión; la respuesta tiene exactamente el mismo formato, con un único ítem en `collaborators`.
* `checkin_engagement` funciona de la misma manera (`{ "action": "checkin_engagement" }`).

<Tip>
  Usa el modo self en pantallas renderizadas **dentro** de la app autenticada; usa la API key (S2S) cuando tu backend compone pantallas **antes** de que el usuario tenga sesión en SalesOS — como un widget en la Home de un superapp.
</Tip>

***

## Manejo de Errores

Todos los errores siguen la estructura compartida:

```json theme={null}
{
  "error": {
    "code": "ERROR_CODE",
    "message": "Descripción legible",
    "details": []
  }
}
```

<AccordionGroup>
  <Accordion title="400 — VALIDATION_ERROR">
    Body inválido, lote sobre el límite, CPF sin 11 dígitos, o ítem sin `cpf` ni `email`. El array `details` indica el `index` del ítem.
  </Accordion>

  <Accordion title="401 — UNAUTHORIZED">
    API key ausente, inválida o expirada, o la firma no coincide.
  </Accordion>

  <Accordion title="403 — FORBIDDEN">
    API key válida, pero sin el scope `checkin:read`.
  </Accordion>

  <Accordion title="405 — METHOD_NOT_ALLOWED">
    Solo se acepta `POST`.
  </Accordion>

  <Accordion title="429 — RATE_LIMITED">
    Demasiadas solicitudes en esta hora. Espera `retry_after` segundos y reintenta.
  </Accordion>

  <Accordion title="500 — SERVER_ERROR">
    Error interno. Reintenta con backoff exponencial (2s, 4s, 8s).
  </Accordion>
</AccordionGroup>

<Tip>
  **Un colaborador desconocido no es un error.** La solicitud responde 200 con `found: false` en ese ítem — un CPF equivocado nunca rompe la renderización de la Home entera.
</Tip>

***

## Rate Limits

| Límite                                      | Valor |
| ------------------------------------------- | ----- |
| Solicitudes por hora (por defecto)          | 1000  |
| Máx. colaboradores por `checkin_status`     | 500   |
| Máx. colaboradores por `checkin_engagement` | 100   |

***

## Seguridad

* Solo lectura: esta API nunca crea ni modifica check-ins
* Cada key está limitada a una única empresa — un CPF de otra empresa responde `found: false`
* Solicitudes firmadas con HMAC (P2S-SIGN-V1) y registradas para auditoría; los documentos nunca se escriben en logs

<Warning>
  Nunca expongas tu API key en código client-side. Esta API debe llamarse solo desde tu backend.
</Warning>

***

## Próximos pasos

<CardGroup cols={2}>
  <Card title="Autenticación" icon="key" href="/es/api/authentication">
    Aprende a crear y gestionar API Keys
  </Card>

  <Card title="Soporte" icon="headset" href="mailto:suporte@play2sell.com">
    ¿Necesitas ayuda? Habla con nuestro equipo
  </Card>
</CardGroup>
