Missions.
The REST Missions API is the most direct way to drive a Houston
agent. Create a mission, then poll it, stream it, or wait for a
signed webhook. All paths are on
https://poc-gateway.gethouston.ai and take
Authorization: Bearer hst_....
Create a mission
Starts a mission and returns 202 Accepted right away.
The agent runs in the background; use the id in the response to
track it.
Request body
| Field | Type | Description |
|---|---|---|
input required | string | The instruction for the agent, in natural language. |
mode optional | string | One of execute, plan, or auto. Defaults to auto (the agent decides whether to plan first or act directly). |
model optional | string | Override the model for this mission. Defaults to the agent's configured model. |
effort optional | string | Reasoning effort for the mission. Defaults to the agent's setting. |
title optional | string | A label for the mission, shown in the Houston app. Auto-generated if omitted. |
webhook optional | object | Delivers a signed callback when the mission finishes. See Webhooks. |
webhook.url | string | Public https:// URL to POST to. Private and internal addresses are rejected. |
webhook.secret | string | Signing secret used to compute the X-Houston-Signature HMAC. |
curl -X POST https://poc-gateway.gethouston.ai/v1/agents/revenue-manager/missions \
-H "Authorization: Bearer hst_9f2c1a7b4e8d0364f1a2b3c4d5e6f7089f2c1a7b4e8d0364f1a2b3c4d5e6f708" \
-H "Content-Type: application/json" \
-d '{
"input": "Draft the weekly competitor summary for the Lisbon property.",
"mode": "auto",
"model": "claude-opus-4-5",
"title": "Weekly competitor summary",
"webhook": {
"url": "https://api.acme.com/hooks/houston",
"secret": "whsec_a1b2c3d4e5f6"
}
}'202 Accepted
{
"id": "msn_7Q4c1a9f2b",
"agentSlug": "revenue-manager",
"conversationId": "cnv_3e8d0364f1",
"status": "running",
"createdAt": "2026-07-12T09:31:07.482Z"
}List missions
Returns the agent's missions, newest first.
| Query | Type | Description |
|---|---|---|
limit optional | integer | How many to return. Defaults to 50, maximum 200. |
curl "https://poc-gateway.gethouston.ai/v1/agents/revenue-manager/missions?limit=20" \
-H "Authorization: Bearer hst_9f2c1a7b4e8d0364f1a2b3c4d5e6f7089f2c1a7b4e8d0364f1a2b3c4d5e6f708"{
"missions": [
{
"id": "msn_7Q4c1a9f2b",
"agentSlug": "revenue-manager",
"conversationId": "cnv_3e8d0364f1",
"status": "completed",
"createdAt": "2026-07-12T09:31:07.482Z"
}
]
}Get a mission
Returns the mission's current state. status is one of
running, completed, failed,
or canceled. Once the status is terminal,
result carries the agent's final text. If the mission
was created with a webhook, the response includes a
webhook block recording when it was delivered, or the
last error if delivery has not yet succeeded.
curl https://poc-gateway.gethouston.ai/v1/agents/revenue-manager/missions/msn_7Q4c1a9f2b \
-H "Authorization: Bearer hst_9f2c1a7b4e8d0364f1a2b3c4d5e6f7089f2c1a7b4e8d0364f1a2b3c4d5e6f708"{
"id": "msn_7Q4c1a9f2b",
"agentSlug": "revenue-manager",
"conversationId": "cnv_3e8d0364f1",
"status": "completed",
"result": "Weekly competitor summary is ready. Three properties moved rates...",
"createdAt": "2026-07-12T09:31:07.482Z",
"webhook": {
"url": "https://api.acme.com/hooks/houston",
"deliveredAt": "2026-07-12T09:34:52.140Z"
}
}Stream events (SSE)
Streams the mission's progress as Server-Sent Events and closes when
the mission reaches a terminal state. Each event has an id; if the
connection drops, reconnect and send the
Last-Event-ID header to resume without gaps. Native SSE
clients and curl do this for you.
curl -N https://poc-gateway.gethouston.ai/v1/agents/revenue-manager/missions/msn_7Q4c1a9f2b/events \
-H "Authorization: Bearer hst_9f2c1a7b4e8d0364f1a2b3c4d5e6f7089f2c1a7b4e8d0364f1a2b3c4d5e6f708"
EventSource cannot send an Authorization
header, so this endpoint also accepts the key as a query
parameter: ?token=hst_.... Keep that URL out of logs.
Cancel a mission
Requests cancellation of a running mission. If the mission has
already reached a terminal state, you get 409 Conflict:
{ "code": "mission_finished" }List agents
Lists the agents this key can reach, with the slugs you pass to the mission endpoints.
curl https://poc-gateway.gethouston.ai/agents \
-H "Authorization: Bearer hst_9f2c1a7b4e8d0364f1a2b3c4d5e6f7089f2c1a7b4e8d0364f1a2b3c4d5e6f708"API-key scope & limits
API keys are deliberately narrow. A key works
only on the programmatic surfaces: the Missions
endpoints, A2A, MCP, and GET /agents. Point it at any
other part of the gateway and you get 403 Forbidden:
{ "code": "api_key_scope" }
You can hold up to 20 active keys. Creating a 21st
returns 400 Bad Request; revoke an old one first.
{ "code": "key_limit" }Webhooks
Pass a webhook object when you create a mission and
Houston will POST a signed payload to your URL the
instant the mission finishes. This is the recommended pattern for
servers: no polling, no open connections.
Payload
{
"event": "mission.completed",
"mission": {
"id": "msn_7Q4c1a9f2b",
"agentSlug": "revenue-manager",
"conversationId": "cnv_3e8d0364f1",
"status": "completed",
"result": "Weekly competitor summary is ready...",
"createdAt": "2026-07-12T09:31:07.482Z"
}
}
event is one of mission.completed,
mission.failed, or mission.canceled.
Headers
| Header | Value |
|---|---|
X-Houston-Event | The event name, e.g. mission.completed. |
X-Houston-Delivery | A unique id for this delivery attempt group. Use it to deduplicate. |
X-Houston-Signature | sha256=<hex>, the HMAC-SHA256 of the raw request body keyed with your webhook.secret. |
Delivery & reliability
- Houston waits up to 10 seconds for your endpoint to respond.
- Delivery is attempted up to 6 times: the initial POST plus 5 retries, backing off 30s, 1m, 2m, 4m, then 8m.
- Delivery is at-least-once. Retries can duplicate an event, so treat handlers as idempotent and deduplicate on
X-Houston-Delivery. - The URL must be a public
https://address. Private, loopback, and internal addresses are rejected.
Verify the signature
Compute the HMAC over the raw request body (not a re-serialized object) and compare in constant time:
import crypto from "node:crypto";
// `rawBody` is the exact bytes Houston sent (a Buffer or string).
// In Express, capture it with express.raw({ type: "application/json" }).
function verifyHoustonWebhook(rawBody, signatureHeader, secret) {
const expected =
"sha256=" +
crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
const a = Buffer.from(signatureHeader ?? "");
const b = Buffer.from(expected);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
app.post("/hooks/houston", express.raw({ type: "application/json" }), (req, res) => {
const ok = verifyHoustonWebhook(
req.body,
req.get("X-Houston-Signature"),
process.env.HOUSTON_WEBHOOK_SECRET,
);
if (!ok) return res.sendStatus(401);
// Deduplicate on the delivery id, then process.
const deliveryId = req.get("X-Houston-Delivery");
const payload = JSON.parse(req.body.toString("utf8"));
// ... handle payload.event / payload.mission ...
res.sendStatus(200);
});Frameworks that auto-parse JSON change the byte stream. Verify against the untouched body Houston sent, or the HMAC will not match even with the right secret.