API do Claritio
Leia colaboradores, ponto e ocorrências. Lance batidas de outro sistema. Sem SDK, sem OAuth: uma chave num cabeçalho.
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/v1O /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.
Autenticação#
Toda chamada leva a chave num cabeçalho:
Authorization: Bearer clr_xxxxxxxxxxxxxxxxxxxxxxxxSe 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:
| Escopo | O que libera |
|---|---|
pessoas:ler | Lista de colaboradores, com CPF |
pessoas:escrever | Cadastrar, corrigir e desligar |
ponto:ler | Batidas e espelho |
ponto:escrever | Lançar batidas |
ocorrencias:ler | Faltas, atrasos e atestados |
ponto:fotos | O 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ódigo | O que houve |
|---|---|
| 400 | Falta um parâmetro, ou ele veio num formato que não dá para ler |
| 401 | Chave ausente, inválida ou revogada |
| 403 | Falta o escopo, ou a empresa não tem a API no plano |
| 404 | A matrícula ou o id que você mandou não existe nesta empresa |
| 429 | Passou do limite de chamadas por minuto |
| 5xx | Foi 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.
| Teto | Padrão | O que ele protege |
|---|---|---|
| Por minuto | 120 | O servidor, de picos |
| Por dia | 20.000 | O 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: 19874Estourou, 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_0000000000000000000000000000Com 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ícula | Nome | Setor | Situação |
|---|---|---|---|
000101 | Ana Paula Ribeiro | Produção | ativa |
000102 | Bruno Carvalho Lima | Atendimento | ativa |
000103 | Carla Souza Nunes | Administrativo | ativa |
000104 | Diego Martins Alves | Produção | desligado |
GET /ping#
sem escopoGET 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"
}
}GET /colaboradores#
pessoas:lerGET https://rh.claritio.com.br/api/publica/v1/colaboradores
Quem trabalha na empresa.
| Parâmetro | Padrão | O que faz |
|---|---|---|
situacao | ativos | ativos, desligados ou todos |
desde | — | Só quem mudou a partir desta data/hora ISO |
limite | 100 | Até 500 |
inicio | 0 | Para 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" }
]
}POST /colaboradores#
pessoas:escreverPOST 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" }| Campo | Obrigatório | Observação |
|---|---|---|
nome | sim | |
cpf | sim | Com ou sem pontuação. Os dígitos são conferidos |
matricula | não | O seu número para essa pessoa |
cargo / setor | não | Texto livre |
email | não | Para onde vai o comprovante de ponto, se a empresa usar |
telefone | não | Só dígitos |
admitido_em | não | AAAA-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.
POST /colaboradores/atualizar#
pessoas:escreverPOST 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 é.
GET /batidas#
ponto:lerGET 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âmetro | Obrigatório | O que faz |
|---|---|---|
de | sim | Primeiro dia (AAAA-MM-DD) |
ate | sim | Último dia — no máximo 92 dias depois do de |
colaborador | não | Matrícula ou id. Sem ele, vêm todos |
negadas | não | sim inclui as batidas recusadas |
foto | não | sim acrescenta a foto (exige ponto:fotos) |
limite / inicio | não | Paginaçã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 pessoaO 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
| Campo | O que é |
|---|---|
id | O identificador dela aqui |
colaborador | O id de quem bateu |
dia | O dia da jornada (Brasília), que pode diferir da data do horário |
quando | O instante, com fuso |
tipo | entry, break_start, break_end, exit, extra |
origem | totem, portal_celular, portal_celular_offline, api, manual… |
conferencia | Como a pessoa se identificou: facial, qrcode, geolocalizacao… |
aparelho | O totem que registrou, quando foi num totem |
origem_da_hora | server (nosso relógio) ou device_clock/elapsed (bateu sem rede) |
recebida_em | Quando chegou até nós — só difere em batida feita offline |
ip_de_origem | De onde veio a chamada, quando pelo celular |
id_externo | O seu número, quando a batida veio pela API |
local | { lat, lng, precisao_m } ou null |
situacao | valida, em_analise ou negada |
negada_em / motivo_da_negativa | Quando e por que foi recusada |
observacao | O que o sistema anotou (fora do perímetro, sem foto…) |
lancada_a_mao / motivo_do_lancamento | Se foi digitada, e por quê |
alterada_em / alterada_por | Quando alguém corrigiu, e quem |
hora_original | O horário antes da correção. null se nunca mudou |
rostos_na_foto | Quantos rostos a foto tinha (mais de um merece um olhar) |
semelhanca_do_rosto / prova_de_vida | O que o reconhecimento facial apurou |
foto | Endereço temporário da foto — só com foto=sim e ponto:fotos |
anexo | Documento anexado à batida, quando houver |
minutos_trabalhados / minutos_no_banco | O que o fechamento gravou |
evento_do_sistema | Preenchido quando a linha é lançamento do sistema, não batida |
atestado | O 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.
POST /batidas#
ponto:escreverPOST 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();
GET /ocorrencias#
ocorrencias:lerGET 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âmetro | Obrigatório | O que faz |
|---|---|---|
de / ate | sim | O intervalo (AAAA-MM-DD) |
colaborador | não | Matrícula ou id |
canceladas | não | sim 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.
GET /espelho#
ponto:lerGET 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âmetro | Obrigatório | O que faz |
|---|---|---|
mes | sim | AAAA-MM |
colaborador | sim | Matrí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": [] }
]
}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.
| Quando | O quê |
|---|---|
| 27/09/2026 | v1 publicada: colaboradores, batidas, ocorrências e espelho |