Errors
Every 4xx and 5xx response carries a JSON body. Three response formats are in use across the API; the format you'll get is fully determined by the kind of error, not by which endpoint you called. They are:
- Problem details —
application/problem+json, RFC 7807. Used for cross-cutting failures: rate-limit, idempotency, missing or invalid token, missing scope, unknown public route. - Standard errors —
application/json,{ errors: [...], request_id }. Used for resource-level and validation failures from specific endpoints. - OAuth errors —
application/json, RFC 6749 §5.2{ error, error_description }. Used only byPOST /oauth/tokenandPOST /oauth/revokefor compatibility with standard OAuth 2.0 client libraries.
Use the HTTP status code as your primary signal; the body adds context. Always read X-Request-Id from response headers — it identifies the call across our logs regardless of which body format you got back.
1. Problem details (RFC 7807)
HTTP/1.1 429 Too Many Requests
Content-Type: application/problem+json
Retry-After: 42
X-Request-Id: 550e8400-e29b-41d4-a716-446655440000
{
"type": "https://developer.risepeople.com/errors/rate-limit-exceeded",
"title": "Too Many Requests",
"status": 429,
"detail": "Rate limit exceeded. Try again in 42 seconds.",
"instance": "/v1/employees"
}
| Field | Always | Purpose |
|---|---|---|
type | yes | Stable URI identifying the problem class. Use this URI as a programmatic match key — we won't rename it. |
title | yes | Short human-readable summary. |
status | yes | HTTP status code (duplicated for clients that lose it). |
detail | usually | Specific explanation for this occurrence. |
instance | usually | Path the error refers to, for tracing. |
Known problem types — each has its own page describing causes and fixes:
| Type URI suffix | Status | Page |
|---|---|---|
/errors/rate-limit-exceeded | 429 | rate-limit-exceeded |
/errors/idempotency-key-reuse | 409 | idempotency-key-reuse |
/errors/idempotency-key-too-long | 400 | idempotency-key-too-long |
/errors/insufficient-scope | 403 | insufficient-scope |
/errors/token-unauthorized | 401 | token-unauthorized |
/errors/public-api-not-found | 404 | public-api-not-found |
The type URI in any RFC 7807 response body deep-links to the matching page above.
2. Standard errors
For resource-level and validation failures from specific endpoints:
HTTP/1.1 404 Not Found
Content-Type: application/json
X-Request-Id: 550e8400-e29b-41d4-a716-446655440000
{
"errors": [
{ "code": "not_found", "detail": "No employee with id `unknown` in this account." }
],
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
| Field | Always | Purpose |
|---|---|---|
errors | yes | Always an array — even for a single error. Validation failures populate one entry per offending field. |
errors[].code | yes | Short machine-readable identifier. |
errors[].detail | usually | Human-readable explanation. Sensitive details are omitted from 5xx bodies in production. |
request_id | usually | UUID echoing X-Request-Id from the response headers. |
Codes you may see today:
| Code | Status | When |
|---|---|---|
invalid_params | 400 | Bad query string, path parameter, or request body |
invalid_token | 401 | Token rejected during request handling |
token_expired | 401 | Token past exp — refresh it |
forbidden | 403 | Authenticated but not authorized for this resource |
not_found | 404 | Resource doesn't exist in this account |
unsupported_media_type | 415 | Wrong Content-Type for this endpoint |
validation_error | 422 | Business-rule failure — see errors[] for each offending field |
internal_server_error | 500 | Our bug — safe to retry |
For human-readable context on any specific code, see the affected endpoint in the API Reference. Match codes exactly in your error handling, but be defensive: a future code you haven't seen before should fall through to a generic handler rather than crash.
Validation errors (422)
Validation failures populate one entry per offending field. The exact extension fields vary per endpoint; minimum is code and detail.
{
"errors": [
{ "code": "invalid_value", "detail": "birth_date must be before hire_date", "source": { "pointer": "/data/attributes/birth_date" } },
{ "code": "already_taken", "detail": "email is already in use by another employee", "source": { "pointer": "/data/attributes/email" } }
],
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
source.pointer is a JSON Pointer (RFC 6901) into the request body where applicable.
3. OAuth errors (RFC 6749)
POST /oauth/token and POST /oauth/revoke follow RFC 6749 §5.2 so standard OAuth client libraries parse them out of the box:
{
"error": "invalid_client",
"error_description": "Client authentication failed."
}
error | Status | When |
|---|---|---|
invalid_request | 400 | Required parameter missing or malformed |
invalid_client | 401 | Bad client_id / client_secret |
invalid_grant | 400 | Authorization code or refresh token is invalid or expired |
invalid_scope | 400 | Requested scope is unknown or not allowed for this client |
unauthorized_client | 400 | Client isn't allowed to use this grant type |
unsupported_grant_type | 400 | grant_type value isn't recognized |
access_denied | 403 | Resource owner refused authorization |
When to retry
| Status | Retry? |
|---|---|
| 400, 401, 403, 404, 409, 422 | No — your code is wrong, retrying won't change it |
| 429 | Yes, after Retry-After seconds |
| 5xx | Yes, with exponential backoff (see Rate limits) |
| Network timeout (no response) | Yes, with an Idempotency-Key — otherwise you risk double-writes |
Reporting a problem
Open a ticket at support.risepeople.com and include:
- The full
X-Request-Idfrom the response headers - The request method, path, and (for writes) body
- The full error response body and
Content-Type - Your
client_id(not the secret!) - The time (in ISO 8601 with timezone) you made the request
With those five pieces we can usually pinpoint the cause in a single round-trip.