Catch silent failures

Your integrations didn't fail. They just stopped doing the right thing.

A webhook that never arrives. A fulfillment step that quietly stalls. An agent that starts a task and never finishes. Upkeel tells you when something didn't happen, not just when something threw.

14-day free trial  ·  TypeScript SDK  ·  Zero dependencies

payment-flow.ts
import { Upkeel } from '@upkeel/sdk'
const keel = new Upkeel({ apiKey: 'uk_live_••••••••' })
// Create a checkout. Stripe will send a webhook
const session = await stripe.checkout.sessions.create({ ... })
// That webhook MUST arrive within 30 seconds
keel.expect('payment.succeeded', {
within: '30s',
correlationId: session.id,
})
// In your webhook handler, confirm it arrived
app.post('/webhooks/stripe', (req) => {
keel.fulfill('payment.succeeded', {
correlationId: req.body.data.object.id,
})
})

200 OK doesn't mean it worked.

APIs respond. Logs look clean. Systems say healthy. But somewhere downstream, the webhook never arrived, the workflow stalled halfway, and your customer found out before you did.

Silent failures

Webhooks drop. Workflows partially complete. Events disappear into queues and never emerge. Traditional monitoring sees none of it. Everything looks fine at the API level.

Workflow blindness

You know your API responded. You have no idea if the downstream outcome materialized. Subscription activated, order fulfilled, email delivered, agent task completed.

Reactive discovery

Your customers report the failure before your monitoring fires. Support tickets, churned accounts, emergency Slack threads. The damage is done before anyone knew to look.

Three primitives. That's the product.

No new mental model to learn. Two SDK calls and a deadline. Upkeel detects what didn't happen.

1
expect. Something should happen
keel.expect('stripe.webhook', {
  within: '30s',
  correlationId: session.id,
})

You declare the outcome you're waiting for, and the deadline. Non-blocking, no extra latency.

2
fulfill. Confirm it did
keel.fulfill('stripe.webhook', {
  correlationId: event.data.id,
})

In your handler. Webhook receiver, queue worker, wherever the outcome lands. Call fulfill. Done.

3
missed. Alert immediately
// 30s elapsed, no fulfillment
⚠ stripe.webhook missed
  cs_live_b1Fx3z…
  → Slack #incidents

If the deadline lapses without fulfillment, Upkeel alerts your team. With the correlation ID, the last successful step, and one-click ack/suppress.

Expected 1,000 events
Actual 742 events
→ ALERT 258 missed in 24h

We watch for the things that didn't show up.

There's a whole category of monitoring that tells you what happened. Errors, latencies, request counts. That's useful, and you should keep doing it. We're for the other category: the things that were supposed to happen and didn't.

Questions Upkeel is built to answer
Did the webhook your code was waiting on actually arrive?
Did the scheduled job at 3am run, and finish?
Did the warehouse confirm the pick within four hours?
Did every step of the agent's tool chain complete?
Did anything you depend on quietly stop in the last hour?

Most monitoring stops at "did the API return 200?" That's where we start.

Tell us what should happen. We'll tell you when it doesn't.

That's the whole product, really. Declare the things your code expects. We watch the clock and notice when those things stop arriving.

Expectation engine

Declare what should happen and when. keel.expect('payment.succeeded', { within: '30s' }). If it doesn't arrive, Upkeel fires immediately.

Flow visibility

See every step of every workflow execution in real time. Identify exactly which step stalled, which event never fired, which integration started degrading.

AI pipeline observability

Monitor multi-step agent workflows end-to-end. Detect silent tool call failures, dropped context, and sub-tasks that need human escalation.

In-app status reactions

Your application automatically reacts to integration health. Disable checkout, show fallback UI, pause queues. Without waiting for anyone to read an alert.

First-class testing

@upkeel/testing makes it trivial to assert on flow behavior, simulate known failure scenarios, and advance virtual time in tests.

🛡️
Built for serious teams

Immutable audit logs, per-customer retention, role-based access, encryption at rest. The boring infrastructure your security team is going to ask about. Already there.

Works with the stacks you already use.

It's a few lines of instrumentation. Drop them around the integration you can't trust, and you start finding out the moment something stops working.

stripe-webhooks.ts
import { Upkeel } from '@upkeel/sdk'
const keel = new Upkeel({ apiKey: process.env.UPKEEL_API_KEY })
// Checkout handler. Creates payment, expects confirmation
app.post('/checkout', async (req, res) => {
const session = await stripe.checkout.sessions.create({
line_items: req.body.items,
metadata: { orderId: req.body.orderId },
})
// Stripe webhook must arrive within 30s
keel.expect('payment.succeeded', {
within: '30s',
correlationId: session.id,
priority: 'critical',
description: 'Stripe payment webhook delivery',
meta: { orderId: req.body.orderId },
})
res.json({ url: session.url })
})
// Webhook handler. Confirm the event arrived
app.post('/webhooks/stripe', (req, res) => {
const event = stripe.webhooks.constructEvent(req.body, sig, secret)
if (event.type === 'checkout.session.completed') {
keel.fulfill('payment.succeeded', {
correlationId: event.data.object.id,
meta: { amount: event.data.object.amount_total },
})
}
// Customer abandoned checkout? Cancel the expectation.
if (event.type === 'checkout.session.expired') {
keel.cancel('payment.succeeded', {
correlationId: event.data.object.id,
})
}
res.sendStatus(200)
})
order-fulfillment.ts
import { Upkeel } from '@upkeel/sdk'
const keel = new Upkeel({ apiKey: process.env.UPKEEL_API_KEY })
async function processOrder(order) {
// Track the full order lifecycle as a flow
const flow = keel.flow('order-fulfillment')
flow.step('payment-confirmed')
flow.expect('warehouse.picked', {
within: '4h',
correlationId: order.id,
meta: { itemCount: order.items.length },
})
await notifyWarehouse(order)
}
// Warehouse callback. Confirm the pick
app.post('/callbacks/warehouse', (req, res) => {
keel.fulfill('warehouse.picked', {
correlationId: req.body.orderId,
})
// Next step: expect shipping label
keel.expect('shipping.label-created', {
within: '2h',
correlationId: req.body.orderId,
})
res.sendStatus(200)
})
// ShipStation webhook. Label created
app.post('/webhooks/shipstation', (req, res) => {
keel.fulfill('shipping.label-created', {
correlationId: req.body.orderId,
meta: { trackingNumber: req.body.tracking },
})
res.sendStatus(200)
})
employee-provisioning.ts
import { Upkeel } from '@upkeel/sdk'
const keel = new Upkeel({ apiKey: process.env.UPKEEL_API_KEY })
// When a new employee is created in your HR system...
async function onNewHire(employee) {
const flow = keel.flow('employee-provisioning')
flow.step('hr-record-created')
// Each downstream system must provision within its SLA
flow.expect('okta.account-created', {
within: '10m',
correlationId: employee.id,
description: 'Okta SSO account must be provisioned',
})
flow.expect('slack.account-invited', {
within: '30m',
correlationId: employee.id,
})
flow.expect('github.team-added', {
within: '1h',
correlationId: employee.id,
})
await provisioningQueue.publish(employee)
}
// Okta SCIM webhook. Account was created
app.post('/webhooks/okta', (req, res) => {
keel.fulfill('okta.account-created', {
correlationId: req.body.employeeId,
})
res.sendStatus(200)
})

Built for integration-heavy applications.

Any time one system needs to know that another system actually did something. Upkeel verifies it happened.

Payments
Webhook delivery you can trust

Payment webhooks drop. They arrive late. Sometimes they don't arrive at all. We catch the gap before a customer notices their subscription didn't activate or their refund didn't process.

Without monitoring you find out when somebody emails support. With Upkeel you find out the moment the deadline lapses.
AI Agents
Agent workflow correctness

LLM tool calls succeed but execute nothing. Sub-agents complete tasks that the orchestrator never receives. Upkeel monitors the complete end-to-end outcome. Not just the API response.

Per-tool scopes (app.agent.tools.gmailapp.agent) roll up to a single dashboard signal. And your agent can react to it via keel.scope().on('degraded').
Operations
Order fulfillment pipelines

Warehouse confirmation never arrived. Carrier label was never created. Customer is waiting. Upkeel catches every missing step in your fulfillment chain. Before it becomes a support ticket.

Multi-step flows correlate every step on a single ID. The alert names the last successful step. You know exactly where it stalled.

Start knowing what's actually happening.

Solo plan is $9/mo. Team is $39. Every paid plan starts with a 14-day free trial. No charge until day 15; cancel any time before from the in-app billing portal.