Error Codes
The SMS Gateway API uses standard HTTP status codes and returns structured JSON error responses to help you quickly diagnose and resolve issues.
Error Response Format
All error responses follow a consistent JSON format:
error-response.json
1{2 "statusCode": 400,3 "message": ["phone must be a valid phone number"],4 "error": "Bad Request",5 "timestamp": "2025-01-15T10:30:00.000Z",6 "path": "/api/v1/sms/send"7}statusCodeThe HTTP status code of the error.messageArray of human-readable error messages.errorThe HTTP status text.timestampISO 8601 timestamp when the error occurred.pathThe API endpoint path that returned the error.HTTP Status Codes
| Status | Meaning |
|---|---|
200 | Success |
400 | Bad Request |
401 | Unauthorized |
403 | Forbidden |
404 | Not Found |
409 | Conflict |
422 | Unprocessable Entity |
429 | Too Many Requests |
500 | Internal Server Error |
Common Error Scenarios
Invalid Phone Number Format
422
Phone number must be in E.164 format (e.g. +251911234567)
error.json
1{2 "statusCode": 422,3 "message": ["phone must be a valid E.164 phone number"],4 "error": "Unprocessable Entity",5 "timestamp": "2025-01-15T10:30:00.000Z",6 "path": "/api/v1/sms/send"7}Insufficient Wallet Balance
402
Your wallet does not have enough funds for this operation
error.json
1{2 "statusCode": 402,3 "message": ["Insufficient wallet balance. Required: 0.012 ETB, Available: 0.005 ETB"],4 "error": "Payment Required",5 "timestamp": "2025-01-15T10:30:00.000Z",6 "path": "/api/v1/sms/send"7}Expired API Key
401
The API key has passed its expiration date
error.json
1{2 "statusCode": 401,3 "message": ["API key has expired"],4 "error": "Unauthorized",5 "timestamp": "2025-01-15T10:30:00.000Z",6 "path": "/api/v1/sms/send"7}Rate Limit Exceeded
429
Too many requests in the current time window
error.json
1{2 "statusCode": 429,3 "message": ["Rate limit exceeded. Try again in 45 seconds"],4 "error": "Too Many Requests",5 "timestamp": "2025-01-15T10:30:00.000Z",6 "path": "/api/v1/sms/send"7}Check the Retry-After header for the recommended wait time before retrying.
Invalid OTP Code
400
The provided OTP code is incorrect or has expired
error.json
1{2 "statusCode": 400,3 "message": ["Invalid OTP code", "Maximum verification attempts exceeded"],4 "error": "Bad Request",5 "timestamp": "2025-01-15T10:35:30.000Z",6 "path": "/api/v1/otp/verify"7}Validation Error (Multiple Fields)
400
Request body failed validation on multiple fields
error.json
1{2 "statusCode": 400,3 "message": [4 "to must be a valid phone number",5 "body must be between 1 and 1600 characters",6 "from must not be empty"7 ],8 "error": "Bad Request",9 "timestamp": "2025-01-15T10:30:00.000Z",10 "path": "/api/v1/sms/send"11}Business Error Codes
In addition to HTTP status codes, the API uses specific business error codes for programmatic error handling:
| Code | HTTP Status |
|---|---|
INVALID_PHONE_FORMAT | 422 |
INSUFFICIENT_BALANCE | 402 |
API_KEY_EXPIRED | 401 |
API_KEY_DISABLED | 401 |
API_KEY_REVOKED | 401 |
IP_NOT_WHITELISTED | 403 |
RATE_LIMIT_EXCEEDED | 429 |
OTP_EXPIRED | 400 |
OTP_INVALID | 400 |
OTP_MAX_ATTEMPTS | 400 |
WEBHOOK_URL_UNREACHABLE | 422 |
DUPLICATE_WEBHOOK | 409 |
CAMPAIGN_INACTIVE | 409 |
CONTACT_BLOCKED | 403 |
Handling Errors in Code
error-handling.ts
1interface ApiError {2 statusCode: number;3 message: string[];4 error: string;5 timestamp: string;6 path: string;7}89async function sendSms(body: unknown): Promise<Response> {10 const response = await fetch('https://api.smsgateway.com/api/v1/sms/send', {11 method: 'POST',12 headers: {13 'Authorization': 'Bearer sg_live_your_key',14 'Content-Type': 'application/json',15 },16 body: JSON.stringify(body),17 });1819 if (!response.ok) {20 const error: ApiError = await response.json();2122 switch (error.statusCode) {23 case 401:24 // Handle auth errors (expired/invalid key)25 console.error('Authentication failed:', error.message);26 break;27 case 402:28 // Handle insufficient balance29 console.error('Insufficient balance:', error.message);30 break;31 case 429:32 // Handle rate limits33 const retryAfter = response.headers.get('Retry-After');34 console.error(`Rate limited. Retry after ${retryAfter}s`);35 break;36 default:37 console.error('API error:', error.message);38 }39 throw error;40 }4142 return response;43}