API and AI agents
What you can do
The API is a list of tools. Each tool does one thing a person could do in the app: see who is working, read a timesheet, add a shift, approve a week. Read tools only look things up. Write tools change real records straight away, and the people affected are told just as if someone had done it in the app.
A key always acts as one person in one company: the person who created it. It can do what that person's role allows, narrowed further to the scopes chosen for the key. Every change a key makes is written to the company's audit log.
Authentication
- In ClockinBee, open Settings, then API and AI agents. You need permission to change company settings.
- Create a key and choose its scopes. The key starts with
cb_live_and is shown once: copy it then. - Send it on every request in the
Authorizationheader.
curl https://clockinbee.com/api/v1/who-is-working \
-H "Authorization: Bearer cb_live_YOUR_KEY"Keep keys on a server. The API does not answer web pages on other sites (it sends no CORS headers), so a key should never be put in browser code. If a key leaks, revoke it in settings: it stops working at once.
Scopes
read: Look things up: who is working, people, locations, projects, timesheets, schedules, time off and what is waiting for approval. Changes nothing.time: Clock in and out, add hours and submit a week's timesheet.time_off: Send a time off request.schedule: Create shifts, assign them and publish a week to the team.approve: Approve or decline timesheets and time off that are waiting.people: Invite people to the company and post announcements to the team.
The HTTP API
Every tool is called the same way: POST /api/v1/tools/<name> with a JSON object of arguments. The list of tools, with a JSON Schema for each one's arguments, is at GET /api/v1/tools and needs no key.
curl https://clockinbee.com/api/v1/toolscurl -X POST https://clockinbee.com/api/v1/tools/list_people \
-H "Authorization: Bearer cb_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{}'For quick reads there are a few plain GET addresses. Anything in the query string is passed to the tool as its arguments.
GET /api/v1/who-is-workingGET /api/v1/peopleGET /api/v1/shifts?from=YYYY-MM-DD&to=YYYY-MM-DDGET /api/v1/approvalsGET /api/v1/suggestions
curl "https://clockinbee.com/api/v1/shifts?from=2026-11-16&to=2026-11-22" \
-H "Authorization: Bearer cb_live_YOUR_KEY"Answers and errors
A call that works answers with status 200 and the result under data. A call that fails answers with a status that says why, and a message written to be read.
{ "data": ... }{ "error": { "code": "forbidden", "message": "This key is not allowed to do that." } }- 400: something is wrong with the arguments. The message says what.
- 401: the key is missing, wrong or revoked.
- 403: the key lacks the scope, or the person it acts as is not allowed to do this.
- 404: there is no tool with that name.
- 405: wrong method for this address.
- 413: the request body is larger than 64 KB.
- 429: too many calls. Wait a minute and try again.
- 500 or 502: something went wrong on our side. Try again shortly.
- 503: the API is not available for a moment. Try again shortly.
Tools
This list is read live from the API, so it is always current.
Read
who_is_workingRead onlyScope: readPeople who are clocked in right now (working or on a break), with where and since when. Shows the people the key's person manages, and the person themself.
list_peopleRead onlyScope: readEveryone in the company: id, name, job, location, role and status. Never includes pay.
list_locationsRead onlyScope: readThe company's locations with their time zones.
list_projectsRead onlyScope: readProjects the key's person can log time to (all active projects if they manage projects).
get_timesheetRead onlyScope: readOne person's timesheet for a week: status, total minutes and each time entry. Leave person_id out for the key's own person.
weekstring (date), required- : Any date in the week; weeks start on Monday.
person_idstring (uuid)
list_pending_approvalsRead onlyScope: readTimesheets and time off requests waiting for the key's person to decide.
list_shiftsRead onlyScope: readShifts between two dates (up to 62 days), optionally for one location. Staff see their own published shifts; schedule planners see everything at their locations.
tostring (date), requiredfromstring (date), requiredlocation_idstring (uuid)
list_open_shiftsRead onlyScope: readPublished shifts with nobody on them yet that the key's person could take.
list_time_offRead onlyScope: readWho is off between two dates (up to 100 days). Pending requests are shown only to the person and their approvers.
tostring (date), requiredfromstring (date), required
time_off_balancesRead onlyScope: readTime off balances by type for one person. Leave person_id out for the key's own person.
person_idstring (uuid)
suggestionsRead onlyScope: readWhat needs attention now: timesheets ready to approve and needing review, time off requests waiting, open shifts in the next 7 days, people late today and people over their contract hours this week.
Make changes
clockMakes changesScope: timeClock the key's person in or out, or start or end a break. location_id is used for clock_in (defaults to their home location). Locations that check where you are (geofence or approved network) cannot be clocked in to through the API.
kind"clock_in" | "clock_out" | "break_start" | "break_end", requiredproject_idstring (uuid)location_idstring (uuid)
add_hoursMakes changesScope: timeAdd worked hours for the key's person on a past day. Times are local to their location, 24-hour HH:MM; an end at or before the start means it ended the next day.
endstring, required- : HH:MM
datestring (date), requirednotestringstartstring, required- : HH:MM
project_idstring (uuid)break_minutesinteger
submit_weekMakes changesScope: timeSubmit the key's person's timesheet for a week for approval.
weekstring (date), required- : Any date in the week; weeks start on Monday.
request_time_offMakes changesScope: time_offAsk for time off for the key's person. type is a time off type id, key or name (for example annual or sick).
tostring (date), requiredfromstring (date), requiredhalf"am" | "pm"notestringtypestring, required
decide_approvalMakes changesScope: approveApprove or send back a waiting timesheet or time off request (ids come from list_pending_approvals). A reason is needed when not approving.
idstring (uuid), requiredreasonstringapproveboolean, required
create_shiftMakes changesScope: scheduleAdd a draft shift at a location. Leave person_id out for an open shift. Staff see it once the week is published.
ends_atstring (date-time), requiredpositionstringperson_idstring (uuid)starts_atstring (date-time), requiredlocation_idstring (uuid), requiredbreak_minutesinteger
assign_shiftMakes changesScope: schedulePut a person on a shift (or move it to someone else). Refused if they already have a shift at that time.
shift_idstring (uuid), requiredperson_idstring (uuid), required
publish_weekMakes changesScope: schedulePublish a location's schedule for a week: draft shifts become visible to the people on them, who are told.
weekstring (date), required- : Any date in the week; weeks start on Monday.
location_idstring (uuid), required
invite_personMakes changesScope: peopleAdd a person to the company as invited. role is a role key (default employee).
namestring, requiredrolestringemailstring (email), requiredlocation_idstring (uuid)
post_announcementMakes changesScope: peoplePost an announcement to everyone in the company.
bodystring, requiredtitlestring, required
AI assistants (MCP)
The Model Context Protocol is the common way for AI assistants such as Claude to use outside tools. ClockinBee has an MCP endpoint, so an assistant can find the tools above by itself and call them when you ask in plain words: "who is in today?", "approve the timesheets that have no problems".
- Address:
https://clockinbee.com/api/mcp - Transport: Streamable HTTP. Each request is a JSON-RPC message sent by POST and answered with JSON.
- Authentication: the same key, as a bearer header.
{
"mcpServers": {
"clockinbee": {
"type": "http",
"url": "https://clockinbee.com/api/mcp",
"headers": {
"Authorization": "Bearer cb_live_YOUR_KEY"
}
}
}
}claude mcp add --transport http clockinbee https://clockinbee.com/api/mcp \
--header "Authorization: Bearer cb_live_YOUR_KEY"You can also talk to the endpoint by hand, which is useful for testing:
curl -X POST https://clockinbee.com/api/mcp \
-H "Authorization: Bearer cb_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"who_is_working","arguments":{}}}'The server supports initialize, ping, tools/list and tools/call. It keeps no session between requests. Read tools are marked with readOnlyHint so assistants can tell them apart.
Webhooks
Instead of asking again and again, give ClockinBee an https address in Settings, API and AI agents, and it will POST a JSON message there when something happens.
clock: Someone clocks in or out, or starts or ends a break.timesheet.submitted: Someone sends a week for approval.timesheet.approved: A week is approved.time_off.requested: Someone asks for time off.time_off.decided: A request is approved or declined.shift.published: Shifts are published to the team.
Check the signature
When you add a webhook you are shown a signing secret, once. Every delivery carries the header X-ClockinBee-Signature: sha256=<hex>, which is the HMAC-SHA256 of the exact request body using that secret. Work out the same value and compare before you trust the message. Use the raw body: parsing and re-writing the JSON changes the bytes and the signature will not match.
The body is JSON with four fields: event (for example timesheet.submitted), org_id, sent_at and data, which holds the details of what happened. The event name is also sent in the X-ClockinBee-Event header. A delivery is sent once and is not retried, so answer quickly and do slow work afterwards.
import { createHmac, timingSafeEqual } from "node:crypto";
import { createServer } from "node:http";
const SECRET = process.env.CLOCKINBEE_WEBHOOK_SECRET;
createServer((req, res) => {
const chunks = [];
req.on("data", (c) => chunks.push(c));
req.on("end", () => {
const body = Buffer.concat(chunks); // the raw bytes, before any JSON parsing
const expected = "sha256=" + createHmac("sha256", SECRET).update(body).digest("hex");
const got = String(req.headers["x-clockinbee-signature"] ?? "");
const ok = got.length === expected.length && timingSafeEqual(Buffer.from(got), Buffer.from(expected));
if (!ok) { res.writeHead(401).end(); return; }
const event = JSON.parse(body.toString("utf8"));
console.log("ClockinBee event", event);
res.writeHead(200).end(); // answer quickly, then do your own work
});
}).listen(3000);The settings page shows the result of the latest delivery to each address, and the error if it failed.
Limits, said plainly
- 120 calls a minute for each key. Past that you get status 429, with a
Retry-After: 60header, until the minute is over. - Request bodies up to 64 KB.
- Write tools are refused while the company's trial has ended or its account is paused. Read tools keep working.
- invite_person adds the person but does not send the invitation email. Send it from Team in the app, where the ⋯ menu has Resend invite and Copy invite link.
- There is no test mode. Every key works on a real company's real data. Try things with a read-only key first.
- Keys only, no sign-in flow. There is no OAuth. An assistant that can only connect by signing in through a browser, and cannot send a bearer header, cannot connect yet.
- One company for each key. If you run several companies, make a key in each.
- No calls from browser pages. Call the API from a server or from an assistant.
- The MCP endpoint answers requests only. It does not push messages to the assistant; use webhooks for that.
- Only the tools listed here. Things such as pay rates, payroll exports, roles and company settings are not in the API. Use the app for those.
- A webhook can arrive late or not at all if your address is down. Treat webhooks as a prompt to look, and use the read tools as the record of what is true.