Concepts
Errors
Every error has a stable code, a message, an optional hint, and a request ID.
Error format
Errors return a non-2xx status with this body. Branch on code, not on message. Quote request_id when you contact support; it is also in the X-Request-Id response header.
You can send your own X-Request-Id header to correlate logs. HelloLedger echoes it on the response, in errors, and in the key's activity. It must be 1–128 characters of letters, digits, and . _ : / + = @ - starting with a letter or digit; anything else is replaced with a generated ID.
json
{
"detail": {
"error": {
"code": "customer_not_found",
"message": "No customer with external_id 'stu_1001'",
"hint": "Create it with PUT /v1/companies/{company_id}/customers/by-external-id/stu_1001, or include a customer block on the invoice.",
"request_id": "req_4f1c2a9b8e7d6c5b4a39"
}
}
}Validation errors
A body that doesn't match the schema (missing field, wrong type, unknown field) returns 422 with a list of problems:
json
{
"detail": [
{ "type": "extra_forbidden", "loc": ["body", "favorite_color"], "msg": "Extra inputs are not permitted" }
]
}Error codes
| Code | HTTP | What to do |
|---|---|---|
| unauthorized | 401 | Send Authorization: Bearer <secret>. Check the key wasn't revoked. |
| bearer_required | 401 | This endpoint doesn't accept HTTP Basic. Send Authorization: Bearer <secret>. |
| ip_not_allowed | 403 | The key has an IP allowlist. Call from an allowed address or update the list. |
| forbidden_company | 403 | The key doesn't cover this company_id. |
| insufficient_scope | 403 | Create a key with the scope the route needs. |
| not_found | 404 | No record with that external ID for your integration. |
| customer_not_found | 422 | PUT the customer first, or include a customer block on the invoice. |
| unknown_item | 422 | Use an item_id from GET /items. |
| item_inactive | 422 | The product or service was deactivated in HelloLedger. |
| unknown_account | 422 | Use an account_id from GET /coa for this company. |
| account_closed | 422 | The account isn't open on that date. |
| invalid_deposit_account | 422 | Deposit to a bank or current-asset (clearing) account. |
| invoice_not_found | 422 | A payment application names an invoice that doesn't exist. |
| invoice_customer_mismatch | 422 | A payment can only pay its own customer's invoices. |
| duplicate_application | 422 | List each invoice once per payment. |
| applications_exceed_amount | 422 | Applications add up to more than the payment or credit. |
| invalid_application | 422 | An application is more than the invoice's or bill's open balance, or the bill belongs to another vendor or isn't open. |
| exceeds_unapplied | 422 | Vendor credits: apply or refund at most the credit's unapplied_amount. |
| invalid_card_account | 422 | Card credits: card_account_id must be a credit card or bank account. |
| unknown_expense | 422 | Card credits: expense_id isn't an expense in this company. |
| invalid_date_range, date_range_too_large | 400 | 'to' must be on or after 'from', and the range must fit the endpoint's limit. |
| report_not_available | 400 | Hotel reports and room statistics are only for Hospitality companies. |
| invalid_credit_account | 422 | Credit lines use income, expense, or liability accounts; deposit_applied needs the deposit liability. |
| invalid_paid_from_account | 422 | Refunds: use a bank or current-asset (clearing) account. Expenses: use a bank, credit card, or cash account. |
| unknown_vendor, unknown_customer | 422 | The vendor_id or customer_id isn't in this company. Read it from GET /vendors, or send a vendor or customer block instead. |
| ap_account_required | 422 | The company has no default Accounts Payable account. Send ap_account_id, or set the default in HelloLedger. |
| payment_not_found | 422 | The refund or payout names a payment that doesn't exist. |
| credit_not_found | 422 | The refund names a credit that doesn't exist. |
| payment_customer_mismatch | 422 | Refund a payment under the customer who made it (same for credit_customer_mismatch). |
| refund_exceeds_unapplied | 422/409 | Refund at most the payment's or credit's unapplied_amount. Read it back to see the current amount. |
| invalid_clearing_account | 422 | Use the bank or current-asset clearing account the payments went to. |
| invalid_fee_account | 422 | Send fee_account_id as an expense account whenever fee is above 0. |
| invalid_bank_account | 422 | bank_account_id must be a bank or current-asset account. |
| duplicate_payment | 422 | List each payment once per payout. |
| invoice_number_taken | 409 | Pick another invoice_number or omit it. |
| invoice_has_payments | 409 | Void the payment, then change or void the invoice. |
| invoice_voided | 409 | Voided invoices stay voided. Use a new external ID. |
| payment_immutable | 409 | Payments can't be edited. Void it and send a new external ID. |
| payment_voided | 409 | Use a new external ID for the corrected payment. |
| payment_has_refunds | 409 | Void the refund before voiding the payment. |
| credit_has_refunds | 409 | Void the refund before voiding the credit. For vendor credits, reverse the refund in HelloLedger. |
| credit_immutable, refund_immutable, payout_immutable | 409 | These can't be edited. Void and send a new external ID. |
| credit_voided, refund_voided, payout_voided | 409 | Use a new external ID for the corrected record. A voided vendor or card credit can't be applied, refunded, or edited. |
| customer_name_taken | 409 | Rare: both the name and "Name (external_id)" are taken. |
| vendor_exists | 409 | A vendor with that name exists. Use its vendor_id from the message, or pick another name. |
| idempotency_key_reused | 409 | The Idempotency-Key was already used with a different body. Send a new key for a new record. |
| not_draft | 409 | Only drafts can be deleted. Void the record instead. |
| bill_has_payments | 409 | Remove the bill's payments or vendor credits in HelloLedger, then edit or void it. |
| bill_voided, expense_voided, journal_voided | 409 | Voided records stay voided. Create a new one. |
| expense_matched | 409 | The expense is matched to a bank transaction. Unmatch it in HelloLedger first. |
| unsupported_expense_type | 409 | Employee reimbursement claims are managed in HelloLedger, not through the API. |
| managed_by_integration | 409 | Another integration created this journal entry. Change it there. |
| period_reconciled | 409 | An account is reconciled through that date. Use a later date, or undo the reconciliation in HelloLedger. |
| invalid_client | 401 | Partner endpoints only: check your client ID and secret. |
| invalid_grant | 400 | Connect only: the code expired or was already used. A code works once, for 10 minutes. |
| invalid_redirect_uri | 400 | Connect only: use the redirect URI exactly as registered. |
| partner_paused | 403 | Connect for this partner app is paused. Contact HelloLedger. |
| company_not_linked | 404 | Partner endpoints only: the company isn't connected to your app. |
| access_denied | redirect | Connect only: the user cancelled on the approval screen. |
| posting_failed | 4xx/5xx | The record is saved as a draft. Retry the same request, or call /post on it. |
| internal_error | 500 | Retry the same request. Contact support with the request_id if it persists. |
| db_unavailable | 503 | Retry with backoff. |