This guide covers the error response format and error codes used by the PRIME API.
All errors return a JSON object with the following structure:
JSON
{
"code": 400,
"message": "Invalid quantity: must be positive",
"status": "INVALID_ARGUMENT",
"details": []
}
Field Type Description codeinteger HTTP status code messagestring Human-readable error description statusstring gRPC status code name detailsarray Additional error context (optional)
Code Status Description 400 INVALID_ARGUMENTRequest validation failed 401 UNAUTHENTICATEDAuthentication required or failed 403 PERMISSION_DENIEDAuthenticated but not authorized 404 NOT_FOUNDResource does not exist 409 ALREADY_EXISTSResource already exists 422 FAILED_PRECONDITIONBusiness rule violation 429 RESOURCE_EXHAUSTEDRate limit exceeded
Code Status Description 500 INTERNALUnexpected server error 503 UNAVAILABLEService temporarily unavailable
The API uses specific reason codes to indicate the type of error.
Reason Description UNAUTHENTICATEDMissing or invalid authentication MFA_REQUIREDMulti-factor authentication needed PERMISSION_DENIEDInsufficient permissions for operation ACCESS_REFUSEDAccess explicitly denied ACCOUNT_IS_SUSPENDEDAccount has been suspended
Reason Description INVALID_ARGUMENTSRequest parameters invalid INCORRECT_PARAMETER_VALUESpecific parameter value invalid WEAK_PASSWORDPassword does not meet requirements USERNAME_ALREADY_EXISTSUsername is taken
Reason Description RESOURCE_NOT_FOUNDRequested resource does not exist ALREADY_EXISTSResource already exists CONSTRAINT_IN_PLACEOperation blocked by constraint
Reason Description NOT_ENOUGH_FUNDSInsufficient balance for operation CANNOT_RESOLVE_PRICEUnable to determine execution price CLIP_SIZE_TOO_SMALLOrder quantity below minimum CLIP_SIZE_TOO_LARGEOrder quantity above maximum
Reason Description DEPOSIT_NOT_ENABLEDDeposits disabled for instrument DEPOSIT_ALREADY_IN_PROGRESSDuplicate deposit detected DEPOSIT_ALREADY_CANCELEDDeposit was already cancelled DEPOSIT_CANNOT_BE_CANCELEDDeposit cannot be cancelled WITHDRAWAL_NOT_ENABLEDWithdrawals disabled for instrument
Reason Description INTERNALInternal server error UNAVAILABLEService temporarily unavailable SERVER_EXHAUSTEDServer resources exhausted CAPTCHA_FAILEDCAPTCHA verification failed
Errors may include a resource name indicating which entity caused the error.
Resource Description USERUser account CLIENT_ACCOUNTClient account entity SUBACCOUNTSub-account/portfolio INSTRUMENTTradable asset PAIRTrading pair ORDERTrading order TRADEExecuted trade BALANCEAccount balance DEPOSITDeposit transaction WITHDRAWALWithdrawal transaction TRANSFERInternal transfer BANK_ACCOUNTBank account ADDRESSCrypto address DOCUMENTKYC document FEEFee calculation HOLDINGAsset holding LISTINGOTC listing EXPOSURE_LINERisk exposure
JSON
{
"code": 400,
"message": "quantity: must be greater than 0",
"status": "INVALID_ARGUMENT",
"details": []
}
JSON
{
"code": 401,
"message": "JWT token was invalid",
"status": "UNAUTHENTICATED",
"details": []
}
When MFA is enabled for a user but the TOTP code (challenge field) was not provided in the login request:
JSON
{
"code": 16,
"message": "MFA challenge required",
"details": [{
"@type": "type.googleapis.com/tgtraded.Error",
"reason": "MFA_REQUIRED"
}]
}
To resolve, re-submit the login request with the challenge field containing the TOTP code.
JSON
{
"code": 403,
"message": "User does not have trade permission on sub-account",
"status": "PERMISSION_DENIED",
"details": []
}
JSON
{
"code": 404,
"message": "Order not found",
"status": "NOT_FOUND",
"details": [{
"resource": "ORDER",
"id": "order-uuid"
}]
}
JSON
{
"code": 403,
"message": "Insufficient CHF balance: required 50000.00, available 25000.00",
"status": "FAILED_PRECONDITION",
"details": [{
"reason": "NOT_ENOUGH_FUNDS",
"resource": "BALANCE",
"required": "50000.00",
"available": "25000.00"
}]
}
JSON
{
"code": 429,
"message": "Rate limit exceeded. Retry after 60 seconds",
"status": "RESOURCE_EXHAUSTED",
"details": [{
"reason": "SERVER_EXHAUSTED",
"retry_after": 60
}]
}
Check HTTP status code first - Categorize by 4xx vs 5xx
Parse the error response - Extract message and details
Handle specific reasons - Implement logic for expected errors
Log unexpected errors - Include correlation ID for support
Implement retries for 5xx - Use exponential backoff
JavaScript
async function apiRequest(url, options) {
const response = await fetch(url, options);
if (!response.ok) {
const error = await response.json();
switch (error.status) {
case 'UNAUTHENTICATED':
// Refresh token or redirect to login
if (error.details?.some(d => d.reason === 'MFA_REQUIRED')) {
return handleMfaChallenge(error.details);
}
return redirectToLogin();
case 'PERMISSION_DENIED':
throw new Error('You do not have permission for this action');
case 'NOT_FOUND':
throw new Error('Resource not found');
case 'RESOURCE_EXHAUSTED':
// Implement retry with backoff
const retryAfter = error.details?.[0]?.retry_after || 60;
await sleep(retryAfter * 1000);
return apiRequest(url, options);
default:
throw new Error(error.message || 'An error occurred');
}
}
return response.json();
}
For transient errors (5xx, rate limits):
JavaScript
async function withRetry(fn, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
if (attempt === maxRetries - 1) throw error;
if (error.status >= 500 || error.status === 429) {
const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
await sleep(delay);
} else {
throw error; // Don't retry client errors
}
}
}
}
Every request is assigned a correlation ID for tracing. Include this ID when contacting support.
Check response headers: X-Correlation-ID
Check error details: may include correlation_id field
You can provide a correlation ID in the request:
Bash
curl -X GET "https://api.t-dx.com/api/rest/v1/orders" \
-H "Authorization: Bearer <token>" \
-H "X-Correlation-ID: your-uuid-here"