Learn how to handle errors and HTTP status codes in the MVMNT API.
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 indicate a problem with the request.
| 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 indicate a problem with the MVMNT API servers.
| 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: Malformed request, invalid JSON, or missing required fields
Example:
{
"error": {
"code": "BAD_REQUEST",
"message": "Invalid JSON in request body"
}
}Solution:
- Verify JSON syntax is correct
- Check that all required fields are included
- Ensure field types match the API specification
Cause: Missing, expired, or invalid authentication token
Example:
{
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or expired access token"
}
}Solution:
- Obtain a new access token using
/oauth2/token - Include
Authorization: Bearer {token}header in all requests - Check that your client ID and secret are correct
Cause: Resource with the specified ID does not exist
Example:
{
"error": {
"code": "NOT_FOUND",
"message": "Vendor not found"
}
}Solution:
- Verify the resource ID is correct
- Check that the resource hasn't been deleted (see Soft Deletes)
- Use filter endpoints to find resources by other criteria
Cause: Duplicate client key or constraint violation
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"
}
]
}
}Solution:
- Use a different, unique client key
- Check if the resource already exists using the key
- For updates, use PATCH instead of POST
Cause: Request is valid but contains semantic errors (validation failures)
Example:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": [
{
"field": "email",
"message": "Invalid email format"
},
{
"field": "phone",
"message": "Phone number is required"
}
]
}
}Solution:
- Review the
detailsarray for specific field errors - Fix each validation error listed
- Consult the API reference for field requirements
Cause: Rate limit exceeded
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
Solution:
- Implement exponential backoff
- Reduce request frequency
- Batch operations where possible
- Contact support to increase rate limits
Cause: Unexpected error on the MVMNT API servers
Example:
{
"error": {
"code": "INTERNAL_ERROR",
"message": "An unexpected error occurred"
}
}Solution:
- Retry the request with exponential backoff
- If the error persists, contact support with the request details
- Check the status page for known issues
- Check HTTP status codes: Always check the status code before processing the response
- Handle all error types: Implement handlers for all possible error codes
- Parse error details: Use the
error.detailsarray for field-specific validation errors - Implement retries: Use exponential backoff for transient errors (500, 503, 429)
- Log error responses: Log the full error response for debugging
- Show user-friendly messages: Translate technical errors into user-friendly messages
- Don't retry 4xx errors: Client errors won't succeed on retry (except 429)
- Don't ignore error details: The
detailsarray contains important field-level information - Don't retry immediately: Use exponential backoff to avoid overwhelming the API
- Don't expose raw errors to users: Translate technical errors into user-friendly messages
- Don't continue on 401 errors: Obtain a new token before retrying
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);
// Success - return response
if (response.ok) {
return response;
}
// Client errors (4xx) - don't retry (except 429)
if (response.status >= 400 && response.status < 500) {
if (response.status === 429) {
// Rate limit - use exponential backoff
const backoffMs = Math.pow(2, attempt) * 1000;
await new Promise((resolve) => setTimeout(resolve, backoffMs));
continue;
}
// Other 4xx errors - throw immediately
throw new Error(`Client error: ${response.status}`);
}
// Server errors (5xx) - retry with backoff
if (response.status >= 500) {
const backoffMs = Math.pow(2, attempt) * 1000;
await new Promise((resolve) => setTimeout(resolve, backoffMs));
continue;
}
} catch (error) {
// Network error - retry with backoff
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')Always log the complete request and response for debugging:
console.error('Request failed:', {
url: request.url,
method: request.method,
headers: request.headers,
body: request.body,
status: response.status,
statusText: response.statusText,
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 various scenarios:
- 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