# Errors

Every error has one shape. Branch on `code`: it is stable, and a code is never renamed or reused. The errors a correct integration still sees often are [`period_locked`](/errors/period_locked), [`send_validation_failed`](/errors/send_validation_failed), and [`rate_limited`](/errors/rate_limited).

Any call can answer the common errors: [`invalid_key`](/errors/invalid_key), [`key_expired`](/errors/key_expired), [`key_revoked`](/errors/key_revoked), [`wrong_environment`](/errors/wrong_environment), [`live_key_from_docs`](/errors/live_key_from_docs), [`api_access_disabled`](/errors/api_access_disabled), [`invalid_request`](/errors/invalid_request), [`rate_limited`](/errors/rate_limited), [`api_error`](/errors/api_error), [`service_unavailable`](/errors/service_unavailable).

The whole list downloads as JSON: [errors.json](/errors.json).

| HTTP | Code | When |
|---|---|---|
| 400 | [`invalid_request`](/errors/invalid_request) | The body is not valid JSON, or a value has the wrong type. `param` names it. |
| 400 | [`unknown_field`](/errors/unknown_field) | The body has a field the API does not accept. |
| 400 | [`read_only_field`](/errors/read_only_field) | The body sets a computed field (e.g., `grand_total`). |
| 400 | [`missing_required_input`](/errors/missing_required_input) | A required field is missing. |
| 400 | [`invalid_line_type`](/errors/invalid_line_type) | A line type other than `product`, `service`, `custom`, or `text`, `heading` included. |
| 400 | [`currency_not_supported`](/errors/currency_not_supported) | `currency` is not `usd`. |
| 400 | [`idempotency_key_required`](/errors/idempotency_key_required) | A create, send, or void with no `Idempotency-Key` and no `external_id`. |
| 401 | [`invalid_key`](/errors/invalid_key) | The key is wrong or malformed. |
| 401 | [`key_expired`](/errors/key_expired) | The key passed its expiry date. |
| 401 | [`key_revoked`](/errors/key_revoked) | The key was revoked, or its member was deactivated. |
| 401 | [`wrong_environment`](/errors/wrong_environment) | A `co_test_` key reached a real tenant, or a `co_live_` key reached QA or a sandbox. |
| 402 | [`billing_locked`](/errors/billing_locked) | The tenant's subscription is locked. |
| 403 | [`api_access_disabled`](/errors/api_access_disabled) | API access is off for this company. Every key gets this until staff turn it on. |
| 403 | [`scope_missing`](/errors/scope_missing) | The key lacks the scope. `param` names it (e.g., `invoices.send`). |
| 403 | [`member_lacks_permission`](/errors/member_lacks_permission) | The key's member is not allowed to do this in the app. |
| 403 | [`agent_key_not_allowed`](/errors/agent_key_not_allowed) | An agent key called the REST API. |
| 403 | [`live_key_from_docs`](/errors/live_key_from_docs) | A `co_live_` key was sent from the docs site's Try it panel. |
| 404 | [`resource_missing`](/errors/resource_missing) | No such record, or the key cannot see it. `param` names the id. |
| 409 | [`idempotency_conflict`](/errors/idempotency_conflict) | The same `Idempotency-Key` with a different body. |
| 409 | [`external_id_exists`](/errors/external_id_exists) | A create with an `external_id` already used. The error carries the existing `id`. |
| 409 | [`books_not_set_up`](/errors/books_not_set_up) | The tenant has no chart of accounts yet. |
| 409 | [`period_locked`](/errors/period_locked) | The write would post on or before the lock date (`books_locked_through`). |
| 409 | [`invoice_not_draft`](/errors/invoice_not_draft) | A change that only a draft allows (lines, amounts, delete). |
| 409 | [`invoice_locked`](/errors/invoice_locked) | A change to an invoice with a payment applied. |
| 409 | [`invoice_fix_in_progress`](/errors/invoice_fix_in_progress) | The owner has the invoice open in "Fix the numbers". |
| 409 | [`invoice_not_voidable`](/errors/invoice_not_voidable) | The app would refuse this void (e.g., already voided). The message gives the app's reason. |
| 422 | [`send_validation_failed`](/errors/send_validation_failed) | Send rules I1 to I9 failed. `errors` lists each one. |
| 429 | [`rate_limited`](/errors/rate_limited) | Too many requests. Carries `Retry-After` and `reason` (`key_rate` or `tenant_rate`). |
| 500 | [`api_error`](/errors/api_error) | Our fault. |
| 503 | [`service_unavailable`](/errors/service_unavailable) | The API is down or overloaded. |
