Action Builder
Put a decision policy to work as an agent: other AI agents call it over A2A or MCP, your systems over REST, people through a chat, with a person approving before it acts.
When to use this API
Use the Action Builder when a decision should lead to something being done, and other software, an AI agent or a person needs to ask for it.
An action is built in the console from a decision policy of the Logic Controller. You choose the interfaces it ships as (a chat, an API, an MCP server, an A2A agent, a schedule or a connector) and its oversight: whether a person approves before it acts (the default) or reviews after. Every request and every decision is written to the audit log.
| Interface | Use it for |
|---|---|
| A2A | Another AI agent asks the action to act on a record, or asks it a question. Requests wait for a person's approval when the oversight says so. |
| MCP server | An AI agent reads the action's recommendations as tools. |
| REST | Your systems get the recommendation for a record, list them, or report a change. |
| Webhooks | Your systems are told when a record's recommendation changes. |
| Website chat | People ask the action in plain words on your site or intranet. |
Authentication
Every interface takes the action's API key. Create one in the console under the action's
API & integration. A key opens this one action only, is shown once (only a hash of it is
kept) and can be set to expire after 30, 90 or 365 days; rotate it with a grace period, or revoke it at once.
Keys start with ok_live_.
x-ok-api-key: ok_live_xxxxxxxxxxxxxxxxxxxxxxxx
Authorization: Bearer ok_live_xxxxxxxxxxxxxxxxxxxxxxxx
Parameters
Path
| Name | Type | Required | Description |
|---|---|---|---|
| actionId | string | required | The action's id, shown under API & integration (for example uc-5e20d7). |
The agent card
GET /a2a/{actionId}/.well-known/agent-card.json is public and describes the action to other
agents: its endpoint, the security schemes (x-ok-api-key or bearer) and its skills. Send
A2A-Version: 1.0 for the 1.0 form; without it the card is served in the 0.3 form.
The card is offered once the action is deployed with A2A among its interfaces.
Skills
Name the skill in message.metadata.skill (or skill in a data part) and put its input in a data part. A plain-text message with no skill is taken as a question.
| Skill | Input | What it does |
|---|---|---|
| request_action | { key, entity?, reason? } | Asks the action to act on a record. With approves before it acts the task waits for a person; with reviews after it acts at once. |
| describe_decisions | { } | The entities, fields and rules the action decides with. |
| decide | { entity, record } | The recommendation for a record your system holds. |
| get_decision | { entity, key } | One record's recommendation with its evidence. |
| list_decisions | { entity?, outcome?, limit?, offset? } | The recommendations the deployed version holds. |
Request
Ask the action to act on a record (A2A 1.0, SendMessage):
curl -X POST https://okp.openknowra.ai/a2a/$ACTION_ID \
-H "x-ok-api-key: $OPENKNOWRA_ACTION_KEY" \
-H "Content-Type: application/json" \
-H "A2A-Version: 1.0" \
-d '{"jsonrpc":"2.0","id":1,"method":"SendMessage","params":{"message":{"messageId":"m-1","role":"ROLE_USER",
"metadata":{"skill":"request_action"},
"parts":[{"data":{"key":"WO-2291","entity":"WorkOrder","reason":"Crew available on site today"}}]}}}'import os, json, urllib.request
body = {"jsonrpc": "2.0", "id": 1, "method": "SendMessage", "params": {"message": {
"messageId": "m-1", "role": "ROLE_USER", "metadata": {"skill": "request_action"},
"parts": [{"data": {"key": "WO-2291", "entity": "WorkOrder", "reason": "Crew available on site today"}}]}}}
req = urllib.request.Request(
"https://okp.openknowra.ai/a2a/" + os.environ["ACTION_ID"],
data=json.dumps(body).encode(),
headers={"x-ok-api-key": os.environ["OPENKNOWRA_ACTION_KEY"], "Content-Type": "application/json",
"A2A-Version": "1.0"},
method="POST")
print(json.load(urllib.request.urlopen(req, timeout=30)))const res = await fetch(`https://okp.openknowra.ai/a2a/${process.env.ACTION_ID}`, {
method: "POST",
headers: { "x-ok-api-key": process.env.OPENKNOWRA_ACTION_KEY!, "Content-Type": "application/json", "A2A-Version": "1.0" },
body: JSON.stringify({
jsonrpc: "2.0", id: 1, method: "SendMessage",
params: { message: { messageId: "m-1", role: "ROLE_USER", metadata: { skill: "request_action" },
parts: [{ data: { key: "WO-2291", entity: "WorkOrder", reason: "Crew available on site today" } }] } },
}),
});
console.log(await res.json());SendMessage,
SendStreamingMessage, GetTask, CancelTask, ListTasks) and
also accepts the 0.3 method names (message/send, tasks/get, ...).
Response
200 OK - a task. With approves before it acts, a request_action task
stays working until a person decides in the console:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"task": {
"id": "8f1c2a90-4b7e-4d2a-9a51-1b2c3d4e5f60",
"contextId": "c-77b1",
"status": {
"state": "TASK_STATE_WORKING",
"message": { "role": "ROLE_AGENT", "parts": [{ "text": "Awaiting approval by the action's approvers: Field dispatch acts on WO-2291 once a person approves. Read the outcome with GetTask." }] }
}
}
}
}
Poll the task with GetTask until it settles:
| State (1.0 · 0.3) | Meaning |
|---|---|
| TASK_STATE_WORKING · working | Waiting for a person's approval (or still running). |
| TASK_STATE_COMPLETED · completed | Approved and done, or acted on at once under reviews after. The result is an artifact named after the skill, with a text part and a data part. |
| TASK_STATE_REJECTED · rejected | Declined by a person; the status message gives who and why. |
| TASK_STATE_FAILED · failed | The request could not be carried out, for example an unknown skill or record. The status message says why. |
curl -X POST https://okp.openknowra.ai/a2a/$ACTION_ID \
-H "x-ok-api-key: $OPENKNOWRA_ACTION_KEY" -H "Content-Type: application/json" -H "A2A-Version: 1.0" \
-d '{"jsonrpc":"2.0","id":2,"method":"GetTask","params":{"id":"8f1c2a90-4b7e-4d2a-9a51-1b2c3d4e5f60"}}'
pushNotifications: false on the card): poll with GetTask, or use
SendStreamingMessage while you wait. Tasks are kept for a limited time, so read a settled task's
outcome soon after it settles; the decision itself stays on the action's audit log.
MCP server
The action is also an MCP server (Streamable HTTP, JSON-RPC 2.0) at
https://okp.openknowra.ai/api/okp/mcp/{actionId}, with the tools
describe_decisions, decide, get_decision and
list_decisions. They read recommendations; asking the action to act is A2A's
request_action.
{
"mcpServers": {
"field-dispatch": {
"type": "http",
"url": "https://okp.openknowra.ai/api/okp/mcp/<actionId>",
"headers": { "Authorization": "Bearer ok_live_xxxxxxxxxxxxxxxxxxxxxxxx" }
}
}
}
REST
The action answers the same REST API as its decision policy, relative to
https://okp.openknowra.ai/api/okp/d/{actionId}: GET / (describe),
POST /decide, GET /decisions, GET /decisions/{entity}/{key},
POST /events and GET /openapi.json. The request and response shapes are on the
Logic Controller page.
curl https://okp.openknowra.ai/api/okp/d/$ACTION_ID/decisions/WorkOrder/WO-2291 \
-H "x-ok-api-key: $OPENKNOWRA_ACTION_KEY"
Webhooks
Under API & integration, add a webhook to be told when a record gains or loses an
outcome (rule_result.gained, rule_result.lost) or a new version is deployed
(version.published). Deliveries are signed with your secret in x-ok-signature
(sha256= HMAC of timestamp + "." + body, with x-ok-timestamp in
milliseconds) and sent once with an 8-second timeout. The headers, payloads and a verification example are on
the Logic Controller page.
Website chat
Paste the action's chat into any page; people ask it in plain words and get its recommendations.
<script src="https://okp.openknowra.ai/okplatform/widget.js"
data-agent="<actionId>" data-key="ok_live_xxxxxxxxxxxxxxxxxxxxxxxx"
data-title="Field dispatch" data-position="right"></script>
Optional attributes: data-title, data-color, data-position
(right or left), data-open="true", data-inline (a CSS
selector to render into) and data-theme (light or dark).
Pricing
Requests over A2A, MCP and REST, and webhook deliveries, carry no per-call charge. Questions people ask in the website chat are billed as Natural Language Queries, and the context graph the action decides on is billed under the Context Compiler. The APIs, AI agents and workers an action can call are priced on their own cards: Action Builder pricing.
Errors
A2A and MCP answer with JSON-RPC errors; REST and the chat with { "error", "message" }.
| Status | Code | Meaning |
|---|---|---|
| 401 | -32001 · invalid_api_key | Missing, revoked or expired key, or a key for another action. |
| 402 | insufficient_credits | Website chat: your organisation's balance cannot pay for the question. |
| 404 | -32001 · not_found | No such action, or it is not offered over A2A (not deployed, or A2A is not among its interfaces). |
| 405 | -32000 | MCP: only POST is supported. |
| 409 | not_published | The action has no deployed version yet. |
| 423 | -32001 · paused | The action is paused in the console. |
| 200 | -32601 · -32602 | JSON-RPC: unknown method, or an unknown tool or bad arguments. |
| 5xx | -32603 · failed | Transient. Retry with exponential backoff. |