convoup Docs
Docs / Resources / Error Codes

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:

CodeStatusMeaningAction
TEMPLATE_NOT_FOUND404Template name doesn't exist for this WABAVerify the name via listTemplates() or dashboard
TEMPLATE_PARAM_MISMATCH400Wrong number of params for the templateCheck param count against template placeholders
TEMPLATE_REQUIRED422sendText() used with no open 24h windowSend an approved template instead
RECIPIENT_SUPPRESSED422Recipient is on the suppression list (blocked)Skip this number

Errors Without a Code

Some failures do not carry a code:

SituationStatusHow to Detect
Invalid request body (e.g. missing to, wrong param types)400err.details holds a Zod errors object; no code
Missing or invalid API key401err.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