[
  {
    "code": "invalid_request",
    "status": 400,
    "type": "invalid_request_error",
    "when": "The body is not valid JSON, or a value has the wrong type. `param` names it.",
    "fix": "Fix the value.",
    "doc_url": "https://qa-docs.cloudoverture.com/errors/invalid_request"
  },
  {
    "code": "unknown_field",
    "status": 400,
    "type": "invalid_request_error",
    "when": "The body has a field the API does not accept.",
    "fix": "Remove it. The message lists the accepted names.",
    "doc_url": "https://qa-docs.cloudoverture.com/errors/unknown_field"
  },
  {
    "code": "read_only_field",
    "status": 400,
    "type": "invalid_request_error",
    "when": "The body sets a computed field (e.g., `grand_total`).",
    "fix": "Remove it. The server computes it.",
    "doc_url": "https://qa-docs.cloudoverture.com/errors/read_only_field"
  },
  {
    "code": "missing_required_input",
    "status": 400,
    "type": "invalid_request_error",
    "when": "A required field is missing.",
    "fix": "Send it.",
    "doc_url": "https://qa-docs.cloudoverture.com/errors/missing_required_input"
  },
  {
    "code": "invalid_line_type",
    "status": 400,
    "type": "invalid_request_error",
    "when": "A line type other than `product`, `service`, `custom`, or `text`, `heading` included.",
    "fix": "Use one of the four.",
    "doc_url": "https://qa-docs.cloudoverture.com/errors/invalid_line_type"
  },
  {
    "code": "currency_not_supported",
    "status": 400,
    "type": "invalid_request_error",
    "when": "`currency` is not `usd`.",
    "fix": "Use `usd`.",
    "doc_url": "https://qa-docs.cloudoverture.com/errors/currency_not_supported"
  },
  {
    "code": "idempotency_key_required",
    "status": 400,
    "type": "invalid_request_error",
    "when": "A create, send, or void with no `Idempotency-Key` and no `external_id`.",
    "fix": "Send one.",
    "doc_url": "https://qa-docs.cloudoverture.com/errors/idempotency_key_required"
  },
  {
    "code": "invalid_key",
    "status": 401,
    "type": "authentication_error",
    "when": "The key is wrong or malformed.",
    "fix": "Check the key.",
    "doc_url": "https://qa-docs.cloudoverture.com/errors/invalid_key"
  },
  {
    "code": "key_expired",
    "status": 401,
    "type": "authentication_error",
    "when": "The key passed its expiry date.",
    "fix": "Roll or create a key.",
    "doc_url": "https://qa-docs.cloudoverture.com/errors/key_expired"
  },
  {
    "code": "key_revoked",
    "status": 401,
    "type": "authentication_error",
    "when": "The key was revoked, or its member was deactivated.",
    "fix": "Create a new key.",
    "doc_url": "https://qa-docs.cloudoverture.com/errors/key_revoked"
  },
  {
    "code": "wrong_environment",
    "status": 401,
    "type": "authentication_error",
    "when": "A `co_test_` key reached a real tenant, or a `co_live_` key reached QA or a sandbox.",
    "fix": "Use the key for that environment.",
    "doc_url": "https://qa-docs.cloudoverture.com/errors/wrong_environment"
  },
  {
    "code": "billing_locked",
    "status": 402,
    "type": "permission_error",
    "when": "The tenant's subscription is locked.",
    "fix": "The owner fixes billing in the app.",
    "doc_url": "https://qa-docs.cloudoverture.com/errors/billing_locked"
  },
  {
    "code": "api_access_disabled",
    "status": 403,
    "type": "permission_error",
    "when": "API access is off for this company. Every key gets this until staff turn it on.",
    "fix": "The owner requests access in Settings → Developers.",
    "doc_url": "https://qa-docs.cloudoverture.com/errors/api_access_disabled"
  },
  {
    "code": "scope_missing",
    "status": 403,
    "type": "permission_error",
    "when": "The key lacks the scope. `param` names it (e.g., `invoices.send`).",
    "fix": "Edit the key's scopes.",
    "doc_url": "https://qa-docs.cloudoverture.com/errors/scope_missing"
  },
  {
    "code": "member_lacks_permission",
    "status": 403,
    "type": "permission_error",
    "when": "The key's member is not allowed to do this in the app.",
    "fix": "Ask an admin to change the member's group.",
    "doc_url": "https://qa-docs.cloudoverture.com/errors/member_lacks_permission"
  },
  {
    "code": "agent_key_not_allowed",
    "status": 403,
    "type": "permission_error",
    "when": "An agent key called the REST API.",
    "fix": "Connect the agent through the MCP address.",
    "doc_url": "https://qa-docs.cloudoverture.com/errors/agent_key_not_allowed"
  },
  {
    "code": "live_key_from_docs",
    "status": 403,
    "type": "permission_error",
    "when": "A `co_live_` key was sent from the docs site's Try it panel.",
    "fix": "Use a `co_test_` key from your sandbox.",
    "doc_url": "https://qa-docs.cloudoverture.com/errors/live_key_from_docs"
  },
  {
    "code": "resource_missing",
    "status": 404,
    "type": "invalid_request_error",
    "when": "No such record, or the key cannot see it. `param` names the id.",
    "fix": "Check the id.",
    "doc_url": "https://qa-docs.cloudoverture.com/errors/resource_missing"
  },
  {
    "code": "idempotency_conflict",
    "status": 409,
    "type": "state_error",
    "when": "The same `Idempotency-Key` with a different body.",
    "fix": "Use a new key for a new request.",
    "doc_url": "https://qa-docs.cloudoverture.com/errors/idempotency_conflict"
  },
  {
    "code": "external_id_exists",
    "status": 409,
    "type": "state_error",
    "when": "A create with an `external_id` already used. The error carries the existing `id`.",
    "fix": "Read or update that record.",
    "doc_url": "https://qa-docs.cloudoverture.com/errors/external_id_exists"
  },
  {
    "code": "books_not_set_up",
    "status": 409,
    "type": "state_error",
    "when": "The tenant has no chart of accounts yet.",
    "fix": "The owner finishes Books setup in the app. No key setting changes this.",
    "doc_url": "https://qa-docs.cloudoverture.com/errors/books_not_set_up"
  },
  {
    "code": "period_locked",
    "status": 409,
    "type": "state_error",
    "when": "The write would post on or before the lock date (`books_locked_through`).",
    "fix": "Use a later date. The lock override is in the app only.",
    "doc_url": "https://qa-docs.cloudoverture.com/errors/period_locked"
  },
  {
    "code": "invoice_not_draft",
    "status": 409,
    "type": "state_error",
    "when": "A change that only a draft allows (lines, amounts, delete).",
    "fix": "Void and create a new invoice, or fix it in the app.",
    "doc_url": "https://qa-docs.cloudoverture.com/errors/invoice_not_draft"
  },
  {
    "code": "invoice_locked",
    "status": 409,
    "type": "state_error",
    "when": "A change to an invoice with a payment applied.",
    "fix": "Fix it in the app with \"Fix the numbers\".",
    "doc_url": "https://qa-docs.cloudoverture.com/errors/invoice_locked"
  },
  {
    "code": "invoice_fix_in_progress",
    "status": 409,
    "type": "state_error",
    "when": "The owner has the invoice open in \"Fix the numbers\".",
    "fix": "Try again later.",
    "doc_url": "https://qa-docs.cloudoverture.com/errors/invoice_fix_in_progress"
  },
  {
    "code": "invoice_not_voidable",
    "status": 409,
    "type": "state_error",
    "when": "The app would refuse this void (e.g., already voided). The message gives the app's reason.",
    "fix": "Read the message.",
    "doc_url": "https://qa-docs.cloudoverture.com/errors/invoice_not_voidable"
  },
  {
    "code": "send_validation_failed",
    "status": 422,
    "type": "state_error",
    "when": "Send rules I1 to I9 failed. `errors` lists each one.",
    "fix": "Fix every listed field, then send again.",
    "doc_url": "https://qa-docs.cloudoverture.com/errors/send_validation_failed"
  },
  {
    "code": "rate_limited",
    "status": 429,
    "type": "rate_limit_error",
    "when": "Too many requests. Carries `Retry-After` and `reason` (`key_rate` or `tenant_rate`).",
    "fix": "Wait, then retry with backoff.",
    "doc_url": "https://qa-docs.cloudoverture.com/errors/rate_limited"
  },
  {
    "code": "api_error",
    "status": 500,
    "type": "api_error",
    "when": "Our fault.",
    "fix": "Retry with backoff. Contact support with the `request_id` if it repeats.",
    "doc_url": "https://qa-docs.cloudoverture.com/errors/api_error"
  },
  {
    "code": "service_unavailable",
    "status": 503,
    "type": "api_error",
    "when": "The API is down or overloaded.",
    "fix": "Retry with backoff.",
    "doc_url": "https://qa-docs.cloudoverture.com/errors/service_unavailable"
  }
]
