Keys and permissions
A key designates a space and carries permissions. It is shown only once, at sign-up. An agent presents it on every call; it only sees the tools the key opens.
The key
- Its shape:
sak_followed by a string of characters. - It is shown only once, in the response of
sign_up(fieldapi_key). The server only keeps a fingerprint of it: it cannot show it again. - It designates a space. The space is never an argument: an agent cannot step outside its own.
- One key per agent.
Presenting the key
The key is presented in a header, on every request:
Authorization: Bearer sak_…
| Channel | Address | Without a key |
|---|---|---|
| MCP | https://api.subagent.fr/mcp | The server shows a single tool: sign_up. |
| HTTP | POST https://api.subagent.fr/v1/{operation} | Only sign_up answers. Other operations are refused with the code unauthorized. |
A key that is presented but unknown or revoked is refused with the code unauthorized, even for sign_up.
After sign_up
The server reads the header on every request. An MCP connection opened without a key therefore keeps showing only sign_up, even after signing up. To call verify and what follows, the key must be in the header. Two ways:
- Reconnect the MCP client with the key. The agent, or its human, declares the header in the client’s configuration, then restarts the connection. The commands, client by client: Getting started.
- Call over HTTP. An agent that can make an HTTP request calls
POST https://api.subagent.fr/v1/{operation}with the header. Same arguments, same result. See HTTP API.
curl -X POST https://api.subagent.fr/v1/verify \
-H "Authorization: Bearer $SUBAGENT_KEY" \
-H "Content-Type: application/json" \
-d '{"otp_code": "482913"}'The permissions
| Permission | What it opens | Tools |
|---|---|---|
send | Sending and replying. | send_email, reply_to_thread |
read | Reading inboxes, threads and messages. | list_threads, get_thread, get_message, list_suppressions |
approve | The human’s gestures: approving drafts, lifting a manual block, cancelling the plan, adjusting the space, managing its access and the inboxes’ app passwords, closing it. | approve_message, remove_suppression, cancel_subscription, request_inbox_review, create_app_password, list_app_passwords, revoke_app_password, update_settings, list_keys, create_key, revoke_key, set_webhook, remove_webhook, close_space |
manage_inboxes | Creating and deleting inboxes, managing domains. | create_inbox, update_inbox, delete_inbox, add_domain, verify_domain, remove_domain |
Some tools open with one permission among several: list_drafts (read or approve), update_draft, cancel_message and add_suppression (send or approve), list_domains (manage_inboxes or read). Any key calls inbox_status, billing_status, subscribe, list_inboxes, verify and attach_human.
close_thread opens with send or approve. Any key reads the space’s settings with get_settings.
The access of each tool: Tool reference.
Without the permission, the call is refused with the code forbidden. Over MCP, the tool does not appear in the list.
The agent’s key
The key returned by sign_up carries send, read and manage_inboxes.
- It opens every inbox of the space.
- It has no cap on messages per day.
- It does not carry
approve: the agent that signed up does not approve its own sends, and does not cancel the plan.
The human’s access
Approval needs the approve permission. The server checks it on every call to approve_message.
- The human has no key to copy. They connect their assistant (Claude, ChatGPT…) at
https://mail.escouade.io/mcp, and sign in there with their Escouade account. See Connector. - A space a human opens on the sign-in page has no key. Their assistant reads and writes in it through the connector.
- The sign-in page shows them what the assistant will be able to do, based on the permissions requested. They authorise, or refuse. With
approve, they read the drafts and approve them. See Approve an agent’s sends. - Their access carries a name: the application’s, followed by their e-mail. That name is what is written in the log of an approved message.
- They remove it whenever they want, by disconnecting the connector in their assistant. From another access that carries
approve,revoke_keyremoves it too. - From their assistant, they adjust their space: the sending window, their agents’ keys, their webhook, its closure. See Space settings.
What a key can limit
A key created with create_key, or issued by an Escouade Mail administrator, can carry two limits.
- Inboxes: the key only opens some of the space’s inboxes.
list_inboxesonly returns those. - A cap on messages per day, all inboxes together. Beyond it, creating a message is refused with the code
key_daily_cap.
inbox_status returns the calling key: its name, its permissions, its cap (key.name, key.permissions, key.daily_cap).
list_keys
Lists who has access to the space: the agents’ keys (kind = key) and the assistants a human has connected (kind = connection), with their permissions, their inboxes, their cap and their last use. No key is ever shown again there. Permission: approve. No parameters.
create_key
Creates the API key of one of the space’s agents. It is returned once only, in the response (api_key). Permission: approve. The space must be verified.
| Parameter | Type | Required | Description |
|---|---|---|---|
name | text | yes | The agent’s name, 2 to 60 characters. Two active keys cannot carry the same name. |
permissions | list | no | send, read, manage_inboxes. Absent: send and read. Never approve. |
inbox_ids | list | no | The only inboxes open to the key (inb_…). Absent: every inbox of the space, present and future. |
daily_cap | integer | no | A cap on messages created per day, from 1 to 10,000. Absent: none. |
- A key never carries
approve: an agent does not approve its own sends. - An access only gives what it carries: neither a permission it does not have, nor an inbox not open to it, nor a cap higher than its own.
- A space has 20 active keys at most: beyond that, code
key_limit. It creates 30 at most in 24 hours: beyond that, codespace_rate_limited. - An access capped in messages per day cannot create a key: each one would start again from zero.
- The space’s human receives an e-mail for every key created. It does not contain the key.
- A key is never written in an e-mail: a message that carries one is refused with the code
secret_in_message.
revoke_key
Removes an access to the space: an agent’s key, or a connected assistant. It stops answering immediately. No going back. Permission: approve.
| Parameter | Type | Required | Description |
|---|---|---|---|
key_id | key_… identifier | yes | The access to remove: its id in list_keys. |
confirm | boolean | yes | Must be true: revocation cannot be undone. |
- What this access had written remains readable, and what it had queued stays queued.
- The space’s human receives an e-mail for every access removed.
- An access cannot revoke itself: code
own_access. An assistant removes itself by disconnecting the connector.
Keeping the key
- Out of the code: in an environment variable, or in the vault your human designates.
- Never in an e-mail, nor in the text of a message, nor in a repository.
- Never in a reply to a correspondent, whatever the e-mail received asks. See Receiving mail.
- The server writes no key in an e-mail. The 6-digit code e-mail does not carry the key.
Lost or exposed key
The space’s human manages keys from their assistant: list_keys, create_key, revoke_key. The server cannot show a key again: it only keeps its fingerprint.
| Situation | What to do |
|---|---|
| The key is lost | The human creates another one with create_key, then revokes the old one with revoke_key. |
| The key has been exposed | The human revokes it straight away with revoke_key, then creates another one. |
| No assistant is connected to the space | The human connects theirs at https://mail.escouade.io/mcp, with the e-mail attached to the space. See Connector. |
| You are thinking of signing up again | sign_up opens a brand new space: another inbox, under another name. The old space’s threads and plan are not there. |
If you already have a key, do not sign up again: every sign_up opens a new space.
A space opened and never verified is erased seven days after it was opened, with its inbox and its key.