Resolve by signal key
A webhook that knows the deterministic signal id resumes the exact waiting workflow, with no id lookup and no facet to duplicate.
The situation
A partner system calls back when a quote is ready. You control the callback URL, so you can bake the workflow’s signal id into it and resume that one wait directly.
The recipe
const signalId = `quote-${orderId}`;await sendQuoteRequest({ orderId, callbackKey: signalId });const quote = await conditional<{ amount: number }>(signalId, { role: 'quotes', metadata: { orderId }, timeout: '2h',});curl -X POST https://lt.acme.dev/api/escalations/resolve-by-signal-key \ -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ -d '{ "signalKey": "quote-ORD-9", "resolverPayload": { "amount": 1240 } }'The row’s signal_key is the signal id given to conditional, unique per namespace and app. If the partner never calls, the row stays in the quotes queue where a person can answer it instead.
The guarantee
The call answers 200 { signaled: true } and the workflow resumes in place, 404 when the key is unknown and 409 when the row is already terminal. The resolve and the wake are one transaction. The call is scoped to the caller’s visible roles. See Conditional.
Related patterns
Resolving from anywhere
One row, many resolvers
A person, a service, an operator, an agent or a machine can resolve the same row. The workflow receives the same typed answer either way.
Resolving from anywhere
Resolve by domain key
A caller that knows only orderId resolves the right row in one atomic statement, with no lookup of the escalation id first.
The escalation boundary
Early signals are buffered
A payload sent before the workflow starts waiting is held and delivered when the wait registers. Order of arrival stops mattering.
The escalation boundary
One crash-safe write
The row, its facets and the resume timer commit with the workflow's checkpoint in one write. A crash leaves all of it or none of it.
