When an API call fails, start with the HTTP status code, then read the JSON error body. MVMNT uses consistent status codes and a consistent error payload to make failures predictable to handle and quick to troubleshoot.
The MVMNT API uses standard HTTP status codes to indicate the success or failure of requests.
| Code | Name | Usage |
|---|---|---|
| 200 | OK | GET, PATCH, and POST /filter operations succeeded |
| 201 | Created | POST (create) operations succeeded |
| 204 | No Content | DELETE operations succeeded (no response body) |
POST /v1/vendorsResponse:
HTTP/1.1 201 Created
Content-Type: application/json
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"friendlyId": "V100001",
"name": "ABC Warehouse Services",
"status": "ACTIVE",
...
}GET /v1/vendors/550e8400-e29b-41d4-a716-446655440000Response:
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"friendlyId": "V100001",
"name": "ABC Warehouse Services",
...
}PATCH /v1/vendors/550e8400-e29b-41d4-a716-446655440000Response:
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"friendlyId": "V100001",
"name": "ABC Warehouse Services Updated",
...
}DELETE /v1/vendors/550e8400-e29b-41d4-a716-446655440000Response:
HTTP/1.1 204 No ContentNo response body is returned for successful DELETE operations.
These errors mean the request needs to change before it will succeed.
| Code | Name | Meaning |
|---|---|---|
| 400 | Bad Request | Invalid request format or parameters |
| 401 | Unauthorized | Invalid or missing authentication token |
| 404 | Not Found | Resource not found |
| 409 | Conflict | Duplicate key or constraint violation |
| 422 | Unprocessable Entity | Validation error |
| 429 | Too Many Requests | Rate limit exceeded |
These errors mean the API couldn’t complete a valid request.
| Code | Name | Meaning |
|---|---|---|
| 500 | Internal Server Error | Unexpected server error |
| 503 | Service Unavailable | API temporarily unavailable (maintenance, etc.) |
All error responses include a JSON body with details about the error:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": [
{
"field": "email",
"message": "Invalid email format"
}
]
}
}| Field | Type | Description |
|---|---|---|
error.code | string | Machine-readable error code |
error.message | string | Human-readable error message |
error.details | array | Additional error details (optional) |
error.details[].field | string | Field name that caused the error |
error.details[].message | string | Field-specific error message |
Cause: The API can’t parse your request (malformed JSON, invalid parameter types, missing required fields).
Example:
{
"error": {
"code": "BAD_REQUEST",
"message": "Invalid JSON in request body"
}
}Troubleshooting:
- Confirm the request body is valid JSON (no trailing commas, quoted keys/strings).
- Compare your payload to the endpoint schema and include required fields.
- Double-check types for query params and JSON fields (string vs number vs boolean).
Cause: The request is missing auth, or the token is expired/invalid.
Example:
{
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or expired access token"
}
}Troubleshooting:
- Request a new access token using
/oauth2/token. - Send the token on every request:
Authorization: Bearer {token}. - Verify you’re using the correct client ID and secret for the environment you’re calling.
Cause: The resource ID doesn’t exist, the endpoint path is wrong, or the resource is no longer available.
Example:
{
"error": {
"code": "NOT_FOUND",
"message": "Vendor not found"
}
}Troubleshooting:
- Verify the ID you’re requesting is correct (copy/paste errors and environment mix-ups are common).
- Check whether the record was deleted (see Soft Deletes).
- If you don’t have the ID, use filter endpoints to locate the record by other fields.
Cause: The request violates a uniqueness constraint (often a duplicate client key).
Example:
{
"error": {
"code": "DUPLICATE_KEY",
"message": "A vendor with this key already exists",
"details": [
{
"field": "key",
"message": "Key 'ERP-VENDOR-123' is already in use"
}
]
}
}Troubleshooting:
- Generate a unique client key and retry the create.
- If you intended to upsert, look up the existing record by key first and then PATCH it.
- If your system retries requests, make sure your key generation is deterministic per record (not per attempt).
Cause: The request is well-formed, but it fails validation rules (missing required values, invalid formats, out-of-range values).
Example:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": [
{
"field": "email",
"message": "Invalid email format"
},
{
"field": "phone",
"message": "Phone number is required"
}
]
}
}Troubleshooting:
- Read
error.detailsand fix each field error listed. - Validate your payload locally before sending (same rules every time, not “trial and error” against the API).
- Re-check the API reference for required fields and accepted formats.
Cause: You hit a rate limit.
Example:
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Try again in 30 seconds."
}
}Response Headers:
X-RateLimit-Limit: Your maximum requests per minuteX-RateLimit-Remaining: Requests remaining in current windowX-RateLimit-Reset: Unix timestamp when limit resets
Troubleshooting:
- Implement exponential backoff (and don’t retry in a tight loop).
- Reduce concurrency and spread requests across time.
- Batch reads/writes where the API supports it.
- If your use case needs higher limits, contact support with expected request volume and endpoints.
Cause: The API encountered an unexpected failure while processing a valid request.
Example:
{
"error": {
"code": "INTERNAL_ERROR",
"message": "An unexpected error occurred"
}
}Troubleshooting:
- Retry with exponential backoff (this is often transient).
- Log the response body and the
X-Request-Idheader, then contact support if it repeats. - Check the status page for ongoing incidents.
Cause: The API is temporarily unavailable (maintenance, deploy, or a short outage).
Troubleshooting:
- Retry with exponential backoff.
- If you have background jobs, pause/resume rather than failing the whole pipeline on the first 503.
- Check the status page for maintenance windows or incidents.
- Check the HTTP status code before parsing the response body.
- Parse
error.codefor branching logic anderror.detailsfor field-level fixes. - Retry only transient failures (typically 429, 500, 503) with exponential backoff.
- Log enough context to reproduce: method, URL, status, response body, and request ID.
- Translate API errors into specific messages for broker ops (what failed, what to change).
- Don’t retry 4xx errors that require a different request (except 429).
- Don’t ignore
error.detailswhen validation fails; it tells you exactly what to fix. - Don’t retry immediately; backoff prevents thundering herds and repeated rate-limit hits.
- Don’t show raw API payloads to end users; map them to actionable UI text.
- Don’t keep calling the API after a 401 until you’ve refreshed the token.
For transient errors (500, 503, 429), implement exponential backoff:
async function requestWithRetry(url, options, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await fetch(url, options);
if (response.ok) {
return response;
}
if (response.status >= 400 && response.status < 500) {
if (response.status === 429) {
const backoffMs = Math.pow(2, attempt) * 1000;
await new Promise((resolve) => setTimeout(resolve, backoffMs));
continue;
}
throw new Error(`Client error: ${response.status}`);
}
if (response.status >= 500) {
const backoffMs = Math.pow(2, attempt) * 1000;
await new Promise((resolve) => setTimeout(resolve, backoffMs));
continue;
}
} catch (error) {
if (attempt < maxRetries - 1) {
const backoffMs = Math.pow(2, attempt) * 1000;
await new Promise((resolve) => setTimeout(resolve, backoffMs));
continue;
}
throw error;
}
}
throw new Error('Max retries exceeded');
}import time
import requests
from typing import Optional
def request_with_retry(
url: str,
method: str = 'GET',
max_retries: int = 3,
**kwargs
) -> requests.Response:
"""Make HTTP request with exponential backoff retry"""
for attempt in range(max_retries):
try:
response = requests.request(method, url, **kwargs)
# Success
if response.ok:
return response
# Client errors - don't retry (except 429)
if 400 <= response.status_code < 500:
if response.status_code == 429:
# Rate limit - use exponential backoff
backoff_seconds = 2 ** attempt
time.sleep(backoff_seconds)
continue
# Other 4xx errors - raise immediately
response.raise_for_status()
# Server errors - retry with backoff
if response.status_code >= 500:
backoff_seconds = 2 ** attempt
time.sleep(backoff_seconds)
continue
except requests.RequestException as e:
# Network error - retry with backoff
if attempt < max_retries - 1:
backoff_seconds = 2 ** attempt
time.sleep(backoff_seconds)
continue
raise
raise Exception('Max retries exceeded')Log enough to reproduce the issue and to help support find it quickly:
console.error('Request failed:', {
url,
method: options?.method ?? 'GET',
status: response.status,
statusText: response.statusText,
requestId: response.headers.get('X-Request-Id'),
responseBody: await response.text(),
});Useful headers for debugging:
X-Request-Id: Unique request identifier for supportX-RateLimit-*: Rate limit informationContent-Type: Response content type
Validate your request locally before sending:
function validateVendorInput(vendor) {
const errors = [];
if (!vendor.name) {
errors.push({ field: 'name', message: 'Name is required' });
}
if (vendor.email && !isValidEmail(vendor.email)) {
errors.push({ field: 'email', message: 'Invalid email format' });
}
if (errors.length > 0) {
throw new ValidationError('Invalid input', errors);
}
}Test your error handling with scenarios you’ll see in production:
- Invalid authentication token (401)
- Nonexistent resource ID (404)
- Duplicate client key (409)
- Invalid field values (422)
- Rate limit exceeded (429)
- Rate Limits - Learn about API rate limiting
- Authentication - Set up OAuth credentials
- API Reference - See all error responses for each endpoint