External callbacks
External bindings let a workflow wait for work outside the Rust engine: browser automation, payment webhooks, scheduled jobs, or custom integrators. When a bound step completes, the runtime emits ExternalBindingStarted, sets the run to Interrupted, and waits for a signed callback on the server.
This guide covers workflow configuration with ExternalBindingConfig, the callback lifecycle, and reporting outcomes with ExternalOutcome.report (Python SDK symbol: report_outcome).
Workflow-level bindings
Declare bindings on the workflow definition, not on individual agents:
| Field | Purpose |
|---|---|
id | Binding identifier in callback URL and traces |
kind | Integrator type (browser_automation, schedule_trigger, custom, or http_callback in JSON payloads) |
attach_to_step_id | Step UUID that triggers the wait after step agent work |
mode | async_callback (default) waits for HTTP callback; sync_tool completes in-process |
outcome_schema | JSON Schema for callback body validation |
recovery | Policy when outcome is failed or needs_input |
Runs with external bindings require recovery_enabled: true and Postgres, same as HITL.
Python: ExternalBindingConfig
Publish the workflow to the server with external_bindings included in the workflow JSON sent to POST /v1/runs, or register via the workflow registry when using workflow_ref.
Runtime lifecycle
- Engine executes the attached step (agent invocation, tools, memory).
- On step completion, engine emits
ExternalBindingStartedand sets run statusInterrupted. - Integrator performs external work (Playwright, payment API, human form).
- Integrator calls
POST /v1/runs/{run_id}/external/{binding_id}with signedExternalOutcomeReportJSON. - Engine validates schema, applies recovery policy, emits
ExternalBindingCompletedorExternalBindingFailed, and resumes or fails the run.
Trace events are metadata-only (binding id, duration, error codes). See trace event reference.
ExternalOutcome.report
Post outcomes from integrator code with the SDK helper exported as report_outcome from arcflow.external (also re-exported from arcflow). Documentation refers to this operation as ExternalOutcome.report.
Outcome statuses:
| status | Meaning |
|---|---|
success | External work succeeded; engine resumes workflow |
failed | Fatal external error; recovery policy applies |
needs_input | More user input required; may trigger agent re-ask |
Optional fields: error_code, fields (object), artifact_refs (string array).
Requires environment variables:
| Variable | Purpose |
|---|---|
ARCFLOW_SERVER_API_KEY | Bearer auth on callback POST |
ARCFLOW_WEBHOOK_SECRET | HMAC signing secret (must match server) |
See examples/external/playwright_stub_callback.py for a CLI stub.
HTTP callback (without SDK)
Send X-Idempotency-Key on retries; duplicate keys return 202 with already_processed.
Async vs sync modes
| Mode | Behavior |
|---|---|
async_callback | Step completes in engine; run waits at Interrupted until callback |
sync_tool | External work modeled as in-process tool completion (no HTTP wait) |
Production integrations (Playwright workers, payment processors) typically use async_callback.
Recovery policy
When status is failed or needs_input, recovery controls the next action:
| Field | Values | Effect |
|---|---|---|
max_retries | integer | Retry external attempt; emits ExternalRecoveryTriggered |
on_needs_input | agent_reask, fail_run | Re-enter agent loop or fail |
on_fatal | hitl_escalate, fail_run | Escalate to HITL or fail run |
Related pages
- Webhook security for HMAC verification and logging rules
- HITL overview when
on_fatalishitl_escalate - Recovery and resume for Postgres requirements