Success response
Success and failure use different shapes that never mix. Tell one from the other by the HTTP status, never by a field in the body.
{
"data": { "id": 1, "name": "Ada" }
}
With pagination
{
"data": [ ... ],
"meta": {
"pagination": { "page": 2, "limit": 2, "totalItems": 7, "totalPages": 4 }
}
}
datais always present on a 2xx response that has a body. An empty list is[], nevernull.- There is no
error,codeormessageon a success response. metais optional and only carries information outside the payload, never domain data.- A
DELETEthat returns nothing answers204with no body.
Error response
The error object is always nested under error, so it can never be mistaken for a success payload.
{
"error": {
"code": 100001,
"name": "USER_NOT_FOUND",
"message": "user not found",
"details": [
{ "field": "startDate", "reason": "required" }
]
}
}
codeandnameare stable. These are the only fields safe to branch on — neither changes once released.messageis not stable. It follows the caller's language and its wording may change at any time, so never match on it; show it to the user instead.detailsis optional, used when one error has several specific causes — usually per-field validation failures. Itsreasonis aimed at developers and is not translated.- Raw server error text, SQL, stack traces, hostnames and table names never appear in a response.
HTTP status
The status comes from the error definition, not from the handler. A failure never answers 200.
| Status | Used for |
|---|---|
| 200 | A successful GET/PUT/PATCH |
| 201 | A POST that created a resource |
| 204 | A successful DELETE, no body |
| 400 | The request or its contents are invalid |
| 401 | Not authenticated, token expired or invalid |
| 403 | Authenticated but not permitted |
| 404 | The resource does not exist |
| 409 | Conflicts with the resource's current state |
| 429 | Over a rate limit or quota |
| 500 | A bug or unexpected failure on the server side |
| 503 | An external dependency is unavailable |
Languages
Only message is translated. The language is chosen in this order:
- The
?lang=query parameter, which beats the header so a client can force one language without touching device settings. - The
Accept-Languageheader, including quality values such asko,en-US;q=0.8. en-US, the default, used when nothing matches.
A tag naming only a language matches its region, so ko resolves to ko-KR.
An unknown language falls back to the default rather than producing an error.
Supported: en-US, id-ID, ko-KR.
GET /api/v1/user/99
Accept-Language: ko-KR
404 Not Found
{
"error": {
"code": 100001,
"name": "USER_NOT_FOUND",
"message": "사용자를 찾을 수 없습니다"
}
}
Pagination
Every list endpoint takes the same two query parameters.
| Parameter | Default | Notes |
|---|---|---|
| page | 1 | 1-based. A value below 1 is treated as the first page. |
| limit | 10 | Clamped to a maximum of 100 rather than rejected. |
Invalid pagination never produces an error: it is read as a request for the
first page. The offset is computed on the server and is never accepted from
the client. The response carries meta.pagination, never fields at
the root of the body.
Error codes
Each module owns one block of 10,000 codes. A released code is never reused
and a name is never renamed, so both are safe to store and branch on.
core
10xxxx · 100000–109999Errors that are not specific to any domain, available to every service.
| Code | Name | Status | Description |
|---|---|---|---|
| 100001 | USER_NOT_FOUND | 404 Not Found | user not found pengguna tidak ditemukan 사용자를 찾을 수 없습니다 |
| 100002 | USER_ALREADY_EXISTS | 409 Conflict | user already registered pengguna sudah terdaftar 이미 등록된 사용자입니다 |
| 100101 | UNAUTHENTICATED | 401 Unauthorized | authentication required silakan masuk terlebih dahulu 로그인이 필요합니다 |
| 100102 | TOKEN_EXPIRED | 401 Unauthorized | session has expired, please sign in again sesi Anda sudah berakhir, silakan masuk kembali 세션이 만료되었습니다. 다시 로그인해 주세요 |
| 100103 | TOKEN_INVALID | 401 Unauthorized | invalid token token tidak valid 유효하지 않은 토큰입니다 |
| 100104 | FORBIDDEN | 403 Forbidden | you do not have access to this resource Anda tidak punya akses ke sumber daya ini 접근 권한이 없습니다 |
| 100201 | BAD_REQUEST | 400 Bad Request | the request could not be processed permintaan tidak dapat diproses 잘못된 요청입니다 |
| 100202 | VALIDATION_FAILED | 400 Bad Request | the submitted data is not valid data yang dikirim tidak valid 입력값이 올바르지 않습니다 |
| 100301 | RESOURCE_NOT_FOUND | 404 Not Found | the requested resource was not found data yang diminta tidak ditemukan 요청한 데이터를 찾을 수 없습니다 |
| 100302 | RESOURCE_CONFLICT | 409 Conflict | the resource cannot be processed in its current state data tidak dapat diproses pada kondisi saat ini 현재 상태에서는 처리할 수 없습니다 |
| 100501 | RATE_LIMITED | 429 Too Many Requests | too many requests, please try again shortly terlalu banyak permintaan, coba lagi beberapa saat lagi 요청이 너무 많습니다. 잠시 후 다시 시도해 주세요 |
| 100801 | UPSTREAM_UNAVAILABLE | 503 Service Unavailable | the service is temporarily unavailable, please try again shortly layanan sedang tidak tersedia, coba lagi beberapa saat lagi 외부 서비스를 일시적으로 사용할 수 없습니다 |
| 100901 | INTERNAL_ERROR | 500 Internal Server Error | an internal server error occurred terjadi kesalahan pada server 서버 오류가 발생했습니다 |
| 100902 | DATABASE_ERROR | 500 Internal Server Error | an error occurred while processing data terjadi kesalahan saat memproses data 데이터 처리 중 오류가 발생했습니다 |
example
20xxxx · 200000–209999Template module. Replace it with the first real domain.
| Code | Name | Status | Description |
|---|---|---|---|
| 200001 | EXAMPLE_NOT_FOUND | 404 Not Found | the example was not found example tidak ditemukan 항목을 찾을 수 없습니다 |
| 200002 | EXAMPLE_NOT_READY | 409 Conflict | the example is not ready yet example belum siap 항목이 아직 준비되지 않았습니다 |
| 200003 | EXAMPLE_EXPIRED | 409 Conflict | the example has expired example sudah berakhir 항목이 만료되었습니다 |
| 200101 | EXAMPLE_ALREADY_PROCESSED | 409 Conflict | this example has already been processed example ini sudah diproses sebelumnya 이미 처리된 항목입니다 |
| 200102 | EXAMPLE_QUOTA_EXHAUSTED | 409 Conflict | the example quota has run out kuota example sudah habis 수량이 모두 소진되었습니다 |
No error code matches that filter.