turn.completed into a Temporal signal that wakes the workflow. If
that webhook never arrives, the workflow notices and polls instead.
Verified with @temporalio/client, worker, workflow, activity and
testing 1.24.0 on Node 24.
Why these pieces
Session creation is an activity, never workflow code. Workflow code must be
deterministic, and an HTTP call is the opposite of that.
Activities
Every call to Gobare. Temporal’s retry policy — five attempts below — is the only retry loop; the activities themselves do not retry.Never ask a clarifying question is in the instructions because this workflow
has no one to ask. A session parked on a question reports its turn as
working, so the fallback poll would keep waiting until the workspace’s
two-hour ceiling. If your work needs a person, send the question somewhere a
person is — Put an agent in a Slack thread — and signal
the workflow with the answer.
The workflow
??=), so a webhook
delivered twice signals twice and changes nothing. A signal that arrives while
the workflow is between activities is buffered by Temporal until the handler
runs.
fallbackAfter is an argument so a test can make it seconds. Fifteen minutes
is a reasonable production default: long enough that a working webhook always
wins, short enough that a lost one costs a quarter of an hour.
The webhook receiver
Verifies the delivery, finds the workflow through the session’smetadata, and
signals it. It answers 200 for everything it does not act on — anything else
is retried by Gobare for hours.
turn_id is null — there was no turn record to name — is
not signalled; the workflow’s fallback poll finds the turn itself.
A workflow that already finished — it polled first, or it was cancelled —
answers the signal with WorkflowNotFoundError. That is a normal outcome, not a
failure to retry. Subscribe the receiver’s URL once:
Running it
The worker, which runs the workflow code and the activities:workflowId: ticket-${id} is Temporal’s half of the idempotency: a second
start for the same ticket is refused with WorkflowExecutionAlreadyStartedError
while the first is running, so a re-delivered ticket starts nothing.
What was verified
Run against production, with a local Temporal dev server (TestWorkflowEnvironment.createLocal() from @temporalio/testing) standing in
for your cluster:
- The webhook path. A workflow started, its session answered, a signed
turn.completedwas delivered over HTTP to the receiver above, the receiver signalled the workflow, and the workflow returned{"status":"completed","reply":"42"}and deleted the session. The delivery was signed with Gobare’s own signing function rather than a copy of it, because the receiver could not be reached from the internet. - The fallback path. With
fallbackAfterat five seconds and no webhook at all, the workflow polled and finishedcompletedwith the correct answer. - Retries. During that run an activity’s connection to
api.gobare.devtimed out after ten seconds; Temporal retried it and the workflow finished as if nothing had happened. That is the case this integration exists for. - Idempotency. A second
startfor a running workflow id was refused withWorkflowExecutionAlreadyStartedError;startSessioncalled twice with one workflow id returned the same session both times.
Next
- idempotency — what a replayed key returns
- webhooks — retry schedule and signature
- Run a fleet of ticket-driven agents in parallel — the same shape with a hand-rolled dispatcher instead of a workflow engine