How to Build Reliable Integration and Webhook Bridges

Many integration flows are simple: something happened here, do something later there. Here is how to bridge systems with durable timing, retries, and delivery visibility.

A payment settles and a downstream ledger needs updating. A partner sends a webhook and a follow-up call needs to happen in 30 minutes. A user triggers an action and an external API needs to be notified after a delay.

The bridge itself is straightforward — schedule a delivery to the downstream endpoint with the right payload and timing. What turns it into infrastructure is everything around the delivery: retries when the downstream system is down, visibility into what was delivered and what failed, alerting when a destination starts dropping requests, and security for payloads crossing system boundaries.

Common use cases

  • Webhook-to-webhook handoffs — receive an inbound webhook from a partner system and schedule a delayed action in your own backend or another service
  • Delayed settlement follow-ups — a payment is submitted, and a reconciliation check needs to run after a settlement window
  • Content publishing pipelines — content is approved in a CMS and needs to be published or syndicated after a scheduled delay
  • Partner system follow-ups — an order is placed with a partner, and a status follow-up needs to happen after a processing window
  • Cross-service notifications — an event in one service needs to trigger a notification or state change in another service, with a delay

The naive approach

Glue code that fires immediately or a cron job that scans for pending handoffs:

// Inline — fires immediately, no delay, no retry
app.post('/webhooks/partner-order', async (req, res) => {
  await fetch('https://internal-api.example.com/process-order', {
    method: 'POST',
    body: JSON.stringify(req.body),
  });
  res.sendStatus(200);
});

This works when the downstream call is immediate, reliable, and does not need a delay. It breaks as soon as any of those assumptions change.

Where it breaks

No timing control

The handoff fires immediately or waits for the next cron tick. There is no way to say “trigger this downstream call in 30 minutes” without adding scheduling infrastructure.

Failures are silent

If the downstream endpoint is down, the fetch call fails and the handoff is lost. There is no retry, no record, no alert. You find out when the downstream system notices missing data.

Retries and visibility are afterthoughts

Adding retry logic means tracking attempt state, implementing backoff, and handling the case where the downstream system is down for an extended period. Adding visibility means logging each attempt and building a way to query failures. Each of these is real infrastructure bolted onto glue code.

Scattered logic

Each integration bridge lives in its own handler with its own retry logic (or lack thereof), its own error handling, and its own monitoring. There is no consistent model for how handoffs work, when they retry, or how you find out about failures.

DIY vs Posthook

CapabilityDIY (glue code + cron)Posthook
Delayed handoffAdd scheduling infrastructure or a cron jobSchedule a hook with postIn or postAt — fires at the right time
Handler modelInline glue code; handler manages retry stateHook fires individually; handler processes one handoff
Retry on downstream failureBuild retry logic per integrationBuilt-in retries with configurable backoff and per-hook overrides
Delivery visibilityLog each attempt manuallyPer-hook status with attempt history and response bodies
Failure alertingBuild monitoring per integrationPer-endpoint anomaly detection with alerts via email, Slack, or webhook
Long-running handoffsManage timeouts in glue codeAsync hooks: return 202, ack/nack within timeout up to 3 hours
InfrastructureGlue code + scheduling + retry logic + monitoring per bridgeAPI key + endpoint

A better approach

Schedule the handoff as a hook. The inbound event arrives, your handler schedules a delivery to the downstream system at the right time with the right payload. Posthook handles persistence, timing, retries, and observability. Your handler on the receiving end processes the handoff and checks state before acting.

Every bridge uses the same model: schedule a hook, receive a delivery, process the payload. Retry behavior, alerting, and visibility are consistent across all integrations rather than reimplemented per bridge.

How Posthook fits

Posthook provides the delivery layer for integration bridges. Your application owns the integration logic — what payload to send and what to do when the handoff arrives.

  • Schedule delayed handoffs with postIn (“trigger downstream in 30 minutes”) or postAt for exact timestamps (e.g., end-of-day settlement)
  • Built-in retries — downstream system returns a 500? Posthook retries with configurable backoff. Critical handoffs can use retryOverride for more aggressive strategies.
  • Anomaly detection — know when a downstream endpoint starts failing before handoffs pile up. Posthook tracks failure rates per endpoint and alerts via email, Slack, or webhook.
  • Async hooks — if the downstream system needs time to process (partner APIs with slow responses, batch processing endpoints), return 202 and ack/nack when done. Configurable timeouts up to 3 hours.
  • Per-delivery observability — inspect each handoff’s attempt history, response body, and outcome. Consistent visibility across all bridges.

Your handler on the receiving end should check state before acting — if the handoff was already processed (by a retry or a duplicate delivery), return success and skip. Posthook provides at-least-once delivery.

When not to use Posthook

If the integration is a synchronous call that does not need a delay, retry, or observability — a simple fetch in the handler is fine. Posthook adds value when the bridge needs timing, durability, or visibility.

If the flow is a complex branching workflow with many steps, conditions, and long-lived state — that is an orchestration problem. Consider a workflow platform like Inngest or Temporal. Posthook handles point-to-point timed delivery, not multi-step orchestration.

If you need fan-out — one event delivered to many downstream endpoints — QStash’s URL Groups are a better fit. Posthook delivers to one endpoint per hook. See QStash vs Posthook for a comparison.

Getting started

Install the SDK and schedule your first integration bridge:

npm install @posthook/node

Receive a partner webhook and schedule a delayed follow-up:

import Posthook from '@posthook/node';

const posthook = new Posthook('phk_...');

// Partner webhook arrives — schedule a settlement check in 30 minutes
app.post('/webhooks/partner-order-received', async (req, res) => {
  await posthook.hooks.schedule({
    path: '/webhooks/check-settlement',
    postIn: '30m',
    data: { orderId: req.body.orderId, partnerId: req.body.partnerId },
  });
  res.sendStatus(200);
});

When the hook fires, your handler processes the handoff:

// In your /webhooks/check-settlement handler
const order = await db.getOrder(payload.orderId);

if (order.settlementStatus === 'completed') {
  // Already settled — no-op
  return res.status(200).json({ status: 'skipped' });
}

await updateLedger(order);
return res.status(200).json({ status: 'processed' });

Frequently asked questions

Ready to get started?

Create your free account and start scheduling hooks in minutes. No credit card required.