Error Codes

Complete reference for 4Quays API error codes and handling

All API errors follow a consistent format with error codes for programmatic handling.

Error Response Format

{
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable description"
  },
  "requestId": "req_abc123"
}

HTTP Status Codes

StatusMeaning
400Bad Request — Invalid input
401Unauthorized — Authentication failed
403Forbidden — Not permitted
404Not Found — Resource doesn't exist
429Too Many Requests — Rate limited
500Internal Server Error — Something went wrong

Authentication Errors (401)

AUTHENTICATION_FAILED

{
  "error": {
    "code": "AUTHENTICATION_FAILED",
    "message": "Invalid or expired API key"
  }
}

Causes:

  • API key is invalid
  • API key has been revoked
  • Authorization header is missing

Resolution:

  • Verify the API key is correct
  • Check if the key has been revoked in the dashboard
  • Ensure the Authorization header is properly formatted

API_KEY_REVOKED

{
  "error": {
    "code": "API_KEY_REVOKED",
    "message": "This API key has been revoked"
  }
}

Causes:

  • API key was explicitly revoked in the dashboard

Resolution:

  • Generate a new API key in the dashboard

Validation Errors (400)

INVALID_PAYLOAD

{
  "error": {
    "code": "INVALID_PAYLOAD",
    "message": "Payload must be a valid JSON value"
  }
}

Causes:

  • Payload is not valid JSON
  • Payload is missing
  • Payload is too large

Resolution:

  • Verify JSON syntax
  • Ensure Content-Type is application/json

INVALID_PROTECTED_PAYLOAD

{
  "error": {
    "code": "INVALID_PROTECTED_PAYLOAD",
    "message": "Payload is not a valid protected format"
  }
}

Causes:

  • Protected payload is malformed
  • Payload was modified after protection
  • Wrong payload passed to unprotect

Resolution:

  • Ensure protected payload is passed unchanged
  • Verify you're using the correct protected payload

INVALID_POLICY_NUMBER

{
  "error": {
    "code": "INVALID_POLICY_NUMBER",
    "message": "Policy number format is invalid"
  }
}

Causes:

  • Policy number contains invalid characters
  • Policy number is empty

Resolution:

  • Check policy number format

Resource Errors (404)

POLICY_NOT_FOUND

{
  "error": {
    "code": "POLICY_NOT_FOUND",
    "message": "Policy 'INVALID-POLICY' does not exist"
  }
}

Causes:

  • Policy number doesn't exist
  • Policy belongs to a different organization
  • Policy has been deleted

Resolution:

  • Verify policy number in the dashboard
  • Check organization context

KEY_NOT_FOUND

{
  "error": {
    "code": "KEY_NOT_FOUND",
    "message": "Key 'key_abc123' not found"
  }
}

Causes:

  • Key ID doesn't exist
  • Key has been retired
  • Key belongs to a different organization

Resolution:

  • Verify key exists in the dashboard
  • Check if key has been rotated

SERVICE_NOT_FOUND

{
  "error": {
    "code": "SERVICE_NOT_FOUND",
    "message": "Service 'svc_xyz' not found"
  }
}

Causes:

  • Service ID doesn't exist
  • Service has been deleted

Cryptographic Errors (400)

DECRYPTION_FAILED

{
  "error": {
    "code": "DECRYPTION_FAILED",
    "message": "Unable to decrypt payload with the configured key"
  }
}

Causes:

  • Payload was encrypted with a different key
  • Payload is corrupted
  • Wrong policy used for unprotect

Resolution:

  • Verify the correct policy is used
  • Check if key has been rotated since encryption

SIGNATURE_INVALID

{
  "error": {
    "code": "SIGNATURE_INVALID",
    "message": "Signature verification failed"
  }
}

Causes:

  • Payload was modified after signing
  • Wrong key used for verification
  • Signature is corrupted

Resolution:

  • This may indicate tampering — investigate
  • Verify correct policy is used

KEY_EXPIRED

{
  "error": {
    "code": "KEY_EXPIRED",
    "message": "Key 'key_abc123' has expired"
  }
}

Causes:

  • Key's validity period has ended

Resolution:

  • Rotate to a new key
  • Update key validity in dashboard

ALGORITHM_NOT_SUPPORTED

{
  "error": {
    "code": "ALGORITHM_NOT_SUPPORTED",
    "message": "Algorithm 'XYZ' is not supported"
  }
}

Causes:

  • Requested algorithm is not available

Resolution:

  • Use a supported algorithm
  • Check documentation for supported algorithms

Rate Limiting (429)

RATE_LIMIT_EXCEEDED

{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Rate limit exceeded. Retry after 60 seconds."
  }
}

Headers included:

Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1707473460

Resolution:

  • Wait for the specified retry period
  • Implement exponential backoff
  • Consider upgrading your rate limit tier

Server Errors (500)

INTERNAL_ERROR

{
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "An unexpected error occurred"
  }
}

Causes:

  • Server-side issue

Resolution:

  • Retry with exponential backoff
  • Contact support if persistent

SERVICE_UNAVAILABLE

{
  "error": {
    "code": "SERVICE_UNAVAILABLE",
    "message": "Service temporarily unavailable"
  }
}

Causes:

  • System maintenance
  • Temporary outage

Resolution:

  • Retry after a short delay
  • Check status page

Error Handling Best Practices

JavaScript/TypeScript

try {
  const result = await fourq.protect(payload, 'POLICY-123');
} catch (error) {
  switch (error.code) {
    case 'AUTHENTICATION_FAILED':
      // Re-authenticate or check API key
      break;
    case 'POLICY_NOT_FOUND':
      // Check policy configuration
      break;
    case 'RATE_LIMIT_EXCEEDED':
      // Wait and retry
      await sleep(error.retryAfter * 1000);
      break;
    case 'INTERNAL_ERROR':
      // Retry with backoff
      break;
    default:
      // Log and re-throw
      console.error('Unexpected error:', error);
      throw error;
  }
}

Retry Logic

async function protectWithRetry(payload, policy, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await fourq.protect(payload, policy);
    } catch (error) {
      if (error.code === 'RATE_LIMIT_EXCEEDED') {
        await sleep(error.retryAfter * 1000);
        continue;
      }
      if (error.code === 'INTERNAL_ERROR' && attempt < maxRetries) {
        await sleep(Math.pow(2, attempt) * 1000);
        continue;
      }
      throw error;
    }
  }
}

Request IDs

Always log the requestId from error responses:

try {
  await fourq.protect(payload, 'POLICY');
} catch (error) {
  console.error(`Error ${error.code} [${error.requestId}]: ${error.message}`);
  // Use requestId when contacting support
}