When a request fails, the Dealroom API returns a JSON envelope with a consistent shape and
an HTTP status code. Error codes are stable strings — switch on error.code in your client
rather than parsing the human-readable message.
Response envelope
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Human-readable description of what went wrong",
"details": { "filter": "launch_date" }
}
}
| Field | Description |
|---|
code | Stable identifier — use this for programmatic error handling |
message | Human-readable explanation. Safe to surface in dev tools, but the exact wording may change |
details | Optional extra context. May be absent, an object (e.g. { "filter": "launch_date" }), or an array of { "path", "message" } entries for schema-validation (422) errors |
Error-code catalog
| Code | HTTP | When it happens | How to fix |
|---|
VALIDATION_ERROR | 400/422 | Missing/invalid header (400) or request body/parameter fails OpenAPI schema validation (422) | Inspect message and details for the offending field, correct, retry |
INVALID_ENTITY_ID | 400 | Entity ID in the path isn’t a number | Use a numeric ID, e.g. /api/data/entities/12345 |
UNKNOWN_FILTER | 400 | Filter key isn’t registered for the scope | See the Filters reference for available filters per scope |
FILTER_PARSE_ERROR | 400 | Filter expression has a syntax error | Check and() / or() parentheses and the field[op]:value form |
FILTER_VALIDATION_ERROR | 400 | Filter value doesn’t match the expected type | Check the filter’s value type in the Filters reference |
UNSUPPORTED_OPERATOR | 400 | Operator not allowed for that filter | Use one of the operators listed in message |
UNAUTHORIZED | 401 | Missing or invalid Bearer token | Re-fetch a token via Authentication |
FORBIDDEN | 403 | Token is valid but lacks the required permission | Check your API key’s scope list in Settings > API |
NOT_FOUND | 404 | Resource ID doesn’t exist or isn’t visible to your key | Verify the ID; check permissions if the entity exists in another dataset |
INTERNAL_SERVER_ERROR | 500 | Unclassified server-side failure | Retry with backoff. If persistent, contact support with the response payload |
DATABASE_ERROR | 500 | Database query failed (e.g. constraint violation) | Retry. If persistent, contact support |
SCHEMA_ERROR | 500 | Database schema mismatch (internal) | Should never reach clients; contact support if you see one |
EXTERNAL_SERVICE_ERROR | 502/503 | Downstream service unavailable (currently Auth0 Management API) | Retry with backoff |
QUERY_TIMEOUT | 504 | Query exceeded the 15-second execution budget | Narrow the query: add filters, reduce limit, or use an aggregate endpoint |
Common scenarios
Invalid filter expression
curl "https://api-next.dealroom.co/api/data/entities?filter=launch_date[invalid_op]:2020" ...
{
"error": {
"code": "UNSUPPORTED_OPERATOR",
"message": "Operator 'invalid_op' not supported for filter 'launch_date'. Allowed operators: eq, neq, gt, gte, lt, lte",
"details": { "filter": "launch_date" }
}
}
The API requires Authorization on every authenticated request. API-key (M2M)
requests additionally require X-Client-Id set to the key’s client_id. The
status code depends on which header is missing:
- Missing
Authorization → 401 UNAUTHORIZED
- Missing
X-Client-Id on an M2M request → 400 VALIDATION_ERROR
{
"error": {
"code": "VALIDATION_ERROR",
"message": "X-Client-Id header is required for API key requests"
}
}
Expired token
After your access_token expires (default 24h):
{
"error": {
"code": "UNAUTHORIZED",
"message": "Token has expired"
}
}
Refresh the token via the OAuth2 client-credentials flow — see the
Quickstart. The SDK snippets in the
Authentication guide handle this automatically.
Query timeout
A heavy aggregate or unfiltered list query may exceed the 15-second execution budget:
{
"error": {
"code": "QUERY_TIMEOUT",
"message": "Query took too long to execute (15s timeout)"
}
}
Narrow the query (add filters, reduce limit) or switch to a purpose-built
aggregate endpoint instead of paging through raw entities.
Handling errors
Switch on error.code — it’s stable across API versions. Messages may change for clarity.
try {
const { data } = await dealroom.get("/entities", { params });
return data;
} catch (err) {
const code = err.response?.data?.error?.code;
switch (code) {
case "UNAUTHORIZED":
// Refresh the token and retry
break;
case "QUERY_TIMEOUT":
// Narrow the query (more filters, smaller limit) or use an aggregate endpoint
break;
case "VALIDATION_ERROR":
case "UNKNOWN_FILTER":
case "FILTER_VALIDATION_ERROR":
case "UNSUPPORTED_OPERATOR":
case "FILTER_PARSE_ERROR":
case "INVALID_ENTITY_ID":
// Bug in your request — surface to your dev team
break;
case "NOT_FOUND":
// Expected for some lookups; handle as a business case
break;
default:
// 5xx — retry with exponential backoff
}
}
Treat the message field as informative, not contractual. Always switch on code.
Endpoint-specific errors
Each endpoint may return a subset of these codes plus endpoint-specific ones. See the
API Reference for the catalog per endpoint.