Skip to content

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.

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.

POST and PATCH /v1/appointments return stable machine codes:

  • 409 means another time may work: slot_unavailable, capacity_exhausted, service_exclusion, closed_day, outside_hours, or in_the_past. outside_hours is a time outside the business’s opening hours for that day; in_the_past is a start that has already passed. Neither is ever offered by GET /v1/availability.
  • 400 means changing only the time cannot help: service_inactive, location_inactive, party_too_large, stay_too_short, stay_too_long, stay_dates_invalid, or no_services_requested; and invalid_parameter for a quantity outside the service’s range.
  • 400 invalid_parameter on PATCH is also a move that changes the length of a booking of one timed service: see Write a booking.
  • 409 booking_refused is 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.

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 in units mode, 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_small when a booking of the service would not fit a resource in resource_ids, or the location’s default resource when neither is sent, and 400 invalid_parameter when both initial_capacity and resource_ids are sent, neither while the location has no default resource yet, or a resource of another location.
  • POST /v1/resources — 400 capacity_below_service_requirement when a service in service_ids can request more units than capacity, and 400 invalid_parameter for a service booked by the day or night, without a duration, or of another location.

Nothing is created when a create is refused.

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. Set is_active: false with PATCH /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 with POST /v1/resources and service_ids, or switch it off. A service booked by the day or night needs a units resource, which this API does not create (POST /v1/resources with its ID is 400 invalid_parameter): relink it in Talki.

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 409 such as slot_unavailable, appointment_cancelled, appointment_not_cancelled, a full list (tag_limit_reached, caller_rule_limit_reached), livemode_required, conflict, 404 resource_not_found, a 400 that 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 400 or 415 that 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), a 409 whose cause clears on its own (request_conflict, document_not_ready, check_in_progress), and any 412, 429 or 5xx. 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.

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.

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.