██╗███╗ ██╗██╗ ██╗███╗ ██╗███████╗
██║████╗ ██║██║ ██║████╗ ██║██╔════╝
██║██╔██╗ ██║██║ ██║██╔██╗ ██║█████╗
██║██║╚██╗██║██║ ██║██║╚██╗██║██╔══╝
██║██║ ╚████║███████╗██║██║ ╚████║███████╗
╚═╝╚═╝ ╚═══╝╚══════╝╚═╝╚═╝ ╚═══╝╚══════╝
A complete, type-safe TypeScript SDK for the Inline message queue service. Publish messages, manage delivery, and query system status with full TypeScript support.
Version: 0.2.1 | License: MIT
- Overview
- Why Inline SDK Exists
- What It Does
- Installation
- Setup
- Quickstart
- API Reference
- Types and Interfaces
- Error Handling
- Examples
- Best Practices
The Inline SDK is a lightweight, type-safe TypeScript client for interacting with the Inline message queue API. It provides a clean, intuitive API for publishing messages to webhook endpoints with automatic retry logic, scheduling, and comprehensive error tracking.
- ✅ Type-Safe – Full TypeScript with complete type definitions
- ✅ Zero Dependencies – Native Bun/Node.js fetch API
- ✅ Validation – Built-in Zod schema validation
- ✅ Error Handling – Specific error types for each failure scenario
- ✅ Scheduling – Relative delays and absolute time scheduling
- ✅ Timezone Support – IANA timezone-aware scheduling
- ✅ Comprehensive API – Methods for publishing, tracking, monitoring, and debugging
The Inline SDK solves the problem of reliably delivering webhooks and asynchronous messages with:
- Reliable Delivery - Automatic retries with exponential backoff for failed callbacks
- Message Tracking - Complete audit trail with event timeline for each message
- Flexible Scheduling - Schedule messages for future delivery with timezone support
- Error Visibility - Detailed error tracking and dead-letter queue for failed messages
- Developer Experience - Type-safe API prevents runtime errors and provides IDE autocomplete
- 📧 Webhook Delivery - Reliably deliver webhooks to external systems
- 🔔 Event Notifications - Send event notifications with guaranteed delivery
- ⏰ Scheduled Tasks - Queue messages for delivery at specific times
- 📊 Audit Logs - Track complete history of message processing
- 🚨 Error Monitoring - Identify and debug delivery failures
- 🔄 Retry Management - Automatic and manual retry of failed deliveries
The Inline SDK provides:
- Publish messages to callback URLs with JSON payload
- Immediate or scheduled delivery (relative delay or absolute time)
- Custom HTTP headers forwarded to callback endpoints
- Timezone-aware scheduling with IANA timezone support
- Retrieve individual messages with full metadata
- List messages with pagination and status filtering
- View complete event timeline for any message
- Manually retry failed or dead-letter messages
- Check API health status
- Get readiness status for orchestration systems
- Monitor message distribution by status
- View aggregated error statistics
- Track errors for specific messages
- Get errors grouped by error code
- Access dead-letter queue for failed messages
- Review database and storage configuration
npm install @dnl-fm/inline-sdk
# or
pnpm add @dnl-fm/inline-sdk
# or
yarn add @dnl-fm/inline-sdknpx jsr add @dnl-fm/inline-sdk
# or
bunx jsr add @dnl-fm/inline-sdkbun add @dnl-fm/inline-sdkCreate an InlineClient instance with your API URL and authentication token:
import { InlineClient } from '@dnl-fm/inline-sdk';
const client = new InlineClient({
apiUrl: 'https://api.inline.example.com',
token: 'your-api-token',
timeout: 30000 // Optional: request timeout in milliseconds (default: 30000)
});The SDK uses Bearer token authentication. Provide your API token when initializing:
const client = new InlineClient({
apiUrl: process.env.INLINE_API_URL,
token: process.env.INLINE_API_TOKEN
});All SDK methods are async and throw specific error types. Always wrap calls in try-catch:
import {
InlineClient,
ValidationError,
NotFoundError,
AuthenticationError,
NetworkError
} from '@dnl-fm/inline-sdk';
const client = new InlineClient({ apiUrl: '...', token: '...' });
try {
const response = await client.publish(url, payload);
console.log(`Published: ${response.id}`);
} catch (error) {
if (error instanceof ValidationError) {
console.error('Invalid input:', error.message);
} else if (error instanceof NotFoundError) {
console.error('Resource not found:', error.message);
} else if (error instanceof AuthenticationError) {
console.error('Authentication failed - check your token');
} else if (error instanceof NetworkError) {
console.error('Network error:', error.message);
} else {
console.error('Unexpected error:', error);
}
}Publish a message for immediate delivery:
const response = await client.publish(
'https://webhook.example.com/events',
{
event: 'user.created',
userId: 123,
email: 'user@example.com'
}
);
console.log(`Message published: ${response.id}`);
console.log(`Created at: ${response.created_at}`);
console.log(`Timezone: ${response.timezone}`);Deliver the message 5 minutes from now:
const response = await client.publish(
'https://webhook.example.com/events',
{ event: 'order.shipped', orderId: 456 },
{
delay: '5m' // Supported units: ms, s, m, h, d
}
);const response = await client.publish(
'https://webhook.example.com/events',
{ event: 'order.shipped', orderId: 456 },
{
notBefore: '2024-10-28T14:30:00Z' // ISO 8601 UTC format
}
);const response = await client.publish(
'https://webhook.example.com/events',
{ event: 'daily.digest', userId: 123 },
{
notBefore: '2024-10-28T09:00:00', // Naive ISO time (no timezone)
timezone: 'America/New_York' // IANA timezone identifier
}
);Forward custom headers to the callback endpoint:
const response = await client.publish(
'https://webhook.example.com/events',
{ event: 'payment.completed', amount: 99.99 },
{
headers: {
'X-API-Key': 'webhook-secret-key',
'X-Request-ID': crypto.randomUUID()
}
}
);Choose which HTTP method to use when delivering to the callback endpoint:
// Use PUT for callback instead of default POST
const response = await client.publish(
'https://webhook.example.com/events',
{ event: 'resource.updated', resourceId: 789 },
{
method: 'PUT' // GET, POST, PUT, PATCH, DELETE
}
);
// GET callback (no payload, webhook-style)
const response = await client.publish(
'https://webhook.example.com/ping',
{ }, // Empty payload for GET
{
method: 'GET'
}
);
// DELETE callback
const response = await client.publish(
'https://webhook.example.com/resources/123',
{ reason: 'cleanup' },
{
method: 'DELETE'
}
);Get a specific message with full metadata:
const message = await client.getMessage('message_01234567890abcdefghijklmn');
console.log(`Status: ${message.status}`); // pending, processing, completed, failed, dead_letter
console.log(`Retry count: ${message.retry_count}`);
console.log(`Max retries: ${message.max_retries}`);
console.log(`Callback URL: ${message.callback_url}`);
console.log(`Payload:`, message.payload);
if (message.last_error) {
console.log(`Last error: ${message.last_error.error_code} - ${message.last_error.error_message}`);
}Get paginated list of messages with optional filtering:
// Get first 10 messages
const result = await client.listMessages();
// Get completed messages with pagination
const result = await client.listMessages({
limit: 50,
offset: 100,
status: 'completed'
});
console.log(`Total: ${result.pagination.total}`);
console.log(`Has more: ${result.pagination.hasMore}`);
for (const message of result.messages) {
console.log(`${message.id}: ${message.status}`);
}View the complete event history for a message:
const timeline = await client.getMessageTimeline('message_01234567890abcdefghijklmn');
console.log(`Total events: ${timeline.event_count}`);
for (const event of timeline.events) {
const timestamp = new Date(event.timestamp);
console.log(`[${timestamp.toISOString()}] ${event.type}`);
if (event.details) {
console.log(` Details:`, event.details);
}
}
// Example output:
// Total events: 5
// [2024-10-21T10:00:00.000Z] MESSAGE_RECEIVED
// [2024-10-21T10:00:05.000Z] MESSAGE_SCHEDULED
// [2024-10-21T10:05:00.000Z] DELIVERY_ATTEMPT
// [2024-10-21T10:05:02.000Z] MESSAGE_COMPLETEDCheck API health and readiness:
// Get detailed health status
const health = await client.getHealth();
console.log(`Health status: ${health.status}`); // healthy, degraded, unhealthy
console.log(`Uptime (seconds): ${health.uptime}`);
if (health.components?.http) {
console.log(`HTTP component: ${health.components.http.status}`);
}
// Get readiness status (for Kubernetes/orchestration)
const ready = await client.getHealthReady();
console.log(`Ready: ${ready.status === 'ready'}`);The main class for interacting with the Inline API.
constructor(config: InlineClientConfig)Parameters:
config.apiUrl(string, required) - Base URL of the Inline APIconfig.token(string, required) - Bearer token for authenticationconfig.timeout(number, optional) - Request timeout in milliseconds (default: 30000)
Throws: ValidationError if apiUrl or token is missing
Example:
const client = new InlineClient({
apiUrl: 'https://api.inline.example.com',
token: 'sk_live_abcd1234',
timeout: 45000
});Publish a message to the queue for delivery to a callback URL.
publish(
callbackUrl: string,
payload: MessagePayload,
options?: PublishOptions
): Promise<CreateMessageResponse>Parameters:
callbackUrl(string, required) - Target URL for HTTP POST deliverypayload(Record<string, any>, required) - JSON-serializable message dataoptions(PublishOptions, optional) - Publishing configuration
Options:
delay(string) - Relative delay: "5m", "1h", "30s", "2d", etc.notBefore(string) - ISO 8601 absolute scheduling datetimetimezone(string) - IANA timezone identifier for contextheaders(Record<string, string>) - Custom HTTP headers to forwardmethod('GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE') - HTTP method for callback (default: POST)
Returns: CreateMessageResponse with:
id- ULID-based message identifiercreated_at- ISO 8601 creation timestamptimezone- Timezone used for scheduling
Throws:
ValidationError- If callbackUrl or payload is missingAuthenticationError- If token is invalidNetworkError- If unable to reach APIRateLimitError- If rate limit exceeded
Examples:
// Immediate delivery
const msg = await client.publish('https://webhook.example.com/events', {
event: 'test',
timestamp: new Date()
});
// Scheduled delivery (5 minutes from now)
const msg = await client.publish(
'https://webhook.example.com/events',
{ event: 'digest' },
{ delay: '5m' }
);
// Scheduled with timezone
const msg = await client.publish(
'https://webhook.example.com/events',
{ event: 'daily_summary' },
{
notBefore: '2024-10-28T09:00:00',
timezone: 'America/New_York'
}
);
// With custom headers
const msg = await client.publish(
'https://webhook.example.com/events',
{ event: 'payment' },
{
headers: { 'X-Webhook-Secret': 'abc123' }
}
);Retrieve a specific message by ID.
getMessage(messageId: string): Promise<MessageResponse>Parameters:
messageId(string, required) - ULID of the message
Returns: MessageResponse with complete metadata:
id- Message identifiercallback_url- Target endpointpayload- Message datastatus- Current statusretry_count- Number of delivery attemptsnext_retry_at- Timestamp of next retrylast_error- Most recent error details
Throws:
ValidationError- If messageId is emptyNotFoundError- If message doesn't existAuthenticationError- If token is invalid
Example:
const msg = await client.getMessage('message_01234567890abcdefghijklmn');
console.log(`Status: ${msg.status}`);
console.log(`Attempts: ${msg.retry_count}/${msg.max_retries}`);Get a paginated list of messages.
listMessages(options?: ListMessagesOptions): Promise<ListMessagesResponse>Parameters:
limit(number, optional) - Results per page (default: 10, max: 100)offset(number, optional) - Starting position (default: 0)status(MessageStatus, optional) - Filter by status
Returns: ListMessagesResponse with:
messages[]- Array of MessageResponse objectspagination.total- Total number of messagespagination.hasMore- Whether more results available
Example:
const result = await client.listMessages({
limit: 50,
status: 'failed'
});
console.log(`Found ${result.pagination.total} failed messages`);Get the complete event timeline for a message.
getMessageTimeline(messageId: string): Promise<TimelineResponse>Parameters:
messageId(string, required) - ULID of the message
Returns: TimelineResponse with:
message_id- The message IDevent_count- Total number of eventsevents[]- Array of TimelineEvent objects
Event Types:
MESSAGE_RECEIVED- Message received by APIMESSAGE_SCHEDULED- Message scheduled for deliveryDELIVERY_ATTEMPT- Callback delivery attemptedMESSAGE_COMPLETED- Message successfully deliveredMESSAGE_FAILED- Message failed to deliverMESSAGE_RETRIED- Message was retried
Example:
const timeline = await client.getMessageTimeline('message_123');
for (const event of timeline.events) {
console.log(`${event.type} at ${event.timestamp}`);
}Manually trigger delivery retry for a failed or dead-letter message.
retryMessage(messageId: string): Promise<RetryMessageResponse>Parameters:
messageId(string, required) - ULID of the message to retry
Returns: RetryMessageResponse with:
success- Whether retry was queuedmessage_id- The retried message IDmessage- Confirmation message
Throws:
ValidationError- If messageId is emptyNotFoundError- If message doesn't exist
Example:
const result = await client.retryMessage('message_123');
if (result.success) {
console.log(`Retry queued for ${result.messageId}`);
}Cancel a pending message before it's processed. Once cancelled, the message will not be delivered to the callback endpoint.
cancelMessage(messageId: string): Promise<MessageResponse>Parameters:
messageId(string, required) - ULID of the message to cancel
Returns: MessageResponse with status set to "cancelled"
Throws:
ValidationError- If messageId is emptyNotFoundError- If message doesn't existInvalidStateError- If message is not inpendingstatus (only pending messages can be cancelled)
Example:
try {
const result = await client.cancelMessage('message_123');
console.log(`Cancelled: ${result.id}`);
console.log(`Status: ${result.status}`); // 'cancelled'
} catch (error) {
if (error instanceof InvalidStateError) {
console.error('Cannot cancel non-pending message');
}
}Constraints:
- Only
pendingmessages can be cancelled - Once a message is
processing,completed,failed, ordead_letter, it cannot be cancelled - Cancelled messages are recorded in the timeline with a
MESSAGE_CANCELLEDevent
Get the health status of the API and its components.
getHealth(): Promise<HealthStatus>Returns: HealthStatus with:
status- "healthy" | "degraded" | "unhealthy"timestamp- ISO 8601 check timestampuptime- Uptime in secondscomponents.http- HTTP component status (optional)
Example:
const health = await client.getHealth();
if (health.status === 'healthy') {
console.log('API is fully operational');
}Get readiness status for Kubernetes/orchestration systems.
getHealthReady(): Promise<HealthReadyResponse>Returns: HealthReadyResponse with:
status- "ready" if all dependencies initializedtimestamp- Unix timestamp in milliseconds
Example:
const ready = await client.getHealthReady();
if (ready.status === 'ready') {
console.log('API is ready to serve requests');
}Get message processing and bandwidth statistics broken down by time period.
getStats(): Promise<StatsResponse>Returns: StatsResponse with:
processed_count- Total number of messages processedsuccess_count- Number of successful deliveriesfailed_count- Number of failed deliverieslast_processed_at- ISO 8601 timestamp of last processed messagelast_failed_at- ISO 8601 timestamp of last failed messagequeue_depth- Current number of pending messagessince_start- Uptime and performance metrics since service startbandwidth- Bandwidth usage broken down by time period:day_bytes- Total bandwidth used in last 24 hoursweek_bytes- Total bandwidth used in last 7 daysmonth_bytes- Total bandwidth used in last 30 daystotal_bytes- Total bandwidth used since service start
Example:
const stats = await client.getStats();
console.log(`Processed: ${stats.processed_count}`);
console.log(`Success: ${stats.success_count}`);
console.log(`Failed: ${stats.failed_count}`);
console.log(`Queue depth: ${stats.queue_depth}`);
console.log(`Total bandwidth: ${stats.bandwidth.total_bytes} bytes`);
console.log(`Today's bandwidth: ${stats.bandwidth.day_bytes} bytes`);Get aggregated error statistics grouped by error code.
getErrorStats(): Promise<ErrorStatsResponse>Returns: ErrorStatsResponse with:
summary.total_errors- Total error countsummary.error_types- Number of unique error typeserrors[]- Array of errors with count and average duration
Example:
const stats = await client.getErrorStats();
console.log(`Total errors: ${stats.summary.total_errors}`);
for (const err of stats.errors) {
console.log(`${err.error_code}: ${err.count} times`);
}Get all errors of a specific error code.
getErrorsByCode(code: string): Promise<CallbackError[]>Parameters:
code(string, required) - Error code to filter by (e.g., 'TIMEOUT', 'HTTP_5XX')
Returns: Array of CallbackError objects with:
id- Error identifiermessage_id- Associated message IDerror_code- Error classificationerror_message- Human-readable descriptionhttp_status_code- HTTP status code (if applicable)attempt_number- Which retry attemptduration_ms- Request duration
Error Codes:
HTTP_4XX- Client error from callback (4xx)HTTP_5XX- Server error from callback (5xx)TIMEOUT- Request timeoutECONNREFUSED- Connection refusedECONNRESET- Connection resetENOTFOUND- DNS resolution failedDNS_FAILURE- DNS lookup errorNETWORK_ERROR- Generic network errorUNKNOWN- Unknown error
Example:
const timeouts = await client.getErrorsByCode('TIMEOUT');
console.log(`Found ${timeouts.length} timeout errors`);Get all errors for a specific message.
getMessageErrors(messageId: string): Promise<CallbackError[]>Parameters:
messageId(string, required) - ULID of the message
Returns: Array of CallbackError objects for that message
Example:
const errors = await client.getMessageErrors('message_123');
for (const err of errors) {
console.log(`Attempt ${err.attempt_number}: ${err.error_code}`);
}Get all messages in the dead-letter queue (failed all retries).
getDeadLetterErrors(): Promise<DeadLetterResponse>Returns: DeadLetterResponse with:
summary.total_messages- Count of DLQ messagessummary.total_errors- Count of errors in DLQmessages- Errors grouped by message ID
Example:
const dlq = await client.getDeadLetterErrors();
console.log(`${dlq.summary.total_messages} messages in DLQ`);
for (const [msgId, errors] of Object.entries(dlq.messages)) {
console.log(`Message ${msgId}: ${errors.length} errors`);
}Get all messages grouped by their current status (debug endpoint).
getDebugMessages(): Promise<DebugMessagesResponse>Returns: DebugMessagesResponse with messages grouped by status:
pending- Waiting to be processedprocessing- Currently being deliveredcompleted- Successfully deliveredfailed- Failed but has retries leftdead_letter- Failed all retries
Example:
const debug = await client.getDebugMessages();
console.log(`Pending: ${debug.summary.pending}`);
console.log(`Failed: ${debug.summary.failed}`);
console.log(`Completed: ${debug.summary.completed}`);Get database and storage configuration (debug endpoint).
getDebugStats(): Promise<DebugStatsResponse>Returns: DebugStatsResponse with:
database- Database typestorage- Storage configurationlocation- Database file pathnote- Additional notes
Example:
const stats = await client.getDebugStats();
console.log(`Database: ${stats.database}`);
console.log(`Location: ${stats.location}`);// Message status in the queue
type MessageStatus = 'pending' | 'processing' | 'completed' | 'failed' | 'dead_letter' | 'cancelled';
// Event types in timeline
type EventType = 'MESSAGE_RECEIVED' | 'MESSAGE_SCHEDULED' | 'DELIVERY_ATTEMPT'
| 'MESSAGE_COMPLETED' | 'MESSAGE_FAILED' | 'MESSAGE_RETRIED';
// Error codes for callback failures
type ErrorCode = 'HTTP_4XX' | 'HTTP_5XX' | 'TIMEOUT' | 'ECONNREFUSED'
| 'ECONNRESET' | 'ENOTFOUND' | 'DNS_FAILURE' | 'NETWORK_ERROR' | 'UNKNOWN';
// Message payload
type MessagePayload = Record<string, any>;
// HTTP headers
type CallbackHeaders = Record<string, string>;interface InlineClientConfig {
apiUrl: string; // API base URL
token: string; // Bearer token
timeout?: number; // Request timeout (ms)
}
interface PublishOptions {
headers?: CallbackHeaders; // Custom headers to forward
delay?: string; // Relative delay (e.g., "5m")
notBefore?: string; // ISO 8601 absolute time
timezone?: string; // IANA timezone
method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'; // HTTP method for callback
}
interface ListMessagesOptions {
limit?: number; // Results per page (default: 10)
offset?: number; // Starting position (default: 0)
status?: MessageStatus; // Filter by status
}interface MessageResponse {
id: string;
callback_url: string;
payload: MessagePayload;
callback_headers: CallbackHeaders;
status: MessageStatus;
created_at: string;
updated_at: string;
scheduled_at?: string;
retry_count: number;
max_retries: number;
next_retry_at?: string;
last_error?: CallbackError;
timezone?: string;
}
interface CreateMessageResponse {
id: string;
created_at: string;
timezone: string;
}
interface TimelineResponse {
message_id: string;
event_count: number;
events: MessageEvent[];
}
interface MessageEvent {
type: EventType;
timestamp: string;
details?: Record<string, unknown>;
}
interface HealthStatus {
status: 'healthy' | 'degraded' | 'unhealthy';
timestamp: string;
uptime: number;
components?: {
http?: {
status: 'healthy' | 'degraded' | 'unhealthy';
secondsSinceUpdate: number;
info: string;
};
};
}
interface CallbackError {
id: string;
message_id: string;
error_code: ErrorCode;
error_message: string;
http_status_code?: number;
created_at: string;
attempt_number?: number;
duration_ms?: number;
}The SDK uses specific error classes for different failure scenarios:
Base error class for all SDK errors.
try {
await client.publish(url, payload);
} catch (error) {
if (error instanceof InlineError) {
console.log(error.code); // Error code
console.log(error.status); // HTTP status
console.log(error.message); // Human-readable message
}
}Thrown when input validation fails.
try {
await client.publish('', payload); // Missing URL
} catch (error) {
if (error instanceof ValidationError) {
console.error('Invalid input:', error.message);
}
}Thrown when authentication fails.
try {
const client = new InlineClient({ apiUrl: '...', token: 'invalid' });
await client.getHealth();
} catch (error) {
if (error instanceof AuthenticationError) {
console.error('Invalid or expired token');
}
}Thrown when user lacks required permissions.
try {
await client.publish(url, payload);
} catch (error) {
if (error instanceof AuthorizationError) {
console.error('Not authorized to perform this action');
}
}Thrown when a requested resource doesn't exist.
try {
const msg = await client.getMessage('non-existent-id');
} catch (error) {
if (error instanceof NotFoundError) {
console.error('Message not found');
}
}Thrown when rate limit is exceeded.
try {
await client.publish(url, payload);
} catch (error) {
if (error instanceof RateLimitError) {
const retryAfter = error.retryAfter || 60000;
console.log(`Rate limited. Retry after ${retryAfter}ms`);
await new Promise(resolve => setTimeout(resolve, retryAfter));
}
}Thrown when API returns a server error.
try {
await client.getHealth();
} catch (error) {
if (error instanceof ServerError) {
console.error(`Server error (${error.status}): ${error.message}`);
}
}Thrown on network connectivity issues.
try {
await client.publish(url, payload);
} catch (error) {
if (error instanceof NetworkError) {
console.error('Network error:', error.message);
console.error('Original error:', error.originalError);
}
}Represents errors from webhook callback delivery (not thrown by client).
const errors = await client.getMessageErrors('message_123');
for (const callbackError of errors) {
const webhookError = new WebhookError(
callbackError.errorMessage,
callbackError.errorCode,
callbackError.httpStatusCode,
callbackError.createdAt
);
console.log(`Webhook failed: ${webhookError.errorCode}`);
}import {
InlineClient,
NotFoundError,
NetworkError,
type MessageResponse
} from '@dnl-fm/inline-sdk';
const client = new InlineClient({
apiUrl: 'https://api.inline.example.com',
token: process.env.INLINE_TOKEN!
});
async function processOrder(orderId: number) {
try {
// 1. Publish webhook for order processing
const publishResp = await client.publish(
'https://webhook.example.com/orders',
{
event: 'order.created',
orderId,
timestamp: new Date().toISOString()
},
{
headers: {
'X-Request-ID': crypto.randomUUID()
}
}
);
console.log(`Order webhook published: ${publishResp.id}`);
// 2. Schedule follow-up notification (24 hours later)
const followUpResp = await client.publish(
'https://webhook.example.com/notifications',
{
event: 'order.reminder',
orderId,
type: 'follow_up'
},
{
delay: '24h'
}
);
console.log(`Follow-up scheduled: ${followUpResp.id}`);
// 3. Check message status
const message = await client.getMessage(publishResp.id);
console.log(`Message status: ${message.status}`);
// 4. View timeline
const timeline = await client.getMessageTimeline(publishResp.id);
console.log(`Timeline events: ${timeline.event_count}`);
// 5. Monitor for errors
const errors = await client.getMessageErrors(publishResp.id);
if (errors.length > 0) {
console.warn(`Message has ${errors.length} errors`);
for (const error of errors) {
console.warn(` - ${error.error_code}: ${error.error_message}`);
}
}
return publishResp.id;
} catch (error) {
if (error instanceof NotFoundError) {
console.error('Order not found');
} else if (error instanceof NetworkError) {
console.error('Failed to reach API');
} else {
throw error;
}
}
}
// Usage
await processOrder(12345);async function monitorSystem() {
const health = await client.getHealth();
const ready = await client.getHealthReady();
console.log('=== System Health ===');
console.log(`Overall: ${health.status}`);
console.log(`Uptime: ${Math.floor(health.uptime / 60)} minutes`);
console.log(`Ready: ${ready.status === 'ready'}`);
if (health.components?.http) {
console.log(`HTTP component: ${health.components.http.status}`);
console.log(` Info: ${health.components.http.info}`);
}
const errors = await client.getErrorStats();
console.log(`\n=== Error Statistics ===`);
console.log(`Total errors: ${errors.summary.total_errors}`);
console.log(`Error types: ${errors.summary.error_types}`);
for (const err of errors.errors) {
console.log(` ${err.error_code}: ${err.count} (avg ${err.avg_duration_ms}ms)`);
}
}
await monitorSystem();async function publishWithRetry(url: string, payload: any, maxAttempts = 3) {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await client.publish(url, payload);
} catch (error) {
if (error instanceof RateLimitError) {
const delay = error.retryAfter || 60000;
console.log(`Rate limited. Waiting ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
} else if (error instanceof NetworkError && attempt < maxAttempts) {
const backoff = Math.pow(2, attempt - 1) * 1000;
console.log(`Network error. Retrying in ${backoff}ms...`);
await new Promise(resolve => setTimeout(resolve, backoff));
} else {
throw error;
}
}
}
}
const result = await publishWithRetry('https://webhook.example.com/events', {
event: 'test'
});Wrap SDK calls in try-catch and handle specific error types:
try {
const msg = await client.publish(url, payload);
} catch (error) {
// Handle errors appropriately
}When scheduling messages for specific local times, always include timezone:
await client.publish(url, payload, {
notBefore: '2024-10-28T09:00:00',
timezone: 'America/New_York'
});Ensure callback URLs are valid before publishing:
if (!url.startsWith('http://') && !url.startsWith('https://')) {
throw new Error('Invalid callback URL');
}Add request IDs and other tracking headers:
await client.publish(url, payload, {
headers: {
'X-Request-ID': crypto.randomUUID(),
'X-User-ID': userId.toString()
}
});Regularly check for messages in the dead-letter queue:
const dlq = await client.getDeadLetterErrors();
if (dlq.summary.totalMessages > 0) {
console.warn(`${dlq.summary.totalMessages} messages in DLQ`);
// Alert or take corrective action
}Verify API health before critical operations:
const health = await client.getHealth();
if (health.status !== 'healthy') {
console.warn('API degraded, consider delaying operations');
}For retries, use exponential backoff:
const delay = Math.pow(2, attempt) * 1000; // 2s, 4s, 8s, 16s...
await new Promise(resolve => setTimeout(resolve, delay));Choose the right delay format for your use case:
// Relative delay for flexible timing
await client.publish(url, payload, { delay: '5m' });
// Absolute time for precise scheduling
await client.publish(url, payload, { notBefore: '2024-10-28T14:30:00Z' });Ensure payloads are JSON-serializable:
try {
JSON.stringify(payload); // Validate
await client.publish(url, payload);
} catch (error) {
console.error('Payload is not JSON-serializable');
}Always log message IDs for tracking:
const response = await client.publish(url, payload);
console.log(`Published message: ${response.id}`);
// Store this ID for later referenceFor issues, questions, or contributions, visit the GitHub repository.
MIT - See LICENSE file for details