Agentic Workforce ME Developer PortalDocs 1.1 · Widget 0.1.0

Backend integration

Triggers & workflows

Turn a business event into a run: create a workflow and a webhook trigger, sign requests with HMAC (exact headers and canonical string), map payloads, test locally.

A trigger turns a business event into an Agentic Workforce ME run without an API key in the caller. You create it once, pointed at a published workflow or agent, and receive a secret. From then on, any system that can send an HTTPS POST with an HMAC signature — your monolith, a serverless function, a no-code tool — can start governed agent work. This is how the demo portals start every automated case.

1. The target: a published workflow (or agent)

Point a trigger at an agent when one agent should simply handle the event. Point it at a workflow when you want orchestration: several agents, branches, HTTP calls, waits for external events, human tasks. A workflow is a graph of typed nodes (start, agent, branch, http_request, mcp_tool, wait_event, human_task, parallel, foreach, subworkflow, end…); the trigger payload is available to every node as {{input}}.

POST /v1/workflowsJSON
{
  "slug": "order-escalation",
  "name": "Order escalation",
  "graph": {
    "nodes": [
      { "id": "start", "type": "start", "config": {} },
      {
        "id": "refunds",
        "type": "agent",
        "config": {
          "agent_id": "…",
          "agent_version_id": "…",            the PUBLISHED version id (pinned)
          "input_template": "Handle this order escalation using your tools. Order {{input.order_id}}; reason: {{input.reason}}; idempotency_key: {{input.idempotency_key}}. Full event: {{input}}"
        }
      },
      { "id": "end", "type": "end", "config": {} }
    ],
    "edges": [
      { "from": "start", "to": "refunds" },
      { "from": "refunds", "to": "end" }
    ]
  }
}
  • An agent node pins agent_version_id to a published version, so a later draft of the agent cannot change a running workflow until you republish the workflow too.
  • input_template is a Mustache-style string; {{input.x}} reads a field, {{input}} injects the whole event as JSON.
  • Keep agent nodes free of approval-gated tools: a child run that pauses for an approval fails the workflow (CHILD_RUN_FAILED). Put a gate node before a sensitive step instead, or run the gated agent directly — see Human-in-the-loop approvals.
  • The finished workflow run’s output is { outputs: { <node_id>: { text, child_run_id } }, variables }, not a bare { text } — read the node you care about when the run.completed webhook arrives.
  • Creating a workflow gives you draft v1; publish it before a trigger can start it (409 WORKFLOW_NOT_PUBLISHED otherwise). Later edits go to the draft (PUT …/versions/draft) and are published as a new version.
Shell
# Create returns { "workflow": { "id" } } with draft v1 → publish it
curl -X POST https://console.agenticworkforce.me/v1/workflows/$WORKFLOW_ID/versions/1/publish \
  -H "Authorization: Bearer $HIVE_API_KEY" -H "X-Tenant-Id: $HIVE_TENANT_ID"

# Later changes: PUT the draft, publish the returned version
curl -X PUT https://console.agenticworkforce.me/v1/workflows/$WORKFLOW_ID/versions/draft \
  -H "Authorization: Bearer $HIVE_API_KEY" -H "X-Tenant-Id: $HIVE_TENANT_ID" \
  -H "Content-Type: application/json" -d @graph.json
# → { "version": 2 }

You can also build the graph visually in the console (Workflows → New) and only script the trigger; the API and the console write the same resource.

2. Create the trigger

POST /v1/triggers with kind: "webhook", the target and an optional config. The response includes the public hook path and, once only, the HMAC secret (64 hex characters). Store it with your secrets; if it is lost, POST /v1/triggers/:id/rotate-secret issues a new one and invalidates the old one immediately — there is no overlap window, so deploy the returned value straight away (or create a second trigger, cut over, then delete the old one).

Shell
curl -X POST https://console.agenticworkforce.me/v1/triggers \
  -H "Authorization: Bearer $HIVE_API_KEY" -H "X-Tenant-Id: $HIVE_TENANT_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Order escalated",
    "kind": "webhook",
    "target_kind": "workflow",
    "target_id": "'$WORKFLOW_ID'",
    "config": {}
  }'
201 responseJSON
{
  "id": "019a1b2c-…",
  "name": "Order escalated",
  "kind": "webhook",
  "target_kind": "workflow",
  "target_id": "…",
  "enabled": true,
  "webhook_path": "/hooks/019a1b2c-…",
  "has_secret": true,
  "secret": "3f9c…64 hex chars…"           shown once; store it with your secrets
}

Payload → run input

By default the parsed JSON body is the run input. For an agent target the agent reads its prompt from text (or a bare string / input); for a workflow target, nodes address fields as {{input.field}}. If the sending system’s field names are fixed, add config.input_mapping to rename top-level fields on the way in:

JSON
{
  "name": "Ticket created",
  "kind": "webhook",
  "target_kind": "agent",
  "target_id": "…",
  "config": {
    "input_mapping": {
      "text": "description",           run input field  TOP-LEVEL body field
      "customer_id": "requester_id"      (no dot paths; unknown fields map to undefined)
    }
  }
}

3. Fire it with a signed request

The hook endpoint is POST /hooks/:triggerId on the API origin. It takes no Authorization or X-Tenant-Id header — the trigger id identifies the tenant and the signature proves the caller holds the secret.

ElementValue
Headerx-hive-signature
Valuesha256=<hex digest> lowercase hex
AlgorithmHMAC-SHA256 keyed with the trigger secret (UTF-8)
Signed materialthe raw request body bytes, exactly as sent
BodyJSON object (any shape). Content-Type: application/json.
Success202 { run_id }
Failures401 INVALID_SIGNATURE · 400 INVALID_BODY (not JSON) · 404 for unknown, disabled or non-webhook triggers (no oracle) · 409 WORKFLOW_NOT_PUBLISHED / AGENT_NOT_PUBLISHED
Shell
BODY='{"event":"order.escalated","order_id":"ord_1001","reason":"Item arrived damaged","idempotency_key":"escalation:ord_1001:1"}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$HIVE_TRIGGER_SECRET" | sed 's/^.* //')

curl -X POST https://console.agenticworkforce.me/hooks/$HIVE_TRIGGER_ID \
  -H "Content-Type: application/json" \
  -H "x-hive-signature: sha256=$SIG" \
  --data-binary "$BODY"
# → 202 { "run_id": "…" }

The Python sample and the example’s hive-signing.mjs are executed by this portal’s tests against the platform’s own verifier; the .NET and Java samples are checked for the same header, prefix and algorithm.

Sign the bytes you send

  • Serialise once, sign that string, send that string. Re-serialising (or letting an HTTP library re-encode the body) changes bytes and breaks the signature.
  • The comparison is constant-time on the full sha256=… string, so the prefix and lowercase hex are part of the contract.
  • An empty body is accepted and yields an empty input; a non-JSON body is a 400.

Replay protection and idempotency

  • Put a unique idempotency_key (your record id plus a version or timestamp) in the payload and make the agent pass it to your mutating MCP tools, which must be idempotent on it.
  • Include occurred_at in the payload and let your tools reject stale events, or check the run’s created_at against it.
  • Only ever send hooks over HTTPS, rotate the secret when a system that held it is decommissioned, and keep one trigger per source system so a leak has a small blast radius.

4. Test it

Two authenticated shortcuts skip the HMAC while you develop: test-run on the workflow and fire on the trigger. Both take the same input shape the hook would deliver and return 202 { run_id }; follow the run with SSE or polling.

Shell
# Run the workflow without HMAC (same input shape the hook would deliver)
curl -X POST https://console.agenticworkforce.me/v1/workflows/$WORKFLOW_ID/test-run \
  -H "Authorization: Bearer $HIVE_API_KEY" -H "X-Tenant-Id: $HIVE_TENANT_ID" \
  -H "Content-Type: application/json" \
  -d '{"input":{"event":"order.escalated","order_id":"ord_1001","reason":"test","idempotency_key":"test-1"}}'

# Or fire the trigger itself through the authenticated API (no signature needed)
curl -X POST https://console.agenticworkforce.me/v1/triggers/$HIVE_TRIGGER_ID/fire \
  -H "Authorization: Bearer $HIVE_API_KEY" -H "X-Tenant-Id: $HIVE_TENANT_ID" \
  -H "Content-Type: application/json" \
  -d '{"input":{"event":"order.escalated","order_id":"ord_1001","reason":"test","idempotency_key":"test-2"}}'

To exercise the real hook from a laptop, run the API locally or point your script at a staging tenant: the hook endpoint is reachable from anywhere, so no tunnel is needed in this direction (you need one for webhooks, where the platform calls you). GET /v1/runs/:id shows origin with the trigger_id, and the audit log records trigger.fire.

Resuming a workflow from your backend

A workflow can park on a wait_event node until your system says so — a payment settled, a document was signed. Deliver the event to the waiting run with POST /v1/workflows/runs/:runId/events; the payload becomes available to the following nodes.

Shell
# A run parked on a wait_event node resumes when your system delivers the event
curl -X POST https://console.agenticworkforce.me/v1/workflows/runs/$RUN_ID/events \
  -H "Authorization: Bearer $HIVE_API_KEY" -H "X-Tenant-Id: $HIVE_TENANT_ID" \
  -H "Content-Type: application/json" \
  -d '{"event_name":"payment.settled","payload":{"settlement_id":"stl_88"}}'
# → 202 { "run_id": "…", "event_name": "payment.settled" }

Trigger endpoints

GET/v1/triggersList triggers (webhook, cron, scheduled, event).
Auth
API key + X-Tenant-Id (tenant admin)
Errors
UNAUTHORIZED TENANT_REQUIRED FORBIDDEN
POST/v1/triggers201Create a trigger. A webhook trigger returns its signing secret once.
Auth
API key + X-Tenant-Id (tenant admin)
Errors
UNAUTHORIZED TENANT_REQUIRED FORBIDDEN VALIDATION NOT_FOUND

Request

FieldTypeDescription
namestringDisplay name (1–120).
kind`webhook` | `cron` | `scheduled` | `event`Fire mechanism.
target_kind`agent` | `workflow`What a fire starts (default agent).
target_iduuidA published agent or workflow.
configobjectPer kind. Webhook: { input_mapping?: { <run field>: <top-level body field> } } (no nested paths). Cron: { cron, timezone?, input? }. Scheduled: { run_at, input? }.
enabledbooleanDefault true.

Response

FieldTypeDescription
iduuidTrigger id — the last path segment of the hook URL.
webhook_pathstring | null/hooks/<id> for webhook triggers.
secretstringWebhook only. 64 hex chars, shown once; encrypted at rest. Sign every request with it.
  • Audited as trigger.create.
GET/v1/triggers/:idOne trigger (has_secret, last_fired_at, next_run_at; never the secret).
Auth
API key + X-Tenant-Id (tenant admin)
Errors
UNAUTHORIZED TENANT_REQUIRED FORBIDDEN NOT_FOUND
PATCH/v1/triggers/:idRename, enable/disable, re-scope or change config. Kind and target are immutable.
Auth
API key + X-Tenant-Id (tenant admin)
Errors
UNAUTHORIZED TENANT_REQUIRED FORBIDDEN VALIDATION NOT_FOUND
DELETE/v1/triggers/:id204Delete a trigger (204). Its hook URL stops answering with 404.
Auth
API key + X-Tenant-Id (tenant admin)
Errors
UNAUTHORIZED TENANT_REQUIRED FORBIDDEN NOT_FOUND
POST/v1/triggers/:id/rotate-secretMint a new HMAC secret (shown once). The old one stops validating immediately.
Auth
API key + X-Tenant-Id (tenant admin)
Errors
UNAUTHORIZED TENANT_REQUIRED FORBIDDEN NOT_FOUND

Response

FieldTypeDescription
secretstringThe new secret.
  • Audited as trigger.rotate_secret. Rotation invalidates the old secret immediately and there is no overlap window (the secret is generated server-side, so it cannot be deployed before it exists): rotate, then deploy the returned value straight away and expect a brief 401 window. For zero downtime, create a second trigger, cut your backend over, then delete the old one.
POST/v1/triggers/:id/fire202Fire a trigger now through the authenticated API (no HMAC).
Auth
API key + X-Tenant-Id (any member; an API key acts as admin)
Errors
UNAUTHORIZED TENANT_REQUIRED NOT_FOUND TRIGGER_DISABLED

Request

FieldTypeDescription
inputunknown?Overrides the trigger’s configured input.

Response

FieldTypeDescription
run_iduuidThe queued run.
  • Audited as trigger.manual_fire. A disabled trigger returns 409 TRIGGER_DISABLED.
POST/hooks/:triggerId202The public hook URL your backend calls. No API key — authenticity is the HMAC over the raw body.
Auth
x-hive-signature: sha256=<hex HMAC-SHA256(secret, raw body)> — no Authorization, no X-Tenant-Id
Errors
INVALID_SIGNATURE INVALID_BODY NOT_FOUND AGENT_NOT_PUBLISHED WORKFLOW_NOT_PUBLISHED

Request

FieldTypeDescription
<body>JSON objectBecomes the run input as-is, or remapped through the trigger’s input_mapping. Sign the exact bytes you send.

Response

FieldTypeDescription
run_iduuidThe queued agent or workflow run.
  • A missing, disabled or non-webhook trigger and a trigger without a secret all answer 404 (no oracle).
  • A wrong signature is 401 INVALID_SIGNATURE; a non-JSON body is 400 INVALID_BODY; an unpublished target is 409.
  • Audited as trigger.fire; the run’s origin records the trigger id.

Workflow endpoints

POST/v1/workflows201Create a workflow (draft v1) from a graph of nodes and edges.
Auth
API key + X-Tenant-Id (tenant admin)
Errors
UNAUTHORIZED TENANT_REQUIRED FORBIDDEN VALIDATION SLUG_TAKEN

Request

FieldTypeDescription
slugstringTenant-unique, lowercase [a-z0-9-].
namestringDisplay name.
graph{ nodes, edges }Exactly one start, at least one end; node types include agent, branch, mcp_tool, http_request, gate, human_task, wait_event, foreach, parallel, subworkflow.

Response

FieldTypeDescription
workflow.iduuidWorkflow id (the trigger target).
PUT/v1/workflows/:id/versions/draftReplace the draft graph (creates the next version when the current one is published).
Auth
API key + X-Tenant-Id (tenant admin)
Errors
UNAUTHORIZED TENANT_REQUIRED FORBIDDEN VALIDATION NOT_FOUND

Request

FieldTypeDescription
graph{ nodes, edges }The new graph.

Response

FieldTypeDescription
versionnumberDraft version number to publish.
POST/v1/workflows/:id/versions/:v/publishPublish a version — structural validation runs here; published versions are immutable.
Auth
API key + X-Tenant-Id (tenant admin)
Errors
UNAUTHORIZED TENANT_REQUIRED FORBIDDEN VALIDATION NOT_FOUND
POST/v1/workflows/:id/test-run202Start the published workflow directly with an input (what a trigger does, without HMAC).
Auth
API key + X-Tenant-Id (any member; an API key acts as admin)
Errors
UNAUTHORIZED TENANT_REQUIRED NOT_FOUND WORKFLOW_NOT_PUBLISHED

Request

FieldTypeDescription
inputunknown?Run input ({{input.*}} in templates).

Response

FieldTypeDescription
run_iduuidThe parent workflow run.
POST/v1/workflows/runs/:runId/events202Deliver a named event to a run parked at a wait_event node (your system decides, the run resumes).
Auth
API key + X-Tenant-Id (any member; an API key acts as admin)
Errors
UNAUTHORIZED TENANT_REQUIRED VALIDATION NOT_FOUND

Request

FieldTypeDescription
event_namestringLowercase slug matching the node’s event_name.
payloadunknown?Saved into the node’s save_as variable.