hookshot-sdk
v0.1.4
Published
Official SDK for the Hookshot webhook delivery service
Maintainers
Readme
hookshot-sdk
Official JavaScript/TypeScript SDK for the Hookshot webhook delivery service.
Installation
npm install hookshot-sdk
# or
yarn add hookshot-sdk
# or
pnpm add hookshot-sdkRequirements
- Node.js 18+ (uses native
fetch) - Or any modern browser with
fetchsupport
Quick Start
import { HookshotClient } from 'hookshot-sdk';
const hookshot = new HookshotClient({
apiKey: 'your-api-key',
});
// Send a webhook event
const result = await hookshot.events.send({
eventType: 'order.created',
payload: { orderId: '12345', amount: 99.99 },
});
console.log('Event IDs:', result.eventIds);API Reference
Configuration
const hookshot = new HookshotClient({
apiKey: string; // Required: Your API key
});Endpoints
Manage webhook endpoints where events will be delivered.
// List all endpoints
const endpoints = await hookshot.endpoints.list();
// Create an endpoint
const endpoint = await hookshot.endpoints.create({
label: 'Production',
url: 'https://api.example.com/webhooks',
maxRetries: 5, // optional, default: 5
enabled: true, // optional, default: true
});
// Get an endpoint
const endpoint = await hookshot.endpoints.get('endpoint-id');
// Update an endpoint
await hookshot.endpoints.update('endpoint-id', {
label: 'Staging',
enabled: false,
});
// Delete an endpoint
await hookshot.endpoints.delete('endpoint-id');Events
Send and manage webhook events.
// Send an event (delivered to all active endpoints)
const result = await hookshot.events.send({
eventType: 'order.created',
payload: { orderId: '12345' },
idempotencyKey: 'order-12345-created', // optional, prevents duplicates
endpointId: 'specific-endpoint-id', // optional, target specific endpoint
endpointIds: ['id1', 'id2'], // optional, target multiple endpoints
});
// List events with filtering
const { events, total } = await hookshot.events.list({
status: 'failed', // optional: 'pending' | 'delivering' | 'delivered' | 'failed' | 'dlq'
limit: 20, // optional, default: 20
offset: 0, // optional, for pagination
});
// Get event details
const event = await hookshot.events.get('event-id');
// Get delivery attempts for an event
const attempts = await hookshot.events.getAttempts('event-id');Dead Letter Queue (DLQ)
Manage events that failed all retry attempts.
// List events in DLQ
const { events, total } = await hookshot.dlq.list({
limit: 20,
offset: 0,
});
// Get DLQ statistics
const stats = await hookshot.dlq.stats();
// { count: 5, oldestEventAt: Date, newestEventAt: Date }
// Replay a specific event
await hookshot.dlq.replay('event-id', {
resetRetryCount: true, // optional, start with fresh retry count
});
// Replay all events in DLQ
const { replayedCount } = await hookshot.dlq.replayAll();
// Purge a specific event
await hookshot.dlq.purge('event-id');
// Purge all events from DLQ
const { purgedCount } = await hookshot.dlq.purgeAll();Error Handling
The SDK throws typed errors for different failure scenarios:
import {
HookshotClient,
HookshotError,
HookshotAuthenticationError,
HookshotRateLimitError,
HookshotValidationError,
} from 'hookshot-sdk';
try {
await hookshot.events.send({ ... });
} catch (error) {
if (error instanceof HookshotAuthenticationError) {
// Invalid or missing API key (401)
console.error('Authentication failed');
} else if (error instanceof HookshotRateLimitError) {
// Rate limit exceeded (429)
console.error('Rate limited, retry after:', error.retryAfter, 'seconds');
} else if (error instanceof HookshotValidationError) {
// Invalid request data (400)
console.error('Validation error:', error.details);
} else if (error instanceof HookshotError) {
// Other API errors
console.error('Error:', error.code, error.message, error.status);
}
}Error Properties
All errors extend HookshotError with these properties:
| Property | Type | Description |
|----------|------|-------------|
| message | string | Human-readable error message |
| code | string | Error code (e.g., RATE_LIMIT_EXCEEDED) |
| status | number | HTTP status code |
| details | object | Additional error details (optional) |
TypeScript Support
The SDK is written in TypeScript and exports all types:
import type {
HookshotConfig,
EndpointResponse,
CreateEndpointInput,
UpdateEndpointInput,
WebhookEventResponse,
IngestEventInput,
IngestResponse,
DeliveryAttemptResponse,
DlqStats,
EventStatus,
ListEventsParams,
PaginationParams,
ReplayOptions,
} from 'hookshot-sdk';Webhook Payload Format
When Hookshot delivers a webhook to your endpoint, it sends this payload:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"type": "order.created",
"idempotencyKey": "order-123-created",
"timestamp": "2024-01-15T10:30:00.000Z",
"attemptNumber": 1,
"data": {
"orderId": "12345",
"amount": 99.99
}
}Headers included with each delivery:
| Header | Description |
|--------|-------------|
| X-Webhook-Id | Unique event ID |
| X-Webhook-Type | Event type |
| X-Webhook-Timestamp | Event creation timestamp |
| X-Webhook-Attempt | Current attempt number |
| X-Webhook-Signature | HMAC-SHA256 signature for verification |
| X-Webhook-Idempotency-Key | Idempotency key (if provided) |
Signature Verification
Verify webhook signatures to ensure authenticity:
import { createHmac } from 'crypto';
function verifySignature(
payload: string,
signature: string,
secret: string
): boolean {
const [version, hash] = signature.split('=');
if (version !== 'v1') return false;
const timestamp = req.headers['x-webhook-timestamp'];
const signedPayload = `${new Date(timestamp).getTime()}.${payload}`;
const expectedHash = createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');
return hash === expectedHash;
}License
MIT
