Error Reference
The ECOSIRE API uses conventional HTTP status codes and returns a consistent JSON error body on every failure.
Error Response Format
{
"statusCode": 422,
"message": "email must be a valid email address",
"error": "Unprocessable Entity"
}
| Field | Type | Description |
|---|---|---|
statusCode | number | HTTP status code |
message | string or string[] | Human-readable description; may be an array for validation failures |
error | string | HTTP status name |
Validation errors return an array in message:
{
"statusCode": 422,
"message": [
"name should not be empty",
"email must be a valid email address"
],
"error": "Unprocessable Entity"
}
HTTP Status Codes
2xx — Success
| Code | Name | When |
|---|---|---|
200 | OK | GET, PUT, PATCH successful |
201 | Created | POST created a resource |
204 | No Content | DELETE successful (no body) |
4xx — Client Errors
| Code | Name | Common Causes |
|---|---|---|
400 | Bad Request | Malformed JSON body; missing required field |
401 | Unauthorized | Missing Authorization header; expired token |
403 | Forbidden | Valid token but insufficient role (e.g., non-admin accessing admin route) |
404 | Not Found | Resource ID does not exist; wrong URL path |
409 | Conflict | Duplicate unique value (e.g., email already registered) |
422 | Unprocessable Entity | Body parsed correctly but fails validation rules |
429 | Too Many Requests | Rate limit exceeded for this endpoint |
5xx — Server Errors
| Code | Name | What to Do |
|---|---|---|
500 | Internal Server Error | Retry once; if persistent, contact support |
502 | Bad Gateway | Upstream service unavailable; retry with backoff |
503 | Service Unavailable | Scheduled maintenance or overload; check status.ecosire.com |
Common Error Scenarios
401 Unauthorized — Missing or invalid token
{
"statusCode": 401,
"message": "Unauthorized",
"error": "Unauthorized"
}
Solutions:
- Confirm the
Authorization: Bearer <token>header is present. - Check the token has not expired. Call
POST /auth/refreshwith your refresh token. - Regenerate your API key from the dashboard if it was rotated.
403 Forbidden — Insufficient permissions
{
"statusCode": 403,
"message": "Forbidden resource",
"error": "Forbidden"
}
Solutions:
- Admin-only routes (e.g.,
GET /admin/licenses) require a user with roleadminorsupport. - Portal routes (e.g.,
GET /portal/downloads) require an authenticated customer account. - Confirm you are using the correct API key for the environment (live vs. test).
404 Not Found
{
"statusCode": 404,
"message": "Contact not found",
"error": "Not Found"
}
Solutions:
- Verify the resource ID (UUID) in the URL path.
- Confirm the resource belongs to your organization (
organizationIdscoping). - IDs are case-sensitive UUIDs in the format
018e1234-abcd-7000-8000-000000000001.
409 Conflict — Duplicate resource
{
"statusCode": 409,
"message": "A contact with this email already exists",
"error": "Conflict"
}
Solutions:
- Use
PATCH /contacts/:idto update the existing resource instead of creating a new one. - Search first using
GET /[email protected].
422 Unprocessable Entity — Validation failure
{
"statusCode": 422,
"message": [
"price must be a positive number",
"sku should not be empty"
],
"error": "Unprocessable Entity"
}
Solutions:
- Review the field-level messages and correct each one.
- Consult the module reference page for the required request body schema.
429 Too Many Requests — Rate limit exceeded
{
"statusCode": 429,
"message": "ThrottlerException: Too Many Requests",
"error": "Too Many Requests"
}
Check the response headers:
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1710000120
Solutions:
- Wait until the Unix timestamp in
X-RateLimit-Resetbefore retrying. - Implement exponential backoff:
delay = Math.min(1000 * 2 ** attempt, 30000). - Cache responses where possible to reduce call frequency.
- Contact support to discuss higher limits for high-volume integrations.
License-specific errors
| Message | Cause | Solution |
|---|---|---|
License not found | Key does not exist | Verify the license key format: PREFIX-XXXX-XXXX-XXXX-XXXX |
License expired | Past expiry date | Renew the license from the dashboard |
Activation limit reached | All slots used | Deactivate an unused domain first |
License revoked | Manually revoked by admin | Contact ECOSIRE support |
Invalid signature | HMAC mismatch | Recompute the X-Ecosire-Signature header |
License suspended | Subscription payment failed | Update payment method in the dashboard |
Error Handling Best Practices
async function apiCall<T>(path: string, options?: RequestInit): Promise<T> {
const res = await fetch(`https://api.ecosire.com/api${path}`, {
...options,
headers: {
Authorization: `Bearer ${process.env.ECOSIRE_API_KEY}`,
'Content-Type': 'application/json',
...options?.headers,
},
});
if (!res.ok) {
const err = await res.json().catch(() => ({
statusCode: res.status,
message: res.statusText,
error: 'Unknown',
}));
// Retry on 429 after the reset window
if (res.status === 429) {
const reset = res.headers.get('X-RateLimit-Reset');
const waitMs = reset ? Number(reset) * 1000 - Date.now() : 60_000;
await new Promise((r) => setTimeout(r, Math.max(waitMs, 0)));
return apiCall(path, options); // single retry
}
throw Object.assign(new Error(Array.isArray(err.message) ? err.message.join('; ') : err.message), {
statusCode: err.statusCode,
});
}
// 204 No Content
if (res.status === 204) return undefined as T;
return res.json();
}