Skip to main content

API tokens

CalKeep exposes a versioned public API at /api/v1 for server-to-server integrations. Workspace admins on Business and Enterprise plans can issue scoped API tokens.

For the buyer-facing positioning, see Wire CalKeep into the rest of your stack.

Companion surface: Webhooks.

Plan tier

PlanAPI tokensRate limits
Free / ProNot available
BusinessAvailableStandard
EnterpriseAvailableHigher limits

Versioning

The public API is versioned at /api/v1. Earlier app routes (everything not under /api/v1) are internal — they back the web/mobile UI and don't carry stability guarantees. Build integrations against /api/v1 only.

Authentication

Bearer-token authentication on every /api/v1/* request:

Authorization: Bearer <token>

Tokens are workspace-scoped: a token issued in workspace A cannot read or write data in workspace B. CalKeep enforces this at the auth boundary and re-checks at the repository layer.

Issue a token

Settings → Workspace Admin → Integrations → API tokens → Generate token. (Admin role + recent MFA required.)

You'll be asked for:

  • A friendly label (e.g., "RevOps Zapier integration").
  • A scope set (see below).
  • Optional expiration (no expiration means the token lives until revoked).

After saving, CalKeep shows the token once. Save it immediately — there's no way to read it back later. CalKeep stores only a hash plus a short display prefix.

Scopes

A token carries one or more scopes. The available scopes are:

ScopePermits
workspace:readRead workspace metadata.
contacts:readList and read contacts.
contacts:writeCreate and patch contacts.
bookings:readList and read bookings.
tasks:readList and read tasks.
tasks:writeCreate and update tasks.
reminders:readList reminders.
reminders:writeCreate reminders.
calendar_health:readRead connected-account sync health.

Scope checks happen at request time — a contacts:read token cannot hit POST /contacts. Tokens are workspace-scoped on top of scopes.

Rotate a token

Best practice: rotate annually or after any team-membership change for the user who owns the integration.

  1. Generate a new token.
  2. Update your integration's stored credential.
  3. Verify the integration is healthy (most integrations have a self-test / ping).
  4. Revoke the old token.

Revoke a token

Settings → Workspace Admin → Integrations → API tokens → [token] → Revoke.

Revocation is immediate. The token row is preserved (soft-deleted) for audit; further requests with that token authenticate fail.

Resource coverage

Available today

ResourceEndpointScope
WorkspaceGET /api/v1/workspaceworkspace:read
Contacts (list)GET /api/v1/contactscontacts:read
Contacts (one)GET /api/v1/contacts/:idcontacts:read
Contacts (create)POST /api/v1/contactscontacts:write
Contacts (patch)PATCH /api/v1/contacts/:idcontacts:write
Bookings (list)GET /api/v1/bookingsbookings:read
Bookings (one)GET /api/v1/bookings/:idbookings:read

Planned

The token scopes for these resources are already defined so you can issue tokens that will work the moment endpoints land:

  • Tasks — list, read, create, status update.
  • Reminders — list and create.
  • Calendar-account sync health — read.

These ship as the underlying V1.2 platform work completes. Watch the handoff queue for updates.

Public mutations are intentionally narrow today. Additional verbs ship as the underlying object semantics stabilize.

Response shape

Every /api/v1/* response carries a success flag plus a meta block with apiVersion and the request id:

// Success
{
"success": true,
"data": { ... },
"meta": { "apiVersion": "v1", "requestId": "req_..." }
}

// Error
{
"success": false,
"error": { "code": "invalid_token", "message": "...", "requestId": "req_..." }
}

The request id is also returned in the X-CalKeep-Request-Id response header. Pass X-Request-Id: <your-id> on the request to thread your own identifier through the response and audit log.

Rate limits

Standard limits on Business; higher on Enterprise. CalKeep returns:

  • HTTP 429 when limited.
  • Retry-After header in seconds.
  • A clear error.code distinguishing rate limit (rate_limited) from resource exhaustion (e.g., AI quota, marketplace seat ceiling).

For sustained-throughput integrations, prefer webhooks over polling. See Webhooks.

Tenant isolation

Every API request is workspace-scoped:

  1. Bearer token resolves to a workspace.
  2. Every read query is scoped by that workspace's id.
  3. Every write validates the target resource belongs to that workspace before accepting.

Cross-workspace access is rejected at the auth boundary (401) and again at the repository (404 or 403 depending on context).

Audit

Token actions write to the audit log:

  • API_TOKEN_ISSUED
  • API_TOKEN_ROTATED
  • API_TOKEN_REVOKED
  • API_TOKEN_USED (sampled, not per-request — for spotting orphan tokens still in active use)

Review at Settings → Audit Log.

Step-up reauthentication

Issuing or rotating a token requires recent MFA (within the past 5 minutes). If it's stale, CalKeep prompts for TOTP or WebAuthn before letting the token surface a value.

Pagination

List endpoints paginate via page number:

GET /api/v1/contacts?page=1&limit=50

Default page size is 50; max is 200. The list response shape:

{
"success": true,
"data": {
"contacts": [ ... ],
"pagination": { "page": 1, "limit": 50, "total": 1234 }
},
"meta": { "apiVersion": "v1", "requestId": "req_..." }
}

The resource-named key inside data (contacts, bookings, etc.) varies by endpoint.

Sample request

curl https://calkeep.com/api/v1/contacts \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json"

Out of scope (today)

  • OAuth-based delegated access (instead of admin-issued tokens) — V2 candidate.
  • Public mutations beyond the slice above — we're letting object semantics stabilize first.
  • Streaming/long-poll endpoints — use webhooks instead.
  • Custom scope-per-token granularity beyond resource-level — V2 candidate.

For the integration-strategy overview, see Platform automation.