# Errors

## v1 response envelope

Every v1 response looks like this:

```json
{ "status": "success", "code": 200, "message": "…", "data": { } }
```

Errors use `"status": "error"`. Validation errors add an `errors` object keyed by field:

```json
{
  "status": "error",
  "code": 422,
  "message": "Validasi gagal",
  "errors": { "0.packageCode": ["The 0.packageCode field is required."] }
}
```

`message` is meant for logs and may be in Indonesian. Branch on the HTTP status, not on the
message text.

## Status codes

| Code | Meaning |
| --- | --- |
| `200` | Done |
| `202` | Accepted, finishing in the background (top-ups, and v2 purchases) |
| `400` | Missing input, insufficient balance, or the action isn't allowed in this state |
| `401` | `X-API-KEY` header missing |
| `403` | API key invalid |
| `404` | Not found, or it doesn't belong to your account |
| `409` | That `transaction_id` is still being processed (v2: or it was used for a different request) |
| `422` | Validation failed, or the eSIM has expired (`error_type: ESIM_EXPIRED`) |
| `500` | Failed. For checkout, your balance was refunded and you can retry with the same `transaction_id`. |
| `503` | Temporarily unavailable. Nothing was charged. Retry. |
