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
import { Upkeel } from '@upkeel/sdk'
const keel = new Upkeel({ apiKey: 'uk_live_••••••••' })
// Create a checkout. Stripe will send a webhookconst session = await stripe.checkout.sessions.create({ ... })
// That webhook MUST arrive within 30 secondskeel.expect('payment.succeeded', { within: '30s', correlationId: session.id,})
// In your webhook handler, confirm it arrivedapp.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.
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.
You know your API responded. You have no idea if the downstream outcome materialized. Subscription activated, order fulfilled, email delivered, agent task completed.
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.
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.
keel.fulfill('stripe.webhook', {
correlationId: event.data.id,
}) In your handler. Webhook receiver, queue worker, wherever the outcome lands. Call fulfill. Done.
// 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.
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.
Declare what should happen and when. keel.expect('payment.succeeded', { within: '30s' }). If it doesn't arrive, Upkeel fires immediately.
See every step of every workflow execution in real time. Identify exactly which step stalled, which event never fired, which integration started degrading.
Monitor multi-step agent workflows end-to-end. Detect silent tool call failures, dropped context, and sub-tasks that need human escalation.
Your application automatically reacts to integration health. Disable checkout, show fallback UI, pause queues. Without waiting for anyone to read an alert.
@upkeel/testing makes it trivial to assert on flow behavior, simulate known failure scenarios, and advance virtual time in tests.
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.
import { Upkeel } from '@upkeel/sdk'
const keel = new Upkeel({ apiKey: process.env.UPKEEL_API_KEY })
// Checkout handler. Creates payment, expects confirmationapp.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 arrivedapp.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)})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 pickapp.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 createdapp.post('/webhooks/shipstation', (req, res) => { keel.fulfill('shipping.label-created', { correlationId: req.body.orderId, meta: { trackingNumber: req.body.tracking }, })
res.sendStatus(200)})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 createdapp.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.
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.
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.
app.agent.tools.gmail → app.agent) roll up to a single dashboard signal. And your agent can react to it via keel.scope().on('degraded').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.
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.