Saltar al contenido principal

API de Check-in

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.
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 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. Con el scope checkin:write, también hace el check-in y el check-out de un colaborador.

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
  4. Opcionalmente, tu backend hace check-in y check-out con una key que también tenga checkin:write, reenviando la lectura de GPS del celular del colaborador

Autenticación

Todas las solicitudes requieren una API Key en el header Authorization:
Consulta la página de Autenticación para detalles sobre creación y gestión de API Keys.

Ambientes

Base URL: https://api.play2sell.com

Referencia del Endpoint

El endpoint acepta estas actions mediante el campo action: 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

string
requerido
Debe ser "checkin_status"
array
requerido
Array de referencias de colaborador (máx. 500)
string
CPF, cualquier formato (52998224725 o 529.982.247-25). Debe contener exactamente 11 dígitos.
string
Email — usado solo cuando cpf no se envía

Ejemplo

Respuesta (200):

Campos de la respuesta

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.
boolean
Si el colaborador tiene al menos un check-in no rechazado hoy (zona horaria de la empresa).
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.
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. location trae la lectura de GPS grabada en el check-in: lat, lng, accuracy (metros) y, para check-ins hechos por esta API, captured_at (cuándo el celular leyó el GPS). Ausente cuando el check-in no tuvo GPS (home office, checkout).
string
Vínculo del colaborador con tu empresa (active, inactive, …). Un colaborador puede ser encontrado y estar inactivo.

Recorrido del dia (include_day)

Envie "include_day": true en checkin_status para recibir, ademas del estado actual, como va el dia turno por turno y cuando se habilita el boton de nuevo. Es lo que una pantalla de inicio necesita para componer el bloque de check-in sin abrir el modulo de SalesOS.
Respuesta (200):
Los turnos salen una sola vez arriba, no repetidos por persona. En un lote de 500 colaboradores esa es la diferencia entre una respuesta liviana y una de megabytes.

Campos

Una unidad puede tener horarios propios, y esos ganan. El arreglo shifts de arriba es el predeterminado de su empresa. Cuando la persona hace check-in en una unidad con horario propio, day.shifts sigue a esa unidad y day.shifts_source lo declara. Al dibujar la pantalla de alguien, lea el recorrido de la persona, no el arreglo de arriba.Antes del primer check-in del día no hay unidad que resolver, así que se usa el predeterminado de la empresa.
La cantidad de turnos es de su empresa, no es fija. Puede tener dos, tres o ninguno, con sus propias etiquetas y horarios. Construya la pantalla a partir del arreglo; nunca asuma tres posiciones ni horarios especificos.
Lee opens_at junto con state. Con state: "available" viene ausente porque hay un turno abierto ahora. Con state: "waiting" y sin opens_at, el dia termino y no hay proxima apertura hoy — la pantalla no debe mostrar una cuenta regresiva que cruce la medianoche.

Action: checkin_engagement

Todo lo que devuelve checkin_status, más conteos de engagement semana/mes por colaborador. Lotes limitados a 100.
string
requerido
Debe ser "checkin_engagement"
array
requerido
Array de referencias de colaborador (máx. 100)

Ejemplo

Cada colaborador encontrado gana un objeto engagement:
  • 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

Registrar check-ins

Con el scope checkin:write, tu backend puede hacer el check-in y el check-out de un colaborador — por ejemplo, desde un botón “Hacer check-in” en tu propia Home. El efecto es el mismo que un check-in hecho en la app SalesOS: el colaborador entra en la fila de leads, recibe el turno y la hora en que termina, gana puntos, y se disparan los webhooks checkin.confirmed / checkin.checked_out.
Esta API escribe. A diferencia de las actions de lectura de arriba, checkin_create y checkin_checkout cambian quién recibe leads. Requieren una key con checkin:write; una key solo con checkin:read responde 403 con code: "scope_insufficient".
Las actions de escritura manejan un colaborador por solicitud, enviado en collaborator (un objeto, no una lista). En la escritura, “no encontrado” es un error — no existe escritura parcial:

Action: checkin_options

Lo que tu pantalla puede ofrecer ahora, para mostrar solo el botón que va a funcionar. Solo lectura — requiere checkin:read.
location es opcional. Cuando se envía, locations vuelve ordenado por distancia y duty indica si algún local está al alcance.
unavailable_reason usa los mismos códigos que devolvería un checkin_create (MODE_DISABLED, TOO_FAR, HOME_OUTSIDE_SHIFT, HOME_SHIFT_NOT_ALLOWED, PRIOR_GEO_REQUIRED, LOCATION_NOT_FOUND). Las etiquetas de modos y turnos son configuración de tu empresa — muéstralas tal como llegan.

Action: checkin_create

string
requerido
duty (turno presencial, requiere location) o home (home office).
object
La lectura de GPS del dispositivo, capturada en el celular del colaborador y reenviada por tu backend. Obligatoria para duty: lat, lng, accuracy (metros) y captured_at (ISO 8601 — cuándo el celular leyó el GPS).
string
El local de turno que eligió el colaborador. Opcional — sin él, SalesOS elige el local más cercano dentro de su radio.
object
Tu app y versión (app, version). Se guarda junto con el check-in para auditoría.
Respuesta (201):
location devuelve la lectura de GPS que quedó grabada — misma forma que latest_checkin.location de checkin_status. Ausente cuando no se envió GPS. Un check-in nuevo cierra el check-in activo anterior del colaborador, igual que en la app. Es válido hasta shift_expires_at — tu app no necesita seguir enviando señales de vida.

Requisitos de la ubicación

La lectura de GPS llega por tu backend, así que SalesOS verifica que sea reciente y precisa antes de confiar en ella:
Lee el GPS cuando el usuario toca el botón, no cuando se abre la pantalla. Una lectura hecha al abrir la pantalla suele llegar vencida, y una lectura en interiores suele ser imprecisa — ambas se rechazan, y pedirle al usuario que salga a un lugar abierto y vuelva a intentarlo lo resuelve.
Un check-in rechazado no graba nada: el colaborador queda exactamente como estaba.

Action: checkin_checkout

Cierra el check-in activo antes del fin del turno. El colaborador sale de la fila de leads en el acto y se dispara checkin.checked_out.
Respuesta (200):
Sin check-in activo, la respuesta sigue siendo 200, con "already_checked_out": true, y no se graba nada — un doble toque nunca rompe tu pantalla.

Reintentos e Idempotency-Key

Envía el header Idempotency-Key (8 a 128 caracteres; un UUID sirve) en checkin_create y checkin_checkout. Repetir la solicitud con la misma clave dentro de 24 horas:
  • devuelve la misma respuesta, con el header Idempotency-Replayed: true, sin crear nada nuevo;
  • con un cuerpo distinto, responde 409 IDEMPOTENCY_KEY_REUSED;
  • mientras la primera solicitud sigue en curso, responde 409 IDEMPOTENCY_IN_PROGRESS — reintenta en unos segundos.
Una solicitud rechazada (4xx) no consume la clave: después de corregir la causa (una lectura de GPS nueva, por ejemplo), puedes reintentar con la misma clave.
El timestamp de la firma vale 30 segundos, así que cada reintento debe firmarse de nuevo — la Idempotency-Key es lo que une los intentos.

Mensajes para el colaborador

Los errores de escritura traen un message en portugués de Brasil, escrito para mostrarse directamente al colaborador (por ejemplo: “Você está a 180 m de Plantão Morumbi. Chegue a até 100 m para fazer o check-in.”). Decide la lógica por el code, nunca por el texto.

Modo self (token del usuario del SSO)

Si tu app ya posee el token de un usuario que entró por SSO, el mismo endpoint responde por el propio usuario del token, sin API key:
  • 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" }).
  • Las actions de escritura y checkin_options son solo para socios: con token de usuario responden 400 SELF_MODE_NOT_SUPPORTED. Dentro de la app SalesOS, el check-in lo hace la propia app.
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.

Manejo de Errores

Todos los errores siguen la estructura compartida:
Son dos formatos, no uno. El bloque anterior es el error del endpoint. Las fallas en la capa de autenticación responden antes de que el endpoint corra, con error como cadena:
El código que asume error.code lee undefined en toda falla de autenticación. Ramifique por el tipo — vea Convenciones de la API.
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.
API key ausente, desconocida o expirada, o la firma no coincide. La respuesta trae error como cadena y códigos como header_missing, api_key_not_found, signature_invalid, timestamp_too_old.
API key válida, pero sin el scope que requiere la action (checkin:read, o checkin:write para escritura). Responde la capa de autenticación: { "error": "...", "code": "scope_insufficient", "missing_scopes": ["checkin:write"] }.
Solo se acepta POST.
Demasiadas escrituras para el mismo colaborador (10 en 10 minutos). Espera los segundos de Retry-After y reintenta. Las actions de escritura tienen su propia tabla de errores en Registrar check-ins.
Error interno. Reintenta con backoff exponencial (2s, 4s, 8s).
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.

Rate Limits


Seguridad

  • La lectura requiere checkin:read; crear o cerrar check-ins requiere checkin:write — emite keys de escritura solo para el backend dueño del botón
  • Todo check-in creado por la API se guarda con su origen, la key que lo creó, tu client y la lectura de GPS original, para auditoría
  • 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
Nunca expongas tu API key en código client-side. Esta API debe llamarse solo desde tu backend.

Próximos pasos

Autenticación

Aprende a crear y gestionar API Keys

Soporte

¿Necesitas ayuda? Habla con nuestro equipo