Some agent tasks run too long to hold an HTTP connection open. Instead of waiting, you supply Scout with a callback URL — Scout starts the task, returns immediately, and POSTs the result to your endpoint when the agent finishes.
When to Use Async
- Long-running tasks — anything that runs longer than 30 seconds, such as order processing, report generation, or bulk data workflows
- Unreliable connections — environments where connections time out or drop before a synchronous response can return
- Queued workflows — cases where you enqueue tasks and handle their results separately
How It Works
Start the interaction
Provide a callback_url when starting the interaction.
Get an immediate response
Scout responds right away with 202 Accepted and a session_id.
The agent runs in the background
Scout runs the agent task without holding your connection open.
Receive the result
On completion, Scout POSTs the result to your callback URL.
API Reference
Start Async Interaction
Start an interaction that runs in the background and reports its result to a callback URL.
POST https://api.scoutos.com/v1/agents/{agent_id}/interact
Content-Type: application/json
Authorization: Bearer YOUR_API_KEY
Parameters
The instruction or input for the agent to act on.
An HTTPS URL where Scout POSTs the result when the agent finishes. Must be publicly reachable — see Requirements.
Request
{
"message": "Process all pending orders and send confirmation emails",
"callback_url": "https://your-app.com/webhooks/scout-callback"
}
Response
Scout returns 202 Accepted immediately, before the agent runs.
The identifier for this agent session. Use it to correlate the callback with the request that started it.
The URL to fetch the full event stream for this session once it completes.
{
"session_id": "sess_abc123",
"events_url": "https://api.scoutos.com/v1/agent-sessions/sess_abc123/events"
}
Callback Payload
When the agent finishes, Scout POSTs a JSON payload to your callback_url.
A unique identifier for this callback delivery. Use it to deduplicate retries — see Retry Behavior.
The session this callback reports on, matching the session_id from the original response.
Either succeeded or failed.
ISO 8601 timestamp of when the agent finished.
The URL to fetch the full event stream for the session.
Present only when status is failed. Contains a code and a human-readable message.
{
"callback_event_id": "2b2f5b7d-7a5d-4f9b-9f6d-8ed0d2c7a1d2",
"session_id": "sess_abc123",
"status": "succeeded",
"completed_at": "2026-03-05T14:30:00Z",
"events_url": "https://api.scoutos.com/v1/agent-sessions/sess_abc123/events"
}
Fetching Results
The callback payload confirms completion but doesn’t include the agent’s full output. Use the events_url to retrieve the complete event stream:
GET https://api.scoutos.com/v1/agent-sessions/sess_abc123/events
Authorization: Bearer YOUR_API_KEY
Callback Authentication
Every callback includes signature headers so you can confirm the request genuinely came from Scout:
X-Scout-Signature-Alg: HMAC-SHA256
X-Scout-Signature: t=1709651400,sig=base64-encoded-signature
Verifying the Signature
Parse the header
Extract t (timestamp) and sig (signature) from the X-Scout-Signature header.
Build the signature base string
Concatenate the timestamp and the raw request body as {timestamp}.{raw_request_body}.
Compute the HMAC
Compute HMAC-SHA256 over the base string using your org secret key.
Compare
Compare your computed value against sig using a constant-time comparison.
import hmac
import hashlib
import base64
def verify_scout_signature(raw_body: bytes, signature_header: str, secret: str) -> bool:
# Parse the header: t=1709651400,sig=base64...
parts = dict(item.split("=", 1) for item in signature_header.split(","))
timestamp = parts.get("t", "")
provided_sig = parts.get("sig", "")
# Build the signed string
signed_string = f"{timestamp}.{raw_body.decode('utf-8')}"
# Compute HMAC-SHA256
expected = hmac.new(
secret.encode("utf-8"),
signed_string.encode("utf-8"),
hashlib.sha256
).digest()
expected_b64 = base64.b64encode(expected).decode("utf-8")
# Constant-time comparison prevents timing attacks
return hmac.compare_digest(expected_b64, provided_sig)
Example Webhook Handler (Express)
const express = require("express");
const crypto = require("crypto");
const app = express();
// Use raw body for signature verification
app.post("/webhooks/scout-callback", express.raw({ type: "application/json" }), async (req, res) => {
const sigHeader = req.headers["x-scout-signature"];
if (!verifyScoutSignature(req.body, sigHeader, process.env.SCOUT_SECRET)) {
return res.status(401).send("Invalid signature");
}
const payload = JSON.parse(req.body);
// Deduplicate using callback_event_id
if (await alreadyProcessed(payload.callback_event_id)) {
return res.status(200).send("Already handled");
}
if (payload.status === "succeeded") {
// Fetch the full event stream if you need step-by-step details
await handleSuccess(payload.session_id, payload.events_url);
} else {
await handleFailure(payload.session_id, payload.error);
}
// Respond within 10 seconds or Scout will retry
res.status(200).send("OK");
});
Always read the raw request body before calling JSON.parse. If the JSON is parsed and re-serialized first, the body bytes change and the signature won’t match.
Retry Behavior
Scout uses at-least-once delivery, so the same callback may arrive more than once.
- Deduplication — check
callback_event_id before processing to avoid duplicate work
- Retry schedule — exponential backoff over roughly 24 hours
- Retry triggers — network errors or
5xx responses from your endpoint
Requirements
callback_url must use HTTPS
- Private and internal URLs aren’t supported (SSRF protection)
- Your endpoint must respond within 10 seconds