botrouter

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, operation tinaba/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:

FieldMeaning
wait_for_metrue: keep the server after the last step, so you can decide what to do next. See Keep the server.
webhook_urlCalled when the chain changes state. See Webhooks.
preferred_serverServer 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

queuedWaiting for a server.
leasedGot a server, no step started yet.
runningA step is running.
idleHolding the server for you, waiting for a new step (wait_for_me only).
closedDone: all steps ran, or you closed it.
expiredYou did not send a new step in time; the server was released.
failedSomething 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:

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

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).

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>.

RequestDoes
POST /api-keyNo auth. Body: identity (email), password. Returns a new api_key, replacing the old one.
POST /chainsRequest a chain. Body: plugin, optional wait_for_me, webhook_url, preferred_server.
GET /chainsYour chains. Optional ?state=.
GET /chains/{id}One chain and its steps.
POST /chains/{id}/stepsAdd 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.

400Invalid body, e.g. an operation not in the chain's plugin or a missing required parameter.
401Missing, wrong or revoked key, or your client is disabled.
404No such chain or step (or not yours).
409The chain is already closed, expired or failed.