Skip to content

Error codes

Every error code the API, the MCP server and the console can return, with its HTTP status and what to do next.

Every error has a stable code, a type, a message, a remediation that says what to do next, and whether retrying can succeed. The docs_url of an error links to its entry on this page. See Errors for the response body.

Types

TypeHTTP statusCodes
invalid_request400, 405, 415132
authentication40110
permission40322
entitlement4034
not_found40411
conflict409152
rate_limited4297
tax_blocked4226
insufficient_funds4023
approval_required2021
processor_error50210
internal50012

Invalid request

agent_role_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

An agent can't hold this role at this scope. Choose a role other than org_owner, at a scope the role allows (org, entity or brand; see the role table in the docs).

amount_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

The amount isn't a valid decimal amount for this currency. Write amounts in major units as decimal strings with at most 6 decimals, e.g. "12.50". Where a whole number of the currency's minor unit is required (payments, invoice totals), use at most its minor-unit decimals (EUR 2, JPY 0, BHD 3).

Amounts are exact integers in micros (millionths of the major unit). A decimal string is parsed without floating point; anything finer than a micro, or than the minor unit where one is required, is refused instead of rounded silently.

api_key_scope_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

One or more scopes can't be put on this kind of API key. Use permission scopes from the catalogue ({resource}:{read|write|admin}, e.g. customers:read) that this kind of key can use. details.problems lists each scope and why.

Brand keys can use brand resources (customers, subscriptions, usage, …); entity keys also the entity's ledger, filings, registrations and tasks; org management keys also members, security, audit, approvals, agents and API keys. On platform data, keys can only read tax knowledge (tax_knowledge:read). details.problems[] has scope, reason (unknown_permission, kind_not_allowed, platform_not_allowed) and message.

api_version_unsupported

HTTP status: 400 · Type: invalid_request · Retryable: no

This API version doesn't exist. Send a supported version in Ownrecord-Version (details.supported_versions), or leave the header out to use your account's pinned version.

app_listing_incomplete

HTTP status: 400 · Type: invalid_request · Retryable: no

The listing application is incomplete. Answer every required question of the current security questionnaire, justify every scope the app asks for, and give a privacy policy URL. details.problems lists what's missing.

An application to list an app names the publisher, links its privacy policy, answers the current version of the security questionnaire (apps.listing.get returns its questions) and justifies each scope the app asks for. A reviewer can't approve an app without a complete application either. Each entry of details.problems has a path and a message.

app_not_installable

HTTP status: 400 · Type: invalid_request · Retryable: no

This app can't be installed in this organization. Ask the app's publisher to list it: private apps install only in the organization that registered them, and other organizations can install a listed app once platform review has approved it.

app_review_checklist_incomplete

HTTP status: 400 · Type: invalid_request · Retryable: no

Tick every required checklist item to approve. Check each required item of the review checklist and tick it, or request changes or reject with a reason instead. details.missing lists the items.

assistant_action_unsupported

HTTP status: 400 · Type: invalid_request · Retryable: no

The assistant can't run this operation. Do it on its page in the console, or through the API. The assistant runs only operations it can preview with a dry run, and never ones that create or reveal secrets.

assistant_batch_too_large

HTTP status: 400 · Type: invalid_request · Retryable: no

Too many items for one action card. Split the change into smaller batches (details.max items per card).

assistant_injection_suspected

HTTP status: 400 · Type: invalid_request · Retryable: no

Data on this page contains text that looks like instructions, so the assistant won't propose changes in this answer. Nothing was changed. If you want a change, ask for it directly in a new message, and check the card's preview before you confirm.

Customer-entered text (names, notes, addresses, metadata) is data, never instructions (spec §43.5). When a tool result contains text that reads like instructions to the assistant, that text is withheld from the model and the assistant proposes no action for the rest of the answer.

assistant_page_context_required

HTTP status: 400 · Type: invalid_request · Retryable: no

The assistant doesn't know which organization you're working in. Open a page of the organization in the console (or turn the page context on for this message), then ask again.

authorization_document_not_found

HTTP status: 400 · Type: invalid_request · Retryable: no

The document isn't an authorization document of this legal entity. Upload the signed document with authorizations.documents.upload and pass its file_… ID as documentFileId.

authorization_secret_refused

HTTP status: 400 · Type: invalid_request · Retryable: no

This looks like a credential. Ownrecord never stores government portal credentials. Keep passwords, PINs, eIDs and certificates in your own vault. Record only non-secret notes: the access method, who holds access and the renewal date. param names the field.

bank_statement_amount_mismatch

HTTP status: 400 · Type: invalid_request · Retryable: no

The line's amount isn't the invoice's amount due. Match the line to the invoice whose amount due it pays exactly (same currency). Partial payments and overpayments stay in the review queue: resolve them with the customer, then ignore the line with a reason.

Invoices are paid in full only (docs/spec-questions.md #429), so a bank line pays an invoice only when its amount equals the amount due. details.line_amount and details.amount_due are the two amounts.

bank_statement_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

The bank statement file can't be read. Fix the rows listed in details.errors (or name the columns, date format and decimal separator) and import the whole file again. Nothing was imported.

The importer reads CSV files with a header row: a booking date, a signed amount (incoming money positive), a reference and optionally the counterparty and a currency (spec §16.9; CAMT.053 files arrive in v1.1). Columns are found by common header names or named in columns. A file with any unreadable row is refused whole, so a corrected file imports cleanly: details.errors lists up to 20 problems as { row, column, message }.

capture_exceeds_reservation

HTTP status: 400 · Type: invalid_request · Retryable: no

The capture amount is more than is still reserved. Capture at most the reserved amount that is still open (details.outstanding, integer micros), or reserve more first.

checkout_amount_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

A line item's quantity or amount isn't valid for its price. Give a quantity from 1 to 1,000 for fixed prices; for a custom-amount price give customAmount within the price's minimum and maximum, in whole minor units of its currency, with quantity 1. param names the line item.

checkout_business_details_required

HTTP status: 400 · Type: invalid_request · Retryable: no

A business purchase needs the company name and its full address. Give the company name and the billing address (street, city, country) when buying as a business (spec §13.2).

checkout_currency_mismatch

HTTP status: 400 · Type: invalid_request · Retryable: no

The line items use different currencies. Put prices of one currency in a session; create one session per currency.

checkout_mode_not_supported

HTTP status: 400 · Type: invalid_request · Retryable: no

Checkout doesn't sell this mode yet. Create the session with mode: payment (one-time prices and credit packs), subscription (recurring prices) or setup (save a payment method). Top-ups arrive with milestone M6 (details.available_from).

The session schema stores every mode of §7.6 (payment, subscription, setup, topup); checkout sells payment, subscription and setup sessions, and topup from the milestone that builds it (details.available_from).

checkout_price_not_sellable

HTTP status: 400 · Type: invalid_request · Retryable: no

This price can't be sold at checkout. Sell one-time prices and credit packs in payment mode and recurring prices in subscription mode. param names the line item.

checkout_promotion_code_not_allowed

HTTP status: 400 · Type: invalid_request · Retryable: no

This checkout doesn't take promotion codes. Create the session with allowPromotionCodes: true, or with promotionCode to apply one for the customer. Promotion codes apply to subscription sessions.

checkout_quantity_not_adjustable

HTTP status: 400 · Type: invalid_request · Retryable: no

This line item's quantity can't be changed here. Only line items created with adjustableQuantity take a new quantity on the hosted page, within its min and max.

checkout_session_expiry_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

The session's expiry time isn't allowed. Set expiresAt between 30 minutes and 24 hours from now, or leave it out for 24 hours.

checkout_terms_required

HTTP status: 400 · Type: invalid_request · Retryable: no

The customer hasn't accepted the terms. Ask the customer to accept the brand's terms and, for digital services delivered at once to EU or UK consumers, to agree to immediate performance and the loss of the withdrawal right (details.missing).

confirmation_mismatch

HTTP status: 400 · Type: invalid_request · Retryable: no

The confirmation doesn't match. Type exactly what the form asks for (the organization's slug, or your email address).

conversation_scope_mismatch

HTTP status: 400 · Type: invalid_request · Retryable: no

This conversation belongs to another organization or console. Start a new conversation here; a conversation never moves between organizations.

currency_mismatch

HTTP status: 400 · Type: invalid_request · Retryable: no

The amounts are in different currencies. Use amounts in the same currency. Prices are set explicitly per currency; nothing is converted automatically.

Money arithmetic never mixes currencies (spec §8.1). details.currencies lists the currencies involved.

customer_deleted

HTTP status: 400 · Type: invalid_request · Retryable: no

This customer was deleted. Create a new customer for this buyer. Deleted customers stay readable for the records they belong to, but can't be changed.

Deleting a customer is a soft delete (deletedAt): invoices, payments and evidence keep pointing at it, and it can still be read, but nothing about it changes any more.

customer_tax_id_limit

HTTP status: 400 · Type: invalid_request · Retryable: no

The customer already has the maximum number of tax IDs. Remove a tax ID the customer no longer uses before adding another (at most 5 per customer).

dev_login_not_allowed

HTTP status: 400 · Type: invalid_request · Retryable: no

Dev login only accepts @ownrecord.test addresses. Sign in as one of the seeded @ownrecord.test users listed on /dev.

Development and test only (spec §6.7.2). The .test domain can't receive email, so no real account can be impersonated.

dispute_assignee_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

This person can't be assigned the dispute. Assign a member of the organization who may work on this brand's disputes in this mode (support, admins and owners at the organization, the entity or this brand). details.userId is the ID given.

dispute_evidence_unsupported

HTTP status: 400 · Type: invalid_request · Retryable: no

This processor doesn't take dispute evidence through its API. Download the evidence pack from the console and submit it in the processor's own dashboard. details.processor names the processor.

domain_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

This isn't a host name a brand can use as its domain. Enter one host name you registered, such as pay.acme.com: no https://, path, port or wildcard, and not an IP address.

details.reason says why: empty, url (a scheme or path was included), port, wildcard, format, too_long, single_label, ip_address, special_use (reserved names such as .local, .test or example.com, which can never point at Ownrecord) or reserved (Ownrecord's own domains and its hosting provider's). Also returned when the domain provider refuses the name.

dry_run_not_supported

HTTP status: 400 · Type: invalid_request · Retryable: no

This operation doesn't support dry runs. Send the request without Ownrecord-Dry-Run (REST) or dry_run (MCP).

einvoice_document_unsupported

HTTP status: 400 · Type: invalid_request · Retryable: no

This document can't be expressed as an EN 16931 e-invoice. Read the message for the reason. Issue taxed lines and lines not subject to VAT on separate documents, make sure a reverse-charge buyer has a VAT ID, and use a currency with at most two decimals.

Raised when an issued invoice or credit note can't be written as EN 16931 UBL (Peppol BIS Billing 3.0): lines not subject to VAT mixed with taxed lines (BR-O-11), a reverse-charge document without the buyer's VAT ID (BR-AE-02), a seller without a VAT ID on a document with VAT, a currency with more than two decimals, a buyer without a country, or a snapshot whose totals don't equal its lines. The e-invoice is marked failed and an EINVOICE_REJECTED alert is raised; the document itself stands.

einvoice_endpoint_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

This isn't a valid electronic address. Write it as <scheme>:<identifier> with a scheme from the Peppol list of electronic address schemes, e.g. 9934:HR12345678903 or 0208:0123456789.

Electronic addresses are Peppol participant identifiers: a four-digit EAS scheme code and the identifier, without spaces.

einvoice_endpoint_missing

HTTP status: 400 · Type: invalid_request · Retryable: no

A party of the e-invoice has no electronic address. For the buyer, set the customer's einvoice_endpoint metadata to its Peppol address (<scheme>:<identifier>, e.g. 0208:0123456789), then retry the e-invoice. For the seller, set the entity's Peppol address in its e-invoicing settings.

Peppol requires both electronic addresses (PEPPOL-EN16931-R010, -R020). Ownrecord derives them from VAT IDs where the Peppol code list has a VAT scheme for the country (e.g. 9934 for Croatia); consumers, and businesses in countries whose VAT numbers aren't Peppol addresses (Sweden, Finland, Denmark), need an explicit address. details.party is buyer or seller.

einvoice_provider_unavailable

HTTP status: 400 · Type: invalid_request · Retryable: no

This e-invoicing provider isn't available here. Choose a provider that is available in this environment. The fake provider exists only in development and test; certified providers are added with the product owner (spec §35 #8).

The fake provider is a development and CI tool: it is refused unless the development gate is open, so a real deployment never pretends to have sent an e-invoice. details.provider names the provider.

einvoice_provider_unknown

HTTP status: 400 · Type: invalid_request · Retryable: no

There is no e-invoicing provider with this ID. Use one of the providers einvoice.settings.get lists under providers.

details.provider names the ID that was given.

einvoice_provider_unsupported

HTTP status: 400 · Type: invalid_request · Retryable: no

The provider doesn't deliver e-invoices for this country and transaction type. Choose another provider for this country and transaction type, or remove the route. providers[].countries in einvoice.settings.get says what each provider covers.

details names the provider, the country and the transaction type (b2b, b2g or b2c).

einvoice_routes_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

The e-invoicing routes aren't valid. Give each country and transaction type at most one provider, with ISO 3166-1 country codes and the types b2b, b2g or b2c.

param points at the route that is wrong.

email_domain_blocked

HTTP status: 400 · Type: invalid_request · Retryable: no

Accounts can't be created with a disposable email address. Sign up with a permanent address, for example your work email.

New accounts need an address from a permanent email provider. Existing accounts are not affected.

email_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

This email address isn't valid. Check the address and try again.

entitlement_feature_not_found

HTTP status: 400 · Type: invalid_request · Retryable: no

This brand has no feature with this key in this mode. Check the feature key against features.list (test and live mode have separate features), or create the feature first with features.create.

Entitlements are values of the brand's own features (spec §36.1). param names the field with the key (feature for entitlements.check, featureKey for an override) and details.feature_key repeats it.

entitlement_value_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

An entitlement value doesn't fit its feature. Use true or false for boolean features, a whole number of 0 or more for limits, and one of the listed values for enum features.

param is the first wrong value; details.issues lists every problem with its path and message.

export_period_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

The export period isn't valid. Give from and to as calendar dates (YYYY-MM-DD) in the entity's time zone, from on or before to, at most 366 days in all, and not ending in the future.

feature_not_found

HTTP status: 400 · Type: invalid_request · Retryable: no

The product names a feature this brand doesn't have. Create the feature first (features.create), or fix the feature key.

A product's entitlements are values of the brand's own features, in the same mode (spec §36.1). param is the first unknown entry, details.feature_keys lists every unknown key.

filing_receipt_not_found

HTTP status: 400 · Type: invalid_request · Retryable: no

The receipt isn't a document of this filing period. Upload the receipt to the period first (filing_periods.documents.upload) and pass its file_… ID, or pass a reference instead.

A submission's receipt is a document uploaded to the same filing period, or the operator's reference to a document kept elsewhere. param names the field.

filing_schedule_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

This filing schedule can't generate filing periods. Give a due rule (due) when the regime's filing spec has none Ownrecord can compute (details.problem: due_other), or complete it (due_days_missing, due_nth_day_missing, due_months_missing). A regime without a filing spec (no_filing_spec) needs its filing rules in the knowledge base first.

A registration's own filing schedule (WP-M8-filing-reports) sets the frequency, the stagger and, where the filing spec's due rule can't be computed (e.g. the UK's "1 calendar month and 7 days"), the due rule. The filing spec is still needed for the filing currency and the report. details has regime_id and problem.

gl_mapping_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

The general-ledger mapping isn't valid. Fix the rules listed in details.problems (index, field, message): account is an account kind (e.g. tax_payable), an account code, or a code prefix ending in * (e.g. tax_payable:EU_OSS_UNION:*), each at most once; glAccount is 1–40 letters, digits, and ., -, _, / or spaces inside; at most 200 rules.

idempotency_key_required

HTTP status: 400 · Type: invalid_request · Retryable: no

An Idempotency-Key header is required on POST requests. Send a unique Idempotency-Key header (for example a UUID) with every POST.

installer_not_eligible

HTTP status: 400 · Type: invalid_request · Retryable: no

Apps are installed and owned by organization owners and admins. Re-assign the app to an org_owner or org_admin of this organization.

invalid_json

HTTP status: 400 · Type: invalid_request · Retryable: no

The request body must be a JSON object. Send the input as a JSON object, e.g. {} when there is nothing to send.

invoice_currency_mismatch

HTTP status: 400 · Type: invalid_request · Retryable: no

This item's currency isn't the invoice's. Every item of an invoice is in the invoice's currency (details.invoice_currency). Create a separate draft for the other currency.

invoice_item_price_unusable

HTTP status: 400 · Type: invalid_request · Retryable: no

This price can't be used on an invoice item. Use an active one-time price with a fixed amount, or give the product (productId) and a unitAmount instead. Recurring prices are billed by subscriptions and credit packs by checkout.

invoice_lines_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

The invoice lines don't match the tax quote. Describe every line of the quote exactly once, with a description of 1 to 500 characters and a whole-number quantity from 1 to 1,000,000. param names the line.

invoice_number_format_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

The legal entity's invoice number format can't produce unique invoice numbers. Change the entity's invoice number format (Invoice settings) to a template with {series}, {seq} and, when the sequence restarts every year, {year}, e.g. {series}-{year}-{seq:06}.

Invoice numbers are the entity's template filled with the brand's series, the issue year and the sequence number (spec §18.2). The template may use {series}, {year}, {seq} and {seq:NN} (zero-padded). It must contain {series} (the brands of an entity share the template) and {seq}, and {year} when sequenceResetYearly is on. Country templates with fiscal elements (Croatian business-premises and device codes) arrive with fiscalisation. details.number_format has the template.

invoice_series_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

The invoice series isn't valid. Use a series of uppercase letters, digits and inner hyphens, at most 30 characters, e.g. GEN or GEN-CN (Brand settings → Invoice series).

lease_usage_mismatch

HTTP status: 400 · Type: invalid_request · Retryable: no

The usage summary doesn't add up to the consumed amount. Make the amounts of the usageSummary lines add up to consumed exactly (details.consumed, details.usageTotal, integer micros), in the lease's currency.

A settle captures consumed and books it as usage revenue per meter from the usageSummary lines (spec §17.3, §11.3), so the lines must account for every micro of it. Lines may have a zero amount (usage that cost nothing).

ledger_account_code_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

This isn't a valid ledger account code. Use an account from the chart of accounts, e.g. processor_clearing:pcon_…:EUR or customer:cus_…:wallet:EUR:available (see details.code).

Account codes follow the chart of accounts (spec §11.2): one ledger per legal entity, one account per code and currency. Codes end with the ISO 4217 currency, except wallet accounts, which carry it after wallet (customer:{cus}:wallet:{currency}:available|reserved|promo). IDs inside a code must be TypeIDs of the right kind; meter names use letters, digits, ., _ and -.

ledger_account_not_adjustable

HTTP status: 400 · Type: invalid_request · Retryable: no

Manual adjustments can't post to customer wallet accounts. Change wallet balances with a credit grant, an expiry or a refund instead, so the wallet's credit grants stay in step with its balance.

A wallet's balance must always equal the remaining amount of its credit grants (spec §11.5, invariant 4). A manual adjustment can't keep that true, so it may not post to customer:…:wallet:… accounts. details.account is the refused account code.

ledger_posting_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

The ledger posting is invalid. Fix the entry or amount named in details (details.reason, and details.index for an entry) and post again.

details.reason is one of: empty (no entries), non_positive_amount (every entry amount must be a positive number of micros), currency_mismatch (an entry's currency differs from its account's currency), negative_amount (an input amount such as a tax line is below zero), inconsistent_amounts (the amounts given for a posting don't add up, e.g. usage lines that don't sum to the captured amount), memo_required (manual adjustments need a reason) or rounding_too_large (a rounding residue of one minor unit or more).

ledger_unbalanced

HTTP status: 400 · Type: invalid_request · Retryable: no

The ledger entries don't balance: debits and credits differ in at least one currency. Make the debits equal the credits in every currency (details.imbalances lists each currency's totals) and post again.

Every ledger transaction must balance per currency (spec §11.5, invariant 1). details.imbalances[] has currency, debits and credits in integer micros. Ownrecord's own postings are built from templates that always balance, so you see this code only for manual adjustments.

HTTP status: 400 · Type: invalid_request · Retryable: no

This version of the document can't be accepted now. Accept the version in effect, or an announced newer version. details.acceptable lists the versions you can accept; read them at /legal/{document}/{version}.

An organization accepts the version of the Terms of Service or the DPA that is in effect, or an announced version ahead of its effective date. Earlier versions, versions that aren't announced yet and unknown versions are refused. details.document names the document and details.acceptable the versions that can be accepted now.

live_keys_unavailable

HTTP status: 400 · Type: invalid_request · Retryable: no

Live API keys can't be created yet. Create a test-mode key, or complete the brand's go-live checklist (console → brand → Go live) and try again.

Live keys need: the organization's plan to include live mode, the organization to be active (not restricted, suspended or closing), and a live brand: the key's own brand for a brand key, any brand of the entity or organization for entity and org keys (go-live checklist, spec §38.3). details.blockers[] lists org_plan, org_status, brand_not_live or no_live_brand.

livemode_mismatch

HTTP status: 400 · Type: invalid_request · Retryable: no

The object belongs to the other mode (test or live). Use credentials for the same mode as the object.

method_not_allowed

HTTP status: 405 · Type: invalid_request · Retryable: no

This endpoint doesn't support this HTTP method. Use one of the methods in the Allow response header (details.allowed_methods).

Answered with HTTP 405. The path exists, but not with this method.

mode_not_supported

HTTP status: 400 · Type: invalid_request · Retryable: no

This operation isn't available in this mode (test or live). Use the mode the operation supports (details.supported_mode): test-only operations such as test clocks need test-mode credentials.

oauth_grant_mismatch

HTTP status: 400 · Type: invalid_request · Retryable: no

The authorization doesn't belong to you and this client. Start the authorization again from the application.

Consent is recorded on the grant of the signed-in user and the client that asked; anything else is refused.

onboarding_step_not_manual

HTTP status: 400 · Type: invalid_request · Retryable: no

This onboarding step is done when the organization's data shows it, not by marking it. Do the step (for example, create the brand), or skip it. Only the registrations review is marked done.

org_required

HTTP status: 400 · Type: invalid_request · Retryable: no

This access token covers several organizations: name the one to act in. Send the Ownrecord-Org: org_… header with the organization's ID.

A token from an app installed in several organizations by the same person, or from an MCP client the user connected to several organizations, needs Ownrecord-Org on every call (spec §41.3).

platform_billing_hold_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

This hold can't be set. Give a reason, a stage (active, restricted or suspended) and, to end it automatically, an until in the future.

platform_billing_thresholds_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

These escalation thresholds can't be used. Use whole days from 1 to 60, with suspendAfterDays later than restrictAfterDays (the defaults are 14 and 30).

platform_plan_unknown

HTTP status: 400 · Type: invalid_request · Retryable: no

There is no Ownrecord plan with this key that you can choose. Pick one of the plans platform_billing.get lists under plans (enterprise is arranged with sales; internal is never sold).

platform_staff_domain_required

HTTP status: 400 · Type: invalid_request · Retryable: no

Platform roles are only for addresses at the platform's staff domains. Use the person's address at one of the staff domains (PLATFORM_STAFF_DOMAINS, shown on the Staff page).

platform_user_self

HTTP status: 400 · Type: invalid_request · Retryable: no

You can't disable your own account. Ask another platform admin to do it.

processor_credentials_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

The processor rejected the API key. Copy the key again from the processor's dashboard (Stripe: Developers → API keys). It may have been rolled or deleted.

Returned when connecting or rotating a key that the processor doesn't accept. Nothing was stored.

processor_key_mode_mismatch

HTTP status: 400 · Type: invalid_request · Retryable: no

The key belongs to the other mode. Use a test key (Stripe rktest…, Mollie test*…) for a test-mode connection and a live key (rk_live*…, live_…) for a live-mode connection, or switch the console's mode.

Test mode and live mode are separate worlds (spec §6.1): a connection's key must match the mode it is created in. details.key_livemode says which mode the key is for.

processor_key_not_restricted

HTTP status: 400 · Type: invalid_request · Retryable: no

Live connections need a restricted key. Create a restricted key (rklive…) with the permissions listed in the docs instead of using the account's secret key (sklive…).

A restricted key limits what Ownrecord can do on the account to what it needs (spec §15.2). Test mode also accepts a secret test key (sktest…) for development.

processor_not_available

HTTP status: 400 · Type: invalid_request · Retryable: no

This payment processor isn't available here. Connect Stripe or Mollie. The fake processor exists only in development and test.

The fake processor is a development and CI tool: it is refused unless the development gate is open (a development or test deployment with development access enabled), so a real deployment can never take a fake payment or accept a fake webhook. details.processor names the processor.

processor_not_configured

HTTP status: 400 · Type: invalid_request · Retryable: no

No processor connection can take payments for this brand in this mode. Connect a processor to the brand's legal entity in this mode (console → entity → Processor connections), or reactivate a disconnected one.

A brand uses its primary connection, or its secondary while the primary is degraded (spec §15.4). details has brand_id and livemode.

processor_permissions_missing

HTTP status: 400 · Type: invalid_request · Retryable: no

The restricted key is missing permissions Ownrecord needs. Edit the restricted key in the processor's dashboard and grant the permissions listed in details.missing_permissions, then try again.

Stripe restricted keys need write access to PaymentIntents, SetupIntents, PaymentMethods, Customers, Refunds, Disputes and Webhook Endpoints, and read access to Balance and Payouts. details.missing_permissions lists what the key lacks as resource:access (e.g. refunds:write). Nothing was stored.

processor_webhook_url_not_public

HTTP status: 400 · Type: invalid_request · Retryable: no

Stripe can't deliver webhooks to this deployment's URL. On a development machine, run stripe listen --print-secret, pass the secret it prints (whsec_…) as webhookSecret, then forward events with stripe listen --forward-to the connection's webhook URL.

Ownrecord creates the webhook endpoint on the Stripe account itself (spec §15.2), which needs a public https address. A local deployment (http://localhost:…) can't receive Stripe's webhooks directly: forward them with the Stripe CLI, whose signing secret stays the same between runs, and give that secret instead. details.webhook_url is the address Stripe would have been given.

proposal_mismatch

HTTP status: 400 · Type: invalid_request · Retryable: no

The confirmation doesn't match the proposed action. Confirm from the action card itself: the confirmation must name the card's proposal and input hash. Nothing ran.

reconciliation_connection_unsupported

HTTP status: 400 · Type: invalid_request · Retryable: no

This processor connection can't be reconciled. Reconciliation reads a connection's balance transactions: the connection must be active (not disconnected) and in the mode of the request. details.status is the connection's status.

reconciliation_payout_mismatch

HTTP status: 400 · Type: invalid_request · Retryable: no

This bank line can't confirm that payout. Pick a payout item (kind: payout) of this entity and mode that didn't fail and isn't confirmed by another line, for exactly the line's amount and currency (details says what differs). Bank fees deducted from a payout aren't matched automatically: resolve the line with a note and book the fee with a ledger adjustment.

refund_amount_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

The refund amount isn't valid. Give a positive amount in the invoice's currency, in whole minor units (integer micros as a string, e.g. 12500000 for 12.50).

refund_type_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

This refund type doesn't fit the payment. Use unused_credits for credit packs and top-ups, subscription_prorated for a subscription invoice with a billing period, partial with an amount, or full. details.reason says what didn't fit.

full credits everything that is left on the invoice (credit packs only up to their unused credits); partial credits amount across the invoice's lines; unused_credits credits the unused credits of the invoice's credit packs; subscription_prorated credits the unused time of the invoice's licensed periods. An amount is given with partial and only with it.

registration_details_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

The registration details are incomplete. Fill in the field named in param: the registration number, the date it is effective from, or at least one proof file.

Marking a registration registered needs the registration number, its effective date and proof (spec §10.6); marking the application submitted needs proof of the submission. param and details.field name the missing value.

registration_event_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

The registration event is incomplete. This is a problem on our side. Contact support with the request ID.

An internal caller passed a registration event without the data it needs, e.g. a regime change without the entity's establishment. details.problem says what is missing.

registry_extract_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

The registry extract must be a PDF, PNG or JPEG of at most 10 MB. Upload the extract from the company register as a PDF (or a clear photo) under 10 MB.

risk_evaluation_not_held

HTTP status: 400 · Type: invalid_request · Retryable: no

This payment wasn't held for review. Only payments the risk rules held are in the review queue (risk.reviews.list). Refund other payments with refunds.create.

risk_payment_declined

HTTP status: 400 · Type: invalid_request · Retryable: no

This payment can't be accepted. Use another payment method, or contact the seller. The seller's team sees why in the console (Brand → Risk); the answer never says.

The brand's risk rules refused the payment before it reached the card (spec §20): a blocklist, a velocity limit, a new customer's top-up limit, a disposable email address or a high risk score. The answer is the same whatever the rule, so it tells nobody how to get around it. Operators read the reasons with risk.evaluations.get (or the list filtered by checkoutSessionId).

scope_required

HTTP status: 400 · Type: invalid_request · Retryable: no

This operation needs a different scope than the request has. Call the operation with credentials or a target at its level: an organization, a legal entity, a brand or the platform (details.required_scope).

Every operation runs at one level: org, entity, brand or platform (shown in the API reference). Brand operations need a brand (and its entity), entity operations need an entity. A broader target such as an organization management key must name the entity or brand to act on. details.required_scope is the level the operation needs.

slack_webhook_url_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

This Slack webhook URL can't be used. Use the incoming-webhook URL Slack (or a Slack-compatible tool) gave you: https, a public host, no user name or password. details.reason says what is wrong.

Webhook URLs follow the same rule as outbound webhooks (spec §6.8): https only, public hosts only (no private, loopback or link-local addresses), no credentials or fragment, at most 2,048 characters. On a development machine with the dev gate open, http to a loopback host is allowed for a local fake.

slug_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

Slugs are 3 to 40 lowercase letters, digits and single hyphens, and can't be a reserved word. Choose a slug such as acme or acme-app. Reserved words such as api, console and docs can't be used.

details.reason is format or reserved.

subscription_anchor_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

The billing cycle anchor isn't allowed. Set billingCycleAnchor after the subscription starts (after the trial, if any) and at most one of its longest interval later, or leave it out to bill from the start.

subscription_change_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

This change can't be made this way. Check details.reason and adjust the request.

details.reason is no_change (nothing would change), collection_method_not_supported (pending_if_incomplete needs charge_automatically) or pending_payment_update (a change is waiting for its invoice to be paid: pay or void that invoice first).

subscription_item_meter_mismatch

HTTP status: 400 · Type: invalid_request · Retryable: no

This subscription item doesn't bill this customer's usage of this meter. Name the customer's live metered item for the event's meter, or leave out subscriptionItemId: usage is billed to the customer's metered item for the meter.

details.reason is customer (the item belongs to another customer), ended (its subscription is canceled or expired), not_metered (a licensed item) or meter (it bills another meter, details.item_meter).

subscription_migration_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

These prices can't be used for a price migration. Migrate between two recurring prices that bill alike: the same currency, interval and usage type (and meter, for metered prices). The new price must be active.

details.reason is same_price, currency_mismatch, interval_mismatch, usage_type_mismatch, meter_mismatch, not_recurring or archived (the new price or its product is archived).

subscription_migration_too_large

HTTP status: 400 · Type: invalid_request · Retryable: no

Too many subscriptions match this price migration. Narrow it with filter.customerIds or filter.statuses, and run several migrations. One migration schedules at most 1,000 subscriptions.

details.matched is how many subscriptions matched and details.max the limit.

subscription_payment_method_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

This payment method can't be used for the subscription. Use a saved payment method (pm_…) of the subscription's customer that isn't detached, or leave it out to use the customer's default.

subscription_price_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

This price can't be used on this subscription. Use active recurring prices of one currency whose intervals are whole multiples of each other (monthly with yearly, weekly with 2-weekly). details.reason says what's wrong and param names the item.

details.reason is not_recurring (a one-time price or credit pack), archived (the price or its product is archived; existing items keep billing, new items need an active price), currency_mismatch (every item of a subscription bills in one currency) or intervals_incompatible (a month-based and a day-based interval, or two intervals that don't divide each other, such as 2 and 3 months).

subscription_refund_not_supported

HTTP status: 400 · Type: invalid_request · Retryable: no

The unused time can't be refunded to the customer's card. An invoice of the current period wasn't paid by card (bank transfer or the credit balance; details.invoice_id). Cancel with prorate: true to credit the unused time to the customer's credit balance, or without it.

task_assignee_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

This person can't be assigned the task. Assign a member of the organization who may work on tasks for this legal entity in this mode (an owner, an admin, a tax manager or finance, at the organization or this entity).

task_authorization_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

This authorization can't back this submission. Use an authorization of this legal entity and mode that is valid now and covers the task's regime. details.problem is revoked, expired, not_yet_valid, regime_not_covered, other_entity or other_mode.

task_authorization_required

HTTP status: 400 · Type: invalid_request · Retryable: no

Submitting this task needs an authorization to act for the entity. Record who may act for the entity under this regime (authorizations.create) and pass its ID as authorizationId. Ownrecord records the authorization; it doesn't grant it.

task_complete_required

HTTP status: 400 · Type: invalid_request · Retryable: no

This task doesn't change a registration or a filing period: complete it with tasks.complete. Call tasks.complete with the task's outputs.

task_output_file_not_found

HTTP status: 400 · Type: invalid_request · Retryable: no

A receipt file in the outputs isn't a document of this task. Upload the receipt to the task first (tasks.documents.upload) and pass its file_… ID, or record the receipt number as a reference instead. details.fileId is the ID that wasn't found.

task_submit_required

HTTP status: 400 · Type: invalid_request · Retryable: no

This task changes a registration or a filing period: submit it with tasks.submit. Call tasks.submit with the task's outputs (and an authorization for *.submit tasks). It is a compliance-tier operation, so your approval policy may ask a person to approve it first.

tax_id_check_not_available

HTTP status: 400 · Type: invalid_request · Retryable: no

This tax ID can't be checked with a public register. Upload an extract from the company register instead: Ownrecord reviews it and verifies the entity.

VIES checks EU VAT IDs, HMRC checks UK VAT numbers and the UID register checks Swiss UIDs. Other tax IDs, and a number that isn't well-formed, are verified from a registry extract (entities.registry_extract.upload).

tax_id_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

The tax ID isn't valid. Check the number and its country for typing errors (details.reason says what is wrong), then send it again. A Northern Ireland business enters its GB VAT number.

The number failed the checks Ownrecord makes before asking a register (spec §9.4). details.reason is one of: format (not the format of a tax ID of that type and country), checksum (the check digit is wrong; checked for Croatian OIB-based VAT IDs, Swiss UIDs and Norwegian organisation numbers), country_mismatch (the number carries another country's prefix), unsupported_country (the type doesn't exist in that country, e.g. eu_vat for Switzerland) or xi_prefix (Northern Ireland XI numbers aren't accepted; use the GB number). Spaces, dots and hyphens are ignored, and the country prefix may be left out (DE + 123456789).

tax_id_type_unsupported

HTTP status: 400 · Type: invalid_request · Retryable: no

This tax ID type isn't supported. Use one of the types in details.supported_types: eu_vat (EU VAT ID), gb_vat (UK VAT number), ch_uid or ch_vat (Swiss UID or VAT number), no_org (Norwegian organisation number) or us_ein (US EIN).

Ownrecord checks EU VAT IDs with VIES, UK VAT numbers with HMRC and Swiss VAT numbers with the UID register; Norwegian organisation numbers and US EINs are checked by format (spec §9.4).

tax_input_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

The tax quote input is inconsistent. Fix the field named in param and quote again: every line needs a unique ID, the same currency and a whole number of the currency's minor unit.

Thrown by the tax engine before it quotes: no lines, duplicate line IDs, mixed currencies, line amounts finer than the currency's minor unit, or a seller that doesn't match the entity's tax state.

test_clock_advance_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

The test clock can't advance to this time. Advance to a time after the clock's frozenTime, at most two years ahead per advance (advance several times to go further).

details.reason is not_forward (the time isn't after the clock's) or too_far (more than 732 days ahead; details.latest is the latest allowed time).

test_clock_customer_limit

HTTP status: 400 · Type: invalid_request · Retryable: no

This test clock already has the most customers it can have. Create another test clock for more customers. A clock holds at most 10 customers.

test_clock_mode

HTTP status: 400 · Type: invalid_request · Retryable: no

Test clocks belong to test mode. Attach test clocks to test-mode customers only. Live customers bill at the real time.

unsupported_media_type

HTTP status: 415 · Type: invalid_request · Retryable: no

The request body must be JSON. Send Content-Type: application/json with a JSON body.

Answered with HTTP 415.

usage_meter_billed_in_arrears

HTTP status: 400 · Type: invalid_request · Retryable: no

A metered price bills this meter in arrears, so wallet usage can't use it. Report prepaid (wallet) usage on a meter that no metered price names, e.g. api.calls.prepaid (details.meter).

Prepaid wallet usage and usage billed in arrears can coexist for a customer on different meters (spec §17.4). Usage of a meter that a metered price of the brand names (in any state) is billed on the renewal invoice, so paying it from a wallet as well would charge it twice.

usage_meter_unknown

HTTP status: 400 · Type: invalid_request · Retryable: no

No metered price of this brand uses this meter. Check the meter key for typos, or create a metered price with this meter first (archived prices count too).

A usage event names a meter (e.g. api.calls). A meter exists in a brand and mode once a metered price names it (spec §16.7), so usage of a misspelled meter is refused instead of silently going unbilled. details.meter is the key the event used.

usage_occurred_at_out_of_range

HTTP status: 400 · Type: invalid_request · Retryable: no

The event's occurredAt is too far in the future or the past. Send when the usage happened: at most 5 minutes ahead of now (clock skew) and at most three years back.

details.reason is future or too_old. Events from a period that was already invoiced are accepted and billed on the next invoice as late usage (spec §16.7).

validation_failed

HTTP status: 400 · Type: invalid_request · Retryable: no

The request is invalid. Fix the fields listed in details.issues and send the request again.

waitlist_confirmation_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

This waitlist confirmation link isn't valid. Links work for 7 days and only the latest one sent works. Join the waitlist again to get a new email.

HTTP status: 400 · Type: invalid_request · Retryable: no

The customer hasn't agreed to be notified. Ask the customer to tick the consent (Notify me when … can sell in …) before joining.

HTTP status: 400 · Type: invalid_request · Retryable: no

This waitlist link isn't valid. Use the link exactly as it is in the email. The checkout link works only after the email saying the brand can sell in your country.

wallet_currency_mismatch

HTTP status: 400 · Type: invalid_request · Retryable: no

The amount's currency doesn't match the wallet's currency. Use the wallet for this currency. A customer has one wallet per currency (details.expected, details.actual).

wallet_payment_method_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

This payment method can't be used for the wallet's auto-reload. Use a saved payment method (pm_…) of the wallet's customer that isn't detached. Customers save one at a subscription or setup checkout.

wallet_posting_mismatch

HTTP status: 400 · Type: invalid_request · Retryable: no

The posting doesn't credit the wallet with exactly the granted amount. Credit the grant's amount to the wallet account its source belongs to (wallet:promo for promo grants, wallet:available for the others) and don't move any other wallet account.

A credit grant and its posting must move the wallet by the same amount, so the wallet's balance always equals the remaining amount of its grants (spec §11.5, invariant 4). details.account names the wallet account that is off, with details.expected and details.actual in integer micros.

webhook_endpoint_limit_reached

HTTP status: 400 · Type: invalid_request · Retryable: no

This brand already has the most webhook endpoints allowed in this mode. Delete an endpoint you no longer use, or subscribe an existing endpoint to more event types.

A brand can have at most 16 webhook endpoints per mode. details.limit is the limit.

webhook_event_type_invalid

HTTP status: 400 · Type: invalid_request · Retryable: no

One or more event types can't be delivered to this endpoint. List event types from the event catalogue that are delivered to webhooks (docs: reference → events), or "*" for all of them.

Brand endpoints receive the brand's events that the catalogue marks as delivered to webhooks. Organization and entity events aren't delivered to brand endpoints, and webhook_endpoint.test is sent only by the test action. details.problems[] has type, reason (unknown or not_deliverable) and message.

webhook_url_not_allowed

HTTP status: 400 · Type: invalid_request · Retryable: no

This URL can't receive webhooks. Use a public https URL without a user name, password or #fragment, e.g. https://example.com/webhooks. In development, http://localhost works while the dev gate is open.

Webhooks POST account data, so Ownrecord only calls public https URLs: never localhost, private networks (10/8, 172.16/12, 192.168/16, fc00::/7, single-label or .local/.internal names), link-local addresses (169.254/16, fe80::/10) or reserved ranges. Live mode is always https. In development and test, while the dev gate is open, loopback and private hosts are allowed, and http for loopback hosts. The same rule is checked again for every address the host resolves to when a delivery is made; a refused address fails the attempt. details.reason is invalid, too_long, scheme, https_required, credentials, fragment or host_not_allowed.

Authentication

account_disabled

HTTP status: 401 · Type: authentication · Retryable: no

This account is disabled. Contact your organization's owner or Ownrecord support.

A disabled user can't sign in, and every session they had ends immediately.

api_key_invalid

HTTP status: 401 · Type: authentication · Retryable: no

The API key is invalid, expired or revoked. Send a current key as Authorization: Bearer or_test_… or or_live_…. Create a new key in the console under Developers → API keys if this one was revoked or has expired.

Returned when the presented key isn't an Ownrecord API key, doesn't exist, was revoked, has expired, or belongs to an organization that can't use the API in that mode (closed, or suspended for live keys). The response never says which, so a leaked or guessed key reveals nothing. Request logs show only the key's 12-character prefix.

assistant_session_required

HTTP status: 401 · Type: authentication · Retryable: no

The assistant works only in the console, signed in with your own account. Open the console in your browser and sign in. API keys, OAuth access tokens and MCP clients can't use the assistant; they call the operations directly.

POST /api/assistant/chat and POST /api/assistant/actions/confirm accept only the console's session cookie, from a page of the same origin. A request with an Authorization header (an API key or an OAuth token) is refused, so the assistant always acts as the person at the keyboard (spec §43.3).

authentication_required

HTTP status: 401 · Type: authentication · Retryable: no

Authentication is required. Send a valid API key or access token in the Authorization header.

dpop_proof_invalid

HTTP status: 401 · Type: authentication · Retryable: no

The DPoP proof is missing, invalid, already used, or doesn't match this request. Make a new proof for every request: a JWT with typ: dpop+jwt, signed with ES256 or EdDSA by the token's key, with its public jwk in the header and the claims jti (new each time), htm (the HTTP method), htu (the URL without query), iat (now) and ath (the base64url SHA-256 of the access token).

Returned for Authorization: DPoP or_at_… when the DPoP header is missing or repeated, isn't a proof JWT, is signed with another algorithm or key, names another method or URL, is more than 60 seconds old or ahead of the server's time, has an ath for another token, or was already used (each jti is accepted once). The message says which check failed. The response carries WWW-Authenticate: DPoP error="invalid_dpop_proof" with the accepted algorithms.

dpop_proof_required

HTTP status: 401 · Type: authentication · Retryable: no

This access token is bound to a DPoP key: send it with the DPoP scheme and a DPoP proof. Send Authorization: DPoP <access token> and a DPoP header holding a new proof, signed with the key the token was issued for, on every request. A token bound to a key never works as a bearer token.

The client asked for the token with a DPoP proof, so Ownrecord bound it to that proof's key (token_type: "DPoP" in the token response). Presented as Authorization: Bearer, it is refused (RFC 9449 §7.2): whoever copied the token doesn't hold the key. The response carries WWW-Authenticate: DPoP error="invalid_token" with the accepted algorithms.

email_unverified

HTTP status: 401 · Type: authentication · Retryable: no

Your identity provider hasn't verified this email address. Verify the address with Google or Microsoft first, or sign in with an email link instead.

oauth_token_invalid

HTTP status: 401 · Type: authentication · Retryable: no

The access token is invalid, expired or revoked. Get a new access token from the Ownrecord OAuth server (/oauth/token): with your agent's client credentials, or with your refresh token for an installed app or an MCP client. If that fails, the agent or app was paused or revoked in the console (Organization → Agents & apps).

Returned for an Authorization: Bearer or_at_… token that doesn't exist, has expired (access tokens last 15 minutes), was revoked, or belongs to an agent, app installation or client that is no longer active, or to a user who lost access to the organization. The response never says which: request logs keep the reason. OAuth errors carry WWW-Authenticate: Bearer with error="invalid_token" on the MCP server. With Authorization: DPoP or_at_… it is also returned for a token bound to another key than the proof's, or for a bearer token (one issued without a proof); those answers carry WWW-Authenticate: DPoP error="invalid_token".

HTTP status: 401 · Type: authentication · Retryable: no

This sign-in link doesn't work any more. Ask for a new sign-in link on the portal's sign-in page. Links work once, for 15 minutes; an operator can also generate a new one.

A customer portal sign-in link is single-use and expires 15 minutes after it was made (spec §29.2). The same answer is given for a link that never existed, one that was already used, one that expired and one made for another brand, so a link can't be probed.

staff_sign_in_method

HTTP status: 401 · Type: authentication · Retryable: no

Platform staff sign in with Google or Microsoft, not with an email link. Use Continue with Google or Continue with Microsoft.

Permission

agent_delegation_not_allowed

HTTP status: 403 · Type: permission · Retryable: no

You can't give an agent more access than you have yourself. Choose a narrower role, scope or token scopes, turn off live mode for the agent, or ask an organization owner to do it.

An agent's role at its scope, its token scopes and live access are each checked against what the caller may do there (spec §41.3). details.problems[] lists the permissions the caller doesn't hold.

api_key_scope_not_allowed

HTTP status: 403 · Type: permission · Retryable: no

An API key can't have permissions that you don't have yourself. Remove the scopes listed in details.problems, or ask an organization owner or admin to create the key.

A key is never broader than its creator: every scope is checked against what the creator may do at the key's organization, entity or brand, in the key's mode. For example, a developer can't create a key with members:admin, and a developer without live access can't create live keys. details.problems[] has scope, code (permission_denied or livemode_not_allowed) and reason.

app_installation_paused

HTTP status: 403 · Type: permission · Retryable: no

This app is paused in the organization. An organization owner or admin can resume it under Organization → Agents & apps. If the member who installed it left, an owner must re-assign it first; calls then resume with the same tokens.

An admin paused the installation (its tokens were revoked; authorize again after it is resumed), or the member who installed it lost access to the organization (spec §41.3 "Installer leaves the org"): the installation is paused and the owners are asked to re-assign it.

app_scopes_not_approved

HTTP status: 403 · Type: permission · Retryable: no

Some of these scopes aren't approved for this listed app yet. Consent only to the scopes platform review approved (details.approved). The others wait for the publisher's re-review; they can be added after it is approved.

Another organization can grant a listed app only the scopes its approved listing covers. While the app's added scopes are under review, existing installations keep the scopes they consented to and new consents are limited to the approved ones (docs/spec-questions.md #1022).

approval_policy_denied

HTTP status: 403 · Type: permission · Retryable: no

The organization's approval policy doesn't let agents and apps run this kind of operation. Nothing ran. The policy denies this risk tier (details.tier) to agents, apps and MCP clients. Ask an organization owner or admin to change it in Settings → Approvals, or have a person do it in the console.

Approval policies set, per risk tier, whether agents, apps and MCP clients may run an operation (allow), need a person's approval (require_approval) or may not run it at all (deny). The strictest setting of the organization's policy and its overrides for the entity, agent or app wins. details has the tier and the operationId. A dry run of the call fails the same way.

approver_not_eligible

HTTP status: 403 · Type: permission · Retryable: no

You can't decide on this approval request. Approval requests are decided by people in the Ownrecord console (not API keys, agents, apps or MCP clients) who may run the operation themselves, at its entity or brand and in its mode. Ask such a person.

assistant_disabled

HTTP status: 403 · Type: permission · Retryable: no

Your organization turned the assistant off. Ask an organization owner or admin to turn it on in Organization → Security → Assistant. Everything else in the console keeps working.

fresh_auth_required

HTTP status: 403 · Type: permission · Retryable: no

This action needs a recent sign-in. Sign in again, then repeat the action within 15 minutes.

invitation_email_mismatch

HTTP status: 403 · Type: permission · Retryable: no

This invitation is for a different email address than the one you're signed in with. Sign out, then sign in with the address the invitation was sent to. Or ask for an invitation to your own address.

livemode_not_allowed

HTTP status: 403 · Type: permission · Retryable: no

You can't do this in this mode (test or live). Use test mode, or credentials for the target's mode. Developers need live access granted by an organization owner or admin.

Returned when the caller is allowed the action in principle but not in the requested mode:

  • an API key used on data of the other mode (a test key on live data, or the reverse);
  • a member whose live access is off (developers start in test mode until an owner or admin grants it);
  • a developer changing products and prices in live mode (developers manage the catalog in test mode only).

org_not_granted

HTTP status: 403 · Type: permission · Retryable: no

This access token doesn't cover that organization. Use an Ownrecord-Org the token was granted for, or ask a member of that organization to install the app or connect the client there.

owner_required

HTTP status: 403 · Type: permission · Retryable: no

Only an owner of the organization can do this. Ask an organization owner (org_owner).

permission_denied

HTTP status: 403 · Type: permission · Retryable: no

You don't have permission to do this. Ask an organization owner or admin for a role or scope that allows this action.

portal_action_not_allowed

HTTP status: 403 · Type: permission · Retryable: no

This can't be done in the customer portal. The brand's portal settings don't allow it for this subscription now (details.action): contact the brand.

A customer portal action the brand's portal settings or the subscription's state don't allow (spec §16.12, §29.2): a plan switch without a row in the plan-change matrix or with plan changes off, a quantity change with quantity changes off, a cancellation with cancelling off, any change while a cancellation is scheduled or a change waits for its payment, reactivating a cancellation the brand scheduled, or a payment method on a subscription paid by bank transfer. details.action names the action: switch_plan, change_quantity, apply_promotion_code, cancel, reactivate or change_payment_method.

refund_limit_exceeded

HTTP status: 403 · Type: permission · Retryable: no

This refund is above the support role's limit. Ask a member with the finance role (or an API key with refunds:admin) to make it, or refund at most details.limit. The limit is the brand's (brands.policies.supportRefundLimits).

Support refunds up to a limit, finance above it (spec §3.1). The limit is per brand and currency (policies.supportRefundLimits on the brand); a currency without one has the default of 100 major units (100.00 EUR, 100 JPY). The credit note's total is compared with it. details.limit and details.amount are integer micros.

refund_outside_policy

HTTP status: 403 · Type: permission · Retryable: no

Refunding these credits is outside the brand's refund policy. Credits are refundable by support within the brand's refund window when none were used; anything else is the finance role's decision (refunds:admin).

Purchased, unused credits are refundable per brand policy: by default within 14 days of the purchase and unused, or at the operator's discretion (spec §17.7). Ownrecord reads the discretion as the finance role's: support refunds credits only inside the policy (details.reason: window or used).

role_not_grantable

HTTP status: 403 · Type: permission · Retryable: no

You can't grant or change access that goes beyond your own. Ask an organization owner to grant this role and scope, or pick a role and scope within your own access.

Nobody grants a role, scope or live-mode access that gives more than they have themselves, and nobody changes or removes a membership they couldn't have granted (for example, an admin can't remove an owner). details.problems lists what's missing.

scope_mismatch

HTTP status: 403 · Type: permission · Retryable: no

The request names an organization, entity or brand outside the scope of your credentials. Use credentials scoped to that organization, entity or brand, or leave the scope fields out of the request.

Every request runs in one scope: an organization, optionally narrowed to one legal entity or brand. Filters and new objects may narrow that scope but never name another organization, entity or brand. Such requests are refused rather than silently rewritten.

self_approval_not_allowed

HTTP status: 403 · Type: permission · Retryable: no

You can't approve your own request. Ask another person who may run this operation to approve it.

signup_closed

HTTP status: 403 · Type: permission · Retryable: yes

New accounts can't be created right now. Try again later. If you already have an account, sign in with the address you used before.

support_access_disabled

HTTP status: 403 · Type: permission · Retryable: no

This organization doesn't allow support access. Help without access, or ask an organization owner to change the support-access policy in Organization → Security → Access log.

The organization's support-access policy is off: Ownrecord staff can't request access to its console. Only an organization owner can change the policy (support_access.update_policy). Under on_request (the default) staff request access and an owner or admin decides; under always_read read access is granted without approval and still logged.

task_claim_not_allowed

HTTP status: 403 · Type: permission · Retryable: no

This credential can't hold tasks. People, organization agents and installed apps claim tasks. With an API key, work on tasks nobody holds without claiming them, or use an agent or app credential (OAuth).

Entitlement

assistant_message_limit_reached

HTTP status: 403 · Type: entitlement · Retryable: no

Your organization has used all of this month's assistant messages. Upgrade the organization's plan for more messages (details.limit is this plan's monthly limit), or wait until the first day of next month (UTC).

Each plan includes a number of assistant messages per organization and calendar month (UTC), the assistant.messages_per_month entitlement. Every message you send counts once, however many tools the assistant uses to answer it. details carries limit, used and month.

entitlement_required

HTTP status: 403 · Type: entitlement · Retryable: no

Your plan doesn't include this. Upgrade the organization's plan, or remove something to get back under the limit.

org_restricted

HTTP status: 403 · Type: entitlement · Retryable: no

Your organization is restricted because its Ownrecord subscription is unpaid, so nothing new can be created in live mode. Pay the open Ownrecord invoice in Organization → Billing; the restriction lifts as soon as it is paid. Test mode keeps working.

Spec §37.5: 14 days (by default) after a failed Ownrecord payment the organization is restricted. The console works, existing checkouts, renewals, usage billing and webhooks keep running, but no new live brands, products, prices or API keys can be created. details.action names what was refused.

org_suspended

HTTP status: 403 · Type: entitlement · Retryable: no

This seller is unavailable: its Ownrecord account is suspended, so it can't start new live sales. If this is your organization, pay the open Ownrecord invoice in Organization → Billing; it is active again as soon as it is paid. Renewals, refunds, the customer portal and filings keep working.

Spec §37.5: 30 days (by default) after a failed Ownrecord payment the organization is suspended. New live checkout sessions and new live subscriptions are refused (the message is safe to show a buyer: the seller is unavailable), on top of the restricted organization's limits. Renewals of existing subscriptions, refunds, portal access and filings continue, so the subscriber can meet its obligations to its customers and tax authorities. A platform admin can also suspend an organization for other reasons (§38.4).

Not found

conversation_not_found

HTTP status: 404 · Type: not_found · Retryable: no

This conversation doesn't exist, or it isn't yours. Start a new conversation. Conversations are deleted after the organization's retention period, and only their owner can open them.

fx_rate_unavailable

HTTP status: 404 · Type: not_found · Retryable: no

There's no ECB reference rate for this currency on or shortly before this date. Check the currency (the ECB publishes about 30) and the date. Rates are fetched every day after 16:00 CET; for a date with no rate yet, try again after the next publication.

A conversion uses the ECB rate of the date, or of the last publication day before it (weekends and TARGET holidays have no rates). If the last rate is more than 7 days older than the date, the conversion is refused rather than using a stale rate. details.date is the requested date, with details.latest_rate_date (the last earlier rates, if any) or details.currency (a currency the ECB doesn't publish).

invitation_invalid

HTTP status: 404 · Type: not_found · Retryable: no

This invitation link isn't valid. Check that you opened the whole link from the invitation email, or ask the person who invited you to send a new invitation.

invoice_upcoming_none

HTTP status: 404 · Type: not_found · Retryable: no

This subscription has no upcoming invoice. Nothing is billed at the next period end: the subscription has ended, is set to end without anything left to bill, is paused without invoices, or is unpaid. Read the subscription for its status.

details.reason is ended, nothing_to_bill, paused or unpaid.

operation_not_found

HTTP status: 404 · Type: not_found · Retryable: no

There is no operation with this ID. Check the operation ID against the API reference. Operation IDs are dotted, e.g. brands.create.

ownership_transfer_not_found

HTTP status: 404 · Type: not_found · Retryable: no

There's no open offer of ownership for you in this organization. Offers expire after 7 days. Ask an owner to offer ownership again.

proposal_not_found

HTTP status: 404 · Type: not_found · Retryable: no

This proposed action doesn't exist in the conversation. Ask the assistant to propose the action again.

resource_not_found

HTTP status: 404 · Type: not_found · Retryable: no

The requested object does not exist or is not visible to you. Check the ID and that your credentials are scoped to the right organization, entity, brand and mode.

route_not_found

HTTP status: 404 · Type: not_found · Retryable: no

There is no API endpoint at this path. Check the method and path against the API reference. Every path starts with /api/v1.

session_not_found

HTTP status: 404 · Type: not_found · Retryable: no

This session doesn't exist or has already ended. Reload your account page to see the sessions that are still active.

subscription_item_not_found

HTTP status: 404 · Type: not_found · Retryable: no

This subscription has no item with this ID. Read the subscription and use one of its items[].id (si_…); to add a new price, leave out id.

Conflict

agent_revoked

HTTP status: 409 · Type: conflict · Retryable: no

This agent was revoked and can't be changed. Create a new agent instead.

alert_resolved

HTTP status: 409 · Type: conflict · Retryable: no

This alert is already resolved. Nothing to acknowledge: the alert is closed. If the problem happens again, a new alert is raised.

already_exists

HTTP status: 409 · Type: conflict · Retryable: no

An object with the same unique value already exists. Use a different value, or fetch the existing object and update it.

api_key_revoked

HTTP status: 409 · Type: conflict · Retryable: no

This API key was revoked and can't be changed. Create a new key instead.

app_listing_not_withdrawable

HTTP status: 409 · Type: conflict · Retryable: no

This app has no application waiting for review or for changes. Only an application that is pending or changes_requested can be withdrawn. Read the listing with apps.listing.get.

app_review_not_pending

HTTP status: 409 · Type: conflict · Retryable: no

This app isn't waiting for review. Reload the review queue: the application was already decided or withdrawn, or the partner hasn't submitted its changes yet.

app_suspended

HTTP status: 409 · Type: conflict · Retryable: no

Ownrecord suspended this app. The app can't be installed, resumed or authorized while the suspension lasts. Its organization's owners and admins, and those of every organization that installed it, were emailed the reason. Once Ownrecord reinstates it, resume its installation under Organization → Agents & apps.

Platform staff suspend an abusive or compromised app everywhere (platform.apps.suspend): its tokens are revoked in every organization, its installations are paused, the agents it owns stop, and the OAuth server issues it nothing. Installing it, resuming one of its installations or consenting to it then fails with this error. Reinstating it (platform.apps.reinstate) doesn't resume any installation: each organization resumes its own with app_installations.resume, after which the app asks for authorization again.

approval_expired

HTTP status: 409 · Type: conflict · Retryable: no

The approval request expired before anyone approved it, so it didn't run. Send the request again with a new Idempotency-Key to create a new approval request. Requests expire after 72 hours.

approval_not_pending

HTTP status: 409 · Type: conflict · Retryable: no

This approval request was already decided. Read the request (GET /approvals/{id}) to see its status and outcome.

approval_policy_override_limit_reached

HTTP status: 409 · Type: conflict · Retryable: no

This organization already has the maximum number of approval policy overrides. Delete an override you no longer need (details.limit is the maximum), or set the rule in the organization's policy instead.

approval_rejected

HTTP status: 409 · Type: conflict · Retryable: no

An approver rejected this request, so it didn't run. Read the reason in details.approval.rejection. To try again, send a new request with a new Idempotency-Key.

aup_report_already_resolved

HTTP status: 409 · Type: conflict · Retryable: no

This AUP report is already resolved. Reload the organization's page. Record a new report if the problem came back.

authorization_status_invalid

HTTP status: 409 · Type: conflict · Retryable: no

This authorization is revoked. A revoked authorization doesn't change. Record a new one with authorizations.create.

bank_statement_already_imported

HTTP status: 409 · Type: conflict · Retryable: no

This bank statement file was imported already. Open the earlier import (details.import_id) instead. Overlapping statements with different files are fine: their common lines are skipped.

bank_statement_line_resolved

HTTP status: 409 · Type: conflict · Retryable: no

This bank statement line was matched or ignored already. Read the line: matchStatus and invoiceId say how it was resolved. A matched line paid its invoice; reverse that with the invoice's own actions if it was wrong.

brand_not_live

HTTP status: 409 · Type: conflict · Retryable: no

This brand isn't live yet, so it can't take live payments. Use test mode, or complete the brand's go-live checklist first.

Live checkout sessions (and live API keys) need a live brand (spec §38.3).

checkout_details_required

HTTP status: 409 · Type: conflict · Retryable: no

The customer's details aren't in yet. Submit the customer's email and country first, so the tax can be quoted.

checkout_fulfilment_pending

HTTP status: 409 · Type: conflict · Retryable: yes

The paid checkout isn't ready to be fulfilled yet. Nothing to do: the payment is fulfilled automatically as soon as the checkout's records are committed. If it stays pending, contact support with the payment's ID.

Fulfilment runs on the processor's webhook (spec §14.1 step 8). A webhook can arrive before the request that prepared the payment has committed; the job then retries with backoff.

checkout_payment_method_required

HTTP status: 409 · Type: conflict · Retryable: no

Choose a payment method first. Start the payment step and choose the card, then confirm. The card's country can change the tax, and subscriptions save it for renewals.

checkout_payment_not_started

HTTP status: 409 · Type: conflict · Retryable: no

The payment step hasn't started for this checkout. Start the payment step first, so the payment can be prepared for the quoted total.

checkout_quote_expired

HTTP status: 409 · Type: conflict · Retryable: yes

The tax quote of this checkout has expired. Submit the customer's details again to get a fresh quote (quotes are valid for 30 minutes).

checkout_session_blocked

HTTP status: 409 · Type: conflict · Retryable: no

This sale is paused for the customer's location, so it can't be paid. Offer the waitlist, or let a business customer enter a valid tax ID (reverse charge). details.blocked_reason says why the sale is paused.

checkout_session_changed

HTTP status: 409 · Type: conflict · Retryable: yes

The checkout changed while this request ran. Send the request again.

checkout_session_not_open

HTTP status: 409 · Type: conflict · Retryable: no

This checkout can't be changed any more. The session is complete, expired or waiting for a payment's outcome (details.status). Create a new session for a new purchase.

checkout_total_changed

HTTP status: 409 · Type: conflict · Retryable: no

The total changed since the customer saw it. Show the customer the new total (details.total) and ask them to confirm it; then confirm again with that total.

The card's country is location evidence (spec §13.3). When it changes the tax, the total changes, and the customer must confirm the new total before they are charged. details.total is the amount to show.

coupon_immutable

HTTP status: 409 · Type: conflict · Retryable: no

This coupon has been used, so what it gives can't change. Create a new coupon with the new terms (and new promotion codes for it), then archive this one. Its name and status can still change.

Once a discount references a coupon or a redemption is counted, its amount or percentage, duration, product and price restriction, maxRedemptions and redeemBy are fixed, so every customer keeps what they were promised. details.fields lists the fields the request tried to change.

coupon_not_redeemable

HTTP status: 409 · Type: conflict · Retryable: no

This coupon can't be redeemed. Check details.reason: reactivate an archived coupon, or create a new coupon when it expired or reached its redemption limit.

details.reason is archived (the coupon is archived), expired (its redeemBy has passed), max_redemptions_reached (its maxRedemptions are used up) or currency_mismatch (an amount-off coupon in another currency than the purchase). Discounts already applied keep running; only new redemptions are refused.

credit_note_pdf_not_ready

HTTP status: 409 · Type: conflict · Retryable: yes

The credit note PDF isn't ready yet. The PDF is rendered right after the credit note is issued, usually within a minute. Try again shortly; pdf.status on the credit note says when it's ready.

customer_email_taken

HTTP status: 409 · Type: conflict · Retryable: no

Another customer of this brand already has this email address. Use the existing customer (details.customer_id, or customers.list with email), or give this customer another address. A deleted customer's address can be used again.

A brand has one active customer per email address in each mode, because checkout finds returning customers and the customer portal signs them in by email. The same address in another brand, or in the other mode, is a different customer (spec §6.6).

customer_tax_id_exists

HTTP status: 409 · Type: conflict · Retryable: no

The customer already has this tax ID. Nothing to do: the tax ID is already on the customer (details.tax_id_id). To check it again, remove it and add it back.

Tax IDs are compared by their canonical number, so ATU12345678, atu 1234 5678 and 12345678 for Austria are the same ID, as are a Swiss ch_uid and ch_vat of one UID.

discount_already_applied

HTTP status: 409 · Type: conflict · Retryable: no

This coupon is already applied here. End the existing discount first (discounts.delete), or apply a different coupon. The same coupon doesn't stack with itself.

A subscription (or a customer, for customer-level discounts) can hold a coupon once at a time: applying it again while its discount is active or scheduled would discount twice. details.discount_id is the existing discount.

dispute_evidence_changed

HTTP status: 409 · Type: conflict · Retryable: yes

The evidence pack changed while it was being submitted. Reload the dispute, check the latest pack and submit again.

Two people built or submitted the pack at the same time. Nothing was recorded for this request; the processor may already have the evidence of the other one (under_review).

dispute_evidence_missing

HTTP status: 409 · Type: conflict · Retryable: no

Build the evidence pack before submitting it. Build the pack with disputes.evidence.build, check it in the console (or in the returned evidence.sections), then submit it.

dispute_evidence_past_due

HTTP status: 409 · Type: conflict · Retryable: no

The deadline to respond to this dispute has passed. The processor no longer takes evidence after evidenceDueBy. The dispute closes as the card network decides; Ownrecord records the outcome from the processor's webhook.

dispute_payment_pending

HTTP status: 409 · Type: conflict · Retryable: yes

The disputed payment isn't recorded yet. Nothing to do: the processor-event job retries shortly, once the payment's own webhook has been handled. If the payment never appears, a RECON_UNMATCHED alert follows.

Raised by the processor-event job, never by an API call: a dispute arrived before the payment it disputes was recorded (the processor delivered both at once).

dispute_status_invalid

HTTP status: 409 · Type: conflict · Retryable: no

This dispute can't take that change in its current status. Reload the dispute. Evidence is built and submitted only while the dispute needs a response (needs_response); once submitted (under_review) or closed (won, lost) it can't change. details.status is the dispute's status.

domain_purpose_taken

HTTP status: 409 · Type: conflict · Retryable: no

This brand already has a domain for this purpose. Remove the brand's current domain for this purpose first, then add the new one.

A brand has at most one checkout domain and one portal domain. details.host is the one it has.

domain_taken

HTTP status: 409 · Type: conflict · Retryable: no

This domain is already attached to a brand. Use another domain, or remove it from the brand that uses it first.

domain_unavailable

HTTP status: 409 · Type: conflict · Retryable: no

This domain is in use elsewhere on the hosting platform and can't be attached. Remove the domain from the other project or account that uses it, or use another host name, then try again.

The domain provider (Vercel) refused to attach the host because another project or account holds it.

einvoice_not_retryable

HTTP status: 409 · Type: conflict · Retryable: no

This e-invoice can't be retried. Only a failed or rejected e-invoice can be sent again. One that is pending, queued, delivered or accepted is on its way or done.

details.status is the e-invoice's current status.

entitlement_override_limit

HTTP status: 409 · Type: conflict · Retryable: no

This customer already has the most active entitlement overrides allowed. Delete overrides the customer no longer needs (customers.entitlement_overrides.delete), or let expiring ones run out, then try again.

A customer can have at most 100 active (unexpired) overrides; expired ones don't count. details.limit is the maximum.

entity_already_verified

HTTP status: 409 · Type: conflict · Retryable: no

This legal entity is already verified. Nothing to do. Change its tax ID to verify it again.

entity_verification_not_pending

HTTP status: 409 · Type: conflict · Retryable: no

This legal entity isn't waiting for document review. Reload the verification queue: the entity was already approved or rejected, or it was verified another way (VIES, HMRC, CH UID).

export_expired

HTTP status: 409 · Type: conflict · Retryable: no

This export has expired. Export files are kept for 30 days after they are ready. Request the same period again.

export_not_ready

HTTP status: 409 · Type: conflict · Retryable: yes

This export's files aren't ready. An export is queued, then running, then ready (usually within a minute); poll exports.get until it is ready. A failed export says why in failure: request a new one.

feature_change_not_allowed

HTTP status: 409 · Type: conflict · Retryable: no

A feature's type can't change, and enum values can be added but not removed. Create a new feature with the new definition, and grant it from your products instead.

Products and customers may already hold values of the feature, so changes that could make those values invalid are refused. details.issues names each refused change.

feature_key_taken

HTTP status: 409 · Type: conflict · Retryable: no

This brand already has a feature with this key in this mode. Choose another key, or update the existing feature.

filing_period_not_ended

HTTP status: 409 · Type: conflict · Retryable: no

This filing period isn't over yet, so it can't be locked. Lock the period from details.lockable_at on: after its last day (plus the lock delay for late documents).

A period is locked after it ends (spec §21.1), so every document of the period is in the report. details has period_end and lockable_at.

filing_period_status_invalid

HTTP status: 409 · Type: conflict · Retryable: no

This filing period can't do that in its current status. Reload the period and check its status: details.allowed_from lists the statuses the action needs. Periods go open → locked → prepared → submitted → paid.

Filing periods follow spec §21.1: lock an open period once it is over; prepare (generate the report) a locked or prepared one; submit a prepared one; mark a submitted one paid. details has status, action and allowed_from.

filing_reconciliation_failed

HTTP status: 409 · Type: conflict · Retryable: no

The report's tax doesn't match the ledger's tax payable for the period. Check the documents in details.documents: each shows the report's and the ledger's tax. Correct the ledger or the documents (for example with a manual ledger adjustment or a credit note), then prepare the period again.

Every report has a reconciliation section (spec §21.2): its tax per jurisdiction and currency must equal the movement of the regime's tax payable accounts in the ledger for the period. When they differ, no report is generated. details.rows lists every jurisdiction and currency that differs (report_tax, ledger_tax, difference, micros as strings), details.documents the invoices, credit notes and ledger transactions behind the difference.

filing_registration_not_effective

HTTP status: 409 · Type: conflict · Retryable: no

The registration this period files for has no effective date yet. Mark the registration registered with its number and effective date first: sales made during the grace period are reported in the first return after it.

Periods are created for regimes in grace too, so their due dates show on the calendar, but a return can only be prepared once the registration has an effective date: that date decides which return reports the grace sales (spec §21.1). details has regime_id and registration_state.

filing_report_kind_unsupported

HTTP status: 409 · Type: conflict · Retryable: no

Ownrecord can't generate this regime's report yet. Prepare the return outside Ownrecord for now; the report for this kind of regime comes in a later release.

Every regime maps to a report kind (spec §21.2): eu_oss for the EU One-Stop Shop schemes, eu_domestic, uk_vat, ch_mwst, us_state and generic. details has report_kind and regime_id.

filing_report_not_ready

HTTP status: 409 · Type: conflict · Retryable: no

This filing period has no report in that format yet. Prepare the period first (filing_periods.prepare), then download its report.

Reports are generated when a period is prepared (spec §21.2) and kept with it as CSV and XLSX files. details has status and format.

filing_schedule_conflict

HTTP status: 409 · Type: conflict · Retryable: no

The new filing schedule would cut an existing filing period. Choose a frequency and stagger whose periods start where the first existing period starts and end where the last one ends (details.period, details.edge, details.at).

Filing periods never overlap and leave no gap. A schedule may change after periods exist only when the existing periods stay whole: their first start and their last end must be period boundaries of the new schedule. details has regime_id, period (the period cut), edge (start or end) and at.

go_live_requirements_unmet

HTTP status: 409 · Type: conflict · Retryable: no

The brand can't go live yet: some go-live requirements aren't met. Open the brand's go-live checklist (brands.go_live_checklist) and complete the open items.

details.unmet lists the requirements still open (plan_livemode, org_status, legal_accepted, entity_verified, invoice_settings, home_registration, processor_live, brand_urls, brand_status) and details.items explains each one.

idempotency_in_progress

HTTP status: 409 · Type: conflict · Retryable: yes

A request with this Idempotency-Key is still being processed. Wait briefly and retry with the same Idempotency-Key.

idempotency_key_reused

HTTP status: 409 · Type: conflict · Retryable: no

This Idempotency-Key was already used with a different request. Use a new Idempotency-Key for a different request, or resend the original request unchanged.

invitation_already_accepted

HTTP status: 409 · Type: conflict · Retryable: no

This invitation was already accepted. Open the console to see your organizations. Each invitation works once.

invitation_exists

HTTP status: 409 · Type: conflict · Retryable: no

This person already has a pending invitation at this scope. Ask them to use the invitation they have, or revoke it and invite them again.

invitation_expired

HTTP status: 409 · Type: conflict · Retryable: no

This invitation has expired. Invitations work for 7 days. Ask the person who invited you to send a new invitation.

invitation_not_pending

HTTP status: 409 · Type: conflict · Retryable: no

Only pending invitations can be revoked. This invitation was already accepted, revoked or has expired. Nothing to do.

invitation_revoked

HTTP status: 409 · Type: conflict · Retryable: no

This invitation was revoked. Ask the person who invited you to send a new invitation.

invoice_bank_transfer_missing

HTTP status: 409 · Type: conflict · Retryable: no

The legal entity has no bank transfer details for this invoice. Add the IBAN, BIC, bank name and account holder in the entity's invoice settings, then send the invoice again. A send_invoice invoice tells the customer where to pay.

invoice_item_attached

HTTP status: 409 · Type: conflict · Retryable: no

This invoice item is already on another invoice. An item is billed once. Remove it from its draft first (details.invoice_id), or create a new item.

invoice_payment_unavailable

HTTP status: 409 · Type: conflict · Retryable: no

This invoice can't be paid by card now. The invoice is paid, voided or has nothing due, or the brand has no processor connection: check details.reason. Pay by bank transfer with the details on the invoice, or ask the seller.

details.reason is not_open (paid, voided or a draft), nothing_due, no_processor (the brand has no usable processor connection) or payment_pending (a card payment of the invoice is being processed).

invoice_pdf_not_ready

HTTP status: 409 · Type: conflict · Retryable: yes

The invoice PDF isn't ready yet. The PDF is rendered right after the invoice is issued, usually within a minute. Try again shortly; pdf.status on the invoice says when it's ready.

invoice_quote_unusable

HTTP status: 409 · Type: conflict · Retryable: no

This tax quote can't be invoiced. Invoice the quote the customer paid: a quote that isn't blocked, whose lines add up to its totals. Create a new quote if the sale changed.

An invoice shows exactly the tax of the quote the customer paid (spec §9.7, §18). A blocked quote, or a line the engine blocked, never gets an invoice (fail closed, §1.3).

invoice_series_taken

HTTP status: 409 · Type: conflict · Retryable: no

Another brand of this legal entity already uses this invoice series. Choose a different series code. Each brand numbers its invoices and credit notes in its own series within the entity.

Invoice and credit-note series are unique across all brands of one legal entity (spec §18.2), so every invoice number is unique and continuous. details.series names the clashing code.

invoice_status_invalid

HTTP status: 409 · Type: conflict · Retryable: no

The invoice's status doesn't allow this. Check the invoice's status (details.status) and what it still allows (details.allowed_actions). Drafts are edited and finalized; open invoices are sent, paid, voided or marked uncollectible; paid and void invoices don't change (corrections are credit notes).

Invoices move draft → open → paid | void | uncollectible (an uncollectible invoice can still be paid). Only a draft's lines and settings change; once finalized an invoice's number, lines and totals are fixed. Invoices issued paid at checkout (issued, pending_registration) allow none of these actions.

invoice_total_zero

HTTP status: 409 · Type: conflict · Retryable: no

An invoice that totals zero isn't finalized. Add an item with an amount, or void the draft. Zero invoices aren't issued (spec §16.2): no number is taken for them.

details.total is the invoice's gross total in integer micros (zero or negative). A draft whose credits exceed its charges isn't issued either: the credit belongs on the customer's credit balance.

knowledge_review_item_transition_invalid

HTTP status: 409 · Type: conflict · Retryable: no

This review item can't do that in its current status. Read the item (GET /tax/knowledge/review_items/{id}): resolve or dismiss an open item, reopen a resolved or dismissed one, and assign only open items.

Review items are open, resolved or dismissed (spec §7.2). Resolving needs an open item and the commit or pull request that made the change; dismissing needs an open item and a reason; reopening needs a resolved or dismissed item. details has the item's status and the requested one.

last_owner

HTTP status: 409 · Type: conflict · Retryable: no

An organization needs at least one owner, and this is the last one. Make another member an owner first, then change or remove this membership.

lease_closed

HTTP status: 409 · Type: conflict · Retryable: no

The lease is already closed. Settle the lease it was renewed into (details.renewedTo), or create a new lease. A lease closes with a final settle or a renewal.

Each lease ID is settled until a settle closes it: final: true captures the consumed amount and releases the rest, and renew replaces it with a new lease ID. A resent copy of the closing settle is answered as a duplicate; any other settle of the closed lease gets this error.

lease_expired

HTTP status: 409 · Type: conflict · Retryable: no

The lease has expired, so it can't be settled. Create a new lease for further usage. To settle usage after a crash, make leases with the expiry policy hold_for_late_settle, which accepts a settle until lateSettleUntil.

A lease can be settled until its expiresAt (spec §17.3). After that its expiry policy applies, whether or not the lease_expiry job has run yet: release returns the funds to the wallet, capture charges the whole remaining amount, and hold_for_late_settle keeps the funds reserved and accepts one closing settle until lateSettleUntil (expiresAt plus lateSettleWindowSeconds). details.expiresAt and, for held leases, details.lateSettleUntil say when the lease ran out.

lease_settle_out_of_order

HTTP status: 409 · Type: conflict · Retryable: yes

An earlier settle of this lease hasn't been applied yet. Send the settles of a lease in order, one at a time: settleSeq must be the lease's settleSeq plus one (details.expected). Retry once the earlier settle has been applied.

settleSeq is optional. When a settle carries it, Ownrecord applies settle n only after settle n − 1, so a final settle that overtakes an earlier one can't close the lease before the earlier usage is captured. A settle numbered at or below the lease's settleSeq was applied already and is answered as a duplicate.

lease_settle_seq_conflict

HTTP status: 409 · Type: conflict · Retryable: no

A settle with this number was already applied with different content. Give each settle of a lease the next number (details.settleSeq is the lease's current one) and resend a settle only unchanged.

ledger_rebuild_refused

HTTP status: 409 · Type: conflict · Retryable: no

This ledger account can't be rebuilt from its entries. An entry in another currency or an unbalanced transaction touches the account (details.problems): that is a different break (invariant 1). Follow docs/runbooks/ledger-invariant-break.md before rebuilding.

ledger_transaction_not_pending

HTTP status: 409 · Type: conflict · Retryable: no

Only a pending ledger transaction can be posted or voided. Check the transaction's status (details.status). A posted transaction is corrected with a reversing transaction, never voided.

Ledger transactions are append-only (spec §11.1). A pending transaction (processor-side uncertainty, such as an asynchronous SEPA payment) is either posted or voided once. Posting an already posted transaction again with the same entries, or voiding an already voided one, returns it unchanged.

ledger_transaction_not_posted

HTTP status: 409 · Type: conflict · Retryable: no

Only a posted ledger transaction can be reversed. Void a pending transaction instead of reversing it (details.status is its current status).

lookup_key_taken

HTTP status: 409 · Type: conflict · Retryable: no

Another price in this brand and mode already has this lookup key. Move the key explicitly with prices.move_lookup_key (POST /prices/{id}/move_lookup_key), or choose another key.

A lookup key names one price per brand and mode (spec §7.4). Moving it from one price to another is an explicit action (spec §12), so setting it on a second price is refused rather than silently moved. details.price_id is the price that has it, when it is visible to the request.

membership_exists

HTTP status: 409 · Type: conflict · Retryable: no

This user already has a membership at this scope. Change the role of the existing membership instead of adding another one.

meter_definition_conflict

HTTP status: 409 · Type: conflict · Retryable: no

Another metered price of this meter aggregates or prices it differently. Use the meter's existing aggregation and pricing (and, for percent of amount, its currency), or choose another meter key.

All metered prices of one meter in a brand and mode agree on what the meter is (docs/spec-questions.md #410): the same aggregation (sum, max or last), the same pricing (per_unit or percent_of_amount) and, for percent_of_amount, the same currency, because the quantity is an amount in that currency. details.field is what differs, details.expected the meter's value and details.price_id the price that has it.

oauth_client_disabled

HTTP status: 409 · Type: conflict · Retryable: no

This OAuth client is disabled. Register a new app, or create a new agent.

onboarding_step_done

HTTP status: 409 · Type: conflict · Retryable: no

This onboarding step is already done. Nothing to do. A done step can't be skipped or marked again.

organization_already_suspended

HTTP status: 409 · Type: conflict · Retryable: no

This organization is already suspended. Nothing to do, or reinstate it first to change the reason.

organization_closed

HTTP status: 409 · Type: conflict · Retryable: no

This organization is closed. Its data is read-only. You can still read and export its data (organizations.export).

organization_closing

HTTP status: 409 · Type: conflict · Retryable: no

This organization is closing and takes no new live checkouts. Existing subscriptions, refunds and filings continue until the organization is closed. Contact the seller for anything else.

organization_export_in_progress

HTTP status: 409 · Type: conflict · Retryable: yes

An export of this organization is already being built. Wait for it to be ready (organizations.exports.list), then download it.

organization_not_closable

HTTP status: 409 · Type: conflict · Retryable: no

This organization can't be closed from its current status. A suspended organization is closed with Ownrecord support; a closing or closed one needs nothing more.

details.status is the organization's status.

organization_not_suspended

HTTP status: 409 · Type: conflict · Retryable: no

This organization isn't suspended for abuse. Only an abuse suspension is reinstated here; a suspension for non-payment ends when the invoices are paid.

ownership_transfer_exists

HTTP status: 409 · Type: conflict · Retryable: no

An offer of ownership is already open for this organization. Cancel the open offer first (ownership_transfers.cancel), then make a new one.

ownership_transfer_not_allowed

HTTP status: 409 · Type: conflict · Retryable: no

Ownership can't be offered to this person. Offer ownership to another member of the organization who isn't an owner yet.

details.problem: not_owner (you aren't an owner), self, not_member (they have no membership in the organization) or already_owner.

partner_terms_not_accepted

HTTP status: 409 · Type: conflict · Retryable: no

The organization hasn't accepted the current Partner and App Terms. An owner or admin reads the Partner and App Terms at /legal/partners and accepts them under Developers → Apps (or with partner_terms.accept), then submits the application again.

Listing an app for every organization needs the owning organization's acceptance of the Partner and App Terms (spec §41.10): the version in effect, or a later one. A new material version in effect asks again before the next application. details.status is the organization's acceptance status and details.acceptable the versions it can accept now.

payment_method_detached

HTTP status: 409 · Type: conflict · Retryable: no

This payment method was detached and can't be used any more. Ask the customer to add a payment method again (a checkout in setup mode).

platform_billing_details_required

HTTP status: 409 · Type: conflict · Retryable: no

Ownrecord needs your organization's VAT ID before it can bill a paid plan. Add your legal entity (its VAT ID is used), or enter your billing details and VAT ID in Organization → Billing → billing portal, then change the plan again.

Ownrecord sells to businesses only (§37.6: the organization is a customer of our entity). A paid plan's invoices are business-to-business sales: reverse charged across EU borders, with domestic VAT at home. Before the first one, the organization's billing account needs a VAT ID the register confirms; Ownrecord copies the first legal entity's home tax ID, legal name and address when the billing details have none.

platform_billing_unavailable

HTTP status: 409 · Type: conflict · Retryable: yes

Ownrecord's own plans aren't set up on this installation yet. A platform admin runs pnpm platform:bootstrap, which creates the plans, prices and features of data/platform/plans.yaml. Try again afterwards.

Platform billing needs the platform brand (§37.1) and its plan catalogue in the billing mode (platform_settings.defaultPlanPriceId). Until then organizations keep their plan cache and aren't billed.

platform_customer_missing

HTTP status: 409 · Type: conflict · Retryable: yes

This organization's Ownrecord billing account is still being set up. Wait a few seconds and reload the billing page. If it persists, contact support.

platform_last_admin

HTTP status: 409 · Type: conflict · Retryable: no

The platform needs at least one platform admin. Make someone else a platform admin first, then change this person's role.

platform_plan_change_pending

HTTP status: 409 · Type: conflict · Retryable: no

A plan change is already waiting for its payment. Finish the checkout you started (its link is on the billing page), or wait until it expires (23 hours), then try again.

platform_plan_currency_fixed

HTTP status: 409 · Type: conflict · Retryable: no

Your organization's paid plan is billed in another currency, and a running paid plan keeps its currency. Choose the plan in the currency it is billed in, or move to the free plan first and then choose the plan in the new currency.

platform_plan_unchanged

HTTP status: 409 · Type: conflict · Retryable: no

Your organization is already on this plan and billing interval. Choose another plan or interval, or leave the plan as it is.

platform_profile_limit_reached

HTTP status: 409 · Type: conflict · Retryable: no

This organization has reached the limit of staff notes or AUP reports. Summarise older entries in one note, or ask an engineer to archive the organization's staff profile.

price_archived

HTTP status: 409 · Type: conflict · Retryable: no

This price is archived. Reactivate the price first (active: true), or use an active price.

Archived prices can't receive a lookup key or be sold. Reactivating a price makes it usable again.

price_immutable

HTTP status: 409 · Type: conflict · Retryable: no

This price has been used, so what it charges can't change. Create a new price with the new terms, move the lookup key to it with prices.move_lookup_key, and archive this one.

Once a checkout session or payment references a price, its amounts, currency, tax behaviour, type and tiers are fixed (spec §12), so every invoice and receipt keeps matching it. The nickname, lookup key, metadata and active can still change. details.fields lists the fields the request tried to change.

price_lookup_key_conflict

HTTP status: 409 · Type: conflict · Retryable: no

This price already has another lookup key. Remove the price's current lookup key first (lookupKey: null), or move the key to a price without one.

Moving a lookup key onto a price that has a different one would silently free that other key, which integrators may still use. details.lookup_key is the key the price has.

processor_account_already_connected

HTTP status: 409 · Type: conflict · Retryable: no

This processor account is already connected to this entity in this mode. Rotate the key of the existing connection (processor_connections.update) instead of connecting again.

details.connection_id is the existing connection. A disconnected connection doesn't count.

processor_connection_disconnected

HTTP status: 409 · Type: conflict · Retryable: no

This processor connection is disconnected. Connect the processor again, or use another connection of the entity.

A disconnected connection's API key is destroyed, so nothing can be charged or refunded through it. Its webhook secret is kept so late events for earlier payments are still recorded.

processor_connection_in_use

HTTP status: 409 · Type: conflict · Retryable: no

Brands still use this processor connection. Assign another connection to the brands listed in details.brand_ids first.

product_archived

HTTP status: 409 · Type: conflict · Retryable: no

This product is archived. Reactivate the product first (active: true), or use an active product.

New prices can't be added to an archived product, and its prices can't be sold.

promotion_code_not_redeemable

HTTP status: 409 · Type: conflict · Retryable: no

This promotion code can't be used here. Check details.reason and tell the customer: the code may have expired, be used up, belong to another customer, need a larger purchase or be for first-time customers only.

details.reason is archived, expired (restrictions.expiresAt has passed), max_redemptions_reached, customer_mismatch (the code is for another customer), first_time_only (the customer has paid before), minimum_amount_not_met (the purchase before discounts is below restrictions.minimumAmount, or in another currency) or coupon_not_redeemable (the code's coupon can't be redeemed; details.coupon_reason says why). Unknown facts fail closed: a code with a minimum needs the purchase amount, a customer's code needs the customer.

promotion_code_taken

HTTP status: 409 · Type: conflict · Retryable: no

This brand already has a promotion code with this code in this mode. Choose another code. Codes are compared without regard to case and are never reused.

Codes are unique per brand and mode, ignoring case (launch50 and LAUNCH50 are the same code), and an archived code keeps its code so a customer can't be surprised by a different offer under an old code. details.promotion_code_id is the code that has it, when visible to the request.

proposal_not_pending

HTTP status: 409 · Type: conflict · Retryable: no

This proposed action was already confirmed, cancelled or replaced. Ask the assistant to propose the action again if you still want it.

reconciliation_item_closed

HTTP status: 409 · Type: conflict · Retryable: no

This reconciliation item is no longer open. Reload the item: it was resolved or ignored already (details.status), by a person or by a later reconciliation run that found what it was waiting for. Only open items can be resolved or ignored.

refund_amount_exceeds

HTTP status: 409 · Type: conflict · Retryable: no

The refund is larger than what is left to refund. Refund at most details.refundable: the payment's amount less earlier refunds, and for credit packs no more than the unused credits are worth.

A refund never returns more than the payment took less earlier refunds, and never credits more than the invoice still has (spec §19.1 step 1). Credit packs and top-ups are refundable only for their unused, unreserved credits; promo and bonus credits never are (§17.7). details.refundable is the most this refund type can return now.

refund_event_pending

HTTP status: 409 · Type: conflict · Retryable: yes

The processor's refund event can't be matched to a refund yet. Nothing to do: the event is processed again shortly, once the refund is recorded.

refund_nothing_to_refund

HTTP status: 409 · Type: conflict · Retryable: no

There is nothing left to refund. The invoice is fully credited, its credits are used, or its period is over. Check the invoice's credit notes and the wallet before trying another type.

refund_payment_not_refundable

HTTP status: 409 · Type: conflict · Retryable: no

This payment can't be refunded. Refund a succeeded card payment that has an invoice (details.status says what the payment is now). A disputed payment is settled through the dispute; an invoice paid by bank transfer or only from the credit balance has no payment to refund.

Refunds go back through the processor that took the payment (spec §19.1), so they need a payment that succeeded (succeeded or partially_refunded) and the invoice it paid. details.reason: status (the payment's status doesn't allow it), no_invoice, no_payment (the invoice was paid by bank transfer or from the credit balance) or currency.

registration_grace_expired

HTTP status: 409 · Type: conflict · Retryable: no

The grace period of the obligation already triggered has passed, so the jurisdiction can't be armed. Complete the registration with the tax authority and mark it registered with its number, effective date and proof.

A threshold crossing starts the registration deadline even while the jurisdiction is blocked. Once that deadline is inside its safety buffer, arming would only start a grace period that ends at once, so it is refused (spec §10.2, §10.3).

registration_grace_not_allowed

HTTP status: 409 · Type: conflict · Retryable: no

This regime has no grace period, so the jurisdiction can't be armed. Register with the tax authority first, then mark the registration registered with its number, effective date and proof.

Only regimes that collect tax before registration and have a grace rule (a number of days, a day of the following month or the end of the following month) can be armed. US states never collect before registration, and regimes with the grace rule none or before_first_sale need the registration first (spec §10.2, §10.3).

registration_prerequisites_incomplete

HTTP status: 409 · Type: conflict · Retryable: no

Every registration prerequisite must be done before the jurisdiction can be armed. Finish the prerequisites on the registration page (portal access, fiscal representative, documents …), mark each one done, then arm again.

Arming tells Ownrecord that the next consumer sale may start the grace period (spec §10.2). Grace needs every prerequisite done, so a registration with open prerequisites can't be armed.

registration_transition_invalid

HTTP status: 409 · Type: conflict · Retryable: no

This registration can't do that in its current state. Check the registration's state (details.state): details.allowed_from lists the states the action needs. Reload the registration and try the action that fits its state.

Registrations follow the state machine in spec §10.2: arming needs blocked_b2c (not armed), marking the application submitted needs grace (once), deregistering needs registered, and marking registered works from any state except registered and not_applicable. details has regime_id, state, armed, event and, for an action no row accepts in that state, allowed_from.

reservation_expired

HTTP status: 409 · Type: conflict · Retryable: no

The reservation has expired, so it can't be captured. Reserve the amount again and capture the new reservation. Expired reservations are released automatically.

A single reservation can be captured until its expiresAt (at most one hour after it was made, spec §17.2). From then on its funds go back to the wallet, whether or not the reservation_expiry job has run yet.

reservation_not_open

HTTP status: 409 · Type: conflict · Retryable: no

The reservation is already closed. Create a new reservation. A reservation is closed once it is captured, released or expired (details.status).

risk_blocklist_entry_exists

HTTP status: 409 · Type: conflict · Retryable: no

This value is already on the blocklist. Nothing to do: details.entryId is the existing entry. Delete it first to change its reason.

risk_review_not_paid

HTTP status: 409 · Type: conflict · Retryable: yes

The held payment hasn't been recorded yet. The review opens when the processor confirms the payment. Wait for its webhook, then decide the review.

risk_review_not_pending

HTTP status: 409 · Type: conflict · Retryable: no

This risk review was already decided. Reload the review: it was approved or rejected (details.status). A decision is final; refund the payment or unfreeze the wallet by hand if something must change.

slack_channel_limit_reached

HTTP status: 409 · Type: conflict · Retryable: no

This organization already has the maximum number of Slack channels in this mode. Remove a channel you no longer use (details.limit is the maximum), then add the new one.

slug_taken

HTTP status: 409 · Type: conflict · Retryable: no

This slug is already in use. Choose another slug. Organization and brand slugs are unique across Ownrecord; entity slugs are unique within an organization.

stale_approval

HTTP status: 409 · Type: conflict · Retryable: no

The approved request can no longer run as it was requested: the data changed, or the requester lost access. Check the current state (details.approval.failure says what failed), then send a new request with a new Idempotency-Key.

An approved request runs exactly as originally requested, as the person or key that requested it, with their current access. If that no longer works (the requester lost access, a referenced object changed or a balance moved), the request becomes stale and nothing runs (spec §41.4). details.approval.failure has the underlying error code and message.

stale_proposal

HTTP status: 409 · Type: conflict · Retryable: no

The data changed after this action was proposed, so it didn't run. Ask the assistant to propose it again: it shows a fresh preview of what will happen.

Confirm re-runs the proposal's dry run on the current data and runs the action only if the preview is the same as on the card (spec §43.3). If something changed in between (another person edited the object, a balance moved, you lost access to the brand), nothing runs and the card becomes stale.

subscription_meter_conflict

HTTP status: 409 · Type: conflict · Retryable: no

The customer already has a metered item for this meter. Change the existing subscription's metered item instead, or cancel it first. A customer has at most one active metered item per meter.

Usage is recorded per customer and meter, and billed through a watermark per customer and meter (spec §16.7), so two live subscription items billing the same meter would bill the same usage twice. details.meter is the meter and details.subscription_id / details.item_id the item that already bills it.

subscription_status_invalid

HTTP status: 409 · Type: conflict · Retryable: no

The subscription's status doesn't allow this. Check details.status: items change only while trialing, active or past due; pausing needs an active subscription and resuming a paused one; reactivating needs a cancellation scheduled for the period end; canceled and expired subscriptions don't change.

details.action is what was asked, details.status the subscription's status and details.reason why it was refused: final_status (canceled or incomplete_expired), not_allowed (the §16.1 lifecycle doesn't allow it from this status), cancel_not_scheduled (reactivate without a scheduled cancellation) or cancel_already_scheduled.

support_access_already_open

HTTP status: 409 · Type: conflict · Retryable: no

You already have an open support-access request or grant for this organization. Wait for the organization to decide on your request, keep using your current grant, or end it before you request another.

support_access_not_active

HTTP status: 409 · Type: conflict · Retryable: no

This support-access grant isn't active. Reload the Access log: the grant has already ended (expired, revoked or ended by the staff member), or it is still a request waiting for a decision (deny it instead).

support_access_not_pending

HTTP status: 409 · Type: conflict · Retryable: no

This support-access request isn't waiting for a decision. Reload the Access log: the request was already approved or denied, withdrawn, or it lapsed 24 hours after it was made. Staff can make a new request.

task_assigned

HTTP status: 409 · Type: conflict · Retryable: no

This task is assigned to a person, so it can't be claimed. Ask the assignee, or someone who may work on the entity's tasks, to unassign it (tasks.update with assignee: null), or work on another task.

task_claimed

HTTP status: 409 · Type: conflict · Retryable: no

Someone else holds this task's lease. Work on another task (tasks.list with claimable: true), or try again after details.claimedUntil: an expired lease frees the task. details.assignee is who holds it.

task_input_not_available

HTTP status: 409 · Type: conflict · Retryable: no

This input isn't available for this task. Read details.reason: a registration packet belongs to registration tasks, a filing package to filing tasks once the period is prepared (filing_periods.prepare), and a playbook exists only where the tax knowledge has one.

task_lease_expired

HTTP status: 409 · Type: conflict · Retryable: no

Your lease on this task ended. Claim the task again with tasks.claim. If someone else claimed it meanwhile, work on another task. Send heartbeats well before claimedUntil.

task_not_claimed

HTTP status: 409 · Type: conflict · Retryable: no

You don't hold this task's lease. Claim the task first with tasks.claim; only the holder can extend or release a lease.

task_not_ready

HTTP status: 409 · Type: conflict · Retryable: no

The registration or filing period isn't where this task needs it yet. Read details: for example, prepare the filing period (lock it, then filing_periods.prepare) before completing filing.prepare, which then finishes by itself.

task_status_invalid

HTTP status: 409 · Type: conflict · Retryable: no

This task can't be changed in its current status. Reload the task: finished tasks (done, failed, cancelled) don't change, and a task waiting for an approval changes only through that approval. details.status is the task's status.

test_clock_advancing

HTTP status: 409 · Type: conflict · Retryable: yes

The test clock is advancing. Wait until the clock's status is ready (read it with test_clocks.get), then try again. An advance processes every due renewal, trial end and cancellation of the clock's subscriptions first.

While a test clock advances (spec §16.13), its subscriptions are billed at each step's time; changing the clock, its customers' subscriptions or attaching new customers would race those steps. details.test_clock_id names the clock.

usage_idempotency_key_reused

HTTP status: 409 · Type: conflict · Retryable: no

This idempotency key was already used for a different usage event. Use a new idempotency key for a new event. Resending the original event unchanged is safe: it is reported as a duplicate and counted once.

Every usage event carries its own idempotency key, unique per brand and mode (spec §17.4). An event whose key is already recorded is a duplicate when its customer, meter, quantity, occurredAt, attributes and subscription item are the same; otherwise it is refused with this error and nothing is counted.

user_already_disabled

HTTP status: 409 · Type: conflict · Retryable: no

This account is disabled already. Reload the account: its sessions and OAuth access already ended when it was disabled.

user_not_disabled

HTTP status: 409 · Type: conflict · Retryable: no

This account isn't disabled. Reload the account: someone may have enabled it already.

version_conflict

HTTP status: 409 · Type: conflict · Retryable: yes

The object was changed by another request. Fetch the object again and retry the change.

waitlist_not_available

HTTP status: 409 · Type: conflict · Retryable: no

The waitlist is offered only when a sale is paused for the customer's location. The customer can buy now: continue the checkout. The waitlist opens when the quote is blocked (spec §23).

wallet_closed

HTTP status: 409 · Type: conflict · Retryable: no

The wallet is closed. Reopen the wallet with wallets.update (status: active). A purchase of credits in its currency reopens it, too.

A closed wallet takes no reservations, leases or operator grants (promo, adjustment). Credits that were paid for (a top-up or a credit pack, subscription credits) are never refused: they reopen the wallet.

wallet_frozen

HTTP status: 409 · Type: conflict · Retryable: no

The wallet is frozen, so its credits can't be spent. Unfreeze the wallet (wallets.update with status: active) once the review is done. Reservations made before the freeze can still be captured or released.

Freezing a wallet (for example while a dispute or a risk review is open) stops new reservations and leases at once. Credits still arrive (top-ups, grants, subscription credits), reservations made before the freeze settle as usual, credits still expire, and no auto-reload is started while the wallet is frozen.

wallet_not_empty

HTTP status: 409 · Type: conflict · Retryable: no

A wallet with credits or open reservations can't be closed. Capture or release its open reservations and wait until its credits are used or expire (details.balance in integer micros, details.open_reservations). To stop spending at once, freeze it instead.

webhook_delivery_in_progress

HTTP status: 409 · Type: conflict · Retryable: no

This delivery is still being attempted. Wait until it succeeds or fails (see nextAttemptAt), then redeliver it if you need to.

Only finished deliveries (succeeded, failed or canceled) can be redelivered.

Rate limited

agent_daily_limit_reached

HTTP status: 429 · Type: rate_limited · Retryable: yes

This agent or app has made all the changes it may make today. Try again after midnight UTC (retry_after_ms), or ask an organization owner or admin to raise its mutations-per-day limit under Organization → Agents & apps.

An agent or app installation with a limits.mutationsPerDay runs at most that many mutating calls per UTC day and mode. Reads, dry runs, replays of an idempotency key and approval requests don't count; a call that fails is given back. A dry run answers this error when the real call would. details carries limit, used and resets_at.

agent_rate_limited

HTTP status: 429 · Type: rate_limited · Retryable: yes

Too many requests from this agent or app. Wait retry_after_ms and try again, and spread your calls out. An organization owner or admin can raise the agent's or app's requests-per-second limit under Organization → Agents & apps.

An agent or app installation with a limits.rps makes at most that many operation calls per second (fixed one-second windows on the real clock, across both modes). Every operation call counts, reads and dry runs too; the organization's API rate limit (rate_limited) applies as well. details.limit is the limit per second.

assistant_budget_exhausted

HTTP status: 429 · Type: rate_limited · Retryable: yes

The assistant has reached its monthly usage budget. Try again next month, or ask Ownrecord support. Everything else in the console keeps working.

Ownrecord caps what the assistant may spend on its language model each month across all organizations (spec §43.6). Platform staff are alerted at 80% of the cap; at 100% the assistant answers this refusal until the next calendar month (UTC).

oauth_rate_limited

HTTP status: 429 · Type: rate_limited · Retryable: yes

Too many requests to the authorization server from your network. Wait as long as the Retry-After header says, then try again. Reuse access tokens until they expire.

Client registrations and token requests are limited per network address (spec §41.3).

rate_limited

HTTP status: 429 · Type: rate_limited · Retryable: yes

Too many requests. Wait for retry_after_ms and retry with backoff.

HTTP status: 429 · Type: rate_limited · Retryable: yes

Too many sign-in links were asked for this address or from your network. Use the newest link in your inbox, or wait 15 minutes and ask again.

At most 5 links per email address in 15 minutes and 60 per network address in an hour, for every address alike, so the limit doesn't tell whether an account exists.

signup_rate_limited

HTTP status: 429 · Type: rate_limited · Retryable: yes

Too many new accounts were created from your network recently. Wait an hour and try again, or sign in to an existing account.

Tax blocked

evidence_conflict

HTTP status: 422 · Type: tax_blocked · Retryable: no

We need to confirm the customer's country before we can calculate tax. Collect another piece of location evidence (billing address, card country, or the customer's confirmation of their country) and quote again.

The customer's location evidence (spec §13.2) disagrees, or there isn't enough of it for the regime's rule (requiredNonConflicting agreeing items, e.g. 2 for EU consumers). Under the majority policy a 1–1 tie has no majority yet; a third item (usually the card country) decides it. Under hold_for_review any conflict blocks until the customer confirms their country (self-declaration plus one matching item) or an operator resolves it. details names the countries seen and the rule applied.

jurisdiction_blocked

HTTP status: 422 · Type: tax_blocked · Retryable: no

Sales to this jurisdiction aren't possible yet. This jurisdiction isn't covered by usable tax knowledge for this seller. Contact support if you need to sell there.

The engine needs a regime it doesn't have: the seller's home-country regime, an EU OSS scheme, a US state regime or a foreign-seller scheme is missing from the knowledge base or not reviewed for this mode, more than one regime would apply at once (stacked taxes are not supported), or the entity's registration marks the regime as not applicable. details.regime names it when known.

rate_unavailable

HTTP status: 422 · Type: tax_blocked · Retryable: no

There's no usable tax rate for this product where the customer is. Sell to this jurisdiction later, once its rate is in the knowledge base and reviewed. In test mode, check that the jurisdiction is covered.

No rate is valid at the tax point for the jurisdiction and tax category (nor a * standard rate), the knowledge has more than one candidate, or the rate record has unsupported rules (e.g. a partial taxable base the engine can't express). Live mode uses only reviewed or verified records (spec §7.2, §9.1 step 7).

registration_required

HTTP status: 422 · Type: tax_blocked · Retryable: no

The seller isn't registered for tax where this customer is, so new sales there are paused. Register the entity for the regime named in details.regime (see its prerequisites and deadline), or arm the jurisdiction once the prerequisites are done. The customer can join the waitlist meanwhile.

Returned by the registration gate (spec §10.4): the regime is blocked_b2c (a zero-threshold regime that isn't armed, a missed grace deadline, or a US state before registration with usBehaviorBeforeRegistration: block). Existing customers' renewals and top-ups continue (spec §10.7). details carries the regime, the registration state and, where known, the deadline and prerequisites.

tax_id_unavailable

HTTP status: 422 · Type: tax_blocked · Retryable: yes

The customer's tax ID couldn't be checked right now. Try again in a few minutes. The tax-ID register (e.g. VIES) is unavailable, and this brand blocks business sales until the ID is verified.

The brand's taxIdUnavailable policy is block. With the default charge_as_b2c, the sale is taxed as a consumer sale instead and can be corrected by a credit note once the ID is validated (spec §9.1 step 1).

unsupported_tax_category

HTTP status: 422 · Type: tax_blocked · Retryable: no

This product's tax category can't be sold to this customer yet. Sell it only where it is supported (e.g. domestically), or change the product's tax category if it was set wrongly.

The regime has no place-of-supply rule for the line's tax category and customer type, or the rule is not_supported (e.g. live online courses to consumers in another country). Ownrecord never guesses a place of supply (spec §9.1 step 5). details.lines lists the affected lines and categories.

Insufficient funds

credit_balance_insufficient

HTTP status: 402 · Type: insufficient_funds · Retryable: no

The customer's credit balance is smaller than the amount taken back. Take back at most the current balance (details.balance, integer micros, in details.currency). The credit balance never goes below zero.

A negative adjustment removes credit the customer hasn't used yet. Credit already applied to invoices can't be taken back this way: void the invoice or issue a credit note instead.

insufficient_funds

HTTP status: 402 · Type: insufficient_funds · Retryable: no

There aren't enough funds for this amount. Reserve or capture a smaller amount, or top up the wallet first. details.requested and details.available are in integer micros.

Returned when a wallet's spendable credits (unexpired and not already reserved) are less than the amount asked for, or when a posting would take a wallet or credit balance below zero (spec §11.1, §17.2).

wallet_fresh_balance_capped

HTTP status: 402 · Type: insufficient_funds · Retryable: no

Credits bought recently can't be spent beyond the brand's cap yet. Ask for less now, or wait until details.freeAt: the brand caps how much of a fresh balance can be used (details.cap, micros) until the credits are details.untilAgeHours hours old.

The brand's usage cap for fresh balances (spec §20 wallet.usageCapUntilAgeHours): credits a payment brought less than untilAgeHours ago can be reserved or spent up to cap in total. The wallet has the credits, but details.locked micros of them are held back until they're older. Older credits are never capped.

Approval required

approval_required

HTTP status: 202 · Type: approval_required · Retryable: no

This request needs another person's approval before it runs. Nothing ran yet. An approver reviews approval.summary and the dry-run result in the console (approval.approveUrl). Poll GET /approvals/{id} or watch for the approval.resolved event; sending the same request again with the same Idempotency-Key returns the same approval.

Not an error: REST answers HTTP 202 with { approval: { id, status, approveUrl, expiresAt, summary, diff, … } } and MCP with a result { approval_required: true, approval, … } (not isError). The organization's approval policy requires a person's approval for this operation (or for this amount): a two-person rule, or, for agents, apps and MCP clients, the operation's risk tier (compliance and security always) or an amount limit. When someone approves it, the operation runs exactly as requested: same input, same Idempotency-Key, as you. Approvals expire after 72 hours.

Processor error

assistant_unavailable

HTTP status: 502 · Type: processor_error · Retryable: yes

The assistant is unavailable right now. Try again in a few minutes. Everything else in the console keeps working, and nothing was changed.

dispute_outcome_unknown

HTTP status: 502 · Type: processor_error · Retryable: no

The processor closed a dispute with a status Ownrecord doesn't know. Nothing was recorded. Check the dispute in the processor's dashboard; platform support updates Ownrecord's status mapping. details.status is the processor's status.

Raised by the processor-event job, never by an API call: a dispute.closed event whose status is none of won, lost, warning_closed or prevented. The job stops (platform staff are alerted) rather than guess the outcome.

domain_dns_unavailable

HTTP status: 502 · Type: processor_error · Retryable: yes

DNS didn't answer for the domain's ownership record. Retry after a short wait. Nothing was changed.

Ownrecord looks up the _ownrecord-challenge TXT record of the domain; the lookup failed (a timeout or a DNS server error, details.code). An answer that the record doesn't exist isn't an error: the domain stays pending.

domain_provider_error

HTTP status: 502 · Type: processor_error · Retryable: yes

The domain provider couldn't complete the request. Retry after a short wait. If it keeps failing, contact support with the request ID.

details.status and details.code carry the provider's answer when there was one. Nothing was changed.

einvoice_provider_failed

HTTP status: 502 · Type: processor_error · Retryable: yes

The e-invoicing provider couldn't take the document. Ownrecord retries on its own while the provider may recover. If the e-invoice ends up failed, read its error and retry it with einvoices.retry once the cause is fixed.

details.provider names the provider and details.code its own error code when it gave one.

fx_feed_invalid

HTTP status: 502 · Type: processor_error · Retryable: yes

The ECB reference-rate file couldn't be read. Nothing was stored. The rates job retries automatically; if it keeps failing, the ECB file format may have changed.

Raised by the fx_rates_fetch job when the ECB XML is malformed: not the ECB envelope, unbalanced elements, an invalid date, currency or rate, a duplicate day or currency, or no rates at all. The whole file is rejected; no partial data is stored.

fx_feed_unavailable

HTTP status: 502 · Type: processor_error · Retryable: yes

The ECB reference-rate file couldn't be downloaded. The rates job retries automatically with backoff. Missing days are backfilled from the 90-day file.

processor_payment_declined

HTTP status: 502 · Type: processor_error · Retryable: no

The payment was declined. Ask the customer for another payment method; details.decline_code says why the card was declined.

The card issuer or the processor declined the payment. details has processor, code and decline_code (e.g. insufficient_funds).

processor_request_failed

HTTP status: 502 · Type: processor_error · Retryable: yes

The payment processor couldn't complete the request. If retryable is true, retry with the same Idempotency-Key after a short wait; otherwise check details.code.

Returned when a call to the processor fails: an outage, a rate limit, a timeout (retryable), or a request the processor refused (not retryable). details has processor, the processor's own code and http_status. Nothing was changed in Ownrecord. When requests to a connection keep failing because the processor doesn't answer, Ownrecord marks the connection degraded and new checkouts use the brand's secondary (spec §15.4).

upstream_timeout

HTTP status: 502 · Type: processor_error · Retryable: yes

An outside service didn't answer in time. Retry the request with the same Idempotency-Key after a short wait.

Returned when an interactive call to an outside service (e.g. a tax ID check or a payment processor) times out before the operation could continue. Nothing was changed.

Internal

checkout_fulfilment_invalid

HTTP status: 500 · Type: internal · Retryable: no

The paid checkout's records don't add up, so it wasn't fulfilled. Contact support with the request ID. Nothing was recorded; the payment is reviewed by hand (a reconciliation alert is open).

credit_note_pdf_unavailable

HTTP status: 500 · Type: internal · Retryable: yes

The stored credit note PDF couldn't be read. Try again in a few minutes. If it keeps failing, contact support with the request ID: the file is checked against its stored SHA-256 and never served when it differs.

domain_provider_not_configured

HTTP status: 500 · Type: internal · Retryable: no

Custom domains aren't set up on this deployment. Contact support: the deployment's domain provider credentials are missing.

The deployment has neither development access (the fake provider) nor the Vercel API settings.

fx_rate_invalid

HTTP status: 500 · Type: internal · Retryable: no

An exchange rate isn't a valid positive decimal number. This is a data problem on our side. Contact support with the request ID.

Rates are stored as decimal strings (e.g. 1.0912) and converted with exact rational arithmetic. A stored rate that isn't a positive decimal is never used.

internal_error

HTTP status: 500 · Type: internal · Retryable: yes

Something went wrong on our side. Retry later. If it persists, contact support with the request ID.

invoice_pdf_unavailable

HTTP status: 500 · Type: internal · Retryable: yes

The stored invoice PDF couldn't be read. Try again in a few minutes. If it keeps failing, contact support with the request ID: the file is checked against its stored SHA-256 and never served when it differs.

jurisdiction_timezone_invalid

HTTP status: 500 · Type: internal · Retryable: no

The jurisdiction has no usable time zone, so no deadline can be calculated. This is a problem on our side. Contact support with the request ID.

Registration deadlines fall at the end of a day in the jurisdiction's time zone (spec §10.3), recorded as an IANA zone such as Europe/Zagreb in the knowledge base. details names the zone or the regime.

registration_grace_rule_invalid

HTTP status: 500 · Type: internal · Retryable: no

The regime's grace rule is incomplete, so no deadline can be calculated. This is a problem on our side. Contact support with the request ID.

A days_after_trigger rule needs days and an nth_day_of_following_month rule needs nthDay (1–31) in the regime record of the knowledge base (spec §10.3). details.rule and details.problem describe it.

service_maintenance

HTTP status: 500 · Type: internal · Retryable: yes

Ownrecord is down for maintenance. Try again after the time in the Retry-After header. The status page says when the maintenance ends.

Answered with HTTP 503 and a Retry-After header to every request while platform staff run maintenance (a database restore). Nothing was changed: retry the same request, with the same Idempotency-Key, later.

tax_fx_rate_required

HTTP status: 500 · Type: internal · Retryable: no

The sale must be converted to a threshold's currency, but no exchange rates were supplied. This is a problem on our side. Contact support with the request ID.

The registration gate compares this sale with a threshold in another currency (spec §10.5) and needs the ECB rates of the tax point. details names both currencies.

tax_snapshot_mismatch

HTTP status: 500 · Type: internal · Retryable: no

The tax knowledge used for this quote doesn't match the quote's tax point or mode. This is a problem on our side. Contact support with the request ID.

The knowledge snapshot must be built for the tax point's calendar date (within one day of its UTC date) and, for a live-mode entity, with live-mode gating (spec §7.2, §9).

threshold_rule_incomplete

HTTP status: 500 · Type: internal · Retryable: no

The regime's threshold is incomplete, so its counter can't be kept. This is a problem on our side. Contact support with the request ID.

A threshold counter needs a currency: the threshold amount's, or for a count-only threshold the currency of the regime's jurisdiction (spec §10.5). details.regime_id names the regime.