Errors
Stable. Describes behaviour that is built and covered by tests in this repository.
Every error of the tenant API has the same shape, whatever went wrong:
{
"error": {
"code": "INVALID_ARGUMENT",
"key": "gateway.request.invalid",
"message": "…",
"details": [{ "field": "events", "description": "…" }],
"request_id": "…"
}
}
keyis stable and always there: dotted, the service first (billing.credit_note.not_paid). An error with no key of its own getscommon.<reason>. Translate the key, not the message: themessageis English text for a log, and its wording can change; keys do not.codeis the class of the error and decides the HTTP status.detailspoints at the fields that were wrong; it is left out when there is nothing to point at.request_idis theX-Request-Idof the call. Quote it when you ask for help.
Status and what to do
| Code | HTTP | Meaning | Retry? |
|---|---|---|---|
INVALID_ARGUMENT |
400 | the request is malformed or a field is not valid | no: fix the request |
UNAUTHENTICATED |
401 | the key is missing, wrong or ended | no: see Authentication |
PERMISSION_DENIED |
403 | the key may not do this, or the environment is not open for it | no |
NOT_FOUND |
404 | there is no such object | no |
ALREADY_EXISTS, ABORTED, FAILED_PRECONDITION |
409 | the object exists, or its state does not allow this now | no: read the state, then decide |
REQUEST_TOO_LARGE |
413 | the body is over 1 MiB (usage events: also over 500 events) | no: send less per request |
RESOURCE_EXHAUSTED |
429 | you are over a rate limit or a quota | yes, after the seconds in Retry-After |
UNAVAILABLE |
503 | a dependency is not ready | yes, with the same idempotency key |
DEADLINE_EXCEEDED |
504 | the call took too long | yes, with the same idempotency key |
An INTERNAL error never carries its cause in the message, and neither does an UNAVAILABLE, DEADLINE_EXCEEDED, RESOURCE_EXHAUSTED or ABORTED error that comes from the network or a database rather than from Payrudder's own rules: its message is a plain sentence that quotes the request_id, and the key (common.unavailable, common.timeout, common.rate_limited, common.conflict) is what to branch on. An error Payrudder wrote itself for these codes (a quota, an invitation limit) keeps its own message. A request that timed out may or may not have been done: retry it only with an idempotency key, or read the state first. The SDKs retry 429 and 5xx for you with the same key.
A usage event is judged on its own inside a 202 answer: an event that cannot be counted is rejected with an error_code (the list is below) and does not fail the others. See Usage metering.
You do not have to catch a rejection in the answer to see it later. Every rejected event is kept for 14 days (at most 10 000 per environment, the oldest dropped first) and can be read back: the Rejected events section of the Connect page in the console, with the hint for each code, or GET /v1/rejected-events (filter by error_code and meter, page with page_token) and GET /v1/rejected-events/summary (the last 24 hours by code and by hour), for a key with the usage:read permission. A row holds the code and the message, the meter code and the customer id as you sent them, the event time and the idempotency key; the properties and the quantity are never stored. A row is written just after the answer, best effort, so it can be a moment late and, under a burst of rejections or a database problem, a rejection can be missing from the list; the answer you got is the record. The sandbox's log is emptied by a sandbox reset.
In the SDKs and the command line
The SDKs turn the status into one typed error and keep the fields of the envelope on it (status, code, key, message, details, request id, Retry-After). Branch on the type or on the key, never on the message.
| Status | Go (Error.Kind) |
TypeScript | Python | PHP (Payrudder\Exception\…) |
|---|---|---|---|---|
| 401 | KindAuthentication |
AuthenticationError |
AuthenticationError |
AuthenticationException |
| 403 | KindPermission |
PermissionError |
PermissionDeniedError |
PermissionException |
| 404 | KindNotFound |
NotFoundError |
NotFoundError |
NotFoundException |
| 400, 422 | KindValidation |
ValidationError |
ValidationError |
ValidationException |
| 409 | KindConflict |
ConflictError |
ConflictError |
ConflictException |
| 429 | KindRateLimit |
RateLimitError |
RateLimitError |
RateLimitException |
| 5xx | KindServer |
ServerError |
ServerError |
ServerException |
| no answer (DNS, refused, reset, timeout) | KindConnection |
ConnectionError, TimeoutError |
APIConnectionError, APITimeoutError |
ConnectionException, TimeoutException |
| any other | KindAPI |
PayrudderError |
APIError |
ApiException |
A rejected usage event is not an error of the call: it is a result with status: "rejected" and an error_code, in all four SDKs.
The command line prints an API error as payrudder: 404 customer.not_found: … (request <id>) and exits 1; a rejected event prints its error_code and exits 1; a command line it cannot understand exits 2 without sending anything. When you ask for help, quote the request id.
The catalogue
The list below is generated from the code (docs/error-keys.md, written by the test of pkg/errkey when UPDATE_ERROR_KEYS=1 is set, and checked by it otherwise) and so is always complete. It leaves out the keys of the operator's and the console's own routes (admin, identity, support), which an integration of the tenant API never receives.
Usage event error codes
An event the API refuses comes back rejected with one of these codes and does not fail the rest of the batch. The console shows the same advice, in its six languages, under the test event and next to each entry of the Rejected events list on the Connect page (the list is also GET /v1/rejected-events).
| error_code | Meaning | What to do |
|---|---|---|
UNKNOWN_METER | No meter has this code. Create the meter first. | No meter has this code. Create the meter on the Meters page, or fix meter_code. |
UNKNOWN_CUSTOMER | No customer has this external id. Create the customer first. | No customer has this id. Create the customer first, or fix customer_external_id. |
EVENT_TOO_OLD | The event_time is more than 35 days in the past. | The event is more than 35 days old. Send usage soon after it happens; older usage cannot be counted. |
EVENT_IN_FUTURE | The event_time is more than 5 minutes ahead of the server's clock. | event_time is more than 5 minutes ahead of Payrudder's clock. Check your server's clock and send the moment the usage happened. |
INVALID_EVENT_TIME | The event_time is missing or is not an RFC 3339 instant. | Send event_time as an RFC 3339 instant, for example 2026-11-03T10:15:00Z. |
INVALID_QUANTITY | The quantity is not a non-negative decimal. | Send quantity as a non-negative decimal, as text such as "1" or "1.25". |
INVALID_QUANTITY_PRECISION | The quantity has more decimal places than the meter's quantity_scale. | The quantity has more decimal places than the meter allows. Round it to the meter's quantity_scale. |
IDEMPOTENCY_KEY_REQUIRED | A live key sent the event without an idempotency_key. | A live key must send an idempotency_key with every event. Derive it from what happened (an order id) so a retry counts once. |
IDEMPOTENCY_KEY_INVALID | The idempotency_key is not 1 to 128 characters of letters, digits and _ . : -. | Use 1 to 128 characters: letters, digits and _ . : -. |
IDEMPOTENCY_KEY_REUSED | The idempotency_key was used before for a different event. | This key was used for a different event. Use a new key for a new event; retry the same event with the same key and the same content. |
INVALID_PROPERTIES | The properties are too many or too large, or a UNIQUE meter's property is missing. | Send at most 32 properties and 4 KiB in total, with text values, and include the property a UNIQUE meter counts. |
INVALID_CORRECTION | A correction needs a reason of 1 to 500 characters, and the key of the event it corrects (not its own). | A correction needs a reason of 1 to 500 characters and the key of the event it corrects (not its own key). |
CORRECTION_UNKNOWN_EVENT | No event with the key in corrects is known: it was never accepted, or it is older than idempotency keys are kept (at least 45 days). | No event with that key was found. It may never have been accepted, or it is older than idempotency keys are kept (at least 45 days). |
CORRECTION_MISMATCH | The customer or the meter is not that of the event being corrected. | A correction must name the same customer and the same meter as the event it corrects. |
CORRECTION_UNSUPPORTED_METER | Only events of SUM and COUNT meters without a property filter can be corrected. | Only events of SUM and COUNT meters without a property filter can be corrected. |
CORRECTION_OF_CORRECTION | The event is itself a correction: correct the original again, with the quantity it should have. | The event is itself a correction. Correct the original event again, with the quantity it should have. |
All keys
510 keys, 15 areas. The meaning is the English message the console shows for the key; the API's own message may differ in wording.
affiliate
Partner program
| Key | Meaning |
|---|---|
affiliate.bad_id | That is not a valid id. |
affiliate.bad_kind | The kind must be commission or reversal. |
affiliate.bad_page_token | This page link is not valid. Reload the list. |
affiliate.bad_period | Write the dates as YYYY-MM-DD, with the end after the start and at most a year apart. |
affiliate.bad_source | The source must be link, code or api. |
affiliate.bad_status | The status must be pending, active or suspended. |
affiliate.code_required | A partner code is required. |
affiliate.code_taken | This code is already taken. Partner codes are unique across the platform: choose another. |
affiliate.commission.not_found | This invoice earned no commission. |
affiliate.customer_not_found | No such customer. |
affiliate.customer_required | Name the customer to attribute. |
affiliate.email_taken | A partner with this e-mail address already exists. |
affiliate.not_found | No such partner. |
affiliate.payout.affiliate_inactive | A suspended or pending partner is not paid. Their balance is kept until you reinstate them. |
affiliate.payout.not_found | No such payout. |
affiliate.payout.not_open | This payout is no longer open: it was paid or cancelled. |
affiliate.payout.nothing_to_pay | After refunds this payout comes to nothing. Cancel it; the balance rolls forward. |
affiliate.payout.tenant_not_active | This live environment is not active, so no payout can be made or paid. |
affiliate.portal.already_applied | You are a partner of this seller already, or you have applied. |
affiliate.portal.program_unavailable | This seller has no partner program open to applications. |
affiliate.portal.try_again | A code could not be made just now. Try again. |
affiliate.program.coupon_inactive | The coupon for referred customers is not active. |
affiliate.program.coupon_not_found | The coupon for referred customers does not exist. |
affiliate.program.not_set | This environment has no partner program yet. |
affiliate.promotion_code_not_found | No such promotion code. |
aggregator
Usage totals
| Key | Meaning |
|---|---|
aggregator.customer.bad_id | The customer id is not a valid id. |
aggregator.meter.not_found | This meter was not found. |
aggregator.period.bad_id | The period must look like 2026-11. |
aggregator.period.cannot_compute | The settings of this period cannot be worked out. Check the period and the account's time zone. |
aggregator.period.frozen | This period is frozen or already closes within five seconds, so it cannot be closed early. |
aggregator.period.not_sandbox | Periods can only be closed early in a sandbox account. |
aggregator.replay.bad_job_id | The replay job id is not a valid id. |
aggregator.replay.bad_state | The replay job is not in a state where this can be done. |
aggregator.replay.bad_tenant_id | The account id is not a valid id. |
aggregator.replay.invalid | This replay request is not valid. |
aggregator.replay.not_found | This replay job was not found. |
aggregator.store.unavailable | Usage data is not available right now. Try again shortly. |
aggregator.usage.bad_batch_size | Ask for between 1 and the maximum number of usage keys at a time. |
analytics
Usage analytics and reports
| Key | Meaning |
|---|---|
analytics.customer.bad_id | The customer id is not a valid id. |
analytics.granularity.unknown | This granularity is not supported. Use hour or day. |
analytics.query.exact_too_long | Exact queries cover at most one day. Use the estimated series for longer ranges. |
analytics.query.invalid | This query is not valid. Check the meter, the period and the filters. |
analytics.range.daily_too_long | A daily series covers at most 400 days. |
analytics.range.hourly_too_long | An hourly series covers at most 31 days. |
analytics.range.required | Both the start and the end of the range are required. |
analytics.store.unavailable | Analytics is not available right now. Try again shortly. |
analytics.tenant.bad_id | The account id is not a valid id. |
analytics.tenant.unknown | This account is not known to analytics. |
assets
File uploads and downloads
| Key | Meaning |
|---|---|
assets.asset.bad_status | The file is not in a state where this can be done. |
assets.asset.cannot_abort | This file is already uploaded or finished. Abort the upload instead of completing it. |
assets.asset.not_found | The file was not found. |
assets.asset.not_in_session | That file is not part of this upload session. |
assets.asset.not_uploadable | This file can no longer be uploaded. |
assets.download.not_ready | Only files that finished uploading can be downloaded. |
assets.download.sign_failed | The download link could not be created. Try again shortly. |
assets.list.bad_page_size | The page size must be between 1 and 500. |
assets.list.bad_page_token | The page token is not valid. Start the list again. |
assets.request.bad_id | An id in the request is not a valid id. |
assets.session.bad_file_count | A session takes between 1 and the maximum number of files. |
assets.session.closed | The upload session is no longer open. |
assets.session.not_found | The upload session was not found. |
assets.storage.quota_exceeded | These files do not fit in the storage quota of this account. |
assets.storage.sign_failed | The upload URLs could not be created. Try again shortly. |
assets.storage.unavailable | File storage is not available right now. Try again shortly. |
assets.upload.bad_from_part | The starting part is beyond the number of parts of this file. |
assets.upload.bad_part_count | Ask for a valid number of part URLs at a time. |
assets.upload.bad_sha256 | The SHA-256 checksum must be 64 hexadecimal characters. |
assets.upload.changed | The upload changed at the same moment. Try again. |
assets.upload.content_type_mismatch | The content of the file does not match its declared type. |
assets.upload.file_too_large | The file is too large. |
assets.upload.header_required | The first message of an upload must be the upload header. |
assets.upload.invalid_filename | The file name cannot be used. |
assets.upload.multipart_gone | The multipart upload no longer exists. Start the upload again. |
assets.upload.part_mismatch | The uploaded parts do not match the file. |
assets.upload.size_mismatch | The size of the uploaded file does not match the declared size. |
assets.upload.size_required | The size of the file is required for direct uploads. |
assets.upload.unsupported_content_type | This type of file is not supported. |
audit
| Key | Meaning |
|---|---|
audit.filter.bad_account | The account id is not valid. |
audit.filter.bad_page_size | The page size must be between 1 and 200. |
audit.filter.bad_tenant | The tenant id is not valid. |
audit.filter.bad_time | The dates are not valid, or the start is after the end. |
audit.page_token.invalid | That page of the log is no longer valid. Reload the list. |
audit.unavailable | The audit log could not be read from every service. Try again. |
billing
Invoices, credits, credit notes, payment links and accounting
| Key | Meaning |
|---|---|
billing.accounting.bad_app | The app is not valid. |
billing.accounting.bad_attachment | The attachment is not valid. |
billing.accounting.bad_category | The category is not one of the known categories. |
billing.accounting.bad_date | Dates look like 2026-10-21. |
billing.accounting.bad_expense | An expense needs a vendor (up to 120 characters), a note of at most 500 characters and an amount above zero. |
billing.accounting.bad_granularity | The series is by day or by month. |
billing.accounting.bad_page_size | The page size must be between 1 and 200. |
billing.accounting.bad_page_token | The page token is not valid. |
billing.accounting.bad_period | Give a first and a last day, in order. |
billing.accounting.bad_recurring | An expense repeats monthly or yearly; its last day cannot be before its first. |
billing.accounting.bad_rule | The rule is not valid: check the vendor text, the percentages and the expected amount. |
billing.accounting.expense_not_found | There is no such expense. |
billing.accounting.expense_voided | The expense was voided and cannot be changed. |
billing.accounting.inbox_item_not_found | There is no such invoice waiting for review. |
billing.accounting.range_too_long | The period is too long: five years at most, and a daily series up to 366 days. |
billing.accounting.reason_required | Give a reason of up to 200 characters to void an expense. |
billing.accounting.rule_not_found | There is no such rule. |
billing.accounting.too_many_rules | A tenant may have at most 200 rules. |
billing.accounting.unknown_currency | The currency is not supported. |
billing.credit_note.bad_amount | The credit amount is not valid: it must be more than zero and no more than what is left to credit on the invoice. |
billing.credit_note.bad_id | The credit note id is not a valid id. |
billing.credit_note.bad_page_size | The page size must be between 1 and 200. |
billing.credit_note.bad_page_token | The page token is not valid. Start the list again. |
billing.credit_note.bad_refund_id | Give the provider's reference of the refund (one line, at most 200 characters) when the refund was made, and none when it was not. |
billing.credit_note.bad_resolution | Choose whether the customer was paid back or not. |
billing.credit_note.fully_credited | This invoice has already been credited in full. |
billing.credit_note.not_found | This credit note was not found. |
billing.credit_note.not_paid | Only a paid invoice can be credited. |
billing.credit_note.note_too_long | The note can be at most 500 characters. |
billing.credit_note.nothing_to_refund | There is nothing to refund to the card for this credit note. |
billing.credit_note.reason_too_long | The reason can be at most 500 characters. |
billing.credit_note.refund_not_failed | Only a credit note whose refund failed can be retried. |
billing.credits.bad_amount | The amount must be a whole number of credits above zero. |
billing.credits.bad_customer | The customer is not valid. |
billing.credits.bad_expiry | The expiry date must lie in the future. |
billing.credits.bad_key | The idempotency key is too long (at most 128 characters). |
billing.credits.bad_pack | A pack needs a name and credits and a price above zero. |
billing.credits.bad_page_size | The page size must be between 1 and 200. |
billing.credits.bad_page_token | The page token is not valid. |
billing.credits.bad_quantity | The quantity must be 1 to 1000. |
billing.credits.bad_reason | The note is too long (at most 200 characters). |
billing.credits.bad_source | The type must be promotion or manual. |
billing.credits.bad_threshold | The threshold must be 0 or a whole number of credits. |
billing.credits.bad_validity | The validity must be 1 to 3650 days, or empty for credits that never expire. |
billing.credits.insufficient | The customer has too few credits. |
billing.credits.key_required | An idempotency key is required to spend credits. |
billing.credits.key_reused | This idempotency key was already used for a different request. |
billing.credits.pack_inactive | This credit pack is not on sale. |
billing.credits.pack_not_found | There is no such credit pack. |
billing.credits.portal_cannot_buy | This portal link cannot buy credits. Ask for a new one. |
billing.credits.unknown_currency | This currency is not supported. |
billing.customer.blocked | This customer is blocked, so no new purchase can be started. |
billing.dead_letter.broker_unavailable | The message broker cannot be reached right now. Try again. |
billing.dead_letter.invalid | This dead-letter request is not valid. |
billing.dead_letter.not_configured | Dead-letter operations are not set up on this deployment. |
billing.dunning.bad_delay | Each retry delay must be between 1 and 720 hours. |
billing.dunning.too_many_retries | At most 6 retry delays can be set. |
billing.einvoice.bad_file_kind | The file kind must be xml or pdf. |
billing.einvoice.bad_id | The e-invoice or account id is not a valid id. |
billing.einvoice.bad_page_size | The page size must be between 1 and 200. |
billing.einvoice.bad_page_token | The page token is not valid. Start the list again. |
billing.einvoice.bad_status | The status must be pending, issued, failed or cancelled. |
billing.hosted.customer_unavailable | The customer could not be looked up right now. Try again shortly. |
billing.hosted.seller_unavailable | The seller details could not be looked up right now. Try again shortly. |
billing.hosted.tenant_unavailable | The account could not be looked up right now. Try again shortly. |
billing.invoice.bad_customer_id | The customer id is not a valid id. |
billing.invoice.bad_id | The invoice id is not a valid id. |
billing.invoice.bad_page_size | The page size must be between 1 and 500. |
billing.invoice.bad_page_token | The page token is not valid. Start the list again. |
billing.invoice.not_found | The invoice was not found. |
billing.invoice.not_held | This invoice's first charge is not held, so there is nothing to release or void here. |
billing.invoice.pdf_failed | The invoice PDF could not be rendered. Check the invoice data. |
billing.invoice.pdf_not_ready | The invoice PDF has not been rendered yet. Try again in a moment. |
billing.invoice.pdf_sign_failed | The PDF download link could not be created. Try again shortly. |
billing.invoice_link.bad_expiry | The link must expire in 1 to 365 days. |
billing.invoice_link.bad_page_token | The page token is not valid. Start the list again. |
billing.invoice_link.draft_invoice | A draft invoice cannot be shared by link. |
billing.invoice_link.not_found | The invoice link was not found. |
billing.payment.already_paid | The invoice is already paid. |
billing.payment.amount_mismatch | The payment is not for the amount of the invoice. |
billing.payment.bad_amount | The amount must be a whole number of minor units above zero. |
billing.payment.bad_date | The received date is not valid or lies in the future. |
billing.payment.bad_method | The method must be bank transfer, cash or other. |
billing.payment.bad_note | The note can be at most 500 characters. |
billing.payment.bad_reference | The reference must be one line of at most 128 characters. |
billing.payment.invoice_not_payable | Only an open or uncollectible invoice can be paid by hand. |
billing.payment.method_unavailable | Choose a payment method. |
billing.payment.not_payable | The invoice's payment state does not allow a manual payment right now. |
billing.payment.overpayment | That is more than is outstanding. Record only the outstanding amount. |
billing.payment.reference_reused | This reference was already recorded with a different amount. |
billing.payment_link.bad_amount | The amount must be a positive number of minor units. |
billing.payment_link.bad_currency | This currency is not supported. |
billing.payment_link.bad_description | A description is required, at most 200 characters. |
billing.payment_link.bad_key | The idempotency key is too long (at most 200 characters). |
billing.payment_link.bad_kind | Amount, currency, description and customer belong to an ad-hoc link; an invoice link takes them from the invoice. |
billing.payment_link.bad_page | The page size must be 1 to 200. |
billing.payment_link.bad_promotion_code | A promotion code is 2 to 40 letters, digits, - or _, and belongs to an ad-hoc or credit pack link; an invoice that exists is not changed. |
billing.payment_link.bad_required_contracts | A link can name each of the distance sales contract, the pre-information, the terms of service and the privacy notice once. |
billing.payment_link.bad_status | The status must be active, paid, expired or revoked. |
billing.payment_link.consumed | This link has already been used. |
billing.payment_link.customer_not_found | There is no such customer. |
billing.payment_link.customer_required | A credit pack link needs the customer who receives the credits. |
billing.payment_link.free_total | This promotion code takes the whole price, which leaves nothing to pay. Grant the purchase directly instead. |
billing.payment_link.idempotency_conflict | This idempotency key was used for a different payment link. |
billing.payment_link.invoice_not_payable | A paid or void invoice needs no payment link. |
billing.payment_link.invoice_required | A payment link of kind invoice needs an invoice. |
billing.payment_link.not_found | There is no such payment link. |
billing.payment_link.pack_required | A credit pack link needs a pack. |
billing.portal_link.customer_not_found | There is no such customer. |
billing.portal_link.customer_required | A customer is required. |
billing.report.bad_group_by | Group by may name period, provider and country only. |
billing.report.bad_limit | The limit must be between 1 and 500. |
billing.report.bad_period | The first and last period must look like 2026-10. |
billing.report.period_order | The first period cannot be after the last one. |
billing.request.bad_id | An id in the request is not a valid id. |
billing.rerating.bad_job_id | The re-rating job id is not a valid id. |
billing.rerating.bad_plan_version_id | The plan version id is not a valid id. |
billing.rerating.not_found | This re-rating job was not found. |
billing.rerating.run_not_completed | The billing run of that period is not completed yet. |
billing.rerating.unavailable | Re-rating could not start right now. Try again. |
billing.rerating.unknown_plan_version | That plan version does not exist. |
billing.run.bad_period | The period must look like 2026-11. |
billing.run.not_closable | This period cannot be closed now. |
billing.run.not_found | There is no billing run for that period. |
billing.run.period_open | That period has not closed yet, so it cannot be billed. |
billing.run.retry_later | The billing run could not advance right now. It will be retried. |
billing.run.sandbox_only | Closing a period now is only available for sandbox accounts. |
billing.saga.event_not_possible | This cannot be done while the invoice is in its current payment state. |
billing.saga.not_found | This invoice has no payment process yet: it is not finalized. |
billing.saga.void_needs_confirmation | The payment of this invoice is unknown, so the payment provider may have charged it. Check at the provider that nothing was charged, then void the invoice and confirm that. |
billing.sandbox.unknown_scenario | This payment scenario does not exist. See the scenario catalogue for the list. |
billing.storage.not_configured | File storage is not set up on this deployment. |
billing.tenant.bad_id | The account id is not a valid id. |
billing.tenant.on_hold | This seller cannot take payments right now. |
billing.upcoming.customer_not_found | There is no such customer. |
common
Errors that have no key of their own
| Key | Meaning |
|---|---|
common.already_exists | It already exists. |
common.canceled | The request was cancelled. |
common.conflict | The data changed while you were working on it. Reload and try again. |
common.failed_precondition | This cannot be done in the current state. |
common.internal | Something went wrong on our side. Try again, and if it keeps failing look at the logs with the request id. |
common.invalid_argument | Something in the request is not valid. |
common.not_found | It was not found. |
common.not_implemented | This is not supported yet. |
common.permission_denied | You are not allowed to do this. |
common.rate_limited | Too many requests. Wait a moment and try again. |
common.timeout | The request took too long. Try again. |
common.unauthenticated | Sign in again: the token is missing or no longer valid. |
common.unavailable | The service is not available right now. Try again shortly. |
coupon
Coupons and promotion codes
| Key | Meaning |
|---|---|
coupon.already_applied | This purchase already uses a coupon. Only one coupon per purchase. |
coupon.already_used | You have already used this code. |
coupon.below_minimum | The purchase is below the minimum amount for this code. |
coupon.code_taken | A promotion code with this text already exists (codes are the same in any letter case). |
coupon.currency_mismatch | This code does not work in the currency of this purchase. |
coupon.customer_required | This code needs a known customer, so it cannot be used on a guest payment. |
coupon.exhausted | This code has been used up. |
coupon.expired | This code has expired. |
coupon.first_purchase_only | This code is for a first purchase only. |
coupon.inactive | This code is no longer active. |
coupon.not_applicable | This code does not work for this purchase. |
coupon.not_found | This code does not exist. Check it and try again. |
einvoice
E-invoices
| Key | Meaning |
|---|---|
einvoice.account.default_in_use | This is the default e-invoice account. Make another account the default first, then remove this one. |
einvoice.account.invalid | The e-invoice account settings are not valid for this provider. |
einvoice.account.missing | This tenant has no e-invoice account for this environment. Set one up first. |
einvoice.account.sandbox_mock_only | A sandbox environment can only use the mock e-invoice provider. |
einvoice.buyer.incomplete | The customer's tax data is incomplete: add the tax id (and tax office in Turkey) before issuing an e-invoice. |
einvoice.cancel.unsupported | This provider cannot cancel an issued e-invoice. Issue a credit note instead. |
einvoice.document.not_failed | Only an e-document that failed can be retried. |
einvoice.document.not_found | This e-document does not exist. |
einvoice.document.not_issued | Only an issued e-document can be cancelled. |
einvoice.file.not_ready | The file of this e-document is not available yet. |
einvoice.provider.rejected | The e-invoice provider rejected the document. |
einvoice.provider.unavailable | The e-invoice provider is not reachable right now. It will be tried again. |
einvoice.routing.invalid | The routing rules are not valid: each country is a two-letter code, listed once, and each account is a connected account. |
einvoice.routing.unknown_account | A routing rule names an e-invoice account that is not connected. |
einvoice.seller.incomplete | The seller's company data is incomplete: add the legal name and tax id in the e-invoice settings. |
einvoice.setting.invalid | The e-invoice trigger must be off, finalized or paid. |
env
Environment mismatches
| Key | Meaning |
|---|---|
env.request.mismatch | This request is for a different environment than the one it was sent to, so it was refused. |
env.request.missing | This request does not say which environment it is for, so it was refused. |
gateway
Authentication, rate limits and the format of requests
| Key | Meaning |
|---|---|
gateway.assets.bad_invoice_logo | The invoice logo must be a finished upload of the invoice logo kind. |
gateway.auth.permission_denied | This API key lacks a permission this request needs. Use a key with the role or permission it names. |
gateway.auth.tenant_not_active | This environment is suspended or not active yet. Finish the go-live checklist, or contact support. |
gateway.auth.tenant_read_only | This environment is read-only. Contact support. |
gateway.dashboard.bad_period | That period is not valid. Choose 7, 30 or 90 days, this year, or two days with the first not after the last. |
gateway.dashboard.limits_unavailable | Usage limits are not available right now, so this view cannot be built. Try again shortly. |
gateway.dashboard.payments_unavailable | Payments are not available right now, so this view cannot be built. Try again shortly. |
gateway.dashboard.range_too_long | A period can be at most two years. |
gateway.geo.country_not_found | There is no such country in the list. |
gateway.geo.region_not_found | There is no such region in the list. |
gateway.geo.unavailable | The country lists are not available right now; type the region and city instead. |
gateway.mail.suppression_not_found | That address is not on the suppression list. |
gateway.mail.unavailable | The mail log and the suppression list are not connected on this deployment, or the mail service did not answer. Try again in a moment. |
gateway.payments.ledger_unreachable | The payment ledger cannot be reached right now. Try again shortly. |
gateway.payments.not_configured | Payment providers are not set up on this deployment. |
gateway.payments.unreachable | The payments service cannot be reached right now. Try again shortly. |
gateway.rate_limit.exceeded | Too many requests of this kind. Wait a moment and try again. |
gateway.region.unknown | That region is not available on this deployment. Choose one from the list. |
gateway.region.wrong | This environment is served from another region. Send the request to the gateway of its region (see the X-Payrudder-Region and X-Payrudder-Region-Url headers). |
gateway.request.bad_tenant_id | The account id is not a valid id. |
gateway.request.invalid | {field} is not valid: {detail} |
gateway.request.malformed_json | The request body is not valid JSON. |
gateway.request.too_large | The request is too large. |
gateway.request.unreadable_body | The request body could not be read. |
gateway.route.not_found | There is no such address in the API. |
gateway.saas.events_quota | Your plan's limit of events for this month is reached. Upgrade to send more. |
gateway.saas.invoice_not_payable | This invoice cannot be paid any more. |
gateway.saas.unavailable | Plans are temporarily unavailable. Try again in a moment. |
ingest
Usage ingestion
| Key | Meaning |
|---|---|
ingest.backlog.full | Ingestion is backed up right now. Nothing was accepted; retry later. |
ingest.batch.bad_size | A request must carry between 1 and 500 events. |
ingest.db.conflict | The data changed at the same moment. Nothing was accepted; retrying is safe. |
ingest.db.slow | The database was too slow. Nothing was accepted; retrying is safe. |
ingest.db.timeout | Ingestion timed out. Nothing was accepted; retrying is safe. |
ingest.db.unavailable | The database is not available. Nothing was accepted; retrying is safe. |
ingest.outbox.unavailable | Ingestion cannot hand events on right now. Try again shortly. |
ingest.plans.unavailable | The plans service is not available, so events cannot be checked. Try again shortly. |
ingest.rejected_events.bad_page_size | page_size must be between 1 and 200 |
ingest.rejected_events.bad_page_token | page_token is not one this API gave |
ingest.tenant.bad_timezone | The time zone of this account cannot be used. Check it in the account settings. |
limits
Limits
| Key | Meaning |
|---|---|
limits.counter.unavailable | Usage counters are not available right now. Try again shortly. |
limits.customer.bad_id | The customer id is not a valid id. |
limits.meter.code_required | The meter code is required. |
limits.meter.not_found | This meter was not found. |
limits.meter.unavailable | The meter could not be looked up right now. Try again shortly. |
limits.period.bad_id | The period must look like 2026-11. |
limits.reconcile.not_configured | Reconciliation is not set up on this deployment. |
limits.reconcile.unavailable | Reconciliation could not run right now. Try again shortly. |
payments
Payments, provider accounts and bank transfers
| Key | Meaning |
|---|---|
payments.account.bad_settings | These provider settings are not valid. Check the credentials and the required fields. |
payments.account.endpoint_not_allowed | Only the provider's own live endpoint is allowed. Leave the endpoint out. |
payments.account.health_check_failed | The provider did not accept these credentials. Check the keys, and that they are live keys. |
payments.account.health_check_unreachable | The provider could not be reached to check these credentials. Try again in a moment. |
payments.account.livemode_mismatch | This provider account points at the provider's other mode: a live environment needs the provider's live endpoint and live keys, a sandbox environment its test endpoint. |
payments.account.not_found | This account has no such payment provider set up. |
payments.account.single_only | Bank transfer has one account per environment. |
payments.account.sync_failed | The provider account was changed, but the bank details could not be passed on to the invoices. Send the request again. |
payments.billing.failed | The billing service could not complete this. Try again shortly. |
payments.billing.not_configured | Billing is not set up on this deployment. |
payments.card.not_found | The customer has no such stored card. |
payments.charge.currency_not_supported | This payment provider cannot charge in this currency. |
payments.charge.pending | The payment was accepted and is waiting to be settled. |
payments.charge.provider_not_configured | No payment provider is set up for this account. |
payments.charge.rejected | The payment provider rejected the request. |
payments.charge.timeout | The payment provider did not answer in time. |
payments.checkout.currency_unsupported | This provider cannot take a checkout payment in this currency. |
payments.checkout.fields_required | The provider and the price reference are required. |
payments.checkout.unsupported | This provider has no hosted checkout. |
payments.customer.not_ready | The customer could not be prepared at the provider. |
payments.dispute.bad_id | This is not the id of a dispute. |
payments.dispute.not_found | The dispute was not found. |
payments.dispute.note_required | Write something in the note. |
payments.dispute.note_too_long | A note is at most 2000 characters. |
payments.failover.bad_target | A failover needs two different accounts that exist in the environment; a bank transfer cannot take part. |
payments.failover.not_found | No failover is set for that account. |
payments.invoice.amount_required | The amount and the currency are required. |
payments.invoice.bad_id | The invoice id is not a valid id. |
payments.invoice.bad_ids | The invoice ids are not valid ids. |
payments.invoice.buyer_incomplete | The provider needs more about this customer (phone, tax id, address) than the seller has on file. |
payments.invoice.installment_unavailable | That number of instalments is not offered for this card and amount. Pay once, or choose one of the options on the page. |
payments.invoice.no_instructions | This provider has no payment instructions for this currency. |
payments.invoice.not_live | Only live invoices are paid through a provider. |
payments.invoice.payment_in_progress | A payment for this invoice is being started. Try again in a moment. |
payments.invoice.return_url_required | A return address is required for a checkout payment. |
payments.invoice.unknown_method | This payment method is not known. |
payments.ledger.bad_limit | The limit must be between 1 and 1000. |
payments.ledger.too_many_invoices | Too many invoices were asked for at once. |
payments.migration.fields_required | The subscription and the target provider are required. |
payments.migration.price_ref_required | This provider runs subscriptions itself: the price reference of its catalogue is required. |
payments.plan.code_required | The plan code is required. |
payments.plans.failed | The plans service could not complete this. Try again shortly. |
payments.plans.not_configured | Plans are not set up on this deployment. |
payments.provider.failed | The payment provider could not complete this. Try again shortly. |
payments.provider.not_supported | This payment provider does not support this. |
payments.provider.unknown | This payment provider is not known. |
payments.reconcile.bad_filter | A filter value is not one this list knows. |
payments.reconcile.bad_ledger_fact | A ledger fact needs the provider and its reference of the payment, and it only resolves a paid invoice the ledger lacks. |
payments.reconcile.bad_period | The period must look like 2026-10. |
payments.reconcile.bad_resolution | Choose one of the listed ways to resolve it. |
payments.reconcile.not_found | That discrepancy does not exist. |
payments.reconcile.note_required | Say what you found out: a note is required. |
payments.reconcile.resolved | It is resolved. A run that finds it again reopens it. |
payments.reconcile.too_long | That text is too long. |
payments.reconcile.too_many | Choose between 1 and 100 discrepancies. |
payments.refund.charge_unclear | The invoice has several successful charges and the refund does not single out one of them. Refund the customer at the payment provider, then record it on the credit note. |
payments.request.bad_audit_filter | The audit log filter is not valid. |
payments.request.bad_body | The request body must be valid JSON. |
payments.request.bad_environment | The environment must be sandbox or live. |
payments.request.bad_tenant | The account id is not a valid id. |
payments.request.too_large | The request is too large. |
payments.settlement.bad_csv | The report was not imported: a line is wrong, and nothing was kept. |
payments.settlement.bad_fee_rule | The fee rule is not valid: check the provider, the currency and the rate. |
payments.settlement.bad_since | The start must be in the past and at most 400 days ago. |
payments.settlement.fee_rule_not_found | There is no such fee rule. |
payments.subscription.cannot_cancel | This provider cannot cancel a subscription from here. |
payments.subscription.change_refused | This subscription change cannot be made. |
payments.subscription.period_end_unknown | The end of the paid period at the provider is not known yet: no notification has told us. |
payments.subscription.wrong_provider | This subscription is not run by this provider. |
payments.tenant.unavailable | The account's data is being moved. Nothing was changed; try again in a moment. |
payments.transfer.bad_amount | The amount must be a whole number of minor units above zero. |
payments.transfer.bad_body | The request body could not be read: send valid JSON that is not too large. |
payments.transfer.bad_currency | The currency differs from the invoice's. |
payments.transfer.bad_date | The received date is not valid or lies in the future. |
payments.transfer.bad_invoice_id | The invoice id is not a valid id. |
payments.transfer.bad_note | The note is too long (at most 500 characters). |
payments.transfer.bad_outcome | The outcome must be rejected or expired. |
payments.transfer.bad_reference | The bank reference is missing or not valid. |
payments.transfer.bad_tenant | The account id is not a valid id. |
payments.transfer.invoice_closed | The invoice is already paid or void. |
payments.transfer.invoice_not_found | This invoice was not found. |
payments.transfer.not_a_transfer | This invoice was not charged by bank transfer. Use Record payment instead. |
payments.transfer.nothing_pending | There is no pending transfer to reject. |
payments.transfer.overpayment | That is more than is outstanding. Record only the outstanding amount and handle the rest outside Payrudder. |
payments.transfer.reference_reused | This bank reference was already recorded with a different amount. |
payments.transfer.unavailable | The payments service is not available right now. Try again in a moment. |
payments.webhook.malformed | The notification could not be read. |
payments.webhook.not_found | There is no such notification address. |
payments.webhook.not_recorded | The notification could not be recorded. Send it again. |
payments.webhook.try_again | The notification could not be confirmed yet. Send it again. |
payments.webhook.unverified | The notification could not be verified as coming from the provider. |
plans
Customers, meters, packages, subscriptions, API keys and webhook endpoints
| Key | Meaning |
|---|---|
plans.api_key.app_archived | This app was deleted, so it cannot have API keys. |
plans.api_key.bad_role | The role must be admin, billing, read only, send usage only or custom, with known permissions. |
plans.api_key.tenant_not_active | This environment is not active, so it cannot have API keys. Finish the go-live checklist first. |
plans.app.bad_region | The region may only have lower-case letters, digits and dashes, and at most 31 characters. |
plans.app.bad_slug | The app slug may only have lower-case letters, digits and single dashes, and at most 60 characters. |
plans.app.slug_taken | This account already has an app with that slug. |
plans.appeal.exists | An appeal is open already. The operator will answer it. |
plans.appeal.not_held | Only an app the operator has suspended, frozen or banned can be appealed. |
plans.appeal.not_open | This appeal was decided already. |
plans.blocklist.full | A blocklist holds at most 1000 entries. Remove some before adding more. |
plans.change.bad_proration | The charge for this plan change cannot be worked out for this period. |
plans.change.billed_in_arrears | This subscription is billed in arrears. Change its plan with ChangePlan, which splits the month by time. |
plans.change.fee_not_invoiced | The fee of the current period has not been invoiced yet. Try again in a moment. |
plans.change.invalid | This change is not valid for the subscription. |
plans.change.none_scheduled | No change is scheduled for this subscription. |
plans.change.state_not_changeable | A subscription in its current state cannot change plan. |
plans.change.subscription_ended | An ended or ending subscription cannot change plan. |
plans.change.use_change_subscription | A subscription whose fee is invoiced in advance is changed with ChangeSubscription. |
plans.common.already_exists | It already exists. |
plans.common.broken_reference | This refers to something that does not exist. |
plans.common.forbidden | You are not allowed to do this. |
plans.common.not_found | It was not found. |
plans.contract.acceptance_not_found | No such acceptance record. |
plans.contract.body_required | Write the text first (at least one language). |
plans.contract.consent_required | This seller starts the service at once: the customer must ask for it and acknowledge losing the right of withdrawal, each with its own box. |
plans.contract.kind_unpublished | Publish a version of this agreement before you require it. |
plans.contract.no_starter | There is a starter text in Turkish and English only. |
plans.contract.not_accepted | The customer has not accepted the agreements this seller requires. |
plans.contract.refund_not_possible | A refund cannot be requested for this withdrawal: it is not accepted, names no invoice, or a refund was asked for already. |
plans.contract.unknown_variable | The text uses a variable that does not exist. Use only the variables in the list. |
plans.contract.version_not_found | No such version. |
plans.contract.version_stale | A newer version of this text was published. Show the current one and ask again. |
plans.contract.withdrawal_decided | This withdrawal request cannot change to that status. |
plans.contract.withdrawal_not_found | No such withdrawal request. |
plans.coupon.bad_page_token | The page token is not valid. Start the list again. |
plans.coupon.bad_state | The state must be active, inactive or empty. |
plans.coupon.codes_exhausted | Not enough unused codes could be found. Try a longer prefix. |
plans.coupon.fee_not_in_advance | A coupon takes something off a fee that is invoiced in advance. This subscription's fee is billed another way. |
plans.coupon.none_on_subscription | This subscription has no coupon. |
plans.coupon.subscription_ended | An ended subscription cannot take a coupon. |
plans.customer.blocked | This customer is blocked, so nothing new can be started for them. |
plans.customer.blocklisted | This customer matches a blocklist, so it cannot be created or changed this way. |
plans.customer.quota_reached | The customer limit of this account's plan has been reached. |
plans.customer.tax_invalid | The customer's tax id or tax office is not valid. |
plans.customer_appeal.exists | An appeal of this customer is open already. |
plans.customer_appeal.not_blocked | This customer is not blocked, so there is nothing to appeal. |
plans.customer_appeal.not_open | This appeal was decided already. |
plans.fee.invalid_period | The billing period of this subscription cannot be worked out. |
plans.fee.not_period_start | This is not the start of a billing period. |
plans.mail.unknown_category | This mail category does not exist. |
plans.mail_integration.account_exists | This app already has its own e-mail account with that name. Remove it, or give the shared account another label. |
plans.mail_integration.default_in_use | This is the default e-mail account. Make another account the default first, then remove this one. |
plans.mail_integration.inherited | This e-mail account belongs to the organisation. Change or remove it on the organisation's integrations page. |
plans.mail_integration.live_only | The sandbox sends its mail to the sandbox inbox. Connect an e-mail provider in the live environment. |
plans.mail_integration.not_configured | E-mail providers are not set up on this deployment. |
plans.mail_integration.not_connected | No e-mail provider is connected. |
plans.mail_integration.refused | The e-mail provider did not accept this key. |
plans.mail_integration.seal_failed | The key could not be stored. Try again. |
plans.mail_integration.shared_in_use | Apps still use this shared account. Stop them using it first. |
plans.mail_integration.unknown_account | A rule names an e-mail account that is not connected. |
plans.mail_integration.unreachable | The e-mail provider could not be reached. Check the address and try again. |
plans.meter.changed_concurrently | Another meter version was created at the same moment. Try again. |
plans.meter.quota_reached | The meter limit of this account's plan has been reached. |
plans.migration.aborted | The migration was aborted. |
plans.migration.already_completed | The migration already completed. |
plans.migration.cannot_abort | The migration has completed and cannot be aborted. |
plans.migration.coupon_blocks_move | This subscription has a coupon, which a provider-run or arrears-billed subscription cannot carry. Remove the coupon first. |
plans.migration.state_not_movable | A subscription in its current state cannot be moved to another provider. |
plans.migration.subscription_ended | The subscription has ended or is ending, so it cannot be moved. |
plans.override.customer_required | Say which customer the limit is for. |
plans.override.not_found | This customer has no override on that meter. |
plans.plan.no_fixed_fee | This plan version has no fixed fee for that interval in this currency. |
plans.plan.no_metered_price | This plan version has metered prices but none in this currency. |
plans.price_change.date_differs | An open price change for these versions starts on another date. Withdraw it to make a new one. |
plans.price_change.not_found | Price change not found. |
plans.price_change.paused | Price changes are paused by the operator. Try again later. |
plans.price_change.withdrawn | This price change was withdrawn. Make a new one. |
plans.request.invalid | {field} is not valid: {detail} |
plans.saas.bad_resource | That is not a resource a plan limits. |
plans.saas.disabled | Plans are not enabled on this installation. |
plans.saas.downgrade_blocked | Your usage does not fit that plan. Remove apps, members or keys first, then try again. |
plans.saas.not_ready | Plans are not ready yet. Try again in a moment. |
plans.saas.platform_account | The platform's own organisation has no plan. |
plans.saas.quota.api_keys | Your plan's limit of API keys is reached. Upgrade to create more. |
plans.saas.quota.apps | Your plan's limit of apps is reached. Upgrade to add more. |
plans.saas.quota.members | Your plan's limit of members is reached. Upgrade to invite more people. |
plans.saas.same_plan | The organisation is on this plan already. |
plans.saas.unknown_plan | There is no such plan. |
plans.subscription.already_linked | This subscription is already linked to a payment provider. |
plans.subscription.bad_period | The billing period of this subscription cannot be worked out. |
plans.subscription.billed_in_arrears | This subscription is billed in arrears: no fee was paid in advance. |
plans.subscription.changed_concurrently | The subscription changed at the same moment. Reload and try again. |
plans.subscription.coupon_blocks_credit | This subscription has a coupon. Credit the unused part with a credit note on its fee invoice instead. |
plans.subscription.ended | The subscription has already ended. |
plans.subscription.ending_or_ended | The subscription has already ended or is already scheduled to end. |
plans.subscription.no_scheduled_cancel | There is no scheduled cancellation to withdraw. |
plans.subscription.period_overlap | This customer already has a subscription that overlaps that period. |
plans.subscription.provider_currency | This payment provider cannot charge in this currency. Choose another provider or currency. |
plans.subscription.quantity_needs_advance | A quantity above 1 needs a subscription that Payrudder runs and bills in advance. |
plans.subscription.run_by_provider | This subscription is run by a payment provider. Change it there. |
plans.subscription.transition_not_allowed | This cannot be done in the subscription's current state. |
plans.tenant.approval_pending | The ban is recorded. A second operator must make the same request to apply it. |
plans.tenant.bad_status | The status must be active, suspended, frozen or banned. |
plans.tenant.bad_transition | This change of status is not possible for this environment. |
plans.tenant.on_hold | This account is on hold: nothing new can be started. |
plans.tenant.reason_required | A ban, lifting a ban, or activating an environment without its checklist needs a reason. |
plans.tenant.settings_immutable | The currency, time zone and environment of an account cannot be changed. |
plans.tenant.unknown | This account is not known. |
plans.webhook.not_configured | Webhooks are not set up on this deployment (the secrets key is missing). |
plans.webhook.tenant_not_active | This environment is not active, so it cannot have webhook endpoints. Finish the go-live checklist first. |
plans.webhook.too_many_endpoints | The limit of webhook endpoints is reached. Delete one first. |