Error codes
Every /api/v1 error is returned in the same format, regardless of the endpoint or the cause.
Error format
{
"error": {
"code": "missing_scope",
"message": "The API key does not have the scope required by this endpoint.",
"hint": "Request the employees.read scope for this key.",
"docs_url": "https://smengo.com/api-docs/reference/errors#missing_scope",
"request_id": "8f14e45f-9e05-4f1a-8a3e-3d2b1c0f9b1b"
}
}
code— a machine-readable code from the catalog below; branch your integration's logic on it.message— a human-readable description, in English.hint— optional: what to do to fix the problem (for example, which scope to request).docs_url— a direct link to this code's description on this page. The one exception issubscription_inactive: itsdocs_urlpoints to the billing page in the Smengo app (where the problem actually gets fixed), not here.request_id— the same uuid as the response'sX-Request-Idheader. Include it when contacting support — it's how a specific request is found in the logs; without it, troubleshooting takes longer.
The HTTP status of the response matches the code (see the table at the end of the page). You can branch on either the status or error.code — but code is more reliable, since it uniquely identifies the cause, while a single HTTP status (for example, 403) covers two different codes.
Code catalog
unauthorized
HTTP status: 401.
The Authorization header is missing, malformed, the key is unknown to the system, or it was revoked. All four causes intentionally return the same response — this keeps the API from letting someone brute-force whether a particular key exists at all.
How to fix it: check that the header is sent as Authorization: Bearer smg_live_... and that the key is current. If the key is definitely correct, contact your organization owner — it may have been revoked.
missing_scope
HTTP status: 403.
The key is valid, but it doesn't have the scope this endpoint requires. Which scope is needed is in the response's hint and in Scopes & permissions.
How to fix it: ask the organization owner to issue a new key with the required scope, or reissue the current one with an expanded set.
subscription_inactive
HTTP status: 403.
The organization that owns the key has an inactive subscription (the trial ended and there's no active subscription). The API is disabled along with the rest of the paid functionality — the same gate as in the Smengo app.
How to fix it: renew the subscription in the Smengo app (Settings → Billing). Once the subscription is active again, the key starts working again without reissuing it.
module_required
HTTP status: 403.
The endpoint belongs to a paid module the organization has not enabled (the module name is in the response hint). For example, /timesheets requires the Timesheet module and /demand-signals requires Core. This is not about the key's scopes: what is missing is an enabled module, not access.
How to fix: enable the module in the Smengo app (Settings → Billing). The key starts working immediately; no need to reissue it.
not_found
HTTP status: 404.
The main source of this error is an unknown path under /api/v1: any request with any method to a path not covered by an endpoint (for example, /api/v1/timesheet instead of /api/v1/timesheets, or bare /api/v1) returns 404 not_found with a message like No such endpoint: METHOD /path. The rare second case is GET /org when no organization row can be found for the key.
How to fix it: check the request path against the endpoint list in the API overview. Note: for list filters (department_id, employee_id), an unrecognized id does not return 404 — it just returns an empty list, see Conventions → Filters.
method_not_allowed
HTTP status: 405.
The endpoint exists, but was requested with a method it doesn't support. All v1 resources are strictly read-only, so any POST, PUT, PATCH, or DELETE on an existing endpoint returns exactly this code. The response includes an Allow header listing the supported methods; authentication is not performed for these responses — they're identical with or without a key.
How to fix it: use a method from the Allow header — for v1 that's GET (plus HEAD and OPTIONS). There are no write endpoints in v1.
validation_error
HTTP status: 400.
The request parameters failed validation: an unknown or unexpected parameter, from later than to, a date range longer than 366 days, from/to on a resource with no time dimension, and so on. The reason is in hint.
How to fix it: read hint and adjust the request parameters per the Conventions.
invalid_cursor
HTTP status: 400.
The cursor value is malformed or belongs to a different dataset — for example, a cursor obtained with one set of filters was passed into a request with different filters.
How to fix it: don't construct a cursor by hand — use only meta.next_cursor from the previous response of the same request. If the error persists with a "fresh" cursor, restart pagination without cursor.
rate_limited
HTTP status: 429.
The key's requests-per-minute limit was exceeded — rate_limit_per_min (default 60, configurable per key). The window is a sliding window (Cloudflare-style), not a fixed one: a sharp burst right at the minute boundary can't be used to double the effective limit. The response includes a Retry-After header — how many seconds to wait before retrying — and, like every response after successful authentication, the X-RateLimit-Limit/X-RateLimit-Remaining/X-RateLimit-Reset headers (they're absent on 401, early 403, unknown-path 404, 405, and OPTIONS responses); see Conventions → Rate limiting for details.
How to fix it: reduce your request rate, or wait for the window given in Retry-After. If the limit is consistently too low for your use case, contact Smengo support to raise the key's limit (the limit is configured per key, not per organization).
internal_error
HTTP status: 500.
An unexpected error on Smengo's side. These errors are logged and investigated by the team — there's no need to work around them on your end.
How to fix it: retry the request later. If the error persists, contact Smengo support and include the request_id from the response.
Status table
| Code | HTTP status |
|---|---|
unauthorized |
401 |
missing_scope |
403 |
subscription_inactive |
403 |
module_required |
403 |
not_found |
404 |
method_not_allowed |
405 |
validation_error |
400 |
invalid_cursor |
400 |
rate_limited |
429 |
internal_error |
500 |
The catalog is exhaustive for v1 — there are no error codes beyond these ten. It describes the responses of the application itself: the infrastructure in front of it (CDN, proxies) may return 502/503 during outages — and not necessarily with a JSON body. Treat such responses as transient and retry with exponential backoff.
What's next
- Conventions — response format, pagination, filters.
- Scopes & permissions — which permission is required for each scope.