API Error Codes
This article covers the HTTP status codes and error responses the ScopeStack API returns, organized by category. Each section explains what the error means and the steps to resolve it.
Required Request Headers
The headers required depend on whether you are reading or writing data.
GET requests (read operations):
| Header | Value |
|---|---|
Authorization | Bearer {your_access_token} |
Accept | application/vnd.api+json |
POST and PATCH requests (create and update operations):
| Header | Value |
|---|---|
Authorization | Bearer {your_access_token} |
Accept | application/vnd.api+json |
Content-Type | application/vnd.api+json |
The Content-Type header is required only when sending a request body. Omitting it on a write request will result in a 400 or 415 error.
Authentication Errors
401 Unauthorized
Your access token is missing, invalid, or expired.
{
"errors": [
{
"status": "401",
"title": "Unauthorized"
}
]
}| Check | Solution |
|---|---|
| Authorization header missing | Add the header: Authorization: Bearer {your_access_token} |
| Token expired | Use your refresh_token to request a new access_token from POST /oauth/token |
| Token malformed | Make sure you are sending only the access_token value, not the full JSON token response |
| User account deactivated | Re-authenticate using an active user account |
OAuth Token Errors (POST /oauth/token)
When requesting or refreshing a token, the response may include one of these error codes in the body:
| Error | Meaning | How to resolve |
|---|---|---|
invalid_client | The client_id was not recognized | Check for character confusion: zero (0) vs the letter O, lowercase l vs uppercase I. Copy the client_id directly from your ScopeStack API credentials page rather than retyping it. |
invalid_grant | The client was recognized but authentication failed | Check that the password is correct, that the user account is active, and that the user has accepted their invitation. If using the Authorization Code flow, confirm that the callback URL matches what is registered for your client credentials. |
invalid_resource_owner | The user was not found | The email address does not exist on any ScopeStack account. Confirm the correct email is being used. |
unsupported_grant_type | The grant_type value is not valid | Accepted values are client_credentials, password, authorization_code, and refresh_token. |
Example error response:
{
"error": "invalid_grant",
"error_description": "The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client."
}Permission Errors
403 Forbidden
Your token is valid, but the user associated with it does not have permission to perform this action.
{
"errors": [
{
"status": "403",
"title": "Forbidden"
}
]
}| Check | Solution |
|---|---|
| User role lacks permission | Review the user’s role in ScopeStack. Read access does not grant write access. |
| Operating in the wrong account | Call GET /v1/me to confirm which account your token is scoped to. You may be authenticated to a different account than expected. |
| Resource belongs to a different account | The resource exists but is in an account this user cannot access. |
| Token edge case | In rare cases, an expired token returns 403 instead of 401. Try refreshing your token first. |
Validation and Request Errors
400 Bad Request
The request was malformed or contained an unsupported parameter.
{
"errors": [
{
"status": "400",
"title": "Bad Request",
"detail": "Filter not allowed: created-before"
}
]
}Common causes:
- A required field is missing from the request body
- A field value is the wrong data type (for example, a string where a number is expected)
- A filter parameter is not supported for this endpoint
- A
page[size]above the maximum of 1000 (see Pagination) - Invalid JSON:API formatting in the request body
Check the API documentation for the endpoint you are calling to confirm which fields are required, which filters are supported, and the expected data types.
422 Unprocessable Entity
The request was well-formed but failed validation. This is the most common error when creating or updating records.
{
"errors": [
{
"status": "422",
"title": "Unprocessable Entity",
"detail": "Name can't be blank"
}
]
}| Cause | Solution |
|---|---|
| A required field is blank or missing | Check the detail field in the error response for the specific field that failed. |
| A field value violates a uniqueness constraint | The value you provided already exists on another record in this account. |
| Cross-account access attempt | You are trying to reference a resource that belongs to a different account. Confirm you are operating in the correct account context. |
The detail property in the error response identifies which field failed and why. Address each error in the errors array before retrying.
Not Found Errors
404 Not Found
The resource does not exist, it exists in a different account than the one your token is scoped to, or your API user is not allowed to see it.
{
"errors": [
{
"status": "404",
"title": "Not Found"
}
]
}| Check | Solution |
|---|---|
| Resource ID is correct | Confirm the ID in the URL matches the record you intend to retrieve. |
| Account context | Call GET /v1/me to verify which account your token is in. The resource may exist, but in a different account. |
| Resource was deleted | The record may have been archived or deleted. Check within ScopeStack to confirm it still exists. |
| URL format | Most endpoints require the account slug: https://api.scopestack.io/{account-slug}/v1/... |
| Record visibility | The record can exist, be undeleted, and be in your own account, and a request for it by ID can still return 404 if your user’s access is narrowed — for example by team assignment, which scopes what a user can see through the API as well as in the app. Confirm with someone who has broader access before concluding the record is gone. |
Rate Limiting
429 Too Many Requests
You have sent too many requests in a short window. The response carries a Retry-After header and a JSON body naming the same interval:
{
"error": "Rate limit exceeded. Retry in 60 seconds."
}Retry-After is the length of the throttle window in seconds, not a countdown to the exact moment it resets. Waiting that long is always safe. The message wording differs slightly between hosts, so match on the 429 status rather than the text.
Read Retry-After and wait that long before retrying. Retrying immediately, or on a fixed short interval, keeps you inside the same window and prolongs the block. Use exponential backoff for anything automated.
The two hosts throttle different things, which is usually the key to an unexpected 429:
| Host | What is throttled |
|---|---|
app.scopestack.io | Token minting (POST /oauth/token) and general request volume, both per IP. A separate ceiling caps total token throughput across the whole platform, shared by all customers — so this one can trigger without your own usage changing. |
api.scopestack.io | Document generation and project exports only, limited both per IP and per account. Ordinary reads and writes against data endpoints are not throttled here. |
Common causes and how to fix them:
| Cause | Solution |
|---|---|
| Requesting a new token for every API call | Cache the access token and reuse it until it expires. This is by far the most common cause. Access tokens last 24 hours, whichever grant type issued them. |
| Generating documents or exports in a tight loop | Space the requests out. These are limited more tightly than ordinary API calls because each one starts background work, and the limit applies per account as well as per IP — so colleagues share it with you. |
| Many users behind one office IP | Per-IP limits count your whole egress IP, not the individual user. Contact support if a shared IP is throttled during normal use. |
| Retrying immediately after a 429 | Honor Retry-After, and back off exponentially. |
A sustained token loop returns 403, not 429. A client that keeps hammering
POST /oauth/tokenpast the sustained threshold is temporarily blocked by IP and gets 403 Forbidden with noRetry-After, until the ban expires (one hour by default). If you are seeing unexplained 403s from a machine that was recently minting tokens in a loop, that is the cause — not a permissions problem. Fix the token caching and wait out the ban.
Server Errors
504 Gateway Timeout
The server did not return a response in time. This typically occurs on requests that involve large amounts of data.
The most common cause is calling the merge data endpoint (GET /{account-slug}/v1/projects/{id}/merge-data) on a project with 20 or more service locations using the v1 response format. The server-side computation for large projects exceeds the request timeout.
How to resolve:
Switch to the v2 merge data format by passing version=2 as a query parameter:
GET /{account-slug}/v1/projects/{id}/merge-data?version=2The v2 format is more efficient and handles large projects reliably. If you are using a reference project to generate merge data, consider using a smaller, representative project instead.
Quick Reference
| Status Code | Category | Short description |
|---|---|---|
400 | Request error | Malformed request, missing field, or unsupported filter |
401 | Authentication | Token missing, invalid, or expired |
403 | Permissions | Valid token, but insufficient permissions |
404 | Not found | Resource does not exist or is in a different account |
422 | Validation | Request parsed correctly but failed business-rule validation |
429 | Rate limit | Too many requests. Wait the number of seconds in Retry-After, then retry |
504 | Server | Request timed out; occurs on large merge data requests |
Still seeing an error? Contact ScopeStack support via chat or email. Include the full error response, the endpoint you were calling, and whether you are using Postman, Workato, or custom code.
New to ScopeStack?
ScopeStack automates scoping, pricing, and SOW generation for IT services teams. See how it fits your process.