API keys
Give each agent a key that can do only its job.
For your agent
agental.io/docs/api-keys.mdA key is made with what it may do: a preset, or actions named. It can also be limited to some sandboxes or templates, to a last day, and to the addresses it may be used from. A key keeps what it was made with. Only its addresses can be changed afterwards.
Make a key
curl -X POST https://agental.io/v1/api-keys \
-H "Authorization: Bearer $AGENTAL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"ci-github","preset":"ci","templateIds":["tpl_coding_agent"],"expiresAt":"2026-11-15T00:00:00Z","allowFrom":["203.0.113.0/24","2001:db8::/48"]}'| Field | What it does |
|---|---|
name | Required. Unique in the account. |
preset | One of the four sets below. Send this or actions, not both: neither is 400. A key that can do everything is asked for with "preset": "admin". |
actions | The actions it may do, named. |
sandboxIds, templateIds | The sandboxes it reaches, and the templates it reaches with every sandbox on them. Left out, all of the account's. |
expiresAt | When it stops working. Left out, never. |
allowFrom | IPv4 and IPv6 addresses and ranges it may be used from, 100 at most. Left out, anywhere. |
The answer has the key's secret. It is shown this once, and only its hash is kept.
Presets
| Preset | Actions |
|---|---|
read-only | Every read but secrets and mail: sandbox.read, file.read, snapshot.read, template.read, account.read. |
full-sandbox | Work in sandboxes that are there: sandbox.read, sandbox.auto-stop, exec, file.read, file.write, agent.prompt, ssh, desktop, host, snapshot.read, snapshot.write, sandbox.stop. It makes and deletes none, and touches no outbound rule, secret or account setting. |
ci | full-sandbox, and sandbox.create, sandbox.fork, sandbox.resume and sandbox.delete-own. |
admin | *: everything a key can do. |
A preset is written out into its actions when the key is made. An action added to agental later is not given to keys made before it.
Actions
| Action | What it allows |
|---|---|
sandbox.read | List and read sandboxes, their operations, stops, usage, metrics, template and outbound rule, and the sizes. |
sandbox.create | Make sandboxes, and reconcile creates whose outcome was not known. |
sandbox.fork | Fork a sandbox. |
sandbox.update | Rename a sandbox, share it, put it on another template, take its time limit away. |
sandbox.auto-stop | Read and move when a running sandbox is to be stopped. |
sandbox.stop | Stop and archive. |
sandbox.resume | Start, resume and recover. |
sandbox.delete | Delete any sandbox it reaches. |
sandbox.delete-own | Delete only the sandboxes this key made. |
exec | Run commands. |
file.read, file.write | Read and write a sandbox's files. Artifacts are read. |
agent.prompt | Prompt the agent in a sandbox, steer and interrupt it, read its conversations and events. |
ssh | SSH on and off, and the keys let in. |
desktop | The desktop and the browser: turn them on and off, and drive them. |
host | Public addresses for a sandbox's ports. |
network.write | Change the outbound rule of a sandbox or a template. |
secret.read, secret.write | Read, set and delete the secret variables and files of sandboxes and templates. |
email.read, email.send, email.manage | Read a sandbox's mail; send it; turn it on and off and delete messages. |
snapshot.read, snapshot.write | List and read snapshots; take, delete and restore them. |
template.read, template.write | Templates, other than their secrets and the change of their outbound rule. Applying counts as a change. |
key.manage | Everything under /v1/api-keys. |
account.read | The account: who it is, usage, limits, bursts, billing, webhooks, telemetry, data retention, metrics, repositories, invites, organizations, its mail. |
account.admin | Changes to all of those, billing checkout and portal, inviting, and the account's export. |
members.read, members.admin | A team's members and invites: list them; invite, revoke invites, change roles and remove members. A key with account.read or account.admin may too. |
* | Everything a key can do. |
network.writeandsecret.readare actions of their own, so that an agent's key cannot lift its own outbound block or read its template's secrets.account.readandaccount.adminare about the account as a whole. A key limited to some sandboxes that holdsaccount.readstill reads the whole account's usage and metrics, which name every sandbox: leave it out of a key you limit.- Whatever its actions, no key reaches what is for whoever runs the service, nor what only a person signed in may do: allowing the agental command to sign in, and closing, reopening or erasing the account.
Keys in a team
A key acts in the organization it was made in, for good: your own, or the team you were acting in when you made it. What it makes is that organization's, and it reaches nothing of any other. GET /v1/me says which in activeOrganizationId.
- A team's owners and admins see and revoke every key of the team, with
createdBysaying who made each. A member sees and revokes their own. - What a key may do in a team is held by its actions and by its person's role there, read on each request: a member's key with
members.adminstill removes no one but its person. - When someone is removed from a team, the keys they made in it go on working, as a member's, until they are revoked. Removing them with
?revokeKeys=truerevokes those keys in the same request, and the console's Remove offers it. - Deleting a team and accepting an invite are for a person signed in. A key cannot.
agental org agental org use Acme agental members agental members remove [email protected] --revoke-keys agental invite [email protected] --role admin
agental org use signs the command in again, in the browser, for a key made in that organization, and deletes the key it had.
Limit it to some sandboxes or templates
With sandboxIds, templateIds or both, a key reaches the sandboxes listed, the templates listed with every sandbox on them, and always the sandboxes and templates it made itself. Everything else of the account answers it 404 not_found, word for word as another account's would, and lists leave it out: sandboxes, snapshots, templates and ports.
- A key limited by
sandboxIdsalone is not limited in templates. - A sandbox that a key limited to templates makes without naming one goes on the
defaulttemplate when that is on its list, otherwise on the one template on its list. With several on its list and notdefault, it has to sendtemplateId, and is told400 template_required. - For a key used inside a sandbox, list that sandbox: it is the limit that holds wherever the sandbox runs.
Limit where it may be used from
allowFrom takes addresses, 198.51.100.7, and ranges, 203.0.113.0/24 or 2001:db8::/48. A request from anywhere else is refused with 403 key_address_forbidden, and the message says which address agental saw. An IPv4 address written the IPv6 way, ::ffff:203.0.113.7, is that IPv4 address.
curl -X PUT https://agental.io/v1/api-keys/key_38b536dc2eabdb52/allow-from \
-H "Authorization: Bearer $AGENTAL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"allowFrom":["203.0.113.0/24"]}'It replaces the list, from the key's next request. An empty list is anywhere. It is the one change a key takes.
While your account has two-factor sign-in on, a list that lets the key be used from more places, with an address outside the old list or no list at all, takes a code when a person signed in sends it, as a new key does. A list that only narrows it needs none.
sandboxIds.A key makes only weaker keys
A key with key.manage may make keys, and may change their addresses, but never past itself:
- no action it was not made with;
- no sandbox or template it does not reach, and a list whenever it has one;
- no later last day, and one whenever it has one;
- addresses inside its own, whenever it has some. That holds for a key changing its own list too: it can narrow it, never widen it.
Anything stronger is refused with 403 key_exceeds_caller, and the message says what.
With two-factor sign-in on
A key outlives the session that made it, so while your account has two-factor sign-in on, a key made in the console asks for a code: the six digits your authenticator app shows now, or a backup code. Signed in to the site, the call sends it as code. Without one it is refused with 400 code_required, a wrong one with 400 invalid_code, and after five wrong codes in a quarter of an hour that session waits (429). A key made with a key needs no code: that key was made behind one.
Read your keys
curl -X GET https://agental.io/v1/api-keys \ -H "Authorization: Bearer $AGENTAL_API_KEY"
Each key says what it may do and where, lastUsedAt and lastUsedFrom: when it was last used, and from which address. Those are written once a minute at most, so a key in use shows the minute. GET /v1/api-keys/{id} reads one, and DELETE deletes it: whatever holds it is refused from its next request, and the sandboxes it made stay yours.
When a key is refused
A request with a key is checked in this order, and stops at the first that fails:
| Answer | Why | |
|---|---|---|
| 1 | 401 unauthorized | There is no such key, or it was deleted. |
| 2 | 401 key_expired | Its last day has passed. |
| 3 | 403 key_address_forbidden | It came from an address the key may not be used from. The message names it. |
| 4 | 403 key_action_forbidden | The key was not made with the action the call takes. The message names it. A route no action covers is refused to every key. |
| 5 | 404 not_found | A sandbox, template, snapshot or port the key does not reach. |
The full machine-readable reference is OpenAPI 3.1. Every page as Markdown, for an agent: llms.txt. Questions: [email protected]