# Errors

When a call fails, the answer is a problem: a JSON body sent as `application/problem+json`, with a `code`.

Branch on `code`. The `title` and `detail` are for people to read.

If you contact support, quote the `request_id`. It is also sent as the `Request-Id` header on every answer, failed or not.

## The shape of a problem

| Field | Type | Always there | What it is |
| --- | --- | --- | --- |
| `type` | string | yes | A page on the documentation site for this code: `https://matrixrewards.co.uk/docs/errors/<code>`. |
| `title` | string | yes | A short summary of the code, for people. Branch on `code`, not on this. |
| `status` | integer | yes | The HTTP status of the answer, repeated in the body. |
| `code` | string | yes | What went wrong, as a word a program can branch on. One of `invalid_request`, `invalid_cursor`, `unauthenticated`, `forbidden_scope`, `not_found`, `unsupported_version`, `rate_limited`, `internal`, `access_not_recorded`, `payload_too_large`, `idempotency_key_reused`, `idempotency_key_in_use`, `confirmation_required`, `confirmation_invalid`, `test_mode_key`, `credit_limit_reached`, `member_has_no_store`, `code_already_used`, `store_not_connected`, `upstream_failed`, `change_not_recorded`, `forbidden_for_assistant`, `webhook_limit_reached`, `settings_changed`, `retroactive_change`, `programme_write_limit`, `last_reward`, `offer_read_only`, `offer_ended`, `programme_not_set_up`, `assistant_limit`, `invalid_image`, `too_many_images`, `shopify_file_unavailable`, `previous_secret`. |
| `detail` | string | no | What happened, in a sentence for people. |
| `instance` | string | no | The path that was called. |
| `param` | string | no | The field, header or query parameter at fault, when there is exactly one. |
| `request_id` | string | yes | This request's id, also sent as the `Request-Id` header. Quote it if you contact support. |
| `doc_url` | string | yes | The same page as `type`: this code's page on the documentation site. |
| `bucket` | one of `read`, `write` | no | With `rate_limited`: which allowance ran out, reads or writes. |
| `required_scope` | one of the [`Scope` values](https://matrixrewards.co.uk/docs/authentication.md#permissions) | no | With `forbidden_scope`: the scope this call needs, which the key does not hold. |
| `key_scopes` | list of [`Scope` values](https://matrixrewards.co.uk/docs/authentication.md#permissions) | no | With `forbidden_scope`: every scope this key holds. |
| `supported_versions` | list of string | no | With `unsupported_version`: the versions this platform serves. |
| `max_bytes` | integer | no | With `payload_too_large`: the largest body accepted, in bytes. |
| `max_points` | integer | no | With `invalid_request` on `delta_points`: the largest change allowed, either way. |
| `max_chars` | integer | no | With `invalid_request` on `reason`: the longest reason allowed. |
| `max_endpoints` | integer | no | With `webhook_limit_reached`: how many enabled endpoints an account may have. |
| `reason` | string | no | With `confirmation_invalid`: `expired` (preview again), `wrong_request` (the key, member, body or Idempotency-Key differ from the preview) or `malformed` (not a confirmation this API issued). With `invalid_request` on a webhook `url`: what is wrong with the address, as listed under `WebhookEndpointRequest`. One of `malformed`, `expired`, `wrong_request`, `not_a_url`, `too_long`, `not_https`, `port_not_allowed`, `has_credentials`, `has_fragment`, `blocked_name`, `own_host`, `blocked_address`, `does_not_resolve`. |
| `direction` | one of `credit`, `debit` | no | With `credit_limit_reached`: `credit` for points added, `debit` for points taken away. |
| `limit_points` | integer | no | With `credit_limit_reached`: the account's limit in this direction. |
| `used_points` | integer | no | With `credit_limit_reached`: what the account has already moved in this direction in the window. |
| `upstream_reason` | string | no | With `upstream_failed`: why Shopify's answer did not confirm the code was switched off. |
| `problem` | string | no | With `invalid_request` or `retroactive_change` from a programme change: the platform's own reason, like `campaign_starts_in_past` or `retroactive_start`. With `assistant_limit`, which limit: `point_value_locked`, `currency_locked`, `expiry_locked`, `more_than_double`, `raised_from_zero`, `earning_more_than_double`, `everyday_rate_moved`, `changes_how_it_pays`, `changes_who_it_pays`, `changes_what_it_gives`, `minimum_more_than_halved`, `new_offer_kind`, `new_reward_too_generous` or `new_reward_kind`. |
| `current_effective_from` | string, or null | no | With `retroactive_change`: when the expiry rule shoppers were told about took effect, or null when there is none. |
| `current_period_months` | integer, or null | no | With `retroactive_change`: that rule's period in months, or null when there is none. |
| `before` | number or string or boolean, or null | no | With `assistant_limit`: the figure at the assistant's starting point, in the unit of `param` (a rate, points, an amount, a percentage, a kind; true or false for whether referrals run). |
| `asked` | number or string or boolean, or null | no | With `assistant_limit`: what the change asked for, in the same unit. |
| `most_allowed` | number or string or boolean, or null | no | With `assistant_limit`, when the figure was raised: the most it may rise to. Null where no value of it would do. |
| `least_allowed` | number or string or boolean, or null | no | With `assistant_limit`, when a cost or a smallest order was lowered, or the everyday rate lowered while an offer rose: the least it may fall to. Null where no value of it would do. |
| `limit` | integer | no | With `programme_write_limit`: how many programme changes the account may make through the API in the window. |
| `used` | integer | no | With `programme_write_limit`: how many it has made in the window. |

## Every code

| Code | Status | What it is | Safe to send again? |
| --- | --- | --- | --- |
| [`invalid_request`](https://matrixrewards.co.uk/docs/errors/invalid_request.md) | 400 | The request could not be understood | Fix it first |
| [`invalid_cursor`](https://matrixrewards.co.uk/docs/errors/invalid_cursor.md) | 400 | The cursor is not one this endpoint issued | Fix it first |
| [`unauthenticated`](https://matrixrewards.co.uk/docs/errors/unauthenticated.md) | 401 | No usable API key was presented | Fix it first |
| [`forbidden_scope`](https://matrixrewards.co.uk/docs/errors/forbidden_scope.md) | 403 | This key does not carry the scope this call needs | Fix it first |
| [`not_found`](https://matrixrewards.co.uk/docs/errors/not_found.md) | 404 | No such resource | Fix it first |
| [`unsupported_version`](https://matrixrewards.co.uk/docs/errors/unsupported_version.md) | 404 | That API version is not served here | Fix it first |
| [`rate_limited`](https://matrixrewards.co.uk/docs/errors/rate_limited.md) | 429 | Too many requests for this key | Yes, after waiting |
| [`internal`](https://matrixrewards.co.uk/docs/errors/internal.md) | 500 | Something went wrong at our end | Yes, unchanged |
| [`access_not_recorded`](https://matrixrewards.co.uk/docs/errors/access_not_recorded.md) | 503 | The access could not be recorded, so the data was not returned | Yes, unchanged |
| [`payload_too_large`](https://matrixrewards.co.uk/docs/errors/payload_too_large.md) | 413 | The request body is larger than this endpoint accepts | Fix it first |
| [`idempotency_key_reused`](https://matrixrewards.co.uk/docs/errors/idempotency_key_reused.md) | 422 | This Idempotency-Key was already used for a different request | Fix it first |
| [`idempotency_key_in_use`](https://matrixrewards.co.uk/docs/errors/idempotency_key_in_use.md) | 409 | A request with this Idempotency-Key is still being processed | Yes, after waiting |
| [`confirmation_required`](https://matrixrewards.co.uk/docs/errors/confirmation_required.md) | 428 | This key must preview the change and send back its confirmation | Fix it first |
| [`confirmation_invalid`](https://matrixrewards.co.uk/docs/errors/confirmation_invalid.md) | 428 | The confirmation does not match this request, or has expired | Fix it first |
| [`test_mode_key`](https://matrixrewards.co.uk/docs/errors/test_mode_key.md) | 403 | A test key can preview a change but never make one | No |
| [`credit_limit_reached`](https://matrixrewards.co.uk/docs/errors/credit_limit_reached.md) | 409 | The daily limit for points changed through the API has been reached | No |
| [`member_has_no_store`](https://matrixrewards.co.uk/docs/errors/member_has_no_store.md) | 409 | This member is not linked to a store the change could be booked to | No |
| [`code_already_used`](https://matrixrewards.co.uk/docs/errors/code_already_used.md) | 409 | The code has already been spent at checkout | No |
| [`store_not_connected`](https://matrixrewards.co.uk/docs/errors/store_not_connected.md) | 503 | The store this code belongs to is not connected | Fix it first |
| [`upstream_failed`](https://matrixrewards.co.uk/docs/errors/upstream_failed.md) | 502 | Shopify did not confirm the code was switched off, so nothing was changed | Yes, unchanged |
| [`change_not_recorded`](https://matrixrewards.co.uk/docs/errors/change_not_recorded.md) | 503 | The change could not be recorded, so it was not made | Yes, unchanged |
| [`forbidden_for_assistant`](https://matrixrewards.co.uk/docs/errors/forbidden_for_assistant.md) | 403 | A key for an assistant cannot manage webhooks | No |
| [`webhook_limit_reached`](https://matrixrewards.co.uk/docs/errors/webhook_limit_reached.md) | 409 | This account already has as many webhook endpoints as it may | Fix it first |
| [`settings_changed`](https://matrixrewards.co.uk/docs/errors/settings_changed.md) | 409 | The programme changed after it was read, so nothing was changed | Fix it first |
| [`retroactive_change`](https://matrixrewards.co.uk/docs/errors/retroactive_change.md) | 422 | The expiry change would reach back before shoppers were told | Fix it first |
| [`programme_write_limit`](https://matrixrewards.co.uk/docs/errors/programme_write_limit.md) | 429 | The daily limit for programme changes through the API has been reached | No |
| [`last_reward`](https://matrixrewards.co.uk/docs/errors/last_reward.md) | 409 | The store's only reward cannot be removed | Fix it first |
| [`offer_read_only`](https://matrixrewards.co.uk/docs/errors/offer_read_only.md) | 409 | This rule is not one the API changes | No |
| [`offer_ended`](https://matrixrewards.co.uk/docs/errors/offer_ended.md) | 409 | The campaign has ended, so it can only be removed | No |
| [`programme_not_set_up`](https://matrixrewards.co.uk/docs/errors/programme_not_set_up.md) | 409 | This store has no programme set up yet | Fix it first |
| [`assistant_limit`](https://matrixrewards.co.uk/docs/errors/assistant_limit.md) | 403 | An assistant's key cannot make a change this large | Fix it first |
| [`invalid_image`](https://matrixrewards.co.uk/docs/errors/invalid_image.md) | 422 | The picture is not one a store can use | Fix it first |
| [`too_many_images`](https://matrixrewards.co.uk/docs/errors/too_many_images.md) | 409 | The store already keeps as many pictures as it may | Fix it first |
| [`shopify_file_unavailable`](https://matrixrewards.co.uk/docs/errors/shopify_file_unavailable.md) | 502 | Shopify did not send the picture | Yes, unchanged |
| [`previous_secret`](https://matrixrewards.co.uk/docs/errors/previous_secret.md) | 403 | A key's previous secret cannot create a webhook endpoint | Fix it first |

---

Page: https://matrixrewards.co.uk/docs/errors
Contract: 2026-10 sha256:35af153960028890fea5738799f5a71e535e1eea4dd3f6b8d5f50b37f02363da
