Errors and rate limits
Every public API error uses one JSON envelope:
{ "error": { "type": "invalid_request", "code": "invalid_cursor", "message": "The cursor parameter is not a cursor this API issued.", "request_id": "req_7f24a2f8c6b64e699901563a458b5031" }}The same ID comes back in X-Request-Id. Log it, and quote it in a support
request. Branch on error.code; the message is for humans.
Common codes
Section titled “Common codes”| HTTP | Code | Meaning | Action |
|---|---|---|---|
| 400 | invalid_parameter |
A path, query, header, or body value is invalid | Fix the request. |
| 400 | invalid_cursor |
The cursor was altered, belongs to the other mode (a list cursor on a feed, or the reverse), was issued by another change feed, or is a change-feed cursor issued under another location_id filter |
Resume with the filter it came with, or restart that feed without a cursor. |
| 400 | idempotency_key_required |
An appointment write (restore included), a service, resource, closure, tag, caller-rule or document create, or teaching the assistant omitted Idempotency-Key |
Add one unique to the logical write. |
| 400 | unsupported_version |
Talki-Version, or the credential’s pin, names a version this API does not serve |
Send one of the versions the message lists. |
| 401 | invalid_credentials |
The key is missing or unusable | Check secret injection or replace the key. |
| 402 | subscription_inactive |
The account has no active subscription | Ask the owner to resolve billing. |
| 403 | insufficient_scope |
The key lacks the endpoint’s scope | Use an appropriately scoped key. |
| 403 | plan_upgrade_required |
The plan does not include the REST API | Ask the owner to upgrade to Business. |
| 403 | plan_feature_unavailable |
The plan includes the API but not this feature: a custom call_opening, or exporting calls over a date range |
Leave the feature out, or ask the owner to upgrade. |
| 403 | account_closed |
The account is scheduled for deletion | Stop syncing and contact the owner. |
| 403 | forbidden |
The credential may not take this action on this account: deleting a call during a free trial, or resetting a partner’s test account | Do not retry unchanged; read the message. A call can be deleted once the trial has ended. |
| 403 | sandbox_api_key_required |
An OAuth access token called a /v1/sandbox/* control, other than the token of the app that created the test account it is on |
Use the sandbox API key from the console. |
| 404 | resource_not_found |
The resource, or the location location_id names, is absent from this account; or a document has no file to download |
Remove or reconcile the stale reference. For a document’s file, wait for file_available, or read its text. |
| 404 | transcript_not_available |
The call exists but has no transcript: the account does not keep transcripts, or nothing usable was said | Do not retry; show the call without a transcript. |
| 409 | idempotency_key_reuse |
The key was already used for a different request | Use a new key for a new write. |
| 409 | request_in_progress |
The first request with this key has not finished | Retry the same request shortly. |
| 409 | appointment_cancelled |
The appointment cannot be changed in its current state | Refresh it and create a new booking if needed. |
| 409 | appointment_not_cancelled |
Restoring an appointment that is not cancelled | Nothing to restore: read the appointment. |
| 409 | livemode_required |
A test-mode credential asked for a real call or a permanent deletion: a forwarding check, store_transcripts: false, or deleting a call |
Run it with a live credential. |
| 409 | unresolved_request_not_resolved |
Teaching the assistant from a request that is not resolved |
Resolve the request first. |
| 409 | faq_full |
The FAQ note the answer would go into has no room left | Ask the business to tidy the note in Talki. |
| 409 | request_conflict |
Another request added the same number to the caller rules at the same moment, or changed a document’s copies at the same time | Retry; for a caller rule the retry answers 200 with the rule. |
| 409 | conflict |
The request conflicts with the current state: a website already imported at that location, or a page delete that would leave a website with no text to search | Do not retry unchanged; read the message. For a website already imported, it names the document holding the site: import that one again with PUT. For a page delete, import the site again with the pages to keep, or delete the document. |
| 409 | document_not_ready |
Replacing a file before the last file sent to it is in place; renaming a file while its file, or with also=copies a copy’s file, is processing; importing a website again or deleting one of its pages during an import, or importing it again while a new version switches in. A note is never refused |
For a file replacement, or a website that is already ready, retry shortly. Otherwise retry once the status, of the copy named if any, is ready or failed. When the message names a copy, you can also send the request without also=copies. |
| 412 | precondition_failed |
If-Match names a version other than the stored one |
Read again, reapply the change, and retry with the new ETag. |
| 413 | payload_too_large |
A file over the plan’s upload size, a note over 100 KB, or a document write or taught answer while the account’s knowledge storage is full for its plan | Send less; for storage, ask the business to free space in Talki, or to upgrade. |
| 415 | unsupported_file_type |
A document file that is not PDF, DOCX, XLSX, TXT or CSV, or whose content does not match its type, or a body neither application/json nor multipart/form-data |
Convert the file, or fix the Content-Type. |
| 429 | rate_limit_exceeded |
The account or installation exceeded its request rate | Honour Retry-After and back off. |
| 429 | ip_rate_limited |
Too many failed authentications, or requests, from your address | Fix the credential; wait for Retry-After. |
| 500 | internal_error |
Talki hit an unexpected failure | Retry with backoff; report the request ID if persistent. |
| 502, 503 | api_error |
A service the request depends on could not answer, such as a site that could not be read for discovery (502), or a knowledge-base file or index that is briefly unavailable (503) |
Retry later; for a site, check the address. |
A resource in another account returns the same 404 as one that does not exist.
Booking refusals
Section titled “Booking refusals”POST and PATCH /v1/appointments return stable machine codes:
409means another time may work:slot_unavailable,capacity_exhausted,service_exclusion,closed_day,outside_hours, orin_the_past.outside_hoursis a time outside the business’s opening hours for that day;in_the_pastis a start that has already passed. Neither is ever offered byGET /v1/availability.400means changing only the time cannot help:service_inactive,location_inactive,party_too_large,stay_too_short,stay_too_long,stay_dates_invalid, orno_services_requested; andinvalid_parameterfor a quantity outside the service’s range.400 invalid_parameteronPATCHis also a move that changes the length of a booking of one timed service: see Write a booking.409 booking_refusedis a refusal with no code of its own. Do not retry it unchanged: try another time, or ask the business.
location_inactive is a booking at a location that is switched off: booking
one of its services, and moving or restoring one of its bookings, are refused.
Such a booking can still be renamed or cancelled.
PATCH /v1/appointments/{id} and POST /v1/appointments/{id}/restore also
answer 400 service_deleted when a service of the booking has been deleted and,
after the move or restore, that service’s part of the booking would end in the
future. Book it again with a service that exists. A booking of a deleted
service can still be renamed, or moved within the past.
Availability is advisory, so a 409 after a clean availability lookup is
normal. Refresh the windows and let the customer pick again.
Capacity refusals
Section titled “Capacity refusals”PATCH /v1/resources/{id} refuses a capacity the business cannot honour:
409 capacity_below_committed— bookings already on the calendar use more of the resource at once than the capacity you asked for. The message carries both numbers and the moment of peak use. The write is refused: a configuration change never cancels existing bookings.400 capacity_below_service_requirement— a service linked to the resource can request more units than the new capacity would ever allow. Lower that service’s quantity range, or unlink it, in Talki first.400 invalid_parameter— the resource is inunitsmode, where capacity is derived from unit classes that this API does not write.
The two creates refuse a link that cannot hold a booking:
POST /v1/services—400 resource_too_smallwhen a booking of the service would not fit a resource inresource_ids, or the location’s default resource when neither is sent, and400 invalid_parameterwhen bothinitial_capacityandresource_idsare sent, neither while the location has no default resource yet, or a resource of another location.POST /v1/resources—400 capacity_below_service_requirementwhen a service inservice_idscan request more units thancapacity, and400 invalid_parameterfor a service booked by the day or night, without a duration, or of another location.
Nothing is created when a create is refused.
Delete refusals
Section titled “Delete refusals”DELETE /v1/services/{id} is refused while the service still has bookings to
honour. DELETE /v1/resources/{id} is refused for the default resource and for
a resource a service cannot be booked without. Nothing is cancelled or unlinked
to let a delete through:
409 service_has_upcoming_bookings— the service has bookings still to come or under way, even if it is already inactive. Setis_active: falsewithPATCH /v1/services/{id}instead: the assistant stops offering it and its bookings are kept. Delete it once its last booking has ended or been cancelled.409 resource_is_default— the resource is its location’s default resource (is_default), which new services draw on. Its capacity can be changed; it cannot be deleted.409 resource_required_by_service— a service draws on no other resource: an active one, or one booked by the day or night even while inactive. The message lists their IDs. Link a service with a duration to another resource first, for example withPOST /v1/resourcesandservice_ids, or switch it off. A service booked by the day or night needs aunitsresource, which this API does not create (POST /v1/resourceswith its ID is400 invalid_parameter): relink it in Talki.
Retry rules
Section titled “Retry rules”Retry 429 and transient 5xx with exponential backoff and jitter, honouring
Retry-After when it is present. An appointment retry must carry the original
Idempotency-Key and the identical request; the replay returns the original
status and body with Idempotency-Replayed: true.
A key keeps only a final answer:
- Replayed for 24 hours: a success, and a refusal that holds until someone
changes the state. That is a booking
409such asslot_unavailable,appointment_cancelled,appointment_not_cancelled, a full list (tag_limit_reached,caller_rule_limit_reached),livemode_required,conflict,404 resource_not_found, a400that depends on the business (service_inactive,location_inactive,service_deleted,party_too_large),413 payload_too_large, and any code not listed below. Once its cause is gone, send the request with a new key. - Released, so the same key runs the request again: a
400or415that asks for a different request (invalid_parameter,invalid_cursor,export_too_large,unknown_scenario,no_services_requested,stay_too_short,stay_too_long,stay_dates_invalid,unsupported_file_type), a409whose cause clears on its own (request_conflict,document_not_ready,check_in_progress), and any412,429or5xx. The corrected request can reuse the key.
A key is released only if the request saved nothing. If it had already saved its
changes, a refusal is replayed as it was sent, and a 412, 429 or 5xx is
replayed as 500 internal_error, saying the changes may have been saved. Read
the resource to see the result before writing again.
409 request_in_progress is never stored: retry it with the same key and the
same request, or a booking may be made twice.
Keys are scoped to the credential: two keys, or two installations, on the same account never collide, and a rolled API key starts a fresh scope.
400, 401, 402, 403 and 404 are not worth retrying unchanged. The named
409 cases are handled above. A 412 precondition_failed needs a fresh read
first; see concurrent edits.
Rate limits
Section titled “Rate limits”Limits are per account for API keys, and per installation for OAuth apps:
| Operation | Limit |
|---|---|
GET /v1/account |
60 requests/minute |
| Public read endpoints | 600 requests/minute |
| Public write endpoints | 120 requests/minute |
GET /v1/sandbox/scenarios counts as a read. POST /v1/sandbox/calls allows 30
requests/minute and POST /v1/sandbox/reset 6.
POST /v1/services, POST /v1/resources, POST /v1/tags and
POST /v1/caller_rules count as writes, and together are also limited to 20
creations/minute.
GET /v1/calls/export and GET /v1/calls/{call_id}/export count as reads, and
together are also limited to 10 exports/minute.
POST /v1/documents, PUT and PATCH /v1/documents/{document_id} and
POST /v1/documents/discover count as writes, and together are also limited to
60 document writes/minute.
POST /v1/forwarding_check counts as a write and places a real call, so it has
quotas of its own: 3 checks an hour per location, and 30 an hour per OAuth app
across all the accounts it is installed on and all their locations. Past either, it answers
429 check_quota_exceeded with Retry-After and none of the X-RateLimit-*
headers.
The per-minute windows are one minute long. Every API key on an account draws from one budget per limit; an OAuth app gets one per installation, so each customer account it serves has its own.
A 429 rate_limit_exceeded carries Retry-After (seconds) and
X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (Unix
seconds). Successful responses carry none of them, so pace yourself rather than
reading headers.
Per-address limits
Section titled “Per-address limits”Two more limits apply to the address a request arrives from, whatever the credential:
| Limit | Counts | Beyond it |
|---|---|---|
| 20/minute | Failed authentications: a 401 invalid_credentials, or invalid_client on /oauth/token and /oauth/revoke |
Every request from the address, valid credentials included, gets 429 ip_rate_limited until the minute ends |
| 20,000/minute | All requests | 429 ip_rate_limited |
They apply to /v1, /oauth/token and /oauth/revoke. 429 ip_rate_limited
carries Retry-After and none of the X-RateLimit-* headers; on /oauth/token
and /oauth/revoke its body has the OAuth shape. The
address is the one that connected to Talki; an X-Forwarded-For you send is
ignored.
