Subscriptions and delivery
Webhook, queue, and consumer group subscriptions with routing filters, retry policies, and circuit breakers
Subscriptions define how messages are delivered from channels to your applications. npayload supports three subscription types: webhooks (push), queues (pull), and consumer groups (shared load-balanced pull).
Subscription types
npayload pushes messages to your HTTP endpoint as they arrive.
const sub = await npayload.subscriptions.create({
channel: 'orders',
type: 'webhook',
endpoint: 'https://api.example.com/webhooks/orders',
headers: {
'X-Custom-Header': 'my-value',
},
});See the Webhooks guide for delivery lifecycle, retries, and signature verification.
Messages accumulate in a queue. Your application pulls them at its own pace.
const sub = await npayload.subscriptions.create({
channel: 'orders',
type: 'queue',
});
// Pull messages (up to 10 at a time)
const messages = await npayload.subscriptions.pull(sub.gid, { limit: 10 });
for (const msg of messages) {
await processOrder(msg.payload);
await npayload.subscriptions.ack(sub.gid, msg.gid);
}Messages that are not acknowledged within the visibility timeout become available for re-delivery.
Multiple consumers share a queue subscription. Each message is delivered to exactly one consumer in the group, providing load balancing.
// Create the consumer group
const group = await npayload.consumerGroups.create({
channel: 'orders',
name: 'order-processors',
});
// Each worker joins the same group
const messages = await npayload.consumerGroups.pull(group.gid, {
consumerId: 'worker-1',
limit: 10,
});
for (const msg of messages) {
await processOrder(msg.payload);
await npayload.consumerGroups.ack(group.gid, msg.gid);
}Best for: scaling processing across multiple workers without duplicating work.
Routing key filters
Filter which messages a subscription receives using glob patterns on routing keys.
// Only receive signup events from Europe
const sub = await npayload.subscriptions.create({
channel: 'events',
type: 'webhook',
endpoint: 'https://api.example.com/eu-signups',
routingKeyFilter: 'user.signup.eu-*',
});| Pattern | Matches | Does not match |
|---|---|---|
order.* | order.created, order.shipped | order.item.added |
order.** | order.created, order.item.added | user.signup |
*.signup.* | user.signup.eu, admin.signup.us | user.login.eu |
user.signup.eu-* | user.signup.eu-west, user.signup.eu-central | user.signup.us-east |
* matches a single segment (between dots). ** matches one or more segments. Filters are case-sensitive.
Custom retry policies
Override the default retry schedule for webhook subscriptions.
const sub = await npayload.subscriptions.create({
channel: 'orders',
type: 'webhook',
endpoint: 'https://api.example.com/webhooks/orders',
retryPolicy: {
maxAttempts: 5,
initialDelay: 1000, // 1 second
maxDelay: 300000, // 5 minutes
backoffMultiplier: 3, // each retry waits 3x longer
},
});| Option | Default | Description |
|---|---|---|
maxAttempts | 6 | Total delivery attempts (including the first) |
initialDelay | 1000 | Milliseconds before the first retry |
maxDelay | 7200000 | Maximum delay between retries (2 hours) |
backoffMultiplier | 2 | Multiplier applied to the delay after each retry |
After all retries are exhausted, the message is moved to the dead letter queue.
Managing subscriptions
Pause and resume
Temporarily stop delivery without losing messages. Paused subscriptions queue messages until resumed.
// Pause delivery (messages queue up)
await npayload.subscriptions.pause(sub.gid);
// Resume delivery (queued messages are delivered)
await npayload.subscriptions.resume(sub.gid);List subscriptions
// List all subscriptions for a channel
const subs = await npayload.subscriptions.list({ channel: 'orders' });
// List all subscriptions across channels
const allSubs = await npayload.subscriptions.list();Update a subscription
await npayload.subscriptions.update(sub.gid, {
endpoint: 'https://api-v2.example.com/webhooks/orders',
headers: {
'X-Version': '2',
},
});Delete a subscription
await npayload.subscriptions.delete(sub.gid);Deleting a subscription is permanent. Any undelivered messages in the queue are lost. Pause the subscription instead if you want to preserve queued messages.
Circuit breaker behaviour
Webhook subscriptions have a built-in circuit breaker that protects both npayload and your endpoint from cascading failures.
| State | Behaviour |
|---|---|
| Closed (normal) | Deliveries proceed normally |
| Open (tripped) | After consecutive 5xx failures, deliveries pause and messages queue |
| Half-open (testing) | After a cooldown, npayload sends a test delivery. On success, the circuit closes |
Key details:
- Only
5xxserver errors count as failures.4xxclient errors are treated as permanent rejections - The circuit trips after 5 consecutive failures
- Cooldown starts at 30 seconds and increases with repeated trips
- You can monitor circuit state via the subscription status in the SDK or dashboard
const sub = await npayload.subscriptions.get(sub.gid);
console.log(sub.circuitState); // "closed" | "open" | "half-open"Consumer group details
Consumer groups distribute messages across workers using round-robin assignment.
// List consumers in a group
const consumers = await npayload.consumerGroups.listConsumers(group.gid);
// Remove a consumer (its unacknowledged messages become available to others)
await npayload.consumerGroups.removeConsumer(group.gid, 'worker-3');
// Get group stats
const stats = await npayload.consumerGroups.getStats(group.gid);
console.log(stats.pendingMessages);
console.log(stats.activeConsumers);Best practices
- Use webhook subscriptions for real-time, push-based integrations. Use queue subscriptions when your consumer controls the processing pace
- Use consumer groups to scale processing horizontally. Each message is delivered to exactly one consumer
- Set routing key filters to reduce unnecessary delivery. Filtering at the subscription level is more efficient than filtering in your application code
- Configure retry policies based on your endpoint's recovery characteristics. Short-lived outages benefit from faster retries, while downstream rate limits benefit from longer backoff
- Monitor circuit breaker state. An open circuit indicates a problem with your endpoint
- Pause subscriptions during planned maintenance to avoid unnecessary retries and DLQ entries
Next steps
Was this page helpful?