Error Reference
All API errors return a JSON object with a single error field. Use the HTTP status code to categorize the error, and the message to show actionable feedback to users or for debugging.
Error format
JSON
{
"error": "order not found"
}HTTP status codes
| Code | Name | Meaning |
|---|---|---|
| 200 | OK | Request succeeded |
| 201 | Created | Resource created successfully |
| 400 | Bad Request | Invalid parameters — fix the request before retrying |
| 401 | Unauthorized | Missing or invalid authentication |
| 403 | Forbidden | Authenticated but not authorized for this resource |
| 404 | Not Found | Resource does not exist or you don't have access |
| 409 | Conflict | Resource conflict (e.g., duplicate idempotency key) |
| 429 | Too Many Requests | Rate limit exceeded — check Retry-After header |
| 500 | Internal Server Error | Server-side error — retry with backoff |
Authentication errors
| HTTP Status | Error message | Description |
|---|---|---|
| 401 | missing authorization header | No Authorization header was provided. |
| 401 | invalid authorization format | The Authorization header format is incorrect. |
| 401 | api key not found | The key_id does not match any active API key. |
| 401 | api key is inactive | The API key has been revoked. |
| 401 | invalid signature | The HMAC signature does not match. |
| 401 | request timestamp too old | The timestamp in the Authorization header is more than 5 minutes in the past. |
Order errors
POST /v1/orders
| HTTP Status | Error message | Description |
|---|---|---|
| 400 | invalid amount | Amount is not a valid positive decimal. |
| 400 | unsupported currency | Currency must be "USDT". |
| 400 | unsupported network | Network must be "TRC20" or "ERC20". |
| 400 | no available address | No blockchain address is available for assignment. Retry in a few moments. |
| 409 | duplicate idempotency key | An order with this idempotency_key already exists with different parameters. |
GET /v1/orders/:id
| HTTP Status | Error message | Description |
|---|---|---|
| 404 | order not found | The order ID does not exist or belongs to a different merchant. |
| 403 | forbidden | The order belongs to a different merchant account. |
Withdrawal errors
POST /v1/withdrawals
| HTTP Status | Error message | Description |
|---|---|---|
| 400 | insufficient balance | Available balance is less than the requested withdrawal amount plus fees. |
| 400 | invalid destination address | The destination address is not a valid USDT address for the specified network. |
| 400 | amount below minimum | Withdrawal amount is below the minimum threshold. |
Webhook errors
| HTTP Status | Error message | Description |
|---|---|---|
| 400 | invalid webhook url | URL must be HTTPS. |
| 400 | invalid event type | One or more event types in the events array are not recognized. |
| 404 | webhook not found | The webhook ID does not exist. |
Rate limiting
The API allows 60 requests per minute per API key. When the limit is exceeded:
429 response headers
HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/json
{"error": "rate limit exceeded"}💡Implement exponential backoff in your client. After receiving a 429, wait at least the number of seconds in the
Retry-After header before retrying.Error handling
JavaScript
async function apiRequest(method, path, body) {
const response = await fetch(`https://api.mpchat.com${path}`, {
method,
headers: {
"Content-Type": "application/json",
"Authorization": buildAuthHeader(),
},
body: body ? JSON.stringify(body) : undefined,
});
if (response.ok) return response.json();
const error = await response.json().catch(() => ({ error: "Unknown error" }));
switch (response.status) {
case 401:
throw new AuthError(error.error);
case 429:
const retryAfter = response.headers.get("Retry-After");
throw new RateLimitError(error.error, parseInt(retryAfter ?? "60"));
case 500:
throw new ServerError(error.error);
default:
throw new ApiError(error.error, response.status);
}
}