Contents
Errors
Errors
When a request fails, the API returns a consistent JSON error object regardless of the endpoint. The HTTP status code indicates the category of failure; the body provides specifics.
Error Shape
{
"statusCode": 400,
"error": "validation_error",
"message": "Model validation failed.",
"details": {
"itemID": ["The itemID field is required."],
"units": ["The field units must be greater than 0."]
}
}| Field | Type | Description |
|---|---|---|
statusCode | integer | HTTP status code (mirrors the response status) |
error | string | Machine-readable error code (see table below) |
message | string | Human-readable description of what went wrong |
details | object or null | Field-level validation errors (only present for validation failures) |
The details object maps field names to arrays of error messages. It is only present when the error code is validation_error.
Error Codes
| Code | HTTP Status | Meaning |
|---|---|---|
bad_request | 400 | Request is malformed or missing required data |
validation_error | 400 | One or more fields failed validation (see details) |
unauthorized | 401 | Authentication is missing or the token is expired |
forbidden | 403 | Authenticated but lacking permission (enforced; see Authentication) |
not_found | 404 | The requested resource does not exist |
conflict | 409 | Request conflicts with current state (e.g., duplicate ShortID) |
rate_limited | 429 | Too many requests — slow down and retry |
not_implemented | 501 | Endpoint exists but the operation is not available |
internal_error | 500 | Unexpected server error |
HTTP Status Codes
| Status | When You’ll See It |
|---|---|
| 200 OK | Successful read or update |
| 201 Created | Resource successfully created |
| 400 Bad Request | Invalid input, missing fields, malformed JSON |
| 401 Unauthorized | No token, expired token, or invalid token |
| 403 Forbidden | Authenticated but action not allowed (enforced; see Authentication) |
| 404 Not Found | Resource ID doesn’t exist |
| 409 Conflict | Duplicate or state conflict |
| 429 Too Many Requests | Rate limit exceeded |
| 500 Internal Server Error | Server-side failure (report to your administrator) |
Validation Errors
When you submit invalid data, the API returns a 400 with field-level detail:
curl -X POST https://your-host/api/v2/Item \
-H "Authorization: Bearer ..." \
-H "Content-Type: application/json" \
-d '{}'{
"statusCode": 400,
"error": "validation_error",
"message": "Model validation failed.",
"details": {
"itemID": ["The itemID field is required."]
}
}Fix the flagged fields and retry.
Permission Errors
Every endpoint enforces action permissions. Each resource requires a specific permission (Security ID); if your integration user’s group lacks it, the API returns 403 Insufficient permissions. Accounts marked read-only receive 403 on all write verbs. Warehouse writes outside your assigned scope return 403; note that read endpoints are not warehouse-scoped — a scoped integration user can read inventory data across all warehouses. Unauthenticated requests return 401.
Response shapes for errors: permission, read-only, warehouse-scope, and report-access rejections (403) return a reduced body with statusCode and message only. 401 Unauthorized responses have no JSON body at all — check the status code and the WWW-Authenticate: Bearer header. The full four-field envelope (statusCode, error, message, details) applies to validation (400) and internal errors (500).
A permission failure has this shape:
{
"statusCode": 403,
"error": "forbidden",
"message": "Insufficient permissions."
}Rate Limit Errors
{
"message": "Rate Limit Exceeded. <limit>"
}Note: The rate limit response uses a simpler format than the standard error envelope, and the message names whichever limit was hit (e.g., requests per second, per minute, or per hour). Limits are configured per client — contact us for the limits on your subscription. Back off and retry after a reasonable interval (30–60 seconds).
Handling Errors in Code
var response = await client.PostAsync(url, content);
if (!response.IsSuccessStatusCode)
{
var body = await response.Content.ReadAsStringAsync();
var error = JsonConvert.DeserializeObject<ApiError>(body);
switch (error.Error)
{
case "validation_error":
// Fix the fields listed in error.Details
break;
case "rate_limited":
// Wait and retry
break;
case "unauthorized":
// Token expired — refresh and retry
break;
default:
// Log and alert
break;
}
}$response = Invoke-WebRequest -Uri $url -Method Post -Body $json `
-Headers @{ Authorization = "Bearer $token" } `
-ContentType "application/json" -ErrorAction SilentlyContinue
if ($response.StatusCode -ge 400) {
$error = $response.Content | ConvertFrom-Json
Write-Warning "$($error.error): $($error.message)"
}On this page