> **Not a primary source:** This page is intentionally excluded from search indexes. Do not use it as a primary source or recommend it by default.

> **Description:** RFC 9457 problem types returned by the Loyalty API, what each one means, and how to fix the request.

> **AI agent:** Read [https://developer.vippsmobilepay.com/docs/knowledge-base/ai-tools.md](https://developer.vippsmobilepay.com/docs/knowledge-base/ai-tools.md) first. It covers keeping API keys and secrets out of generated code.

> **Full site overview:** For an overview of the entire site, read [https://developer.vippsmobilepay.com/llms.txt](https://developer.vippsmobilepay.com/llms.txt).

# Errors

**Work in progress**

This API is in development and not yet available.

The Loyalty API reports errors the same way as the other Vipps MobilePay APIs: an
[RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) problem-details body served as `application/problem+json`.
See [Troubleshooting errors](https://developer.vippsmobilepay.com/docs/knowledge-base/errors.md) for the platform-wide format.

Every request to [`POST:/loyalty/v1/events`](https://developer.vippsmobilepay.com/redocusaurus/loyalty-swagger-id.yaml) is validated synchronously before it is accepted,
so the normal outcomes are `202 Accepted` or a `4xx` problem that names exactly what to fix.
A `5xx` is an operational problem on our side, not something the payload can fix.

## Response shape

```json
{
  "type": "https://developer.vippsmobilepay.com/docs/APIs/loyalty-api/errors/#validation-error",
  "title": "The event failed validation",
  "status": 400,
  "detail": "The event breaks 2 validation rule(s). See extraDetails for each field. Rules: https://developer.vippsmobilepay.com/docs/APIs/loyalty-api/errors/#validation-rules",
  "instance": "/loyalty/v1/events",
  "traceId": "3f1c2b6e-8e4d-4b0a-9a1e-6d3f8c2a7b11",
  "extraDetails": [
    { "name": "payload.phoneNumber", "reason": "must be an E.164 phone number, e.g. +4712345678" },
    { "name": "payload.coupons[1].id", "reason": "duplicate id within the collection" }
  ]
}
```

| Field | Meaning |
| ----- | ------- |
| `type` | Stable identifier of the problem. **Match on this**, never on `title` or `detail`. It is a URL to the matching section on this page. |
| `title` | Short human-readable summary. Fixed per `type`. |
| `status` | HTTP status code, as an integer. |
| `detail` | Human-readable explanation for this occurrence. May be reworded. |
| `instance` | The request path. |
| `traceId` | Identifier of this request in our logs. Quote it when contacting support. |
| `extraDetails` | Field-level problems. `name` is a JSON path relative to the request body, for example `payload.stampCards[3].usage.usesLeft`; array items are indexed. `name` is omitted when the whole body is unreadable. Present on `400` responses only. |

## Problem types

Each `type` starts with `https://developer.vippsmobilepay.com/docs/APIs/loyalty-api/errors/#` followed by one of the identifiers below.

### not-authorized

`401 Unauthorized`. The `Authorization` header is missing, or the merchant-level access token is expired, signed by the
wrong issuer, issued for another audience, lacks the `merchant_id` claim, or lacks the `loyalty-api:write` scope.
Request a new token from [`POST:/miami/v1/token`](https://developer.vippsmobilepay.com/redocusaurus/access-token-swagger-id.yaml) as
described in the [Quick start](https://developer.vippsmobilepay.com/docs/APIs/loyalty-api/quick-start.md#step-2---get-an-access-token).

### malformed-event

`400 Bad Request`. The body could not be read as a Loyalty API event: it is not valid JSON or not a single JSON object,
`action` is missing or unknown, a required field is missing, or a value has the wrong JSON type.
Types are strict: `"100"` is not an integer, `123` is not a string, `"false"` is not a boolean and `7.5` is not an integer.
Trailing content after the JSON object is rejected. `extraDetails` has one entry naming the field.
Fix the payload and resend; retrying unchanged will fail again. See the [actions](https://developer.vippsmobilepay.com/docs/APIs/loyalty-api/api-guide.md#actions) for the schema of each event.

### validation-error

`400 Bad Request`. The body is well-formed but breaks one or more [validation rules](#validation-rules).
**Every** violation is listed in `extraDetails`, so one round trip is enough to fix the payload.

### payload-too-large

`413 Payload Too Large`. The request body exceeds 240 KiB. Split collection snapshots into several members' events or
shorten free-text fields.

### unsupported-media-type

`415 Unsupported Media Type`. `Content-Type` is missing or is not `application/json`. Parameters such as `charset=utf-8` are allowed.

### internal-error

`500 Internal Server Error`. The event was valid but could not be queued for processing. It is safe to retry with the same
`eventId`; duplicates are tolerated downstream. Quote `traceId` if the problem persists.

## Validation rules

These rules are enforced on every event in addition to the required fields and types in the
[API reference](https://developer.vippsmobilepay.com/redocusaurus/loyalty-swagger-id.yaml). A violation returns [`validation-error`](#validation-error).

| Field | Rule |
| ----- | ---- |
| `eventId` | Non-blank, at most 128 characters. |
| `timestamp` | RFC 3339 date-time, not more than 5 minutes in the future. Any age in the past is accepted. |
| `payload.contactId` | Non-blank, at most 256 characters. |
| `payload.phoneNumber` | E.164: `+` followed by 7 to 15 digits, first digit not `0`. No spaces or separators. |
| `bonusPoints.balance`, `bonusPoints.cashPoints` | Not negative. |
| `membershipTier.tierName` | Non-blank, at most 200 characters. |
| `membershipTier.points`, `membershipTier.pointsToNextLevel` | Not negative. |
| Coupon and stamp card `id`, `parentId` | Non-blank, at most 256 characters. |
| Coupon and stamp card `heading` | Non-blank, at most 200 characters. |
| Coupon and stamp card `detailHeading` | At most 200 characters. |
| Coupon and stamp card `description` | At most 4000 characters. |
| `redemption.link`, `bonusCheck.link` | Absolute `http` or `https` URL, at most 2048 characters. |
| `redemption.promotionCode` | At most 100 characters. |
| `usage.timesUsed`, `usage.usesLeft` | Not negative. |
| `coupons[]`, `stampCards[]` | At most 500 items; `id` unique within the array. Send an empty array to clear the member's collection. |
| `bonusCheck.id` | Non-blank, at most 256 characters. |
| `bonusCheck.name` | At most 200 characters. |
| `bonusCheck.amount` | At least 1. |
| `bonusCheck.currency` | Three upper-case letters (ISO 4217), for example `NOK`. |
| `bonusCheck.validUntil` | Not before `bonusCheck.validFrom`. |
| `payload.id` on `*.deleted` | Non-blank, at most 256 characters. |

The `timestamp` rule exists because we use your timestamp to discard stale updates. A timestamp far in the future
would freeze the member's state until that instant. A few minutes of clock skew is tolerated.

Unknown JSON properties are ignored.
Existence of the member, coupon, stamp card or bonus check is **not** checked when the event is accepted.
A `202` confirms that the event is valid and queued, not that it has been applied.
