SuperAgent API
Runs, artifacts, webhooks, and errors
Use a direct call when your app can wait for the answer. Use a run when work may continue after the request ends. Use a webhook when HivemindOS should tell your app that something changed.
| Need | Use |
|---|---|
| A quick result in the current request | Direct service invocation |
| A durable status for longer work | Run |
| A saved result file | Artifact |
| An automatic notification | Webhook |
Durable runs
Create a run with runs:write and service access for the selected service:
const created = await hive.runs.create(
{
serviceId: "hive-research",
operationId: "analyses.create",
input: { question: "Create an evidence map for this decision." },
},
{ idempotencyKey: "evidence-map-run-001" },
);
if (!created.ok) throw new Error(created.error);
console.log(created.run.id, created.run.status);
A run records:
- the managed
serviceIdand stable operation id; status,progress,output, and a customer-safeerror;- maximum and charged HivemindOS credits when the run owns that accounting; and
- creation and update times.
Run creation returns HTTP 202 with status queued. HivemindOS starts the work outside the request, then records running, succeeded, or failed. Read runs with runs.list() or runs.get(id). Only a run that is still queued can be cancelled.
Run listings and reads are filtered through the caller’s service boundary. A key cannot read a run for a service it is not allowed to use.
Result artifacts
Successful JSON run results are also saved as authenticated result.json artifacts. Durable artifact storage accepts results up to 25 MB; a deployment without managed artifact storage retains smaller results only.
const listed = await hive.artifacts.list(runId);
if (!listed.ok) throw new Error(listed.error);
for (const artifact of listed.artifacts) {
const response = await fetch(artifact.downloadUrl, {
headers: {
Authorization: `Bearer ${process.env.HIVEMINDOS_API_KEY}`,
},
});
if (!response.ok) throw new Error(`Artifact download failed: ${response.status}`);
const result = await response.json();
}
Artifact access requires artifacts:read, account ownership, and service access for the source run. Download URLs are not public links.
Create a signed webhook
A webhook needs webhooks:write, a public HTTPS destination, at least one event name, and an idempotency key.
const created = await hive.webhooks.create(
{
url: "https://example.com/webhooks/hivemindos",
events: [
"run.succeeded",
"run.failed",
"approval.created",
"approval.approved",
"approval.rejected",
"wallet.transaction.broadcast",
"trade.submitted",
],
},
{ idempotencyKey: "example-webhook-v1" },
);
if (!created.ok) throw new Error(created.error);
saveSecret(created.signingSecret);
Use "*" to receive all events permitted by the webhook’s service boundary. A webhook inherits the creating key’s resolved service allowlist. Revoking or expiring that key or an ancestor stops new delivery. A key may list or disable only webhooks created by itself or its descendants.
Each delivery has its own receipt. Failed deliveries retry with increasing delays, up to eight attempts, and can be replayed after the destination is fixed:
const failed = await hive.webhooks.deliveries({ status: "failed", limit: 50 });
if (!failed.ok) throw new Error(failed.error);
for (const delivery of failed.deliveries) {
await hive.webhooks.replayDelivery(delivery.id, {
idempotencyKey: `replay-${delivery.id}`,
});
}
Use webhooks.update() to change the URL, event set, status, or narrower service boundary. Use webhooks.rotateSecret() to issue a new signing secret; the full new value is returned once.
The signing secret begins with hmos_whsec_ and is returned when the webhook is created or the same creation request is replayed. It is not included in later webhook listings.
Webhook URLs must use public HTTPS without embedded credentials or a custom port. Redirects are not followed.
Event format
Each delivery includes:
X-HivemindOS-Event: run.succeeded
X-HivemindOS-Delivery: delivery_...
X-HivemindOS-Signature: t=1787551200,v1=<hex-hmac>
Content-Type: application/json
{
"id": "event_...",
"type": "run.succeeded",
"serviceId": "hive-research",
"createdAt": "2026-08-24T12:00:00.000Z",
"data": {
"run": {}
}
}
Current event names include:
run.started,run.succeeded, andrun.failedapproval.created,approval.approved, andapproval.rejectedwallet.created,wallet.policy.updated, andwallet.signature.createdwallet.transaction.broadcastandtrade.submitted
Design consumers to accept new event names and additive data fields without failing.
Verify a signature
Compute HMAC-SHA256 over <timestamp>.<raw-request-body> with the webhook signing secret. Compare the hexadecimal digest in constant time and reject stale timestamps before parsing or acting on the event.
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyHivemindOSWebhook(
rawBody: Buffer,
signatureHeader: string,
secret: string,
nowSeconds = Math.floor(Date.now() / 1_000),
) {
const values = Object.fromEntries(
signatureHeader.split(",").map((part) => part.split("=", 2)),
);
const timestamp = Number(values.t);
if (!Number.isSafeInteger(timestamp)) return false;
if (Math.abs(nowSeconds - timestamp) > 300) return false;
const expected = Buffer.from(
createHmac("sha256", secret)
.update(`${timestamp}.`)
.update(rawBody)
.digest("hex"),
"hex",
);
const received = Buffer.from(values.v1 ?? "", "hex");
return received.length === expected.length && timingSafeEqual(received, expected);
}
Verify against the raw bytes exactly as received. Re-serializing parsed JSON changes the signature. After verification, deduplicate on the event id, place work on your own queue, and return a 2xx response promptly.
Error envelope
All JSON errors include ok: false and a customer-safe error. Some errors add fields that tell your application what to do next.
| HTTP | Meaning | Recommended handling |
|---|---|---|
| 400 | The request shape or value is invalid. | Fix the request; do not repeat it unchanged. |
| 401 | The key is invalid, revoked, or expired. | Stop using it and rotate credentials. |
| 402 | The HivemindOS credit balance or managed database allowance is insufficient. | Ask the account owner to top up, activate an eligible plan, or reduce usage. |
| 403 | The key scope, service boundary, delegated authority, approval, or wallet policy rejects the action. | Use an appropriately restricted key or change the account-owned policy through its normal review path. |
| 404 | The route or account-owned resource was not found. | Check the id and the key’s service boundary. |
| 409 | The state, confirmation, quote, approval, or idempotency key conflicts with the request. | Read the current resource and create a fresh quote or idempotency key only for a genuinely new action. |
| 410 | A downloadable database archive expired. | Start a new download copy. |
| 413 | The request or upload part is too large. | Reduce or split the input within the documented limits. |
| 424 | Required managed-service setup is incomplete. | Ask the account owner to finish setup before trying again. |
| 429 | An API-key limit was reached. | Wait for retryAfterSeconds and respect Retry-After. |
| 500–503 | The managed operation is temporarily unavailable. | Preserve the same idempotency key and retry with bounded backoff when the action is safe to repeat. |
For HTTP 429, read the per-endpoint limits guide. For the exact request schema, use the OpenAPI contract.