Errors
A successful response always has status 200. An error comes with a 4xx or 5xx status and an RFC 9457 body.
#Format
An error is returned with the Content-Type: application/problem+json header and a body with eight fields:
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json; charset=utf-8
Request-Id: 0f3c7a95-2e8b-4d16-b9a4-6c5e1d8f7b20
Cache-Control: no-store
{
"type": "urn:meliora:problem:invalid_parameter",
"title": "Invalid parameter",
"status": 400,
"detail": "Check the parameter value",
"instance": "urn:request:0f3c7a95-2e8b-4d16-b9a4-6c5e1d8f7b20",
"code": "invalid_parameter",
"param": "limit",
"request_id": "0f3c7a95-2e8b-4d16-b9a4-6c5e1d8f7b20"
}| Field | Description |
|---|---|
type | URI of the error type: urn:meliora:problem: followed by the code. |
title | Short name in English. |
status | HTTP status, the same as the response status. |
detail | Human-readable explanation. The language depends on the Accept-Language header. |
instance | URI of the request: urn:request: followed by the request ID. |
code | Machine-readable error code. |
param | The parameter the error relates to, or null. |
request_id | Request ID, the same as in the Request-Id header. |
#Rejections before key validation
Some requests are rejected before the key is validated: the per-IP rate limit is exceeded, the path is unknown, the request has a body, or the service is overloaded. These responses have the same status, code, param and request_id fields, but type is about:blank, and title and detail match code:
{
"type": "about:blank",
"title": "rate_limit_exceeded",
"status": 429,
"code": "rate_limit_exceeded",
"detail": "rate_limit_exceeded",
"instance": "urn:request:0f3c7a95-2e8b-4d16-b9a4-6c5e1d8f7b20",
"request_id": "0f3c7a95-2e8b-4d16-b9a4-6c5e1d8f7b20",
"param": null
}So rely on status and code, not on type and title.
#Handling errors
- Branch your logic on
statusandcode. Thedetailtext may change, so don't parse it in code. - If you get an unfamiliar
code, handle the error by its HTTP status: 4xx means a problem with the request or access, 5xx means a temporary problem on our side. - For a parameter error,
paramcontains the parameter name:limit,status,cursor,include_textand so on. - An error never advances the cursor. When you retry, a request with the same cursor returns the same data.
#Explanation language
By default, detail is in Russian. To get English text, send the Accept-Language: en header. The code and title fields don't depend on the language.
#Request-Id
Every response, including errors, has a Request-Id header with the request's UUID. Log it together with the endpoint and the time. When you contact Support, the Request-Id is all we need to find the request. Never send us the key itself.
#Error codes
| Status | Code | What happened |
|---|---|---|
400 | invalid_parameter | The parameter value has an invalid format, is out of range or is used in an invalid combination. The parameter name is in param. |
400 | unknown_parameter | The route does not accept this parameter. An API key in the URL also causes this error. |
400 | cursor_mismatch | The cursor is corrupted, was issued for a different route or filters, or the key's permissions and scope have changed. |
401 | invalid_api_key | The key is missing, invalid, revoked or expired. |
403 | scope_required | The key does not have permission for this read. Texts also require "Message text" to be enabled. |
403 | api_disabled | API access is disabled in Meliora. |
403 | api_not_available | The organization's plan does not include the API. Read access requires Pro or higher. |
403 | subscription_inactive | The organization's subscription is not active. |
403 | module_not_available | The outreach module is unavailable for this organization. |
403 | organization_suspended | Organization access is suspended. |
403 | ip_not_allowed | The request came from an IP address that is not on the key's allow list. |
404 | not_found | The object was not found or is outside the key's scope. An unknown path also returns 404. |
405 | method_not_allowed | Only the GET method is allowed. The public endpoint answers other methods with a 404 without a body, so this code is not seen from outside. |
410 | cursor_expired | The cursor expired: 15 minutes for lists, 30 days for the event feed. |
429 | rate_limit_exceeded | The request limit of the key, organization or IP address is exhausted. Retry after Retry-After seconds. |
503 | service_unavailable | The data could not be read right now. Retry later. |
503 | server_busy | The service is overloaded. Retry after Retry-After seconds. |
503 | integration_initializing | The new key is still being prepared. Retry later. |
#What to retry
| Status | Retry? | How |
|---|---|---|
| 400 | No | Fix the parameter named in param. For cursor_mismatch, restart the list. |
| 401, 403 | No | Check the key, its permissions and your organization settings. |
| 404 | No | The object doesn't exist or is outside the key's scope. |
| 410 | No | The cursor expired: restart the list or recover the feed. |
| 429 | Yes | Wait Retry-After seconds and add random jitter. |
| 503 | Yes | Wait Retry-After seconds and increase the delay if the error repeats. |
For more on delays between retries, see Rate limits and retries.
#Special cases
- An unknown path or an object ID that isn't a valid UUID returns 404
not_found. Write the path exactly as in the reference: a trailing/or encoded characters in the path also return 404. - Methods other than GET, including
HEADandOPTIONS, and URLs outside/v1/get 404 with no body. You won't see themethod_not_allowedcode from outside. - A body in a GET request, a URL longer than 8 KiB or headers larger than 8 KiB return 400
invalid_parameterwithparamset tobody,uriorheaders. - A repeated parameter (
?status=new&status=replied) and an empty value (?status=) return 400invalid_parameter. - A long-running request is aborted after 10 seconds with 503
service_unavailable.