Skip to main content
Your workflow doesn’t run in isolation. It needs to notify your dashboard when a step completes, wait for a human to approve a document, let a monitoring service check progress, or accept configuration changes mid-run. Output gives you primitives for both directions: sending data out and receiving data in.

Quick Reference

Sending Data Out

Post a Notification

The simplest integration: call an external API when something happens. Use sendHttpRequest to POST to a webhook — notify any HTTP-based service, kick off a Zapier automation, etc.
Each sendHttpRequest fires and continues — the workflow doesn’t wait for a response beyond the HTTP acknowledgment.

Parameters

The response includes status, statusText, headers, body, and ok.

Signed Webhooks

When sending data out, it’s common practice for the receiving end to require a shared secret that is signed. Here’s an example for how to sign a webhook payload using HMAC-SHA256:
Signing must happen in a step, not directly in the workflow. Cryptographic operations like crypto.createHmac are non-deterministic and must run inside a step.
The receiver verifies by computing the same HMAC and comparing signatures.

Receiving Data In

Sometimes your workflow needs input from the outside world while it’s running. A human approves a document. A dashboard checks progress. An operator changes a setting. Output exposes three primitives for this, built on Temporal’s message passing.

Query: Read State Without Changing It

A query lets external systems read workflow state. The workflow keeps running — queries are read-only and don’t affect execution.
Why two imports? Output wraps Temporal for workflow orchestration. High-level helpers like workflow and sendHttpRequest come from @outputai/core. Low-level primitives like defineQuery, defineSignal, and setHandler come directly from @temporalio/workflow — Output doesn’t wrap these since Temporal’s API is already clean.
Job: Your dashboard needs to show which step the workflow is on.
Query the workflow via HTTP:
Response:
Your dashboard can poll this endpoint to show a progress bar — no webhooks needed.
About the payload wrapper: When sending data to signals, queries, or updates, wrap your data in { "payload": {...} }. The API extracts payload and passes it to your handler. For queries with no arguments, send an empty object: { "payload": {} } or omit the body entirely.

Query with Arguments

Queries can accept arguments from the caller. Use this to filter results, control verbosity, or request specific data. Job: An agent makes multiple tool calls per step. The dashboard wants to show either a summary or detailed tool call history.
Query with arguments:

Signal: Push Data In

A signal pushes data into a running workflow. The caller doesn’t wait for processing — the API returns immediately. Use signals for fire-and-forget input like “stop processing” or “here’s new data.” Job: An operator wants to stop a long-running research workflow early.
Send the stop signal:
The workflow checks shouldStop between iterations and exits early. The operator gets 200 OK immediately — they don’t wait for the workflow to actually stop.

Update: Change State and Get Confirmation

An update changes workflow state and returns a result. The caller blocks until the workflow processes the update. Use updates when you need confirmation that a change was applied. Job: An admin wants to change the quality threshold mid-workflow and see the previous value.
Send the update:
Response:
The admin sees the previous threshold (0.8) and knows their change was applied.

Waiting for External Input

Two patterns for pausing a workflow until the outside world responds.

Pattern 1: Workflow Initiates the Request

Use sendPostRequestAndAwaitWebhook when your workflow knows who to ask and wants to pause until they respond. Job: Send a document for human approval, pause until they decide.
Output automatically includes the workflowId in the request. The approval service stores it and calls back when the human decides:
The workflow resumes with the approval data.

Pattern 2: External System Initiates

Use a signal with condition() when external systems contact your workflow unprompted. The condition() function pauses the workflow until a predicate becomes true — the workflow stays durable and doesn’t consume resources while waiting. Job: Wait for Stripe to confirm a payment succeeded.
Configure Stripe to send webhooks to your Output API. When Stripe confirms payment:
The workflow wakes up and continues.

Which Pattern to Use?

Full Example: Research Agent with Human Override

A research agent that runs autonomously but accepts human intervention. Combines all the patterns: signals to stop or add questions, queries to check progress and tool calls, and updates to change settings.

Interacting with the Agent

Start the research:
Check progress (summary):
Check progress (with full tool call history):
Inspect a specific tool call:
Add a question the agent should investigate:
Extend the research (and see old limit):
Stop early when quality is good enough:

Reference

Outbound Functions

Both are imported from @outputai/core and used inside workflow fn. sendHttpRequest(options) — Fire-and-forget HTTP request. sendPostRequestAndAwaitWebhook(options) — POST then pause until callback. Callback endpoint: POST /workflow/:id/feedback with { "payload": {...} }.

Inbound Primitives

All are imported from @temporalio/workflow and used inside workflow fn. Signal — Async, no return value.
HTTP: POST /workflow/:id/signal/signalName with { "payload": {...} } Query — Sync, read-only, returns value.
HTTP: POST /workflow/:id/query/queryName Update — Sync, can mutate, returns value.
HTTP: POST /workflow/:id/update/updateName with { "payload": {...} } Condition — Pause until predicate is true.

Error Handling

If the workflow doesn’t exist, the API returns a 404 error. If the workflow exists but doesn’t have a handler for the given signal/query/update name, Temporal throws an error. Make sure your handler names match between the workflow code and the API calls. For advanced patterns (validators, async handlers, timeouts), see Temporal’s message passing docs.