Claritiodocs

API do Claritio

Leia colaboradores, ponto e ocorrências. Lance batidas de outro sistema. Sem SDK, sem OAuth: uma chave num cabeçalho.

Começar em 3 minutos

Usando uma IA para escrever a integração? Ela lê tudo de uma vez em /api.md ou /llms.txt.

Visão geral#

A API do Claritio deixa o seu sistema ler e lançar dados de RH: quem trabalha na empresa, o ponto que essas pessoas bateram, as ocorrências do período e o espelho de cada mês.

Ela é HTTP com JSON, autenticada por uma chave que a empresa gera dentro do sistema. Não há SDK a instalar, não há OAuth, não há sessão: um cabeçalho e pronto.

O endereço de tudo:

https://rh.claritio.com.br/api/publica/v1

O /v1 faz parte do endereço de propósito. No dia em que um campo precisar mudar de forma, sai um /v2 e o /v1 continua respondendo — a sua integração não quebra porque nós evoluímos.

Começar em 3 minutos#

Não precisa de cadastro, nem de chave, nem de falar com ninguém para ver a API funcionando. Escolha a sua linguagem e cole:

curl -H "Authorization: Bearer clr_demo_0000000000000000000000000000" \
  "https://rh.claritio.com.br/api/publica/v1/ping"
$cabecalho = @{ Authorization = "Bearer clr_demo_0000000000000000000000000000" }

# O ConvertTo-Json no fim nao e enfeite: sem ele, a tabela padrao do
# PowerShell mostra os objetos de dentro como "empresa=", em branco, e
# parece que a resposta veio vazia.
Invoke-RestMethod -Uri "https://rh.claritio.com.br/api/publica/v1/ping" -Headers $cabecalho |
  ConvertTo-Json -Depth 5
const r = await fetch("https://rh.claritio.com.br/api/publica/v1/ping", {
  headers: { Authorization: "Bearer clr_demo_0000000000000000000000000000" }
});
console.log(await r.json());
import requests

r = requests.get("https://rh.claritio.com.br/api/publica/v1/ping",
                 headers={"Authorization": "Bearer clr_demo_0000000000000000000000000000"})
print(r.json())

Essa é a chave do simulador — pública, e de propósito. Ela responde dados de uma empresa que não existe e não grava nada. Serve para você escrever e testar o seu cliente inteiro antes de pedir uma chave de verdade ao seu cliente.

Quando estiver pronto, troque a chave e o mesmo código passa a falar com os dados reais. Nada mais muda.

No Windows, use a aba do PowerShell: lá o curl é apelido de Invoke-WebRequest, que não entende -H nem a barra de quebra de linha — o exemplo de curl colado no PowerShell dá um erro que não fala de HTTP.

E termine sempre com | ConvertTo-Json -Depth 5: a tabela padrão do PowerShell mostra os objetos aninhados como empresa=, em branco, e dá a impressão de que a resposta veio vazia quando ela veio inteira.

roda de verdade, contra dados de exemplo

Autenticação#

Toda chamada leva a chave num cabeçalho:

Authorization: Bearer clr_xxxxxxxxxxxxxxxxxxxxxxxx

Se for mais fácil no seu cliente, X-Api-Key: clr_... funciona igual.

Como a empresa gera a chave: dentro do sistema, em *Configurações → Integrações → API de integração*. A chave aparece uma vez, na hora em que é criada — o Claritio guarda só um resumo criptográfico dela, do mesmo jeito que uma senha. Perdeu, é só criar outra e revogar a antiga.

Guarde a chave como senha. Ela dá acesso ao cadastro de pessoas da empresa. Não a coloque no código do aplicativo, no repositório nem no navegador de ninguém: ela é para o seu servidor.

Escopos#

Cada chave carrega só o que precisa. Quem cria escolhe:

EscopoO que libera
pessoas:lerLista de colaboradores, com CPF
pessoas:escreverCadastrar, corrigir e desligar
ponto:lerBatidas e espelho
ponto:escreverLançar batidas
ocorrencias:lerFaltas, atrasos e atestados
ponto:fotosO endereço da foto tirada na batida

Chamar uma rota sem o escopo dela responde 403 dizendo qual escopo falta e quais a chave tem. Se a sua integração só monta relatório, peça uma chave sem ponto:escrever — ela não vai poder estragar nada nem por engano.

pessoas:escrever dá mais poder do que parece. O CPF é o login do Portal do Colaborador: quem tem este escopo decide quem consegue bater ponto. Não peça se a sua integração só lê.

ponto:fotos é separado de propósito. Uma batida é hora e lugar; a foto é o rosto de uma pessoa. São riscos de tamanhos diferentes, e por isso são interruptores diferentes: quem só concilia jornada não precisa receber biometria junto.

Erros#

Resposta boa é sempre assim:

{ "ok": true, "total": 28, "inicio": 0, "limite": 100, "dados": [ … ] }

Erro é assim:

{ "ok": false, "error": "esta chave não tem o escopo \"ponto:escrever\"." }
CódigoO que houve
400Falta um parâmetro, ou ele veio num formato que não dá para ler
401Chave ausente, inválida ou revogada
403Falta o escopo, ou a empresa não tem a API no plano
404A matrícula ou o id que você mandou não existe nesta empresa
429Passou do limite de chamadas por minuto
5xxFoi problema nosso — tente de novo com espera crescente

As mensagens de erro são escritas para serem lidas por gente. Mostre a error no seu log: ela quase sempre diz exatamente o que corrigir.

Limites#

São dois tetos, e eles protegem coisas diferentes.

TetoPadrãoO que ele protege
Por minuto120O servidor, de picos
Por dia20.000O dado, de uma chave que vazou ou de um laço maluco

Os dois sobem sob pedido quando a integração justificar. Toda resposta traz os quatro cabeçalhos:

X-RateLimit-Limit: 120
X-RateLimit-Remaining: 118
X-RateLimit-Reset: 47
X-RateLimit-Day-Limit: 20000
X-RateLimit-Day-Remaining: 19874

Estourou, vem 429 com Retry-After em segundos. Espere o que ele diz, em vez de tentar de novo na hora. O teto do dia zera à meia-noite de Brasília.

Por que um teto diário: 120 por minuto são 172 mil chamadas por dia, e cada uma pode levar 500 batidas. O teto por minuto nunca seria atingido por uma integração mal escrita que reconsulta o mês inteiro a cada volta de um laço — e é justamente esse tipo de acesso que ninguém quer descobrir só na conta do mês.

Peça o teto certo, não o maior. Uma sincronização diária de 500 pessoas com 90 dias de ponto cabe em menos de 100 chamadas. Teto apertado é o que faz uma chave roubada parar sozinha.

O simulador tem limite próprio e menor (60/min no total, somando todo mundo): ele é público e não tem dono.

Segurança#

A chave é do seu servidor. Não a coloque no aplicativo, no repositório nem em código que roda no navegador de alguém. Quem tem a chave lê o cadastro de pessoas da empresa inteira.

Trave a chave nos seus IPs. Ao criar, a empresa pode listar os endereços do seu servidor — aceita IP exato, e faixa 200.1.2.0/24 em IPv4. Com a lista preenchida, a chave só funciona de lá: se o arquivo de configuração vazar, ela não serve em outra máquina. É a trava mais barata e a mais eficaz.

(IPv6 casa por endereço exato, não por faixa. Preferimos dizer isso a prometer o que não fazemos.)

Peça só os escopos que usa. Uma chave de relatório com ponto:escrever pode lançar batida por engano de código — e batida lançada mexe na jornada de uma pessoa de verdade.

Revogar é imediato. A validade é conferida a cada chamada: revogou, a próxima já responde 401. Não há cache para esperar.

O que o Claritio registra: quando a chave foi usada pela última vez e quantas chamadas ela fez, no dia e no total. Isso aparece na tela de quem contratou — é como a empresa descobre a chave esquecida que ninguém usa mais, ou a que passou a usar dez vezes mais que o normal.

Dados de colaborador são dados pessoais. O que sai daqui é regido pela LGPD tanto no seu lado quanto no nosso: guarde só o que for usar, pelo tempo em que for usar.

Datas, fusos e páginas#

Datas são AAAA-MM-DD. Horários vão e voltam em ISO 8601 com fuso: 2026-09-26T08:03:00-03:00.

O dia de uma batida é o dia em Brasília, que é o fuso em que a jornada é apurada. Uma batida às 22h de terça é de terça, mesmo que em UTC já seja quarta. Mandar o horário com fuso é o que garante isso — evite mandar sem.

Na resposta, o deslocamento pode vir como +00:00 em vez de -03:00: é o mesmo instante escrito de outro jeito. Converta com a biblioteca de datas da sua linguagem em vez de recortar o texto — e use sempre o campo dia para saber a que jornada a batida pertence, que é o que o RH enxerga.

Páginas: as listas aceitam limite (padrão 100, teto 500) e inicio (padrão 0), e devolvem total. Para varrer tudo, vá somando inicio até passar do total.

Simulador#

A chave abaixo é pública e pode ficar no seu README:

clr_demo_0000000000000000000000000000

Com ela, todas as rotas respondem dados de exemplo de uma padaria que não existe. As conferências são as mesmas da API de verdade: data em formato errado dá 400 igual, matrícula que não existe dá 404 igual, e o POST de batidas responde repetida quando você reenvia o mesmo id_externo — sem gravar nada em lugar nenhum.

Os nomes e as matrículas são estáveis entre chamadas (para você comparar duas execuções), e as datas acompanham o dia de hoje (para o exemplo não envelhecer).

As quatro pessoas do simulador:

MatrículaNomeSetorSituação
000101Ana Paula RibeiroProduçãoativa
000102Bruno Carvalho LimaAtendimentoativa
000103Carla Souza NunesAdministrativoativa
000104Diego Martins AlvesProduçãodesligado
roda de verdade, contra dados de exemplo

GET /ping#

sem escopo

GET https://rh.claritio.com.br/api/publica/v1/ping

Diz se a chave funciona, de que empresa ela é e o que ela pode fazer. É a primeira chamada de quem está configurando — e a que você deve usar num healthcheck.

Não exige escopo nenhum: basta a chave estar viva.

{
  "ok": true,
  "dados": {
    "empresa": { "id": "…", "nome": "PADARIA AURORA LTDA", "cnpj": "…" },
    "chave": { "nome": "ERP da contabilidade",
               "escopos": ["pessoas:ler", "ponto:ler"],
               "limite_por_minuto": 120 },
    "versao": "v1"
  }
}
roda de verdade, contra dados de exemplo

GET /colaboradores#

pessoas:ler

GET https://rh.claritio.com.br/api/publica/v1/colaboradores

Quem trabalha na empresa.

ParâmetroPadrãoO que faz
situacaoativosativos, desligados ou todos
desde—Só quem mudou a partir desta data/hora ISO
limite100Até 500
inicio0Para paginar

O CPF vem na lista porque é ele que identifica a pessoa no seu sistema — e porque sem ele a sua tela de edição abriria com o campo vazio, dando a entender que o cadastro está sem CPF. Trate-o como dado pessoal: ele é o login do portal.

Use o desde. Numa sincronização diária, ele é a diferença entre puxar quatro mil cadastros toda noite e puxar os três que mudaram.

{
  "ok": true, "total": 3, "inicio": 0, "limite": 100,
  "dados": [
    { "id": "…", "nome": "Ana Paula Ribeiro", "matricula": "000101",
      "cpf": "52998224725", "telefone": "11999998888",
      "email": "[email protected]", "setor": "Produção",
      "cargo": "Padeira", "admitido_em": "2023-03-06",
      "desligado_em": null, "tipo": "clt",
      "atualizado_em": "2026-09-24T09:14:22-03:00" }
  ]
}
roda de verdade, contra dados de exemplo

POST /colaboradores#

pessoas:escrever

POST https://rh.claritio.com.br/api/publica/v1/colaboradores

Cadastra uma pessoa. Nome e CPF são o mínimo — o resto é opcional.

{ "nome": "João da Silva", "cpf": "529.982.247-25" }
CampoObrigatórioObservação
nomesim
cpfsimCom ou sem pontuação. Os dígitos são conferidos
matriculanãoO seu número para essa pessoa
cargo / setornãoTexto livre
emailnãoPara onde vai o comprovante de ponto, se a empresa usar
telefonenãoSó dígitos
admitido_emnãoAAAA-MM-DD

O CPF é o login. É com ele que a pessoa entra no Portal do Colaborador para bater ponto — não é um campo de cadastro qualquer. Por isso o dígito verificador é conferido aqui: um número trocado criaria alguém que nunca consegue entrar, e a tela diria só "CPF não encontrado".

CPF repetido responde quem já é dono dele (409), com o id — assim a sua integração pode usar o cadastro que já existe em vez de travar.

O que esta rota não faz

Não cria escala, turno nem salário. Uma pessoa sem escala bate ponto normalmente e tem a localização gravada; o que ela não tem é jornada esperada — então o sistema não lança falta nem atraso, e o banco de horas fica em zero.

Para muita integração isso é o certo: inventar uma jornada de 8h para quem trabalha em obra faria o sistema acusar falta de quem estava trabalhando. Quem precisa de apuração cadastra o turno pelo sistema, que é onde essa decisão pertence.

roda de verdade, contra dados de exemplo

POST /colaboradores/atualizar#

pessoas:escrever

POST https://rh.claritio.com.br/api/publica/v1/colaboradores/atualizar

Corrige o cadastro, ou desliga.

{ "colaborador": "000101", "cargo": "Pintor", "telefone": "11999998888" }

colaborador aceita o id ou a matrícula. Campo que não vem não é tocado; mandar null apaga o valor.

desligado_em com uma data desliga a pessoa — e ela perde o acesso ao portal no mesmo instante, porque o login confere isso a cada entrada. Com null, reativa.

Trocar o CPF troca o login. Se o novo já for de outra pessoa da mesma empresa, a resposta é 409 dizendo de quem é.

roda de verdade, contra dados de exemplo

GET /batidas#

ponto:ler

GET https://rh.claritio.com.br/api/publica/v1/batidas

As batidas de um período.

A batida sai inteira: hora, lugar, aparelho, se foi negada, se alguém mexeu nela e o que ela era antes. É o registro que sustenta uma contestação trabalhista — um campo que faltasse aqui seria uma resposta que o seu cliente não conseguiria dar.

ParâmetroObrigatórioO que faz
desimPrimeiro dia (AAAA-MM-DD)
atesimÚltimo dia — no máximo 92 dias depois do de
colaboradornãoMatrícula ou id. Sem ele, vêm todos
negadasnãosim inclui as batidas recusadas
fotonãosim acrescenta a foto (exige ponto:fotos)
limite / inicionãoPaginação (até 500 por página)

Os três recortes que você provavelmente quer:

/batidas?de=2026-09-26&ate=2026-09-26                 → o dia de hoje, todo mundo
/batidas?de=2026-09-01&ate=2026-09-30                 → o mês inteiro, todo mundo
/batidas?de=2026-09-01&ate=2026-09-30&colaborador=000101   → o mês de uma pessoa

O teto de 92 dias existe porque a primeira chamada de todo integrador pede cinco anos, e a resposta não caberia na memória — nem na dele. Para carregar histórico, vá trimestre a trimestre.

O que vem em cada batida

CampoO que é
idO identificador dela aqui
colaboradorO id de quem bateu
diaO dia da jornada (Brasília), que pode diferir da data do horário
quandoO instante, com fuso
tipoentry, break_start, break_end, exit, extra
origemtotem, portal_celular, portal_celular_offline, api, manual…
conferenciaComo a pessoa se identificou: facial, qrcode, geolocalizacao…
aparelhoO totem que registrou, quando foi num totem
origem_da_horaserver (nosso relógio) ou device_clock/elapsed (bateu sem rede)
recebida_emQuando chegou até nós — só difere em batida feita offline
ip_de_origemDe onde veio a chamada, quando pelo celular
id_externoO seu número, quando a batida veio pela API
local{ lat, lng, precisao_m } ou null
situacaovalida, em_analise ou negada
negada_em / motivo_da_negativaQuando e por que foi recusada
observacaoO que o sistema anotou (fora do perímetro, sem foto…)
lancada_a_mao / motivo_do_lancamentoSe foi digitada, e por quê
alterada_em / alterada_porQuando alguém corrigiu, e quem
hora_originalO horário antes da correção. null se nunca mudou
rostos_na_fotoQuantos rostos a foto tinha (mais de um merece um olhar)
semelhanca_do_rosto / prova_de_vidaO que o reconhecimento facial apurou
fotoEndereço temporário da foto — só com foto=sim e ponto:fotos
anexoDocumento anexado à batida, quando houver
minutos_trabalhados / minutos_no_bancoO que o fechamento gravou
evento_do_sistemaPreenchido quando a linha é lançamento do sistema, não batida
atestadoO id do documento, quando a linha veio de um

Negadas ficam de fora por padrão. Batida negada não conta jornada; entregá-la misturada com as boas faria qualquer soma sua sair errada sem você ter como saber. Quem está auditando pede negadas=sim.

A foto tem duas travas: o escopo ponto:fotos na chave e o foto=sim no pedido. O endereço que volta vale uma hora — é para abrir e conferir, não para guardar no seu banco. Se você precisa arquivar, baixe o arquivo; o endereço vence.

roda de verdade, contra dados de exemplo

POST /batidas#

ponto:escrever

POST https://rh.claritio.com.br/api/publica/v1/batidas

Lança batidas vindas de outro relógio ou sistema. Até 200 por chamada.

{
  "batidas": [
    { "colaborador": "000101",
      "quando": "2026-09-26T08:03:00-03:00",
      "id_externo": "REL-88213" }
  ]
}

colaborador aceita a matrícula ou o id — use a matrícula, que é o que o seu sistema já conhece.

id_externo é o seu número para aquela batida. Mande sempre. É ele que faz o reenvio ser seguro: se a conexão cair depois de gravarmos, você tenta de novo e a resposta vem marcada "repetida": true, sem criar uma segunda batida. Sem ele, o reenvio duplica — e o estrago não é uma linha repetida, é o tipo de todas as batidas seguintes andando uma casa.

Aceita qualquer texto: "REL-88213", "1042", um UUID. Só precisa ser único para aquela batida, no seu sistema.

A resposta é por item, e o HTTP é 200 mesmo com falhas:

{
  "ok": true, "gravadas": 1, "repetidas": 0, "falhas": 1,
  "dados": [
    { "indice": 0, "ok": true, "repetida": false, "id": "…",
      "colaborador": "…", "dia": "2026-09-26", "tipo": "entry" },
    { "indice": 1, "ok": false, "erro": "não achei a matrícula \"9999\"" }
  ]
}

Olhe falhas e o erro de cada item — não só o código HTTP. Um lote com 199 sucessos e 1 erro é um 200.

O tipo você não manda. Ele é calculado pela posição da batida no dia, e reordenado depois de cada importação: pode mandar o dia inteiro fora de ordem que entry, break_start, break_end e exit saem certos no fim.

Batida com mais de 30 minutos no futuro é recusada.

curl -X POST "https://rh.claritio.com.br/api/publica/v1/batidas" \
  -H "Authorization: Bearer $CHAVE" \
  -H "Content-Type: application/json" \
  -d '{"batidas":[{"colaborador":"000101","quando":"2026-09-26T08:03:00-03:00","id_externo":"REL-88213"}]}'
$corpo = @{ batidas = @(
    @{ colaborador = "000101"
       quando      = "2026-09-26T08:03:00-03:00"
       id_externo  = "REL-88213" }
) } | ConvertTo-Json -Depth 5

Invoke-RestMethod -Method Post -Uri "https://rh.claritio.com.br/api/publica/v1/batidas" `
  -Headers @{ Authorization = "Bearer $CHAVE" } `
  -ContentType "application/json" -Body $corpo
const r = await fetch("https://rh.claritio.com.br/api/publica/v1/batidas", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${CHAVE}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({ batidas: [
    { colaborador: "000101",
      quando: "2026-09-26T08:03:00-03:00",
      id_externo: "REL-88213" }
  ]})
});
const { dados } = await r.json();
roda de verdade, contra dados de exemplo

GET /ocorrencias#

ocorrencias:ler

GET https://rh.claritio.com.br/api/publica/v1/ocorrencias

Faltas, atrasos e atestados do período, com o que cada um mexeu no banco de horas.

ParâmetroObrigatórioO que faz
de / atesimO intervalo (AAAA-MM-DD)
colaboradornãoMatrícula ou id
canceladasnãosim inclui as canceladas

minutos_banco é o número que a folha usa: negativo desconta. Canceladas ficam de fora por padrão — elas existem para auditoria, e quem soma uma lista que as inclui soma o que foi desfeito.

pontos é o desconto na nota de desempenho, quando a empresa usa esse controle.

roda de verdade, contra dados de exemplo

GET /espelho#

ponto:ler

GET https://rh.claritio.com.br/api/publica/v1/espelho

O espelho de ponto de uma pessoa num mês: cada dia com as batidas e o que entrou ou saiu do banco de horas.

ParâmetroObrigatórioO que faz
messimAAAA-MM
colaboradorsimMatrícula ou id

O colaborador é obrigatório de propósito: a empresa inteira num mês são milhares de dias, e uma resposta que não dá para paginar é a que derruba o servidor de quem pediu.

{
  "ok": true,
  "colaborador": { "id": "…", "nome": "Ana Paula Ribeiro", "matricula": "000101" },
  "mes": "2026-09",
  "saldo_do_mes_em_minutos": -18,
  "dados": [
    { "dia": "2026-09-01",
      "batidas": [ { "quando": "2026-09-01T08:00:00-03:00", "tipo": "entry" } ],
      "minutos_banco": 0, "lancamentos": [] }
  ]
}
roda de verdade, contra dados de exemplo

Receitas#

Sincronizar o cadastro toda noite

const desde = ultimaSincronizacao.toISOString();
const r = await fetch(`${BASE}/colaboradores?situacao=todos&desde=${desde}&limite=500`,
                      { headers: { Authorization: `Bearer ${CHAVE}` } });
const { dados } = await r.json();
for (const pessoa of dados) gravarNoMeuSistema(pessoa);

Mandar as batidas do relógio, em lote

const lote = batidasPendentes.slice(0, 200).map(b => ({
    colaborador: b.matricula,
    quando: b.horario.toISOString(),
    id_externo: String(b.id),          // o SEU id, para o reenvio ser seguro
}));

const r = await fetch(`${BASE}/batidas`, {
    method: 'POST',
    headers: { Authorization: `Bearer ${CHAVE}`, 'Content-Type': 'application/json' },
    body: JSON.stringify({ batidas: lote }),
});
const { dados } = await r.json();

// Sucesso E repetida saem da fila: repetida quer dizer "já está lá".
for (const item of dados) {
    if (item.ok) marcarComoEnviada(lote[item.indice]);
    else registrarErro(lote[item.indice], item.erro);
}

Fechar o mês de uma pessoa

curl -H "Authorization: Bearer $CHAVE" \
  "https://rh.claritio.com.br/api/publica/v1/espelho?mes=2026-09&colaborador=000101"

Perguntas frequentes#

Preciso do id do Claritio? Não. Onde cabe um colaborador, cabe a matrícula que você já usa.

A chave vence? Não. Ela vale até alguém revogar.

Perdi a chave. Crie outra e revogue a antiga. Ninguém consegue recuperá-la — nem nós.

Dá para criar colaborador pela API? Ainda não. Admitir alguém envolve escala, turno e documentos, e uma criação pela metade seria pior que nenhuma. Hoje a API lê cadastro e escreve ponto.

Como sei que a empresa tem a API? Chame /ping. Se responder 403 dizendo que a API não está ativa, é plano.

Vocês mandam webhook quando algo muda? Ainda não. Por enquanto, pergunte com desde — é barato e não exige que você exponha um endereço.

Posso chamar do navegador? Tecnicamente sim (a API responde a qualquer origem), mas não faça isso com uma chave de produção: qualquer visitante veria a chave. Do navegador, só com a chave do simulador.

Mudanças#

Dentro do /v1, prometemos:

  • não remover campo nem rota;
  • não mudar o tipo nem o significado de um campo existente;
  • poder acrescentar campos novos — então ignore o que você não conhece, em vez de recusar a resposta.

Mudança que quebre alguma dessas promessas sai como /v2, e o /v1 continua no ar.

QuandoO quê
27/09/2026v1 publicada: colaboradores, batidas, ocorrências e espelho