Errors
All One Stack APIs return errors in a consistent JSON format with standard HTTP status codes.
Error Response Format
{
"error": "Human-readable error message",
"code": "MACHINE_READABLE_CODE",
"details": {}
}
| Field | Type | Description |
|---|---|---|
error | string | A human-readable description of what went wrong. |
code | string | A machine-readable error code for programmatic handling. |
details | object or null | Optional additional context (e.g., validation errors, conflicting resource IDs). |
HTTP Status Codes
400 Bad Request
The request was malformed or failed validation.
{
"error": "Validation failed",
"code": "VALIDATION_ERROR",
"details": {
"fields": {
"title": "Title is required",
"priority": "Must be one of: low, medium, high, critical"
}
}
}
Troubleshooting:
- Check that all required fields are included in the request body.
- Verify field values match expected types and allowed values.
- Ensure the
Content-Typeheader is set toapplication/json.
401 Unauthorized
Authentication credentials are missing or invalid.
{
"error": "Authentication required",
"code": "UNAUTHORIZED"
}
Troubleshooting:
- Verify your API key is correct and has not been revoked.
- Check that the
X-Integration-Keyheader is present and spelled correctly. - For session-based auth, ensure the user is logged in and the session has not expired.
- For portal tokens, ensure the JWT has not expired.
403 Forbidden
The authenticated user or key does not have permission for the requested action.
{
"error": "Access denied to PSA product",
"code": "FORBIDDEN",
"details": {
"requiredProduct": "psa",
"requiredPermission": "tickets.write"
}
}
Troubleshooting:
- Verify the API key has been granted access to the target product.
- Check that the key has the required permission level (Read Only vs. Read/Write).
- For user sessions, verify the user's role includes access to the product and the specific action.
- Portal tokens can only access portal-designated endpoints.
404 Not Found
The requested resource does not exist, or the URL path is incorrect.
{
"error": "Ticket not found",
"code": "NOT_FOUND",
"details": {
"resourceType": "ticket",
"resourceId": "tkt_nonexistent"
}
}
Troubleshooting:
- Verify the resource ID is correct.
- Ensure the resource has not been deleted.
- Check that the URL path and product base URL are correct.
- The resource may exist but belong to a different tenant.
409 Conflict
The request conflicts with the current state of a resource.
{
"error": "Ticket has been modified by another user",
"code": "CONFLICT",
"details": {
"currentVersion": "v3",
"submittedVersion": "v1"
}
}
Troubleshooting:
- Fetch the latest version of the resource and retry with the updated data.
- For concurrent edits, implement optimistic locking using the
_etagfield if provided.
429 Too Many Requests
The tenant has exceeded the rate limit.
{
"error": "Rate limit exceeded",
"code": "RATE_LIMIT_EXCEEDED",
"details": {
"limit": 100,
"window": "1m",
"retryAfter": 23
}
}
Troubleshooting:
- Wait for the number of seconds specified in the
Retry-Afterheader. - Implement exponential backoff in your client.
- Reduce request frequency by using batch operations or webhooks.
- See Rate Limits for detailed best practices.
500 Internal Server Error
An unexpected error occurred on the server.
{
"error": "An unexpected error occurred",
"code": "INTERNAL_ERROR"
}
Troubleshooting:
- Retry the request after a short delay. Transient errors often resolve on their own.
- If the error persists, contact support at [email protected] with the request details and the
X-Request-Idresponse header value.
Error Codes Reference
| Code | Status | Description |
|---|---|---|
VALIDATION_ERROR | 400 | Request body or query parameters failed validation |
INVALID_JSON | 400 | Request body is not valid JSON |
UNAUTHORIZED | 401 | Missing or invalid authentication |
TOKEN_EXPIRED | 401 | JWT or session has expired |
FORBIDDEN | 403 | Insufficient permissions |
PRODUCT_ACCESS_DENIED | 403 | API key does not have access to this product |
NOT_FOUND | 404 | Resource does not exist |
CONFLICT | 409 | Resource version conflict |
DUPLICATE_RESOURCE | 409 | A resource with the same unique identifier already exists |
RATE_LIMIT_EXCEEDED | 429 | Too many requests |
INTERNAL_ERROR | 500 | Unexpected server error |
Handling Errors in Code
async function apiRequest(url, options) {
const response = await fetch(url, {
...options,
headers: {
'X-Integration-Key': API_KEY,
'Content-Type': 'application/json',
...options?.headers,
},
});
if (!response.ok) {
const error = await response.json();
switch (error.code) {
case 'RATE_LIMIT_EXCEEDED':
const retryAfter = parseInt(response.headers.get('Retry-After') || '5', 10);
await new Promise(r => setTimeout(r, retryAfter * 1000));
return apiRequest(url, options); // retry once
case 'VALIDATION_ERROR':
console.error('Validation errors:', error.details.fields);
throw new Error(`Validation failed: ${error.error}`);
case 'UNAUTHORIZED':
case 'TOKEN_EXPIRED':
throw new Error('Authentication failed. Check your API key or session.');
default:
throw new Error(`API error (${error.code}): ${error.error}`);
}
}
return response.json();
}