Errors, limits & paging
Errors
Every error has the same shape, and every answer carries Daysli-Request-Id:
{ "error": { "code": "invalid_input", "message": "Check the field named in this error.",
"field": "to", "requestId": "6e28c492-5607-4358-8972-5f0d2dd649dd" } }| HTTP | code | Meaning |
|---|---|---|
| 400 | bad_request | The body is not JSON. |
| 401 | invalid_token | Missing, malformed, unknown or revoked token. |
| 401 | token_expired | The token reached the end of its life. Make a new one. |
| 403 | insufficient_scope | The token was not granted the scope this needs (named in WWW-Authenticate). |
| 403 | app_suspended | The app has been suspended. |
| 404 | not_found | Unknown, deleted, another owner’s, out of your scopes, or booking is off — never told apart. |
| 405 | method_not_allowed | The path exists; the method does not. |
| 409 | slot_unavailable | That time is no longer free. |
| 409 | invalid_transition | The request is not in a state that allows that (e.g. withdrawing one already handled). |
| 422 | invalid_input | A parameter or field is wrong; `field` names it. Unknown parameters and fields are refused too. |
| 422 | idempotency_mismatch | The Idempotency-Key was used with a different body. |
| 429 | rate_limited | A limit was reached; wait for Retry-After. |
| 429 | too_many_pending | This person already has three requests waiting for the owner. |
| 500 | internal | Our fault. Quote the requestId. |
| 503 | unavailable | Something we depend on is down. Retry with backoff. |
Codes are stable; messages may change. Branch on code, show message.
Rate limits
| What | Limit |
|---|---|
| Reads, per token | 600 a minute |
GET /availability, per token | 60 a minute |
| Creating and withdrawing requests, per token | 30 an hour and 200 a day |
| Everything for one owner, all tokens together | 3,000 a minute |
| Requests from one requester’s email or phone, from any source | 5 a day — the same limit as the booking page, so the API cannot be used to email anyone more |
Every answer carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset (seconds); a 429 carries Retry-After.
Idempotency
Every POST needs an Idempotency-Key of 16 to 64 letters, digits, - or _ — a UUID is ideal. Sending the same key and body again returns the first answer and creates nothing; the same key with a different body is 422 idempotency_mismatch. Keys last 24 hours and belong to one token. Retry a POST that timed out with its original key, never a new one.
Paging and sync
- Lists take
limit(1–100, default 50) and answer{ "data": [...], "nextCursor": "…" | null }. Passcursorback with the same parameters for the next page. A cursor belongs to its query and its token and lasts 24 hours. /tasksand/requestsare ordered by last change. Keep the time of your last sync and pass it asupdatedSinceto get only what changed;includeDeleted=trueaddsdeleted: [{ id, deletedAt }]for the last 30 days./eventstakes a window (from,to, at most 62 days) and returns occurrences in start order, a repeating event expanded as the owner’s calendar draws it. One occurrence’s id is<series id>@<original start>.
Every answer is sent with Cache-Control: no-store.