1 month free trial. Every feature, your whole team, no card needed
Developers

API and AI agents

Read and control a company's ClockinBee from your own code, or let an AI assistant do it for you. One set of tools, reachable three ways: a plain HTTP API, a Model Context Protocol (MCP) endpoint for assistants, and signed webhooks that tell you when something happens.

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

  1. In ClockinBee, open Settings, then API and AI agents. You need permission to change company settings.
  2. Create a key and choose its scopes. The key starts with cb_live_ and is shown once: copy it then.
  3. Send it on every request in the Authorization header.
Who is working right now
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.

List the tools
curl https://clockinbee.com/api/v1/tools
Call a tool
curl -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-working
  • GET /api/v1/people
  • GET /api/v1/shifts?from=YYYY-MM-DD&to=YYYY-MM-DD
  • GET /api/v1/approvals
  • GET /api/v1/suggestions
Shifts in one week
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.

Success
{ "data": ... }
Failure
{ "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: read

    People 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: read

    Everyone in the company: id, name, job, location, role and status. Never includes pay.

  • list_locationsRead onlyScope: read

    The company's locations with their time zones.

  • list_projectsRead onlyScope: read

    Projects the key's person can log time to (all active projects if they manage projects).

  • get_timesheetRead onlyScope: read

    One person's timesheet for a week: status, total minutes and each time entry. Leave person_id out for the key's own person.

    week string (date), required
    : Any date in the week; weeks start on Monday.
    person_id string (uuid)
  • list_pending_approvalsRead onlyScope: read

    Timesheets and time off requests waiting for the key's person to decide.

  • list_shiftsRead onlyScope: read

    Shifts between two dates (up to 62 days), optionally for one location. Staff see their own published shifts; schedule planners see everything at their locations.

    to string (date), required
    from string (date), required
    location_id string (uuid)
  • list_open_shiftsRead onlyScope: read

    Published shifts with nobody on them yet that the key's person could take.

  • list_time_offRead onlyScope: read

    Who is off between two dates (up to 100 days). Pending requests are shown only to the person and their approvers.

    to string (date), required
    from string (date), required
  • time_off_balancesRead onlyScope: read

    Time off balances by type for one person. Leave person_id out for the key's own person.

    person_id string (uuid)
  • suggestionsRead onlyScope: read

    What 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: time

    Clock 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", required
    project_id string (uuid)
    location_id string (uuid)
  • add_hoursMakes changesScope: time

    Add 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.

    end string, required
    : HH:MM
    date string (date), required
    note string
    start string, required
    : HH:MM
    project_id string (uuid)
    break_minutes integer
  • submit_weekMakes changesScope: time

    Submit the key's person's timesheet for a week for approval.

    week string (date), required
    : Any date in the week; weeks start on Monday.
  • request_time_offMakes changesScope: time_off

    Ask for time off for the key's person. type is a time off type id, key or name (for example annual or sick).

    to string (date), required
    from string (date), required
    half "am" | "pm"
    note string
    type string, required
  • decide_approvalMakes changesScope: approve

    Approve or send back a waiting timesheet or time off request (ids come from list_pending_approvals). A reason is needed when not approving.

    id string (uuid), required
    reason string
    approve boolean, required
  • create_shiftMakes changesScope: schedule

    Add a draft shift at a location. Leave person_id out for an open shift. Staff see it once the week is published.

    ends_at string (date-time), required
    position string
    person_id string (uuid)
    starts_at string (date-time), required
    location_id string (uuid), required
    break_minutes integer
  • assign_shiftMakes changesScope: schedule

    Put a person on a shift (or move it to someone else). Refused if they already have a shift at that time.

    shift_id string (uuid), required
    person_id string (uuid), required
  • publish_weekMakes changesScope: schedule

    Publish a location's schedule for a week: draft shifts become visible to the people on them, who are told.

    week string (date), required
    : Any date in the week; weeks start on Monday.
    location_id string (uuid), required
  • invite_personMakes changesScope: people

    Add a person to the company as invited. role is a role key (default employee).

    name string, required
    role string
    email string (email), required
    location_id string (uuid)
  • post_announcementMakes changesScope: people

    Post an announcement to everyone in the company.

    body string, required
    title string, 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.
Config for an MCP client
{
  "mcpServers": {
    "clockinbee": {
      "type": "http",
      "url": "https://clockinbee.com/api/mcp",
      "headers": {
        "Authorization": "Bearer cb_live_YOUR_KEY"
      }
    }
  }
}
Claude Code
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:

Call a tool over MCP
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":{}}}'
An assistant with a key that has write scopes can change real schedules, timesheets and time off. Start with a read-only key. Add write scopes when you trust the setup, and keep your assistant's "ask before acting" setting on for tools marked as making changes.

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.

Verify a delivery (Node.js)
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: 60 header, 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.

Create a keyAsk us a question