Error Codes
All failed API responses throw a typed ConvoupError with structured information.
Error Object
TypeScript
class ConvoupError extends Error {
readonly status: number; // HTTP status code (400, 404, 422, 500)
readonly code?: string; // Machine-readable error code
readonly details?: unknown; // Additional error context
}
Basic Error Handling
TypeScript
import { Convoup, ConvoupError } from 'convoup';
try {
await client.sendOtp({
to: '+918851479441',
template: 'test_welcome_template',
code: '123456',
});
} catch (err) {
if (err instanceof ConvoupError) {
console.error(`Status: ${err.status}`);
console.error(`Code: ${err.code}`);
console.error(`Message: ${err.message}`);
}
}
Error Codes Table
These are the machine-readable code values the API returns:
| Code | Status | Meaning | Action |
|---|---|---|---|
TEMPLATE_NOT_FOUND | 404 | Template name doesn't exist for this WABA | Verify the name via listTemplates() or dashboard |
TEMPLATE_PARAM_MISMATCH | 400 | Wrong number of params for the template | Check param count against template placeholders |
TEMPLATE_REQUIRED | 422 | sendText() used with no open 24h window | Send an approved template instead |
RECIPIENT_SUPPRESSED | 422 | Recipient is on the suppression list (blocked) | Skip this number |
Errors Without a Code
Some failures do not carry a code:
| Situation | Status | How to Detect |
|---|---|---|
Invalid request body (e.g. missing to, wrong param types) | 400 | err.details holds a Zod errors object; no code |
| Missing or invalid API key | 401 | err.status === 401 |
Fallback strategy
Always branch on err.status as a fallback when err.code is undefined.
Error Decision Tree
graph TD
A[catch err] --> B{err instanceof ConvoupError?}
B -- No --> C[Unexpected error - log and alert]
B -- Yes --> D{err.code defined?}
D -- Yes --> E{err.code}
E -- TEMPLATE_NOT_FOUND --> F[Verify template name in dashboard]
E -- TEMPLATE_PARAM_MISMATCH --> G[Check param count vs placeholders]
E -- TEMPLATE_REQUIRED --> H[Send approved template instead]
E -- RECIPIENT_SUPPRESSED --> I[Remove number from list]
D -- No --> J{err.status}
J -- 400 --> K[Invalid request body - check err.details]
J -- 401 --> L[Bad API key - check dashboard credentials]
J -- Other --> M[Log status and message]
Handling Specific Errors
TypeScript
try {
await client.sendInvoice({
to: '+918851479441',
template: 'test_welcome_template',
params: ['INV-1234'],
pdfUrl: 'https://files.example.com/invoices/INV-1234.pdf',
});
} catch (err) {
if (err instanceof ConvoupError) {
switch (err.code) {
case 'RECIPIENT_SUPPRESSED':
console.log('Recipient suppressed, skipping.');
break;
case 'TEMPLATE_PARAM_MISMATCH':
console.error('Param mismatch:', err.message);
console.error('Details:', err.details);
break;
case 'TEMPLATE_NOT_FOUND':
console.error('Template not found. Create it in the Convoup dashboard.');
break;
default:
console.error(`API error ${err.status}: ${err.message}`);
}
} else {
console.error('Unexpected error:', err);
}
}
Common Scenarios
Invalid Request Body
TypeScript
// Missing 'to' field - err.details shows Zod validation errors
catch (err) {
if (err instanceof ConvoupError && !err.code) {
console.error('Validation errors:', err.details);
}
}
API Key Issues
TypeScript
// 401 response - key is wrong or missing
catch (err) {
if (err instanceof ConvoupError && err.status === 401) {
console.error('Check your CONVOUP_API_KEY environment variable');
}
}
Next Steps
- FAQ & Troubleshooting - Common issues and solutions
- Methods Reference - All SDK methods
- Bulk Sending - Error handling in batch operations
