Conventions
Times#
LifeButler lives in Europe/Berlin (Germany, Austria, Switzerland), and so does the API.
- Responses use ISO 8601 with the Berlin offset:
"2026-11-20T19:30:00+01:00"(winter),"2026-07-01T10:00:00+02:00"(summer). Dates are"2026-11-20". - Requests accept any ISO 8601 instant (
…Z,…+01:00). A time without offset ("2026-11-20T19:30") is Berlin time. A plain date is the start of that day in Berlin. - All-day appointments take dates:
startis the first day,endthe last day (inclusive). - Repeating appointments keep their wall-clock time across daylight-saving changes, like in the app.
GET …/eventsexpands them into single occurrences.
IDs#
IDs are opaque strings (letters, digits, _, -). Workspace IDs come from GET /workspaces, member IDs from GET /workspaces/{id}/members – use those for attendeeIds and assigneeIds.
Paging#
Lists that can grow without end (documents) are paged:
{ "data": [ … ], "nextCursor": "Qm9x1…" }Pass cursor=<nextCursor> for the next page; nextCursor is null on the last one. limit sets the page size. Other lists return everything at once, or a capped list with "truncated": true.
Writing#
POSTcreates and answers201with the new object.PATCHchanges only the fields you send. Sendnullto clear a field where the reference says so.DELETEanswers{ "id": "…", "deleted": true }.- Unknown fields are ignored.
- Requests aren't deduplicated. If a
POSTtimes out, check (e.g. list the items) before you send it again.
Errors#
Errors use the HTTP status and a stable code:
{ "error": { "code": "workspace_not_found", "message": "No such workspace for this API key.", "requestId": "4c1e…" } }| Status | code | Meaning |
|---|---|---|
| 400 | invalid_request, invalid_cursor | Something in the request is wrong – see message |
| 401 | missing_key, invalid_key, key_expired | No usable key |
| 402 | no_credits | The workspace owner has no AI credits left this month |
| 403 | plan_required | The key's account has no Pro/Premium plan (any more) |
| 403 | insufficient_scope | The key lacks the scope |
| 403 | admin_required | You are not owner/admin of the workspace (any more) |
| 403 | workspace_limit | More shared workspaces than the plan allows |
| 404 | workspace_not_found, not_found | Doesn't exist – or isn't yours |
| 409 | read_only, duplicate, ai_disabled, cannot_analyze | Not possible in the current state |
| 413 / 415 | too_large, unsupported_type | Upload too big / wrong file type |
| 429 | rate_limited | Too many requests – wait Retry-After seconds |
| 503 | ai_unavailable | The AI is busy – try again shortly |
| 507 | storage_full | The workspace owner's storage is full |
Quote the requestId (also in the X-Request-Id header) when you contact support.
Rate limits#
120 requests per minute and key. Every response tells you where you stand:
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 117
X-RateLimit-Reset: 42Over the limit you get 429 with Retry-After. Back off and retry after that many seconds; for 5xx use exponential backoff.
AI credits#
ask_butler and document analysis cost AI credits of the workspace owner, exactly like in the app (butler: usually 1–2 per question; documents: 1 per page). Smart search is free. AI features only work where the workspace admins turned AI on (Workspace settings → AI); otherwise you get 409 ai_disabled.
Versioning#
The API is versioned in the path (/v1). Within v1 we only add things: new endpoints, new optional fields, new values in fields documented as open lists. Build your client to ignore unknown fields. Breaking changes would come as /v2, announced well ahead.