Docs
Request a chain, send it steps, check how it is going.
The idea
Compute servers run one job at a time, and a job can take up to an hour. botrouter sits in front of them: you ask for a chain, it waits in a queue until a server is free, then the server is yours until the chain ends.
- Chain
- Your turn on one server. It has one plugin and a list of steps, all run on the same server, in order.
- Step
- One operation of the chain's plugin, with its payload. Each step has a result.
- Plugin and operation
- What a server can do, e.g. plugin
bank, operationtinaba/login. Your admin tells you which ones you can use and their parameters. - Class
- Set on your account by the admin. It gives your chains their priority and how long the server is held for you.
Get access
Your admin creates your client and gives you an API key (starts with brk_, shown only once). Send it on every request:
export BR=https://botrouter.example.com
export KEY=brk_...
curl -H "Authorization: Bearer $KEY" $BR/api/chains
No key yet? Ask for one with the email and password you were given:
curl -X POST $BR/api/api-key \
-H 'Content-Type: application/json' \
-d '{"identity": "pc1@example.com", "password": "..."}'
# {"api_key": "brk_..."}
Each call makes a new key and the old one stops working at once (keys are stored hashed, so they cannot be shown again). Ask once and keep it.
Keep the key secret. If it leaks, get a new one with the call above (or ask the admin): the old one stops working at once.
Request a chain
curl -X POST $BR/api/chains \
-H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
-d '{"plugin": "bank"}'
201 {"id": "k3x9...", "state": "queued"}
The chain is queued. Keep its id. Optional fields:
| Field | Meaning |
|---|---|
wait_for_me | true: keep the server after the last step, so you can decide what to do next. See Keep the server. |
webhook_url | Called when the chain changes state. See Webhooks. |
preferred_server | Server name you would like. Only a hint, and only for privileged classes. An unknown name is refused with 400. |
Send steps
You can send steps right away, even while the chain is still queued. They wait and run in order once a server is assigned.
curl -X POST $BR/api/chains/k3x9.../steps \
-H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
-d '{"operation": "tinaba/login", "payload": {"user": "..."}, "idempotency_key": "login-1"}'
202 {"seq": 1, "state": "pending"}
The operation must belong to the chain's plugin, and the payload must carry its required parameters, or the step is refused with 400. Payloads up to 10 MB; results up to 50 MB.
The idempotency_key is yours to choose, unique within the chain. Sending the same key again does not add a second step, so retrying after a network error is safe.
A chain without wait_for_me ends as soon as it has no step left to run. Send all its steps before the last one finishes.
Check status
All your chains, newest first (optionally ?state=running):
curl -H "Authorization: Bearer $KEY" $BR/api/chains
One chain, with its steps:
curl -H "Authorization: Bearer $KEY" $BR/api/chains/k3x9...
{"id": "k3x9...", "plugin": "bank", "state": "running", "server": "s1",
"steps": [{"seq": 1, "operation": "tinaba/login", "state": "running"}]}
One step, with its result:
curl -H "Authorization: Bearer $KEY" $BR/api/chains/k3x9.../steps/1
Results are kept for 1 day after the step ends (your admin may set a different time). Fetch them before then: afterwards the step is still there, with "result_pruned": true and no result.
Chain states
queued | Waiting for a server. |
leased | Got a server, no step started yet. |
running | A step is running. |
idle | Holding the server for you, waiting for a new step (wait_for_me only). |
closed | Done: all steps ran, or you closed it. |
expired | You did not send a new step in time; the server was released. |
failed | Something broke, e.g. the server was lost. The reason is in closed_reason. |
Step states
pending → running → done or failed. If a step fails, the steps queued after it are not run: they are marked failed too. Then the chain ends as usual: closed, or idle with wait_for_me, so you can decide what to do.
Keep the server
Sometimes the next step depends on a result. Create the chain with "wait_for_me": true: when its last step ends, the chain goes idle and the server stays yours for a while (set by your class). Then either:
- send a new step: the chain runs again, and the wait starts over after it;
- close the chain when you are done;
- do nothing: after the wait the chain becomes
expiredand the server goes to the next in line.
Close as soon as you know you are done: someone else may be waiting for that server.
Close a chain
curl -X DELETE -H "Authorization: Bearer $KEY" $BR/api/chains/k3x9...
Frees the server. A queued chain leaves the queue. A running step cannot be stopped: the server finishes it first.
How the queue works
- There is one queue for everyone. A chain gets no server when you request it: the server is chosen only when one frees up.
- When a server frees up, the waiting chain with the highest priority gets it, among those whose plugin the server can run. The chain then keeps that server until it ends.
- Priority = your class's base priority + a little more for every second waited, so every chain gets its turn.
- Some servers go first to privileged classes.
- A running chain is never interrupted for a higher-priority one.
- If a server is lost mid-chain, the chain fails with
server_lost. Request it again: it gets a priority bonus.
Webhooks
With webhook_url, botrouter calls you: it sends a POST to that URL every time the chain changes state. You need a server listening there, reachable from the internet (no localhost or private addresses).
- The call says what changed. It does not carry step results: fetch them with
GET. - Answer with any
2xxstatus to confirm. - No
2xx? botrouter tries 3 times in all: right away, then after 5 seconds, then after 10 more (your admin may set longer waits). After that it gives up on that event.
Webhooks are a shortcut, not a guarantee: if you miss one, poll the chain to catch up.
Full example
Log in, then check the result before deciding the next step, holding the server in between.
H="Authorization: Bearer $KEY"
J='Content-Type: application/json'
# 1. Request a chain that keeps its server
ID=$(curl -s -X POST $BR/api/chains -H "$H" -H "$J" \
-d '{"plugin": "bank", "wait_for_me": true}' | jq -r .id)
# 2. First step (queued until a server is free)
curl -s -X POST $BR/api/chains/$ID/steps -H "$H" -H "$J" \
-d '{"operation": "tinaba/login", "payload": {"user": "..."}, "idempotency_key": "login"}'
# 3. Wait for it to end
step() { curl -s -H "$H" $BR/api/chains/$ID/steps/$1 | jq -r .state; }
while [ "$(step 1)" = pending ] || [ "$(step 1)" = running ]; do sleep 10; done
# 4. Decide: the chain is idle, the server still yours
if [ "$(step 1)" = done ]; then
curl -s -X POST $BR/api/chains/$ID/steps -H "$H" -H "$J" \
-d '{"operation": "tinaba/balance", "payload": {}, "idempotency_key": "balance"}'
fi
# 5. ...and when you are done, free the server
curl -s -X DELETE -H "$H" $BR/api/chains/$ID
Not holding the server? Drop wait_for_me, send all the steps at once, and poll the chain until it is closed.
Reference
All paths start with /api. Every request needs Authorization: Bearer <key>.
| Request | Does |
|---|---|
POST /api-key | No auth. Body: identity (email), password. Returns a new api_key, replacing the old one. |
POST /chains | Request a chain. Body: plugin, optional wait_for_me, webhook_url, preferred_server. |
GET /chains | Your chains. Optional ?state=. |
GET /chains/{id} | One chain and its steps. |
POST /chains/{id}/steps | Add a step. Body: operation, payload, idempotency_key. |
GET /chains/{id}/steps/{seq} | One step and its result. |
DELETE /chains/{id} | Close the chain, free the server. |
Errors
Errors come as {"status": 400, "message": "...", "data": {...}}, with field details in data.
400 | Invalid body, e.g. an operation not in the chain's plugin or a missing required parameter. |
401 | Missing, wrong or revoked key, or your client is disabled. |
404 | No such chain or step (or not yours). |
409 | The chain is already closed, expired or failed. |