Saltar para o conteúdo principal

API de Check-in

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.
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 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. Com o scope checkin:write, ela também faz o check-in e o check-out de um colaborador.

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
  4. Opcionalmente, seu backend faz check-in e check-out com uma key que também tenha checkin:write, repassando a leitura de GPS do celular do colaborador

Autenticação

Todas as requisições exigem uma API Key no header Authorization:
Veja a página de Autenticação para detalhes sobre criação e gestão de API Keys.

Ambientes

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

Referência do Endpoint

O endpoint aceita estas actions pelo campo action: 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

string
obrigatório
Deve ser "checkin_status"
array
obrigatório
Array de referências de colaborador (máx. 500)
string
CPF, qualquer formato (52998224725 ou 529.982.247-25). Deve conter exatamente 11 dígitos.
string
Email — usado apenas quando cpf não é enviado

Exemplo

Resposta (200):

Campos da resposta

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.
boolean
Se o colaborador tem ao menos um check-in não-rejeitado hoje (fuso da empresa).
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.
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. location traz a leitura de GPS gravada no check-in: lat, lng, accuracy (metros) e, para check-ins feitos por esta API, captured_at (quando o celular leu o GPS). Ausente quando o check-in não teve GPS (home office, checkout).
string
Vínculo do colaborador com a sua empresa (active, inactive, …). Um colaborador pode ser encontrado e estar inativo.

Trilha do dia (include_day)

Envie "include_day": true em checkin_status para receber, além do estado atual, como está o dia turno a turno e quando o botão libera de novo. É o que uma tela inicial precisa para desenhar o bloco de check-in sem abrir o módulo do SalesOS.
Resposta (200):
Os turnos saem uma vez no topo, não repetidos por pessoa. Num lote de 500 colaboradores isso é a diferença entre uma resposta enxuta e uma de megabytes.

Campos

Uma unidade pode ter horários próprios, e eles vencem. O array shifts do topo é o padrão da sua empresa. Quando a pessoa faz check-in numa unidade com grade própria, day.shifts segue aquela unidade e day.shifts_source diz isso. Ao desenhar a tela de alguém, leia a trilha da pessoa, não o array do topo.Antes do primeiro check-in do dia não há unidade a resolver, então vale o padrão da empresa.
A quantidade de turnos é da sua empresa, não fixa. Ela pode ter dois, três ou nenhum, com rótulos e horários próprios. Monte a tela a partir do array — nunca assuma três posições nem horários específicos.
Leia opens_at junto com state. Com state: "available" ele vem ausente porque há um turno aberto agora. Com state: "waiting" e sem opens_at, o dia acabou e não há próxima abertura hoje — a tela não deve exibir uma contagem atravessando a virada do dia.

Action: checkin_engagement

Tudo que checkin_status retorna, mais contagens de engajamento semana/mês por colaborador. Lotes limitados a 100.
string
obrigatório
Deve ser "checkin_engagement"
array
obrigatório
Array de referências de colaborador (máx. 100)

Exemplo

Cada colaborador encontrado ganha um objeto engagement:
  • 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

Registrando check-ins

Com o scope checkin:write, seu backend pode fazer o check-in e o check-out de um colaborador — por exemplo, a partir de um botão “Fazer check-in” na sua própria Home. O efeito é o mesmo de um check-in feito no app SalesOS: o colaborador entra na fila de leads, recebe o turno e o horário em que ele termina, ganha pontos, e os webhooks checkin.confirmed / checkin.checked_out disparam.
Esta API grava. Diferente das actions de leitura acima, checkin_create e checkin_checkout mudam quem recebe lead. Elas exigem uma key com checkin:write; uma key só com checkin:read responde 403 com code: "scope_insufficient".
As actions de escrita tratam um colaborador por requisição, enviado em collaborator (um objeto, não uma lista). Na escrita, “não encontrado” é erro — não existe escrita parcial:

Action: checkin_options

O que a sua tela pode oferecer agora, para mostrar só o botão que vai funcionar. Somente leitura — exige checkin:read.
location é opcional. Quando enviado, locations volta ordenado pela distância e duty diz se algum local está ao alcance.
unavailable_reason usa os mesmos códigos que um checkin_create devolveria (MODE_DISABLED, TOO_FAR, HOME_OUTSIDE_SHIFT, HOME_SHIFT_NOT_ALLOWED, PRIOR_GEO_REQUIRED, LOCATION_NOT_FOUND). Os rótulos de modos e turnos são configuração da sua empresa — exiba como vierem.

Action: checkin_create

string
obrigatório
duty (plantão presencial, exige location) ou home (home office).
object
A leitura de GPS do aparelho, capturada no celular do colaborador e repassada pelo seu backend. Obrigatória para duty: lat, lng, accuracy (metros) e captured_at (ISO 8601 — quando o celular leu o GPS).
string
O local de plantão escolhido pelo colaborador. Opcional — sem ele, o SalesOS escolhe o local mais próximo dentro do raio.
object
Seu app e versão (app, version). Guardado junto com o check-in para auditoria.
Resposta (201):
location devolve a leitura de GPS que ficou gravada — mesma forma do latest_checkin.location do checkin_status. Ausente quando não foi enviado GPS. Um check-in novo encerra o check-in ativo anterior do colaborador, exatamente como no app. Ele vale até shift_expires_at — seu app não precisa ficar enviando sinal de vida.

Requisitos da localização

A leitura de GPS chega pelo seu backend, então o SalesOS confere se ela é recente e precisa antes de confiar nela:
Leia o GPS quando o usuário tocar no botão, não quando a tela abrir. Uma leitura feita ao abrir a tela costuma chegar vencida, e uma leitura em ambiente fechado costuma ser imprecisa — as duas são recusadas, e pedir para o usuário ir a um lugar aberto e tentar de novo resolve.
Um check-in recusado não grava nada: o colaborador continua exatamente como estava.

Action: checkin_checkout

Encerra o check-in ativo antes do fim do turno. O colaborador sai da fila de leads na hora e checkin.checked_out dispara.
Resposta (200):
Sem check-in ativo, a resposta continua 200, com "already_checked_out": true, e nada é gravado — um clique duplo nunca quebra a sua tela.

Novas tentativas e Idempotency-Key

Envie o header Idempotency-Key (8 a 128 caracteres; um UUID serve) em checkin_create e checkin_checkout. Repetir a requisição com a mesma chave em até 24 horas:
  • devolve a mesma resposta, com o header Idempotency-Replayed: true, sem criar nada novo;
  • com um corpo diferente, responde 409 IDEMPOTENCY_KEY_REUSED;
  • enquanto a primeira requisição ainda está rodando, responde 409 IDEMPOTENCY_IN_PROGRESS — tente de novo em alguns segundos.
Uma requisição recusada (4xx) não consome a chave: depois de corrigir a causa (uma leitura de GPS nova, por exemplo), você pode tentar de novo com a mesma chave.
O timestamp da assinatura vale por 30 segundos, então cada nova tentativa precisa ser assinada de novo — é a Idempotency-Key que liga as tentativas.

Mensagens para o corretor

Os erros de escrita trazem uma message em português, escrita para ser exibida direto ao corretor (por exemplo: “Você está a 180 m de Plantão Morumbi. Chegue a até 100 m para fazer o check-in.”). Decida a lógica pelo code, nunca pelo texto.

Modo self (token do usuário do SSO)

Se o seu app já possui o token de um usuário que entrou pelo SSO, o mesmo endpoint responde pelo próprio usuário do token, sem API key:
  • 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" }).
  • As actions de escrita e checkin_options são só para parceiros: com token de usuário, respondem 400 SELF_MODE_NOT_SUPPORTED. Dentro do app SalesOS, o check-in é feito pelo próprio app.
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.

Tratamento de Erros

Todos os erros seguem a estrutura compartilhada:
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:
Código que assume error.code lê undefined em toda falha de autenticação. Ramifique pelo tipo — veja Convenções da API.
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.
API key ausente, desconhecida ou expirada, ou assinatura não confere. A resposta traz error como string e códigos como header_missing, api_key_not_found, signature_invalid, timestamp_too_old.
API key válida, mas sem o scope que a action exige (checkin:read, ou checkin:write para escrita). Quem responde é a camada de autenticação: { "error": "...", "code": "scope_insufficient", "missing_scopes": ["checkin:write"] }.
Apenas POST é aceito.
Escritas demais para o mesmo colaborador (10 em 10 minutos). Aguarde os segundos de Retry-After e tente de novo. As actions de escrita têm a própria tabela de erros em Registrando check-ins.
Erro interno. Faça retry com backoff exponencial (2s, 4s, 8s).
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.

Rate Limits


Segurança

  • Leitura exige checkin:read; criar ou encerrar check-in exige checkin:write — emita keys de escrita só para o backend dono do botão
  • Todo check-in criado pela API é guardado com a origem, a key que o criou, o seu client e a leitura de GPS original, para auditoria
  • 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
Nunca exponha a sua API key em código client-side. Esta API deve ser chamada apenas do seu backend.

Próximos passos

Autenticação

Aprenda a criar e gerenciar API Keys

Suporte

Precisa de ajuda? Fale com nosso time