Error Handling Guide

The mCards API uses standard HTTP status codes and returns structured error responses. This guide covers common errors and best practices for handling them.

HTTP Status Codes

CodeMeaningDescription
200OKRequest succeeded
201CreatedResource created successfully
400Bad RequestInvalid request parameters
401UnauthorizedMissing or invalid authentication
403ForbiddenValid auth but insufficient permissions
404Not FoundResource does not exist
422Unprocessable EntityValidation error
429Too Many RequestsRate limit exceeded
500Internal Server ErrorServer-side error

Error Response Format

json
{
  "error": "Resource not found",
  "message": "No wallet found with UUID: abc-123",
  "code": "not_found",
  "details": {}
}

Common Error Scenarios

Authentication Errors (401)

json
{
  "error": "Unauthorized",
  "message": "HMAC signature verification failed",
  "code": "invalid_signature"
}

Permission Errors (403)

json
{
  "error": "Forbidden",
  "message": "Token scope 'partner:read' does not include 'cards:write'",
  "code": "insufficient_scope"
}

Validation Errors (422)

json
{
  "error": "Validation failed",
  "message": "One or more fields are invalid",
  "code": "validation_error",
  "details": {
    "cardholder_uuid": ["is required"],
    "program_uuid": ["must be a valid UUID"]
  }
}

Rate Limit Errors (429)

json
{
  "error": "Rate limit exceeded",
  "message": "120 requests per minute exceeded",
  "code": "rate_limited",
  "details": {
    "retry_after": 45,
    "limit": 120,
    "used": 120
  }
}

Handling Errors in the SDKs

TypeScript

typescript
const result = await client.cards.get('nonexistent-uuid');

if (!result.ok) {
  switch (result.status) {
    case 401:
      console.error('Authentication failed');
      break;
    case 403:
      console.error('Insufficient permissions');
      break;
    case 404:
      console.error('Card not found');
      break;
    case 429:
      const retryAfter = result.headers['retry-after'] || '60';
      console.error(\`Rate limited — retry after \${retryAfter}s\`);
      break;
    default:
      console.error(\`Error: \${result.status} — \${result.error}\`);
  }
  return;
}

console.log(result.data);

Python

python
from mcards_sdk.exceptions import McardsSdkError

# Check-based
result = client.cards.get("nonexistent-uuid")
if not result.ok:
    if result.status == 401:
        print("Authentication failed")
    elif result.status == 404:
        print("Card not found")
    elif result.status == 429:
        retry_after = result.headers.get("Retry-After", "60")
        print(f"Rate limited — retry after {retry_after}s")

# Exception-based
try:
    result = client.cards.get("nonexistent-uuid")
    result.raise_for_error()
except McardsSdkError as e:
    print(f"API error: {e} (status={e.status})")

Retry Strategy

typescript
async function withRetry(fn, maxRetries = 3, baseDelay = 1000) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const result = await fn();

    if (result.ok || (result.status >= 400 && result.status < 500 && result.status !== 429)) {
      return result;
    }

    if (attempt === maxRetries) return result;

    const delay = result.status === 429
      ? (parseInt(result.headers['retry-after'] || '60', 10) * 1000)
      : baseDelay * Math.pow(2, attempt) * (0.5 + Math.random() * 0.5);

    await new Promise(resolve => setTimeout(resolve, delay));
  }
}

Best Practices

  1. Always check result.ok before accessing result.data
  2. Handle 401 by refreshing credentials — Bearer tokens expire after 1 hour
  3. Implement exponential backoff for 429 and 5xx errors
  4. Log error details including status, error message, and request context
  5. Use idempotency keys for write operations to safely retry
  6. Set reasonable timeouts — do not wait indefinitely for API responses
  7. Monitor error rates — a spike in 5xx errors may indicate a platform issue