---
title: Errors
description: The structure of an error response from the Seamless OS API, and what each status code means.
---

The Seamless OS API answers with a standard HTTP status code and a structured error body. The
body names the fault in a form that a person can read and in a form that your code can match
on.

## Error response structure

Every error response has the same structure:
```json
{
  "message": "Validation failed",
  "internalCode": "4240",
  "code": "invalid_input",
  "details": [
    {
      "message": "email is required",
      "code": "invalid_argument",
      "property": "contact.email"
    },
    {
      "message": "msisdn is not a phone number",
      "code": "invalid_argument",
      "property": "subscriber.msisdn"
    }
  ],
  "hint": "Correct the fields that the details name, then send the request again.",
  "traceId": "cc4a73acca1bb07e0e54bd41f5ce1e7e",
  "spanId": "37cec694d3b99f0f"
}
```

## Error fields

**`message`** (required): A description of the fault for a developer to read in a log or in a
console.

**`internalCode`**: A string of digits that names the condition that failed. It comes from our own
registry, so the same condition always carries the same code. It is independent of the HTTP
status and of which system reported the fault, and it is stable across releases. Branch on this
field. An unexpected fault on our side can carry no `internalCode`. Then use the HTTP status.

**`code`** (required, deprecated): Use `internalCode` instead. This field mixes three unrelated
codes, and it does not say which one you have. The three are a code that we publish, an
operator's own code, and the request status.

**`details`**: A list of the individual faults. Each entry carries these fields:

- `message`: A description of the one fault for a person to read.
- `code`: The request status of the one fault, such as `invalid_argument`.
- `property`: The field or the parameter that caused the fault. A nested field uses dot
  notation, such as `billing.email`.
- `suggestion`: A correct value, when the API can propose one.

**`hint`**: One more sentence about how to correct the request.

**`traceId`** and **`spanId`**: The trace that your request produced, and the span inside it that
failed. Quote the trace identifier when you report a fault to us. It is what lets us find your
request among everything else the platform served.

## HTTP status codes

The status code gives the category of the fault:

| Status Code | Description                                                                                                       |
| ----------- | ----------------------------------------------------------------------------------------------------------------- |
| `400`       | **Bad Request** - Invalid request syntax, or a validation fault                                                   |
| `401`       | **Unauthorized** - The authentication credentials are absent or invalid                                           |
| `403`       | **Forbidden** - The credentials are valid, but the permissions are not sufficient                                 |
| `404`       | **Not Found** - The requested resource does not exist                                                             |
| `409`       | **Conflict** - The request conflicts with the current state, such as a reused idempotency key with different data |
| `412`       | **Precondition Failed** - The resource is not in a state that allows this request                                 |
| `429`       | **Too Many Requests** - You reached the rate limit                                                                |
| `500`       | **Internal Server Error** - An unexpected fault on our side                                                       |
| `501`       | **Not Implemented** - The endpoint is not available for your brand                                                |
| `503`       | **Service Unavailable** - A system that we depend on did not answer. Send the request again                       |
