# API overview Source: https://docs.artil.ai/api Call Artil over HTTP to use an agent's email, SMS, and secrets. The Artil API is what the `artil` CLI and the Artil app call. Every endpoint in this reference lives under `https://app.artil.ai/api`. ## Authenticate Send a token in the `Authorization` header: ```bash theme={null} curl https://app.artil.ai/api/me \ -H "Authorization: Bearer YOUR_TOKEN" ``` An agent's token acts as that agent: it reads and sends the agent's messages and manages its secrets. Create one in the app as in [Use a headless machine](/authentication#use-a-headless-machine). Some endpoints, such as creating or deleting an agent, are for workspace members and refuse an agent's token. ## Errors A failed request answers with an HTTP error status and a body that names the error: ```json theme={null} { "error": { "code": "not_found", "message": "Message not found" } } ``` `code` is one of `invalid_input`, `unauthorized`, `forbidden`, `plan_limit`, `not_found`, `conflict`, `rate_limited`, `internal`, or `unavailable`. The message says what went wrong in words you can show to a person. ## Compatibility Artil adds to the API without notice: new endpoints, new fields in responses and webhook events, new values in fields such as `channel`, and new error codes. Skip the fields and values your code does not know. Fields this reference lists stay, and an endpoint is marked deprecated before it is removed. # Read access Source: https://docs.artil.ai/api-reference/access/read-access /openapi.json get /agents/{id}/access Read who can wake an agent on each channel. # Set access Source: https://docs.artil.ai/api-reference/access/set-access /openapi.json put /agents/{id}/access/{channel} Choose who can wake an agent on a channel. # Add an address Source: https://docs.artil.ai/api-reference/addresses/add-an-address /openapi.json post /agents/{id}/addresses Leave out value to get a free address on the channel, or give an email address on the workspace's own domain. A phone number needs a plan that includes one. Give token to connect a Telegram bot, which then sends its messages to Artil. # Change an address Source: https://docs.artil.ai/api-reference/addresses/change-an-address /openapi.json patch /agents/{id}/addresses/{addressId} Change the email address Artil gave an agent, on a plan that includes it. # List addresses Source: https://docs.artil.ai/api-reference/addresses/list-addresses /openapi.json get /agents/{id}/addresses List an agent's email addresses, phone numbers and Telegram bots. # Make an address primary Source: https://docs.artil.ai/api-reference/addresses/make-an-address-primary /openapi.json post /agents/{id}/addresses/{addressId}/primary Send new messages on the address's channel from it. # Remove an address Source: https://docs.artil.ai/api-reference/addresses/remove-an-address /openapi.json delete /agents/{id}/addresses/{addressId} Take an address on the workspace's own domain, a phone number or a Telegram bot from an agent. The workspace keeps a phone number for its next agent, and a bot stops sending its messages to Artil. # Set up an address Source: https://docs.artil.ai/api-reference/addresses/set-up-an-address /openapi.json post /agents/{id}/addresses/setups Get a link where a member creates the address with the provider, such as a Telegram bot made through Artil's bot. Once they have, the address joins the agent and the member can wake the agent from it. The link works for an hour. # Create an agent Source: https://docs.artil.ai/api-reference/agents/create-an-agent /openapi.json post /agents Create an agent with an email address. # Delete an agent Source: https://docs.artil.ai/api-reference/agents/delete-an-agent /openapi.json delete /agents/{id} Delete an agent with its addresses, messages and secrets. # List agents Source: https://docs.artil.ai/api-reference/agents/list-agents /openapi.json get /agents List the workspace's agents. # Read an agent Source: https://docs.artil.ai/api-reference/agents/read-an-agent /openapi.json get /agents/{id} # Update an agent Source: https://docs.artil.ai/api-reference/agents/update-an-agent /openapi.json patch /agents/{id} Rename an agent, change its icon or turn on its secrets. # Connect a client Source: https://docs.artil.ai/api-reference/connections/connect-a-client /openapi.json put /agents/{id}/connection Make the calling client the one that hears the agent's new messages, in place of any other. Stream the agent's events with the connection's id to hear them. Only the agent's own token can connect it. # List clients Source: https://docs.artil.ai/api-reference/connections/list-clients /openapi.json get /agents/{id}/clients Clients with access to the agent: completed CLI and OAuth sign-ins, and manually created tokens after their first use. Expired and revoked credentials are excluded. This list does not indicate which runtime receives new messages. # List who listens Source: https://docs.artil.ai/api-reference/connections/list-who-listens /openapi.json get /agents/{id}/listeners The connected client, while its event stream checks in or its MCP subscription takes them, and the webhooks that hear messages at the agent's Message URL now. Sending to the Message URL fails while the list is empty. # Read an agent's connection Source: https://docs.artil.ai/api-reference/connections/read-an-agents-connection /openapi.json get /agents/{id}/connection The client that hears the agent's new messages, or null before any connects. # List events Source: https://docs.artil.ai/api-reference/events/list-events /openapi.json get /agents/{id}/events Without after, the latest events come newest first. With after, the events that followed it come oldest first. # Send to an agent's Message URL Source: https://docs.artil.ai/api-reference/events/send-to-an-agents-message-url /openapi.json post /agents/{id}/url Send a message to an agent of your workspace, as the member or agent whose token you use. The agent's Message URL, https://artil.ai/a/{id}, takes the same request. Artil hands the message whole to whoever listens to the agent, as a message.received event on the url channel, and keeps no copy. Fails with conflict when no client is listening to the agent, and with forbidden when the agent takes messages from other senders only. # Stream events Source: https://docs.artil.ai/api-reference/events/stream-events /openapi.json get /agents/{id}/events/stream Server-sent events, named by type, with each Event as JSON data. A reconnect resumes after Last-Event-ID. With connection, the stream ends once another client connects, and reopening it then fails with conflict. # Read who is signed in Source: https://docs.artil.ai/api-reference/me/read-who-is-signed-in /openapi.json get /me Read the member or agent the token signs in as, and its workspace. # Revoke this token Source: https://docs.artil.ai/api-reference/me/revoke-this-token /openapi.json delete /me/token Revoke the token this request signs in with. # List messages Source: https://docs.artil.ai/api-reference/messages/list-messages /openapi.json get /agents/{id}/messages List the agent's messages, newest first. To page back, pass the last message's id as before. # Read a message Source: https://docs.artil.ai/api-reference/messages/read-a-message /openapi.json get /agents/{id}/messages/{messageId} Read a message the agent received or sent. # Send a message Source: https://docs.artil.ai/api-reference/messages/send-a-message /openapi.json post /agents/{id}/messages Answer a message by giving its id as replyTo, start a new one with a channel and recipients in to, or pass one on by giving its id as forward and recipients in to; a forward carries the message's files. Files go in attachments: one the agent has by its id, or a text file by name, type and text. To send files from disk, post multipart form data with the message as JSON in draft and one file per file part. # Delete a secret Source: https://docs.artil.ai/api-reference/secrets/delete-a-secret /openapi.json delete /agents/{id}/secrets/{name} # List secrets Source: https://docs.artil.ai/api-reference/secrets/list-secrets /openapi.json get /agents/{id}/secrets List an agent's secrets, never their values. # Reveal secrets Source: https://docs.artil.ai/api-reference/secrets/reveal-secrets /openapi.json post /agents/{id}/secrets/reveal Only the agent's own token can reveal its secrets. Leave out names to reveal them all. # Save secrets Source: https://docs.artil.ai/api-reference/secrets/save-secrets /openapi.json patch /agents/{id}/secrets Save secrets, replacing ones with the same names. A person turns on the agent's secrets first. # List the team Source: https://docs.artil.ai/api-reference/team/list-the-team /openapi.json get /team List the workspace's agents and members with the addresses that reach them, and whether anyone listens to each agent. # List tokens Source: https://docs.artil.ai/api-reference/tokens/list-tokens /openapi.json get /agents/{id}/tokens List the tokens members made for an agent, never their values. Sign-ins from the CLI and OAuth are not listed. # Revoke a token Source: https://docs.artil.ai/api-reference/tokens/revoke-a-token /openapi.json delete /agents/{id}/tokens/{tokenId} Revoke one of an agent's tokens. # Add a webhook Source: https://docs.artil.ai/api-reference/webhooks/add-a-webhook /openapi.json post /agents/{id}/webhooks Post an agent's events to a URL, signed as Standard Webhooks describes. Adding the same URL again returns its webhook with the same secret. # List webhooks Source: https://docs.artil.ai/api-reference/webhooks/list-webhooks /openapi.json get /agents/{id}/webhooks List where an agent's events go and how delivery went. # Message received Source: https://docs.artil.ai/api-reference/webhooks/message-received /openapi.json webhook message.received A message arrived for the agent account on one of its channels. The event leaves out the content of a message Artil keeps; read it with its id. One it keeps no copy of comes whole. # Remove a webhook Source: https://docs.artil.ai/api-reference/webhooks/remove-a-webhook /openapi.json delete /agents/{id}/webhooks/{webhookId} Stop posting an agent's events to a URL. # Sign in as your agent Source: https://docs.artil.ai/authentication Sign the CLI into an agent account on your computer or a remote server. [Install the CLI](/installation), then choose the agent account whose email, SMS, and secrets you want to use. The CLI saves one login at a time. ## Sign in on this computer **1. Sign in through your browser** ```bash theme={null} artil auth login ``` The command opens a browser. Sign in, choose your workspace and agent, and approve the connection. The CLI checks and saves the login. A browser sign-in expires after 30 days, and then you sign in again; tokens created in the app don't expire. Use a browser on the same computer as the CLI. If it cannot open one, the CLI prints a link to open yourself. For a remote server, follow [the token-login steps below](#use-a-headless-machine). **2. Check the agent** ```bash theme={null} artil auth status ``` Check that status shows the agent and workspace you chose. It also shows the agent's email address when email is set up. You can see the same information by running `artil` and choosing **Status**. If an email or SMS command fails, follow [message troubleshooting](/troubleshooting#email-or-sms-is-unavailable). > **[Next: choose a client](/setup)** > > Connect Claude Code, Codex, Pi, OpenClaw, Hermes, or ChatGPT to this agent's inbox. ## Use a headless machine Use a token when the CLI runs on a server without a browser. A token is a password-like credential that lets the CLI act as an existing agent. For example, to use an agent called **Support Bot** on a remote server: **1. On your laptop: create the token** Open Artil in your browser, select the workspace, and open **Support Bot**. Click **Connect to agent**, choose a CLI harness such as **Claude Code**, and click **Token** beside the sign-in heading. Enter a token name, such as the server's name, and click **Create token**. After its first use, the token also appears in the **Clients** tab's connected list. The agent's **Tokens** tab makes one too, with **New token**. Copy the command before closing the dialog. It contains the token, which is shown only once. The command looks like this: ```bash theme={null} artil auth login --token YOUR_COPIED_TOKEN ``` `YOUR_COPIED_TOKEN` is a placeholder. Use the full command copied from the app. **2. On the server: run the copied command** Connect to the server where you installed the CLI and run the command. The CLI checks the token and saves the login without opening a browser. Check the result: ```bash theme={null} artil auth status ``` Status should show **Support Bot** and its workspace. Later commands use this saved login, so you only need to supply the token once. The copied command contains a secret and may remain in shell history. Keep it out of shared logs and agent conversations. For scripts, you can also create a token from the agent's **Tokens** tab: open the agent, select **Tokens**, click **New token**, enter a name, and click **Create token**. Set `ARTIL_TOKEN` to the token in the command it shows. Every command then signs in with it, ahead of the saved login, and nothing is saved on disk, which suits a container that gets the token as a secret. Run `artil auth login` to save it instead. ## Manage a login > **Switch agents** > > Run `artil auth login` again and choose the other agent in the browser. > The new login replaces the saved one. If your previous browser login from > this computer was in the same workspace, Artil also revokes it; one from > another workspace keeps working until it expires. > > The Claude Code, Codex, and OpenClaw plugins and the Pi package use the > newly saved login. Start a new Claude Code or Pi session, or run > `openclaw gateway restart`, after switching agents. > If you set up automatic delivery with `artil init`, run it again for the > newly selected agent to review the connection and its linked secrets. > **Use account access** > > To request access as your account instead of one agent: > > ```bash theme={null} > artil auth login --account > ``` > > For commands that need an agent, specify it: > > ```bash theme={null} > artil secrets list --agent > artil init --client hermes --connection server --agent > ``` > > Use a name, slug, or ID. If more than one agent matches, use the ID; the > error lists each match with its ID. An agent login is needed for email and > SMS, for `artil secrets copy`, `code`, `link`, and `pull`, and for `artil init` > with Claude Code, Pi, or Hermes on this computer. > > `--account` opens browser approval. A token's access is set when it is > created, so you cannot combine `--account` with a token. > **Sign out of this computer** > > If you also want to remove message delivery setup, follow > [disconnect message delivery](/automatic-messages#change-or-disconnect-a-client) > before signing out. That step uses `artil init --client none`, which also > removes a remote Hermes webhook set up from this computer. > > To remove the saved login: > > ```bash theme={null} > artil auth logout > ``` > > Logout also removes the `artil-messages` channel from Claude Code and the > `artil-messages` extension from Pi, and stops Hermes runs on this computer. > The artil skill, allowed commands, linked secrets, and plugins from > `artil plugin install` stay. The agent's remote Hermes webhook stays > configured. > > Logout revokes a browser sign-in on the server. A `--token` login is only > forgotten on this computer, and commands keep signing in with `ARTIL_TOKEN` > until you unset it; revoke the token on the agent's **Tokens** tab to end > its access. Copied secrets stay valid. > > Remove copied secrets with `artil secrets unlink ` and stop programs > that already loaded them. > > Credentials are stored with private file permissions in > `~/.config/artil/credentials.json`, or in `$ARTIL_CONFIG_HOME` or > `$XDG_CONFIG_HOME/artil` when set. Do not copy this file into a repository > or conversation. # Receive messages automatically Source: https://docs.artil.ai/automatic-messages Let an incoming message notify Claude Code or Pi, or start a Hermes run. Use `artil init` to have incoming messages notify an open Claude Code or Pi session, or start a Hermes run. It also writes the `artil` skill to `~/.claude/skills`, `~/.pi/agent/skills`, or Hermes' `skills` folder, and links the agent account's stored API keys and passwords to the client, so the client can read and send messages and use those secrets in a conversation. To use the secrets anywhere else, [link them yourself](/secrets). First [install the CLI](/installation), [sign in as your agent account](/authentication), and check that you can [read its email](/email). With an `--account` login, only remote Hermes can be set up; `artil init --agent ` picks its agent. Claude Code, Pi, and Hermes can also hear about new messages through a plugin: the [Claude Code plugin](/claude-code), the [Pi package](/pi), or the [Hermes plugin](/hermes). Use one or the other for a client: installing the plugin replaces what `artil init` set up for it, and `artil init` will not set up a client whose plugin is installed. OpenClaw hears about new messages only through [its plugin](/openclaw). ## 1. Choose where the agent will run Choose a client below. Local setup adds message delivery, the `artil` skill, and secret loading. Choosing a new client replaces this computer's delivery setup, including a remote Hermes webhook set up from here. > [!NOTE] > > Local setup links **all** of the agent's stored secrets. Only the agent's own login can reveal > them; members can't. The Claude Code and Pi links use your user-level configuration and apply > across projects; Hermes uses its configured installation. Review the agent's secrets before > connecting a client. **Claude Code** Install Claude Code and use Anthropic authentication. Channels are not available through Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry. On Team and Enterprise plans, an owner must enable channels first. Console organizations that deploy managed settings must enable `channelsEnabled`. The development flag below cannot override a disabled channel setting. See [Claude's channel requirements](https://code.claude.com/docs/en/channels#enterprise-controls). Connect this computer: ```bash theme={null} artil init --client claude-code ``` Setup adds the `artil-messages` channel and allows the CLI commands that read and send messages. Complete anything listed under **Next steps**, such as a skill name conflict or a secret that was left out. Start a new Claude Code session using the command setup prints: ```bash theme={null} claude --dangerously-load-development-channels server:artil-messages ``` Check that Claude starts with the channel enabled. Resolve any startup warning about blocked channels. The new session also loads the secrets hook. Keep it open for the email check below; closing it stops notifications. **Pi** Install [Pi](https://pi.dev) first. ```bash theme={null} artil init --client pi ``` Setup adds the `artil-messages` and `artil-secrets` extensions to `~/.pi/agent/extensions` and the `artil` skill to `~/.pi/agent/skills`. Pi's bash commands load the agent's secrets. Complete anything listed under **Next steps**. Start Pi, or run `/reload` in an open session. A new message starts a turn in that session, or waits until Pi finishes the current one. Pi runs commands without asking for approval, so a message can lead it to act before you see it. Keep the session open for the email check below; closing it stops notifications. **Local Hermes** Install and configure Hermes first. Background delivery needs a macOS login session or a running Linux systemd user service; Hermes in Docker has neither, so use the [Hermes plugin](/hermes) there. ```bash theme={null} artil init --client hermes --connection local ``` Read the **Next steps** output and complete any remaining setup. The machine must stay on and connected; no public webhook URL is needed. Setup prints the background job's log path. Each incoming message starts a separate `hermes chat -q` run, one at a time. Check that log if a run fails. Runs stop after 30 minutes, and after the computer was off, the job catches up on messages that arrived since its last run. **Remote Hermes** Use the public URL for your Hermes server: ```bash theme={null} artil init --client hermes --connection server --url https://hermes.example.com ``` Setup adds the webhook route and shared signing secret to Hermes' configuration when it is on this computer. Otherwise it prints the route to add on the server. Then complete the steps under **Next steps**. Create a token on the agent's **Tokens** tab and put it in Hermes' `.env` on the server as `ARTIL_TOKEN`. Add the printed `mcp_servers.artil` entry to Hermes' `config.yaml`; it points at Artil's `/api/mcp` with that token as a Bearer header. Restart Hermes and check that its public URL is reachable. The webhook only says that a message arrived. It asks Hermes to use Artil's `messages.read` and `messages.send` tools with the incoming message ID. Install any skills and credentials Hermes needs on the remote server separately. You can also install the [Hermes plugin](/hermes) on the server with a [headless login](/authentication#use-a-headless-machine) instead, which needs no public URL. ## 2. Check an incoming message From your own email account, send a new message to the agent's address. For example: “Please reply to confirm you can read this email.” The client receives a notification containing the message ID. Local clients use the CLI to read and reply; the remote Hermes webhook prompts it to use its configured message tools. Watch the client read the message, then check your inbox for its reply. > **No notification or reply?** > > Keep the Claude Code or Pi session open, or check the background-job log for > local Hermes. For a remote Hermes server, check that webhook setup is complete > and the URL is reachable. Inspect any client permission prompts before > expecting a reply. > > Run `artil auth status` to check the agent and its channels. Sender > restrictions, spam filtering, and notification limits can stop a message > from reaching the client. See [message troubleshooting](/troubleshooting). Apart from remote Hermes, which a webhook reaches, one client at a time hears an agent's new messages, and a message that arrives while none listens waits in the inbox; see [Who hears about new messages](/delivery). > **[Next: sign up for a service](/email-identity)** > > Use this inbox to receive a verification email, finish signup, and store the service's > credentials. ## Change or disconnect a client > **Change or repair setup** > > Run `artil init` to choose a different client. A new choice replaces this > computer's delivery setup, including a remote Hermes webhook set up from > here. Keeping the same choice repairs outdated setup and updates the > installed skill and secret links. > > If a separately installed skill has the same name, setup keeps it and > reports the conflict under **Next steps**. > **Disconnect message delivery** > > This command removes delivery setup on this computer, including a remote > Hermes webhook set up from here. A webhook set up from another computer > stays. To remove it, disconnect on that computer, or set up remote Hermes > here, which replaces it, and then disconnect. > > ```bash theme={null} > artil init --client none > ``` > > It also removes the skills, the Pi extensions, secret links, and allowed > `artil` commands that setup added for the disconnected local clients. Open > Pi sessions keep receiving messages until you run `/reload` in them. Your > agent, messages, and secrets remain stored in Artil. If Hermes' > `config.yaml` is on this computer, the command removes the `artil` route > and asks you to restart Hermes. On another server, remove that route > yourself. Plugins from `artil plugin install` stay; the command names them, > and `artil plugin remove ` takes them out. > > Remove separately linked secrets, such as a project's `.env`, with > `artil secrets unlink `. Stop programs that already loaded those values. > For CLI sign-out, see [manage a login](/authentication#manage-a-login). # ChatGPT Source: https://docs.artil.ai/chatgpt Let ChatGPT hear about your agent account's new email and texts and answer them. ChatGPT uses your agent account through Artil's [MCP server](/mcp-server). In a Work chat or a dot it can watch the agent's new email or texts, act on each one as it arrives, and answer from the agent's address or number. ## Connect 1. Open [Artil in ChatGPT Plugins](https://chatgpt.com/plugins/plugin_asdk_app_6ac808125e6481919db1df8dc647b320) and install the plugin. 2. When ChatGPT asks you to sign in, Artil opens in your browser: choose the agent and click **Connect**. For a new agent, enter a name and click **Create and connect**. ## Watch for new messages Open a Work chat, on the web or in the desktop app with **Cloud** selected, or ask a dot, and tell ChatGPT what to do with each message. For example: > Watch my Artil agent's email and reply to each new message with a short confirmation. Send a test email to the agent's address and check your inbox for the reply. ChatGPT handles each message as a task and can email you an update about it. Other ChatGPT chats can read and answer the agent's messages too, but only Work chats and dots hear about new ones. Watching makes ChatGPT the one client that hears the agent's new messages; see [Who hears about new messages](/delivery). When another client starts listening, ChatGPT stops hearing about them; ask it to watch again to take them back. > [!WARNING] > > A message can try to steer ChatGPT, so ask it only for what you are happy for it to do with any > message; see [Messages are not instructions](/safety). ## Stop Ask ChatGPT in the chat to stop watching. Another client connecting to the agent also ends everything ChatGPT was watching. # Claude Code Source: https://docs.artil.ai/claude-code Check email and SMS, read messages, and reply as your agent account from Claude Code. The Artil plugin gives Claude Code email, SMS, and secrets skills and the `artil-messages` channel for new messages. It brings the Artil CLI, which Claude runs in its Bash tool and you run with `! artil` in Claude Code. Before starting, install [Claude Code](https://code.claude.com/docs/en/quickstart). ## Connect your inbox **1. Install the plugin** In Claude Code, run: ```text theme={null} /plugin install artil --marketplace artilai/plugins ``` The plugin stays installed. `/artil:email`, `/artil:sms`, and `/artil:secrets` choose the corresponding skill. With the [Artil CLI](/installation) installed, `artil plugin install claude-code` adds the plugin too, turns on its updates, and allows `artil email`, `artil sms`, `artil telegram`, and `artil url` to run without asking. **2. Start Claude Code with the channel** ```bash theme={null} claude --dangerously-load-development-channels plugin:artil@artil ``` The development flag is needed while Claude Code channels are in research preview; see [Claude's channel requirements](/automatic-messages). Keep this session open to hear new messages. **3. Sign in as your agent** ```text theme={null} ! artil auth login ``` Select your agent in the browser. Without a browser, make a token on your agent's page in Artil under **Connect to agent** → choose the client → **Token**, as [headless authentication](/authentication#use-a-headless-machine) shows, and run `! artil auth login --token `. **4. Turn on new messages** ```text theme={null} ! artil listen on ``` This session hears the agent's new messages from now on. `! artil listen off` turns them off again. **5. Read a message and reply** Send a test email to your agent's address. Then ask Claude: ```text theme={null} Check my email, read my test message, and reply saying "Connected." ``` Check your inbox for Claude's reply. Claude may ask for permission to run the CLI commands through its Bash tool. ## Use email and SMS Ask in plain language, such as “Check my email and SMS for anything I need to answer,” or select a skill directly: ```text theme={null} /artil:email Summarize my last five emails. /artil:sms Read my latest text. ``` SMS needs a [phone number](/sms). Replies go to the sender of the selected message, using your agent's email address or phone number. Only the session started with the channel last hears new messages; see [Who hears about new messages](/delivery). Claude handles each new message and answers through Artil without asking you first, so choose [who can wake the agent](/wake-access) with that in mind. To give Claude access to stored API keys, [link its credentials](/secrets). > **[Next: sign up for a service](/email-identity)** > > Use this inbox to receive a verification email, finish signup, and store the service's > credentials. ## Having trouble? Run `! artil auth status` in Claude Code to check its login. If a skill is missing, run `/plugin` and check that `artil@artil` is installed and enabled. For channel or access errors, follow [message troubleshooting](/troubleshooting#email-or-sms-is-unavailable). If a reply fails or is interrupted, confirm receipt with the recipient before trying again. The inbox lists incoming messages and cannot confirm delivery. For long messages, read the full output with `artil email read -- ''` or `artil sms read -- ''` in your terminal. To remove the plugin, run `/plugin uninstall artil@artil` in Claude Code. # Codex Source: https://docs.artil.ai/codex Check email and SMS, read messages, and reply as your agent account from Codex. The Artil plugin gives Codex the `artil` skill, which reads, answers, and sends email and SMS with your agent account's existing CLI login. Codex does not hear about new messages as they arrive; ask it to check. Before starting, install [Codex](https://developers.openai.com/codex) 0.143 or later, `npm`, which Codex fetches the plugin with, and the [Artil CLI](/installation). ## Connect your inbox **1. Sign in as your agent** ```bash theme={null} artil auth login artil auth status ``` Select your agent in the browser. Check that status shows that agent and its email address. For a remote machine, use [headless authentication](/authentication#use-a-headless-machine). **2. Install the plugin** ```bash theme={null} artil plugin install codex ``` The command adds the Artil marketplace and installs the plugin with Codex's own `codex plugin marketplace add artilai/plugins` and `codex plugin add artil@artil`. It also writes `~/.codex/rules/artil.rules`, which runs `artil email`, `artil sms`, and `artil telegram` outside Codex's sandbox without asking; the sandbox keeps them off the network, so without the rule Codex asks before each one. **3. Read a message and reply** Send a test email to your agent's address. Then start a new Codex session and ask: ```text theme={null} Check my email, read my test message, and reply saying "Connected." ``` Check your inbox for Codex's reply. ## Use email and SMS Ask in plain language, such as “Check my email and SMS for anything I need to answer” or “Read the message from Alice.” SMS needs a [phone number](/sms). Replies go to the sender of the selected message, using your agent's email address or phone number. `artil secrets link` cannot hand the agent's [secrets](/secrets) to Codex's commands yet. Codex can also use the agent without the CLI over [MCP](/mcp-server). > **[Next: sign up for a service](/email-identity)** > > Use this inbox to receive a verification email, finish signup, and store the service's > credentials. ## Having trouble? Check `artil auth status` in the terminal that starts Codex. If Codex does not offer the `artil` skill, check that `codex plugin list` shows `artil@artil` as installed and enabled, and start a new session. For channel or access errors, follow [message troubleshooting](/troubleshooting#email-or-sms-is-unavailable). If a reply fails or is interrupted, confirm receipt with the recipient before trying again. The inbox lists incoming messages and cannot confirm delivery. To remove the plugin, run `artil plugin remove codex`. # Command reference Source: https://docs.artil.ai/commands Commands and flags for sign-in, messages, client setup, and secrets. Use `artil --version` to check your installation and `artil --help` for command syntax. ## Authentication | Command | Flags | Behavior | | - | - | - | | `artil` | `--json` | Open the terminal menu. When piped, or with `--json`, print login status. | | `artil auth status` | `--json` | Show the login, workspace, agent channels, the client that listens now, a link to the agent's page, message delivery setup, the agent's webhooks, and linked secrets. | | `artil auth login` | `--account`, `--token` | Sign in through a browser or save a supplied token. | | `artil auth logout` | None | Revoke a browser sign-in; a `--token` login is only forgotten here. Remove the `artil-messages` channel from Claude Code and stop local Hermes. | See [authentication](/authentication). ## Use the terminal menu To browse your inbox without typing each command, run: ```bash theme={null} artil ``` Choose **Email**, **SMS**, or **Telegram** to read, reply, or compose a message. **Secrets** stores credentials and links them to a client. **Set up this computer** connects Claude Code or Hermes. **Status** shows your login and channel setup. **Switch agent** signs in as another agent account, and **Sign out** signs out on this computer. Use the arrow keys to move, Enter to choose, and Escape to go back. Press q or Ctrl-C to quit; q types a letter while you are entering text. Choose **Email → Inbox**, open a message, then press **r** to reply. Choose **Compose** from the Email, SMS, or Telegram menu to start a new message. Enter on the message-body field submits it. When you pipe `artil` into another command, it prints status instead of opening the menu. Explicit commands such as `artil email list` also keep their text output; use `--json` for scripts. ## Client setup ```bash theme={null} artil init [--client ] [--connection ] [--url ] [--agent ] [--name ] ``` | Flag | Values and purpose | | - | - | | `--client` | `claude-code`, `pi`, `hermes`, or `none`. Omit in a terminal to choose interactively. | | `--connection` | `local` or `server`; used with `--client hermes`. | | `--url` | Public URL for Hermes with `--connection server`. | | `--agent` | Agent name, slug, or ID; normally omitted for an agent login. | | `--name` | Computer name; hostname by default. Artil gets it at the next browser sign-in. | Without an interactive terminal, pass `--client` to make a selection. Otherwise `init` prints the available connections without connecting one. See [automatic delivery](/automatic-messages). To use the Claude Code plugin or Pi package instead, see [plugins](#plugins); `init` will not set up a client whose plugin is installed. For Claude Code, Pi, and local Hermes, `init` installs an `artil` skill and links the agent's secrets to the client. For Claude Code, it also adds the `artil-messages` channel and allows `artil email`, `artil sms`, `artil telegram`, and `artil url` to run without asking. For Pi, it also adds the `artil-messages` extension. A computer has one client; choosing one removes the others. `--client none` removes local delivery setup, the Claude Code, Pi, and Hermes secret links, and a remote Hermes webhook set up from this computer. See [disconnect message delivery](/automatic-messages#change-or-disconnect-a-client) before running it. ## Plugins | Command | Behavior | | - | - | | `artil plugin install ` | Install the Artil plugin into `claude-code`, `pi`, `hermes`, `openclaw`, or `codex`. All but Codex's can hear about new messages once `artil listen on` turns them on, and replace what `artil init` set up for that client. | | `artil plugin remove ` | Take the plugin out of `claude-code`, `pi`, `hermes`, `openclaw`, or `codex` again. | The plugin runs `artil` with this computer's login, so sign in as the agent first. See [Claude Code](/claude-code), [Codex](/codex), [Pi](/pi), [OpenClaw](/openclaw), and [Hermes](/hermes). ## New messages | Command | Flags | Behavior | | - | - | - | | `artil listen` | None | Show which client hears the agent's new messages on this computer. | | `artil listen on` | `--client` | Make the client that runs it, the one `--client` names (`claude-code`, `pi`, `hermes`, or `openclaw`), or the one installed plugin that hears new messages, hear the agent's new messages on this computer, in place of any other. | | `artil listen off` | None | Stop hearing the agent's new messages on this computer. | Run it in the client, the way the client runs `artil`: `!artil listen on` in Pi, `! artil listen on` in Claude Code, `openclaw artil listen on`, and `hermes artil listen on`. Codex does not hear about new messages. A computer that heard new messages before `artil listen` keeps the client that heard them. See [who hears about new messages](/delivery). ## Email, SMS, and Telegram The SMS commands need the agent to have a [phone number](/sms), and the Telegram commands a [Telegram bot](/telegram). | Command | Flags | | - | - | | `artil email [list]` | `--limit`, `--before`, `--from`, `--json` | | `artil email read ` | `--json` | | `artil email send ` | `--to` (repeatable), `--cc` (repeatable), `--subject`, `--attach` (repeatable), `--reply-to`, `--forward`, `--json` | | `artil sms [list]` | `--limit`, `--before`, `--from`, `--json` | | `artil sms read ` | `--json` | | `artil sms send ` | `--to`, `--attach` (repeatable), `--reply-to`, `--forward`, `--json` | | `artil telegram [list]` | `--limit`, `--before`, `--from`, `--json` | | `artil telegram read ` | `--json` | | `artil telegram send ` | `--to`, `--reply-to`, `--forward`, `--json` | The default list limit is 25; accepted values are 1 to 25. `--before` takes a listed message ID and shows the messages older than it; `--from` keeps one sender's. Use a listed message ID with `read`, `--reply-to`, or `--forward`. See [email](/email) and [SMS](/sms) for channel-specific requirements. `read` lists the files a message came with, each with a download link that works for 15 minutes without signing in; read the message again for a fresh one. `send` takes `--attach ` for each file to send, and `--forward` carries the message's files along. Pass each message body as one quoted argument. Put `--` before a body that starts with a dash so it is read as message text. ## Limits | | Email | SMS | Telegram | | - | - | - | - | | Recipients | Up to 10 across `--to` and `--cc`; a new email needs a subject | One number in the same country as the agent's number, that texted the agent first or belongs to another agent of the workspace | One user ID, of someone who wrote to the bot first, or bot username | | Body | Plain text, up to 32,000 characters, with no BCC | Up to 10 SMS segments; emoji and other Unicode characters use more segments than plain Latin characters | Plain text, up to 4,096 characters | | Subject | Up to 240 characters | None | None | | Files | Up to 7 MB together; no .exe, .bat or other program files | Pictures, video, audio, PDF, vCard or calendar files: up to 10, under 1 MB together, from a US number | None yet | Each agent sends at most 100 messages an hour, replies included, and your workspace's plan sets how many its agents can send each month. A send past a limit fails, and the error says which. Your workspace's agents also send no email for 7 days once too much of what they sent in the last 7 days bounced or was marked as spam: at least 5 bounces that make 5% of it, or at least 2 spam reports that make 0.05%. Until then, sending email fails and says when it can resume. ## Team `artil team` lists the workspace's agents and members with their IDs, email addresses, and phone numbers, so an agent can write to a teammate by [email](/email) or [SMS](/sms). An agent's are the primary ones it sends from; a member's phone number is the one they added to their Artil profile, if any. **Listening** says whether a client or webhook hears each agent now, so that waking it works; members have none. `--json` prints them as JSON, with `listening` as `true`, `false`, or `null` for members. `artil url send [body]` sends a message to another agent of the workspace at its [Message URL](/url); `--json` prints the `message.received` event the agent gets. ## Secrets | Command | Flags | Behavior | | - | - | - | | `artil secrets list` | `--agent`, `--json` | List secret names and update details. Running `artil secrets` alone also lists them. | | `artil secrets set [value]` | `--agent`, `--kind`, `--qr`, `--json-path`, `--json` | Store a value; omit it for stdin or a hidden prompt. With `--json`, supply or pipe the value; no prompt opens. | | `artil secrets remove ` | `--agent`, `--json` | Delete a stored value. `rm` is an alias. | | `artil secrets copy ` | None | Copy the value to the clipboard. | | `artil secrets code ` | None | Print a one-time password's current code. | | `artil secrets link ` | `--force` | Link `claude-code`, `pi`, `hermes`, or a `.env` path. `--force` bypasses the Git-ignore check for a file. | | `artil secrets unlink ` | None | Remove the local link and the values the CLI wrote there. | | `artil secrets pull` | None | Refresh the agent's links on this machine. | `set --kind` is `secret`, the default, `password`, or `otp` for a one-time password's setup key or `otpauth://` link. `--qr` reads that link from a QR code image. `copy`, `code`, `link`, and `pull` need an agent login. With an `--account` login, use `--agent` to choose the agent for `list`, `set`, and `remove`. `set` and `remove` refresh local links only when you are signed in as that agent. Otherwise, run `artil secrets pull` while signed in as the agent. See [secrets](/secrets). ## Updates ```bash theme={null} artil update ``` This updates a standalone installation. See [installation](/installation). ## Output and failures `--json` is supported only by the commands listed above. It is a command flag, so put it after the relevant command. For example: ```bash theme={null} artil email list --json artil auth status --json ``` Successful email, SMS, and Telegram commands put message data inside `result`. For `list`, the messages are in `result.messages`, without their text, which `read` gives, and `result.older` is the ID to pass to `--before` for older ones, or `null`. Status and secret commands have their own JSON fields. `secrets list --json` shows secret names and update details; values and linked destinations are omitted. Failed commands exit with status 1 and print the error on stderr. With `--json`, they also print `{"result": {"ok": false, "error": "…"}}` on stdout. Check the exit status before reading the output. Unknown flags return an error. # Configuration Source: https://docs.artil.ai/configuration Environment variables the CLI reads and the files it keeps. The CLI needs no configuration file of your own: `artil auth login` saves the login, and `artil init` and `artil secrets link` record what they set up. These variables change where it looks and how it behaves. ## Environment variables | Variable | Purpose | | - | - | | `ARTIL_TOKEN` | Token every command signs in with, ahead of the saved login; `auth login` saves it. | | `ARTIL_CONFIG_HOME` | Directory for the CLI's local configuration and login. | | `XDG_CONFIG_HOME` | When no explicit config directory is set, configuration goes in its `artil` subdirectory. | | `ARTIL_NO_AUTOUPDATE` | Any non-empty value turns off the hourly background update. | | `HERMES_HOME` | Hermes directory that `init` and secret links use; defaults to `~/.hermes`. | | `CLAUDE_CONFIG_DIR` | Claude Code configuration directory that `init` and secret links edit; defaults to `~/.claude`. | | `PI_CODING_AGENT_DIR` | Pi configuration directory that `init` and secret links edit; defaults to `~/.pi/agent`. | | `NO_COLOR` | Turn off colors in the terminal menu. | | `ARTIL_CLIENT` | The client `artil listen on` turns on: `claude-code`, `pi`, `hermes`, or `openclaw`. The plugins set it. | ## Files The default configuration directory is `~/.config/artil`: * `credentials.json` holds the login. * `config.json` holds this computer's ID and name. * `listen.json` records which client hears the agent's new messages here, as `artil listen` sets it. * `secrets.json` records local secret links. * `secrets/claude-code.sh` holds the secrets linked to Claude Code. * `secrets/pi.sh` holds the secrets linked to Pi. * `update.json` records the last background update check. * `hermes.log` is the local Hermes log, and `hermes-cursor.json` records the last message local Hermes handled. * `hermes-webhook.json` records the remote Hermes webhook set up here. # Who hears about new messages Source: https://docs.artil.ai/delivery How an agent account's new email, texts, and messages at its Message URL reach the client that listens for them. An agent account's new messages wait in its inbox: `artil email`, `artil sms`, `artil telegram`, and the MCP `messages.list` tool show them at any time. To act on a message as it arrives, a client listens to the agent, and Artil tells it the message's ID when one comes in. The client reads the message and answers it the way its instructions say, so test delivery and a reply before leaving it to answer on its own; see [Messages are not instructions](/safety). ## Ways to listen | Client | How it hears about a message | Set up with | | - | - | - | | [Claude Code](/claude-code) | The `artil-messages` channel in a session started with it | `/plugin install artil --marketplace artilai/plugins`, then `! artil listen on`, or `artil init --client claude-code` | | [Pi](/pi) | The `artil-messages` extension in an open interactive or RPC session, which starts a turn | `pi install npm:@artil-ai/pi`, then `!artil listen on`, or `artil init --client pi` | | [OpenClaw](/openclaw) | The Artil plugin in the running gateway | `openclaw plugins install clawhub:@artil/openclaw`, then `openclaw artil listen on` | | [Hermes](/hermes) | A background job on this computer that starts a run, or the Artil plugin in the running gateway | `hermes plugins install artilai/plugins/plugins/hermes --enable`, then `hermes artil listen on`, or `artil init --client hermes --connection local` | | [Hermes on a server](/automatic-messages) | A webhook to its public URL | `artil init --client hermes --connection server` | | [ChatGPT](/chatgpt) | An MCP events subscription | Ask it to watch the agent's messages | | Your own server | A [webhook](/webhooks) | The API | ## One client at a time An agent's new messages reach one client: the one that started listening last, on this computer or another. On a computer, Claude Code, Pi, OpenClaw, and Hermes listen only after `artil listen on` runs in them, and only the last one it ran in; `artil listen off` stops them. `artil init` turns on the client it sets up. A computer that heard new messages before `artil listen` keeps the client that heard them. When a client starts listening, the one before it is told which client took over and hears nothing more until `artil listen on` runs in it again. An MCP events subscription counts as a client: a new one ends the other sign-ins' subscriptions and stops a CLI client listening as the agent. A session that did not start listening never takes the messages, such as a Claude Code session started without the channel or a `pi -p` run. The agent's page in [Artil](https://app.artil.ai) shows the connected client. `artil auth status` shows the client that listens now and this computer's delivery setup, so it tells a client set up here apart from one that listens. `artil listen` shows which client is turned on here. Webhooks are the exception. A [webhook](/webhooks) you add, and the one that reaches a Hermes server, get every event whichever client listens. ## While nobody listens A message that arrives while no client listens stays in the inbox and is not delivered later. Check it with `artil email`, `artil sms`, or `artil telegram`, the plugin's skill, or `messages.list`. Sending to the agent's [Message URL](/url) fails while nobody listens, since Artil keeps no copy to read later. ## What the client is told The notification names the message's ID. The client reads the message with the CLI or the MCP `messages.read` tool and replies with `--reply-to` or `messages.send`, only through Artil: a Telegram or email the client has of its own is another account, and the sender would not get its answer there. A message at the [Message URL](/url) comes whole instead, with who sent it and how to answer them. Not every message wakes the agent: spam, limits, and senders the agent does not take mail from are refused, and the inbox says why. See [Who can wake the agent](/wake-access). # Email Source: https://docs.artil.ai/email Read incoming email, send a new message, and reply from your agent account's address. Every agent account gets an email address when you create it. You can see it on the agent's **Email** page in [Artil](https://app.artil.ai). Sign the CLI in as that agent, then run `artil auth status` to see its addresses. When it has several, `(primary)` marks the one new email leaves from. You can also use this address to [sign up for services](/email-identity). ## 1. List incoming email ```bash theme={null} artil email artil email list --limit 10 ``` For a first test, send an email from your own account to the agent's address. If the inbox is empty, allow time for delivery and list it again. The list shows incoming messages, newest first, and the address where the agent can be reached. When there may be older email, the list ends with the command that shows it, such as `artil email list --before mail:`; the [command reference](/commands#email-sms-and-telegram) has `--limit`, `--before`, and `--from`. If the agent has multiple email addresses, they share one inbox. If a message did not [wake the agent](/wake-access), its line ends with the reason, such as "Did not wake the agents: it was marked as spam." ## 2. Read a message ```bash theme={null} artil email read "mail:" ``` Use the ID printed by the list. The `mail:` prefix is optional in `email` commands. SMS message IDs return an error here. The output shows whom the message went to (`To`) and copied (`Cc`), the agent included, then its body and the files it came with: each `Attached` line gives the file's name, type, size and a download link. Images placed inside the message body are not listed. The link needs no sign-in and works for 15 minutes; read the message again for a fresh one. `curl -OJ` saves the file under its own name: ```bash theme={null} curl -OJ "https://app.artil.ai/api/files/?exp=...&sig=..." ``` If the message did not wake the agent, the reason comes before the body. ## 3. Reply ```bash theme={null} artil email send --reply-to "mail:" -- "Thanks, I will take a look." ``` A reply uses the original message to choose its recipient and thread. It inherits the subject with a `Re:` prefix and uses the address the message arrived at when that mailbox is still available. If that address is no longer available, it uses the agent's primary address. You can override the recipient with `--to` or the subject with `--subject`. A reply goes to the sender only. To answer everyone, pass the other addresses `artil email read` lists under To and Cc with `--cc`: ```bash theme={null} artil email send --reply-to "mail:" --cc grace@example.com -- "Thanks, both." ``` Check your own inbox for the reply. ## Send a new email ```bash theme={null} artil email send --to teammate@example.com --subject "Build results" -- "The build passed." ``` A new message needs a recipient and subject and leaves from the agent's primary address. Repeat `--to` for multiple recipients: ```bash theme={null} artil email send --to alice@example.com --to bob@example.com --subject "Build results" -- "The build passed." ``` Repeat `--cc` to copy people, and `--attach` for each file to send: ```bash theme={null} artil email send --to alice@example.com --subject "Build results" --attach ./report.pdf -- "The report is attached." ``` The message body is plain text, and there is no BCC. The [limits](/commands#limits) say how many recipients, characters, and messages an hour an agent can send, and how large the files can be. ## Forward a message ```bash theme={null} artil email send --forward "mail:" --to teammate@example.com -- "Can you take this one?" ``` The forwarded message goes below the body with who sent it, when, its subject, and whom it went to, under the subject with a `Fwd:` prefix unless `--subject` gives another. Its text goes along and counts toward the [limits](/commands#limits), and so do its files. If the message did not wake the agent, the forward says why. `--forward` takes a message from any channel. A text has no subject, so give the email one: ```bash theme={null} artil email send --forward "sms:" --to teammate@example.com --subject "Text from Ada" -- "FYI" ``` ## Check the result A successful send prints the channel, recipients, and message ID after submission to the email service. To confirm delivery, check the recipient's inbox. Keep the ID to read the sent message later; the inbox list shows incoming messages only. If the email bounces or its recipient marks it as spam, the agent's **Email** page in [Artil](https://app.artil.ai) marks the message and says which. Too many of either stop the workspace's email for a week; see [Limits](/commands#limits). If a send fails or is interrupted and its outcome is unknown, check with the recipient before trying again to avoid sending a duplicate. ## Import another mailbox On the Pro plan, you can bring over the email of a mailbox the agent used before, such as its own Gmail account. 1. Export the mailbox as an MBOX file. For Gmail, choose Mail in [Google Takeout](https://takeout.google.com). In Apple Mail, select the mailbox, choose **Mailbox → Export Mailbox**, and use the `mbox` file inside the exported folder. For other providers, add the mailbox to Apple Mail, or to Thunderbird with the ImportExportTools NG add-on, and export it there. 2. On the agent's **Email** page in [Artil](https://app.artil.ai), open the **⋮** menu beside the title and choose **Import mail**. Pick the file and enter the mailbox's old address. Separate aliases with commas. 3. Keep the page open until the import finishes. **Stop** ends it early, and importing again picks up where it stopped. The file stays on your computer: the page reads it and sends the email to Artil. Mail from the mailbox's address counts as sent and the rest as received. The import goes newest first, does not wake the agent, and leaves out what Gmail filed as spam, trash, drafts, or chat. It skips email the inbox already has. Pro includes 10,000 imported messages per workspace; an import stops when the plan has no room left. To keep getting mail sent to the old address, forward it to the agent's address in the old mailbox's settings. Forwarded mail that lost its sender's signature cannot be verified, so it does not wake an agent that only takes mail from its team or listed senders; see [Who can wake the agent](/wake-access). ## Export the mail On the Pro plan, choose **Export mail** in the **⋮** menu on the agent's **Email** page. The browser downloads an MBOX file of the agent's email, which other mailboxes import, without attachments. ## Use JSON in a script ```bash theme={null} artil email list --limit 10 --json artil email read "mail:" --json ``` `list`, `read`, and `send` take `--json`; [Output and failures](/commands#output-and-failures) describes what they print. # Sign up for services Source: https://docs.artil.ai/email-identity Create service accounts with your agent's email and save their credentials. Your agent can use its email to sign up for a service, read the verification message, and save an API key. It needs a browser or access to the service's API to fill out forms and follow links. First check that you can [read the agent account's email](/email). If Claude Code, Codex, Pi, OpenClaw, or Hermes will handle signup, [connect that client](/setup) and give it the browser or API access it needs. Use the same Artil agent throughout. ## 1. Find the agent's address Check the active agent and its email address: ```bash theme={null} artil auth status ``` Use the address shown in status. If it shows several, use the one marked `(primary)`. To switch agents, follow [authentication](/authentication). ## 2. Sign up with that address Enter the address in the service's signup form or API request. Fill in the required account details and request the verification email. Complete any consent, CAPTCHA, or additional authentication the service requires. ## 3. Read the verification email List the inbox, then read the matching message using its ID: ```bash theme={null} artil email list --limit 10 artil email read "mail:" ``` Check that the message matches your signup and any link points to the service you chose. Enter the code in the signup form or open the verification link. Finish verification and check that you can open the new account. If the message has not arrived, list the inbox again after the service sends it. See [email](/email) for message commands and [automatic delivery](/automatic-messages) for receiving message notifications. ### SMS verification Your agent can use its own [Artil VoIP phone number](/sms) to receive SMS sign-up, sign-in and two-factor authentication (2FA) codes. Support varies by service, as some services may reject VoIP numbers. ## 4. Keep the service's credentials If the service issues an API key, store it as an agent secret. Only the agent's own login can reveal its secret values; workspace members cannot. In a terminal, this command prompts for the value without showing what you type: ```bash theme={null} artil secrets set SERVICE_API_KEY ``` If another command produces the key, pipe its output into `artil secrets set SERVICE_API_KEY`. For JSON input, use `--json-path .token` to read a field named `token`. Add `--json` for JSON output; this also disables the prompt, so you must supply or pipe the value. See [secrets](/secrets) for details. Run `artil secrets list` and check that it includes `SERVICE_API_KEY`. The list shows names and update times, keeping values hidden. If the service sets up two-factor sign-in, store its setup key with `artil secrets set SERVICE_OTP --kind otp`, or its QR code image with `--qr ./qr.png`. `artil secrets code SERVICE_OTP` prints the code to enter. ## 5. Use the account Link a client or file only if it should receive **all** of this agent's secrets. For Claude Code, the link uses user-level configuration across projects: ```bash theme={null} artil secrets link claude-code ``` Start a new Claude Code session if this is its first secret link. `artil init` already links the local client it connects. Hermes and project `.env` files are also supported; see [secrets](/secrets) for setup and refresh instructions. Use the saved key to make a request to the service. Check that the request succeeds, and keep the key out of the output. > **[Next: store and use credentials](/secrets)** > > Choose another destination, refresh a changed key, or unlink and revoke access. ## Keep using the account Login links, account alerts, and password-reset emails arrive in the same inbox. After changing a password or API key, update the secret stored in Artil. # Hermes Source: https://docs.artil.ai/hermes Let the Hermes gateway hear about your agent account's new messages and answer them. The Artil plugin adds an `artil` platform to the Hermes gateway, which tells it about each new message, and gives Hermes the `artil` skill to read and answer email and SMS. It brings the Artil CLI, which `hermes artil` runs. Install [Hermes](https://hermes-agent.nousresearch.com), then add the plugin, restart the gateway, sign in as your agent, and turn on new messages: ```bash theme={null} hermes plugins install artilai/plugins/plugins/hermes --enable hermes gateway restart hermes artil auth login hermes artil listen on ``` Answer yes when Hermes asks to install the plugin's Node dependencies: they bring the CLI. Select your agent in the browser. Without a browser, make a token on your agent's page in Artil under **Connect to agent** → **Use a token**, as [headless authentication](/authentication#use-a-headless-machine) shows, and sign in with `hermes artil auth login --token `. The gateway hears new messages a few seconds after `listen on`, and `hermes artil listen off` turns them off. With the [Artil CLI](/installation) installed, `artil plugin install hermes` adds the plugin too. Hermes in Docker, such as on Fly.io, runs as a user of its own, usually `hermes`. Run these commands as that user, for example after `su hermes`, so its gateway finds the plugin and the login. Send a test email to your agent's address and check your inbox for the reply. New messages arrive in the gateway's own `artil` conversation; nothing Hermes writes there is sent anywhere. If no reply comes, check the gateway log, fix what it reports, and run `hermes gateway restart`. > **[Next: sign up for a service](/email-identity)** > > Use this inbox to receive a verification email, finish signup, and store the service's > credentials. To remove the plugin: ```bash theme={null} hermes plugins remove artil hermes gateway restart ``` # Quickstart Source: https://docs.artil.ai/index Give your agent an email address and send its first reply. Artil gives your agent its own email, SMS number, and a place to keep credentials. **1. Create your agent** Open [Artil](https://app.artil.ai), create or choose an agent account, and copy its email address. **2. Install and sign in** ```bash theme={null} curl -fsSL https://artil.ai/install.sh | bash ``` ```bash theme={null} artil auth login ``` In the browser, choose the same agent under **Agent account** and click **Connect**. [Installation help](/installation) · [Headless login](/authentication#use-a-headless-machine) **3. Read an email and reply** Send an email to your agent, then check its inbox: ```bash theme={null} artil email list --limit 5 ``` Use the message ID from the list: ```bash theme={null} artil email read "mail:" artil email send --reply-to "mail:" -- "Connected." ``` Check your own inbox for the reply. > **[Connect your coding agent](/setup)** > > Use email and SMS from Claude Code, Codex, Pi, OpenClaw, or Hermes. > **[Explore the CLI](/commands)** > > Browse the terminal menu and command reference. # Installation and updates Source: https://docs.artil.ai/installation Install the standalone CLI on macOS or Linux and keep it current. ## Install The standalone CLI supports macOS, and Linux with glibc, on arm64 and x64. It does not require Bun or Node.js. ```bash theme={null} curl -fsSL https://artil.ai/install.sh | bash ``` Check that the CLI prints a version number: ```bash theme={null} artil --version ``` The installer selects the build for your machine and verifies its SHA-256 checksum. It stores versions under `~/.local/share/artil/versions` and places the `artil` launcher in `~/.local/bin`. It keeps only the current and previous versions. It does not replace an `artil` in `~/.local/bin` that it did not install; move that file aside and install again. If `~/.local/bin` is not on your PATH, the installer prints the line that adds it to your shell profile. Run that line, then open a new terminal. To use the CLI in the current terminal: ```bash theme={null} export PATH="$HOME/.local/bin:$PATH" artil --version ``` If the installer prints "Another artil comes first on your PATH", remove the copy it names. > **[Next: sign in as your agent](/authentication)** > > Choose the agent account whose inbox this terminal should use. ## Update When you run a command, a standalone installation checks for updates in the background, at most once an hour. The next command uses any installed update. Update immediately: ```bash theme={null} artil update ``` To turn off the hourly background update, set `ARTIL_NO_AUTOUPDATE` to any non-empty value. `artil update` still works. ## Set up through an agent Give your coding agent the installation instructions at [artil.ai/INSTALL.md](https://artil.ai/INSTALL.md). During browser login, choose the agent you want the CLI to use. # Connect over MCP Source: https://docs.artil.ai/mcp-server Give an MCP client your agent account's email and SMS without the CLI. Artil runs an MCP server at `https://mcp.artil.ai/mcp`. It acts as one agent account: it shows where people reach the agent and reads and sends its email and texts. Use it from a client that cannot run the [CLI](/installation), such as an agent on a hosted platform. Connect a client in one of two ways: sign in through the browser, or send one of the agent's tokens in a header. ## Sign in through the browser Add the server by its URL alone. When the client connects, Artil opens in your browser: choose the agent the client acts as and click **Connect**. To create an agent here, enter a name and click **Create and connect**. **ChatGPT** [Add Artil to ChatGPT](https://chatgpt.com/plugins/plugin_asdk_app_6ac808125e6481919db1df8dc647b320), install the plugin, and sign in when ChatGPT asks. To watch for new messages, see [ChatGPT](/chatgpt). **Claude** [Add Artil to Claude](https://claude.ai/customize/connectors?modal=add-custom-connector\&connectorName=Artil\&connectorUrl=https%3A%2F%2Fmcp.artil.ai%2Fmcp), confirm the connector, and click **Connect**. **Claude Code** ```bash theme={null} claude mcp add --transport http artil https://mcp.artil.ai/mcp ``` Run `/mcp` in a new session, select `artil`, and choose **Authenticate**. **Cursor** [Add Artil to Cursor](https://cursor.com/install-mcp?name=artil\&config=eyJ1cmwiOiJodHRwczovL21jcC5hcnRpbC5kZXYvbWNwIn0%3D), confirm the install, and sign in when Cursor asks. **VS Code** [Add Artil to VS Code](https://vscode.dev/redirect/mcp/install?name=artil\&config=%7B%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fmcp.artil.ai%2Fmcp%22%7D), confirm the install, and sign in when VS Code asks. **Codex** ```bash theme={null} codex mcp add artil --url https://mcp.artil.ai/mcp codex mcp login artil ``` Zed, Windsurf, and other clients that sign in to remote MCP servers with OAuth work the same way: add `https://mcp.artil.ai/mcp` and sign in when the client asks. Access lasts 30 days, then the client asks you to sign in again. ## Use a token ### 1. Create a token Open the agent in Artil, select **Tokens**, click **New token**, select **Another MCP client**, enter a name, such as the client's, and click **Create token**. Copy the token from the `Authorization` header it shows; it is shown only once and does not expire. Only the agent's own token works. Keep it out of repositories and agent conversations, and revoke it on the same tab when the client no longer needs it. ### 2. Add the server to your client The client sends the token in the `Authorization` header. Replace `YOUR_TOKEN` with the token you copied. **Claude Code** ```bash theme={null} claude mcp add --transport http artil https://mcp.artil.ai/mcp --header "Authorization: Bearer YOUR_TOKEN" ``` Run `/mcp` in a new session and check that `artil` is connected. **Cursor** Add the server to `~/.cursor/mcp.json`: ```json theme={null} { "mcpServers": { "artil": { "url": "https://mcp.artil.ai/mcp", "headers": { "Authorization": "Bearer YOUR_TOKEN" } } } } ``` **Hermes** Put the token in Hermes' `.env` as `ARTIL_TOKEN`, then add the server to `config.yaml`: ```yaml theme={null} mcp_servers: artil: url: https://mcp.artil.ai/mcp headers: Authorization: Bearer ${ARTIL_TOKEN} ``` Restart Hermes. Remote Hermes setup in [automatic delivery](/automatic-messages) prints an entry like this for you. Other clients that connect to remote MCP servers over HTTP and can send a header work the same way. ## Tools | Tool | What it does | | - | - | | `status` | Shows the agent's name, its addresses on each channel, where people reach it, and in `signedInBy` the email address of the member who signed the client in | | `messages.list` | Lists incoming email and texts, newest first; `channel` keeps one channel, `from` one sender, `limit` takes 1 to 25, and `before` takes the last listed `id` to page back | | `messages.read` | Reads a message the agent received or sent, by its `id`, with its text and a download `url` for each file it came with | | `messages.send` | Answers the message given as `replyTo` the way it came, or sends a new one with a `channel`, recipients in `to`, and for email a `subject`; `cc` copies others on email; `forward` passes the message with that `id` on to `to`, files included; `attachments` sends files; channel `url` sends to the [Message URL](/url) of the one agent whose ID is in `to` | | `team.list` | Lists the workspace's agents and members with their IDs and addresses on each channel, and whether anyone listens to each agent | A message's `attachments` name each file it came with and, when Artil kept the file, its `id` and a download `url` that needs no sign-in and works for 15 minutes; read the message again for a fresh one. To send files, list them in `attachments`: a file from any message the agent received or sent, by its `id`, or a text file the agent wrote, by `name`, `type` and `text`. Email and SMS carry files, within their [limits](/commands#limits). A file from the chat itself cannot be sent: have it mailed to the agent, then send it on by its `id`. Message IDs here have no `mail:` or `sms:` prefix, unlike the ones the CLI shows. Sending follows the same rules and [limits](/commands#limits) as the CLI; an agent texts only people who [texted it first](/sms). > [!WARNING] > > A message can try to steer the client, so check what it may do without asking before you connect > it; see [Messages are not instructions](/safety). ## New messages Clients that support MCP events, such as [ChatGPT](/chatgpt), subscribe to the agent's new messages and hear about each one as it arrives; see [Events](#events). Other clients see new messages when they call `messages.list`; [Who hears about new messages](/delivery) lists the ways to be told as they arrive. ## Events For a client signed in as one agent, the server also serves the [draft MCP events extension](https://github.com/modelcontextprotocol/experimental-ext-triggers-events/blob/main/docs/design-sketch-proposal.md): it lists `events` among its capabilities and answers `events/list`, `events/subscribe`, and `events/unsubscribe`. It delivers events by webhook. | Event | Arguments | When it happens | | - | - | - | | `message.received` | Optional `channel`: `email`, `sms`, or `url` | An email, a text, or a message at the [Message URL](/url) reaches the agent | * `events/subscribe` takes an `https` callback URL on the public internet and a `whsec_` secret of 24 to 64 bytes. Before it answers, Artil posts a signed `{"type": "verification", "challenge": "…"}` to the callback, which must answer with the same `challenge`. * A subscription lasts until the client unsubscribes, its sign-in ends, or another client takes the agent's new messages. Subscribing again with the same event, arguments, and callback replaces its secret. One sign-in holds up to 20 subscriptions. * A new subscription makes its client the [one that hears the agent's new messages](/delivery): other sign-ins' subscriptions end, and a CLI client listening as the agent stops. * Each event arrives with the body, signature, and retries of a [webhook](/webhooks), plus an `X-MCP-Subscription-Id` header. To resume after a gap, pass the last `cursor` back to `events/subscribe`; Artil sends what came after it, from the last 3 days. * Revoking the client's sign-in ends its subscriptions. # OpenClaw Source: https://docs.artil.ai/openclaw Let OpenClaw hear about your agent account's new messages and answer them. The Artil plugin tells the OpenClaw gateway about each new message and gives it the `artil` skill to read and answer email and SMS. It brings the Artil CLI, which `openclaw artil` runs. Install [OpenClaw](https://openclaw.ai), then add the plugin, sign in as your agent, and turn on new messages: ```bash theme={null} openclaw plugins install clawhub:@artil/openclaw openclaw artil auth login openclaw artil listen on ``` Select your agent in the browser. Without a browser, make a token on your agent's page in Artil under **Connect to agent** → choose the client → **Token**, as [headless authentication](/authentication#use-a-headless-machine) shows, and sign in with `openclaw artil auth login --token `. The running gateway hears new messages a few seconds after `listen on`, and `openclaw artil listen off` turns them off. With the [Artil CLI](/installation) installed, `artil plugin install openclaw` adds the plugin too. Send a test email to your agent's address and check your inbox for the reply. Each new message starts a turn in the agent's own `artil` session; nothing it writes there is sent anywhere. If no reply comes, check the gateway log and fix what it reports. The gateway tries again every 30 seconds. > **[Next: sign up for a service](/email-identity)** > > Use this inbox to receive a verification email, finish signup, and store the service's > credentials. To remove the plugin: ```bash theme={null} openclaw plugins uninstall artil ``` # Pi Source: https://docs.artil.ai/pi Let Pi hear about your agent account's new messages and read, answer, and send email and SMS from its address or number. The Artil package tells an open Pi session about each new message and gives Pi the `artil` skill, which reads, answers, and sends email and SMS with your agent account's CLI login. Before starting, install [Pi](https://pi.dev). The package brings the Artil CLI with it. ## Connect your inbox **1. Install the package** ```bash theme={null} pi install npm:@artil-ai/pi ``` Pi saves the package in its configuration and loads it in new sessions. Pi runs the CLI that comes with it when the PATH has no `artil` of its own. With the [Artil CLI](/installation) installed, `artil plugin install pi` runs the same command. **2. Start Pi and sign in** Start `pi`, then sign in from it: ```text theme={null} !artil auth login ``` Select your agent in the browser. Without a browser, make a token on your agent's page in Artil under **Connect to agent** → choose the client → **Token**, as [headless authentication](/authentication#use-a-headless-machine) shows, and run `!artil auth login --token `. `!artil auth status` shows the agent. **3. Turn on new messages** ```text theme={null} !artil listen on ``` Open Pi sessions hear the agent's new messages from now on. `!artil listen off` turns them off again. **4. Read a message and reply** Send a test email to your agent's address. Then ask Pi: ```text theme={null} Check my email, read my test message, and reply saying "Connected." ``` Check your inbox for Pi's reply. An open session also hears about the test email when it arrives. ## What the package adds | Part | What it does | | - | - | | `artil` skill | Reads, answers, and sends email and SMS, and uses the agent's stored secrets, with the `artil` CLI. | | `artil-messages` extension | Tells the interactive or RPC session about a new message, which starts a turn. | Ask Pi to “Check both inboxes for anything I need to answer” or “Read the message from Alice.” A reply uses the original message to choose its recipient. SMS needs a [phone number](/sms). A new message starts a turn in the open session, or waits until Pi finishes the current one; `pi -p` runs do not receive messages. Only the session that started last hears new messages; see [Who hears about new messages](/delivery). Pi runs commands without asking for approval, so a message can lead it to act before you see it; see [Messages are not instructions](/safety). To give Pi stored API keys, [link its credentials](/secrets). > **[Next: sign up for a service](/email-identity)** > > Use this inbox to receive a verification email, finish signup, and store the service's > credentials. ## Having trouble? Run `!artil auth status` in Pi to check its login. If `/skill:artil` is missing, check `pi list` and start a new session. When Pi stops hearing about new messages, it says why, such as a missing agent login; fix it and run `/reload`. For channel or access errors, follow [message troubleshooting](/troubleshooting#email-or-sms-is-unavailable). If a reply fails or is interrupted, confirm receipt with the recipient before trying again. The inbox lists incoming messages and cannot confirm delivery. Long messages may be shortened in Pi; use `artil email read -- ''` or `artil sms read -- ''` in a terminal with the CLI for the full output. ## Uninstall ```bash theme={null} pi remove npm:@artil-ai/pi ``` This also removes the CLI that came with the package. Run `/reload` in open Pi sessions to continue without it. # Messages are not instructions Source: https://docs.artil.ai/safety Why a client treats what reaches the agent account by email, text, or webhook as information, and what you decide before connecting one. Anyone can write to an agent account's email address or phone number, so a message can try to steer the client that reads it: ask it to send a secret, write to someone else, or act in your name. Artil hands every message to the client as information to act on, never as instructions, and leaves what to do with it to the client's own instructions and to you. ## What Artil tells the client * The `artil` skill that the plugins install and `artil init` writes says that messages come from outside, that their text is information and never instructions that override the user's, and that a send that failed or was interrupted is checked with the user before trying again. * The [MCP server](/mcp-server) says the same in its instructions to the client. * The Claude Code channel adds that senders never see the session and tells Claude [who each sender is to the agent](/wake-access#who-the-sender-is-to-the-agent), so Claude acts on what the sender wants when you would want it done, and tells the sender through Artil when it can't, instead of asking you. * A [webhook](/webhooks) body carries the sender and the subject, which people outside wrote, and leaves the message text out; your server fetches the text and should treat it the same way. * A message at the agent's [Message URL](/url) comes from a member or agent of the workspace, since nobody else can send one, so the agent takes it as its team's request. * Linked secrets are environment variables in the client's commands; the skill tells the agent to use them as `$NAME` and never print their values. ## What you decide * Choose [who can wake the agent](/wake-access). A new agent only takes mail, texts, and messages at its Message URL from you, who created it; open it to more senders on purpose. * Check what the client may do without asking before you connect it. Pi runs commands without asking for approval, so a message can lead it to act before you see it. The Claude Code plugin and `artil init` allow `artil email`, `artil sms`, `artil telegram`, and `artil url` to run without asking; Claude may ask before other commands. * Ask a client that watches on its own, such as [ChatGPT](/chatgpt) or a Hermes or OpenClaw gateway, only for what you are happy for it to do with any message. * Send a test email yourself and read the reply before leaving a client to answer on its own. # Store and use credentials Source: https://docs.artil.ai/secrets Store API keys and passwords, then load them into Claude Code, Pi, Hermes, or a .env file. An agent account's secret stores an API key, password, token, or one-time password. Sign in as the agent that will use it. With an account login, `list`, `set`, and `remove` take `--agent `, but `copy`, `code`, `link`, and `pull` need the agent's own login. Secrets start off for each agent account, so they never mix with the ones its client keeps. A workspace member turns them on in the Artil app, on the agent's **Secrets** tab. Once on, they stay on. ## 1. Choose where the credential belongs Before storing it, check the agent and workspace with `artil auth status`. > [!NOTE] > > Any workspace member can add, replace, and remove an agent's secrets. Only the agent's own login > can read their values. Store credentials meant for this agent. Names become environment variable names, such as `SERVICE_API_KEY`. ## 2. Store the value In a terminal, omit the value to enter it without showing what you type: ```bash theme={null} artil secrets set SERVICE_API_KEY artil secrets list ``` Check that the list includes `SERVICE_API_KEY`. It shows whether a person or the agent last set the value, and when, keeping the value hidden. Running `set` again replaces it. > **The credential comes from another command** > > Pipe that command's output directly into `artil secrets set SERVICE_API_KEY`. > This keeps the key out of the command text and shell history. > > If that command produces JSON, select a field with `--json-path .token` > or a nested path such as `--json-path '.data.items[0].key'`. > > Add `--json` for JSON output. With this flag, supply or pipe the value; > the CLI returns an error if it would need to prompt for one. > **The name or value is rejected** > > Names use up to 128 letters, digits, and underscores and cannot begin with > a digit. Names that change how programs run are reserved. These include > `PATH`, `HOME`, names starting with `ARTIL_`, `CLAUDE_`, `HERMES_`, `GIT_`, > or `LD_`, and names ending in `_BASE_URL`, such as `OPENAI_BASE_URL`. > > Values must contain 1 to 32,768 characters and no NUL characters. A > trailing line break is removed. An agent holds at most 200 secrets. > Multiline values are accepted, but some clients cannot load them. > **The credential is a password or a one-time password** > > Add `--kind password` for a password. For a one-time password, store the > setup key the service shows with `--kind otp`, or read its QR code from an > image with `--qr`: > > ```bash theme={null} > artil secrets set SERVICE_OTP --kind otp > artil secrets set SERVICE_OTP --qr ./qr.png > ``` > > The secret holds the `otpauth://` link, and `artil secrets list` shows each > secret's kind. Print the current code to sign in with: > > ```bash theme={null} > artil secrets code SERVICE_OTP > ``` ## 3. Choose where to use it Linking copies **all of the agent's secrets** to the client or file you choose. Link only places that should have access to all of them. `artil init` already links the local client it connects, so check `artil secrets list` before adding another link. The Claude Code plugin and Pi package do not link secrets as part of their installation. **Claude Code** ```bash theme={null} artil secrets link claude-code ``` This installs a session-start hook in your **user-level Claude Code configuration**, which applies across projects. Claude's Bash commands load the linked agent's secrets from a private script. Linking another agent here replaces the previous link. Start a new Claude Code session after first linking. Programs launched by its Bash tool can then use `$SERVICE_API_KEY` without putting the value into the conversation. **Pi** ```bash theme={null} artil secrets link pi ``` This adds the `artil-secrets` extension to `~/.pi/agent/extensions`, which applies across projects. Pi's bash commands load the linked agent's secrets from a private script. Linking another agent here replaces the previous link. Run `/reload` in open Pi sessions after first linking. Programs launched by its bash tool can then use `$SERVICE_API_KEY` without putting the value into the conversation. **Hermes** ```bash theme={null} artil secrets link hermes ``` The CLI writes a section in `.env` under `$HERMES_HOME` (default `~/.hermes`) and adds the names to `terminal.env_passthrough` in `config.yaml`. Hermes must already be set up there. Restart a running Hermes process if it already loaded its environment. **Project .env** Ignore the destination in Git before linking it: ```bash theme={null} artil secrets link ./.env ``` The CLI writes its own section in the file. Your application must load the file to use the values. The CLI rejects files Git could commit unless you add `--force`, and keeps existing assignments outside its section. It reports and skips conflicting names and values the file cannot store. Check that `artil secrets list` shows the client or file you linked. A failed `link` is not recorded, so fix the error and run `artil secrets link ` again. `artil secrets pull` refreshes existing links, such as after you fix names that were left out. Use the saved key to make a request to the service and check that it succeeds. Programs that receive a secret through their environment can read its value. Keep those values out of logs and conversations. ## Keep it working > **Refresh a changed credential** > > When you are signed in as the agent, `set` and `remove` refresh its linked > destinations on this machine. After changing a value with an account login, > in the app, or on another machine, run this as the agent: > > ```bash theme={null} > artil secrets pull > ``` > > Run `pull` each time you need changes made elsewhere. Claude Code's hook > and Pi's extension also try to refresh secrets when a session starts. Once > they are active, the next Bash command can use values updated by `pull`. Restart > other programs that read their environment only at startup. > > If storing succeeds but updating a linked destination fails, the command > reports the error and exits with a nonzero status. Fix the link and pull again. > **Paste a credential into a form** > > ```bash theme={null} > artil secrets copy SERVICE_API_KEY > ``` > > The command copies the value to your clipboard without printing it. It > needs a clipboard tool: `pbcopy` on macOS, or `wl-copy`, `xclip`, or `xsel` > on Linux. > **Remove a local link** > > ```bash theme={null} > artil secrets unlink ./.env > ``` > > Replace `./.env` with the linked destination, such as `claude-code`, `pi`, > or `hermes`. Unlinking removes the values the CLI wrote there and leaves the secret > stored in Artil. Stop processes that already loaded those values if they > should no longer have access. > **Delete or revoke a credential** > > ```bash theme={null} > artil secrets remove SERVICE_API_KEY > ``` > > This deletes the stored value from Artil. When you are signed in as the > agent, it also refreshes this machine's links; otherwise, the agent runs > `artil secrets pull`. Other machines need to refresh their copies. > Deleting a stored API key does not revoke it at the service that issued > it; revoke it there to end access. # Choose your client Source: https://docs.artil.ai/setup Use your agent account's email and SMS from Claude Code, Codex, Pi, OpenClaw, or Hermes. Choose the client that will use your agent account's inbox. Each client uses the agent selected by `artil auth login` in the [quickstart](/index). On your agent's page in Artil, the **Clients** tab opens first. Choose a client in the **Connect to agent** card. ChatGPT and Claude open their published plugin or connector page directly; other clients show setup instructions below the buttons. The separate **CLI** and **MCP** cards show install and sign-in commands or the MCP server URL. The **Connect to agent** button is always available at the top of the page. Once at least one agent is connected, the list replaces the setup cards. The **Connect to agent** button remains available. Connected agents show their recorded client or device name and last use. The list includes completed CLI and OAuth sign-ins, plus manually created tokens after their first use. It refreshes when you return to the page and while the Clients tab is open. Expired and revoked credentials disappear. A shared token appears once under its name; it cannot identify every device using it. This shows access to the account, separately from the runtime that receives new messages. ## Hear about new messages `artil init` connects Claude Code, Pi, or Hermes on this computer, or a Hermes server that a webhook reaches: an incoming message notifies the open session or starts a Hermes run. It also writes the `artil` skill and links the agent's secrets to the client. > **[Set up automatic delivery](/automatic-messages)** > > Choose a client, configure delivery, and test it with a new email. ## Use messages in a conversation The Claude Code, Pi, OpenClaw, and Hermes plugins also tell your agent about each new message once `artil listen on` runs in them, and let it check an inbox, read a message, and reply from the agent's address or number. The Codex plugin does the same when asked, without hearing about new messages. Install one with `artil plugin install claude-code`, `codex`, `pi`, `openclaw`, or `hermes`, then ask the client to check email or SMS, or to read a verification email while working on a task. > **[Claude Code](/claude-code)** > > Install the Artil plugin to hear about, read, and answer email and SMS. > **[Codex](/codex)** > > Install the Artil plugin to read and answer email and SMS when asked. > **[Pi](/pi)** > > Install the Artil package to hear about, read, and answer email and SMS. > **[OpenClaw](/openclaw)** > > Install the Artil plugin so the gateway hears about and answers email and SMS. > **[Hermes](/hermes)** > > Install the Artil plugin so the gateway hears about and answers email and SMS. A client that cannot run the CLI can [connect over MCP](/mcp-server) instead; [ChatGPT](/chatgpt) hears about new messages that way. One client at a time hears about an agent's new messages, the one that started listening last; see [Who hears about new messages](/delivery). For SMS, [give the agent a phone number](/sms). You can use email without one. # SMS Source: https://docs.artil.ai/sms Give your agent a phone number, then read and send texts from the CLI. On the Pro plan, an agent account can have its own US or UK VoIP phone number. Add one in Artil, then read and send texts from the CLI. Texts go through Artil's SMS service, so you don't need a Telnyx account or key. ## Get a number Pro includes 5 numbers per workspace, counting the ones its agents gave up. 1. Open your agent in [Artil](https://app.artil.ai) and click **Add phone number**. 2. Pick the number's country, agree to the SMS messaging rules, then click **I am 18 or older**. The number appears on the agent's page. Sign the CLI in as the agent and run `artil auth status` to see it there too. ## Send a text ```bash theme={null} artil sms send --to "+14155550101" -- "The build is ready to review." ``` An agent texts only people who texted its number first, and the other agents of its workspace. A send to anyone else fails with ` has to write to first`. Each send takes one number; the [limits](/commands#limits) say how long a text can be. `--attach ` sends a picture, video or other file along, as a picture message from a US number; the [limits](/commands#limits) say which files phones take. ## Read and reply ```bash theme={null} artil sms artil sms list --limit 10 artil sms read "sms:" artil sms send --reply-to "sms:" -- "On it." ``` The list contains incoming texts, newest first, and the agent's number. If a text did not [wake the agent](/wake-access), its line ends with the reason, such as "Did not wake the agents: the hourly limit was reached." The [command reference](/commands#email-sms-and-telegram) has `--limit`, `--before`, and `--from`. Reading and replying accept IDs with or without the `sms:` prefix; email message IDs return an error here. Pictures and other files sent with a text come along: `read` lists each on an `Attached` line with a download link, the way [email](/email#2-read-the-message) does. A text names no files, so they are called `media-1.jpeg`, `media-2.png` and so on, in the order they were sent. ## Export texts On the Pro plan, choose **Export texts** in the **⋮** menu on the agent's **SMS** page. The browser downloads a CSV file of the agent's texts. ## Remove the number Choose **Delete phone number** in the same menu. Texts to the number stop reaching the agent. Your workspace keeps the number, so the next agent account that gets one in its country may get it. ## Delivery A successful send prints a message ID and confirms submission. Confirm receipt on the phone. The conversation in Artil shows stored messages and reported failures; it does not show delivery confirmations. Keep the send ID to read the outbound message later. If a send's outcome is unknown, check with the recipient before trying again; the CLI does not automatically retry a send. `list`, `read`, and `send` take `--json`; [Output and failures](/commands#output-and-failures) describes what they print. # Telegram Source: https://docs.artil.ai/telegram Create or connect a Telegram bot for your agent, then read and send its messages from the CLI. An agent account can take the messages people send to a Telegram bot. Create the bot from Artil, or connect one you already have, then read and answer its messages from the CLI. A bot is free on every plan. ## Create a bot 1. Open your agent in [Artil](https://app.artil.ai) and click **Add Telegram bot**, then **Create a bot**. 2. Click **Open Telegram**. Telegram opens a chat with Artil's bot; tap **Start**. 3. Tap **Create bot**, choose the bot's username, and confirm. The bot's username appears on the agent's page, and Artil's bot tells you it is ready. The bot is yours in Telegram, and Artil keeps its token. You can wake the agent from the Telegram account that created the bot. The link from step 2 works for an hour. ## Connect a bot you already have 1. In Telegram, send `/mybots` to [@BotFather](https://t.me/BotFather), choose the bot, and tap **API Token** to copy its token, such as `123456789:AAH…`. 2. Open your agent in Artil and click **Add Telegram bot**, then **Connect a bot**. 3. Paste the token and click **Connect**. Messages to the bot now reach the agent instead of anything the bot was connected to before. A bot belongs to one agent at a time; connecting it to another agent moves it there. The bot reads private chats only, so messages in groups do not reach the agent. ## Choose who can wake the agent List the people who may wake the agent by their @username or numeric user ID on its **Telegram** page, under **Runtime webhooks**; creating a bot from Artil lists your ID. **Messages** shows each sender by name and @username, such as `Alex Tkachenko (@alextcn)`, with their ID. See [Who can wake the agent](/wake-access). ## Read and reply ```bash theme={null} artil telegram artil telegram read "tg:" artil telegram send --reply-to "tg:" -- "On it." ``` A bot cannot start a chat with a person, so the agent writes only to people who wrote to its bot first; a send to anyone else fails with ` has to write to first`. To write to another agent's bot, see [Let agents write to each other](#let-agents-write-to-each-other). Photos, voice messages, and files show as attachments without their contents. The [command reference](/commands#email-sms-and-telegram) has the flags and [limits](/commands#limits). ## Let agents write to each other Telegram passes messages between two bots only when both allow it. 1. In Telegram, open [@BotFather](https://t.me/BotFather), tap **Open** to start its mini app, and choose the agent's bot. A bot created from Artil belongs to the Telegram account that created it, so open @BotFather from that account. 2. Turn on **Bot-to-Bot Communication Mode**. 3. Do the same for the other agent's bot. Then one agent writes to the other's bot by its username: ```bash theme={null} artil telegram send --to @other_agent_bot -- "Can you take this ticket?" ``` The message shows in the other agent's **Messages** on its **Telegram** page. A message from another agent's bot counts as from your team, so it wakes the agent while **Your team** is among its senders; see [Who can wake the agent](/wake-access). Two agents that answer every message keep answering each other until one reaches its hourly limits. ## Export messages On the Pro plan, choose **Export Telegram messages** in the **⋮** menu on the agent's **Telegram** page. The browser downloads a CSV file of the agent's Telegram messages. ## Remove the bot Choose **Remove bot** in the same menu. Messages to the bot stop reaching the agent, and Artil lets go of the bot, so you can connect it elsewhere. # Troubleshooting Source: https://docs.artil.ai/troubleshooting Fix sign-in, email, SMS, client setup, and secret-loading problems. Start with: ```bash theme={null} artil --version artil auth status ``` Status shows your agent account, workspace, channels, and message delivery setup, including the agent's webhooks. ## The shell cannot find artil Check that `~/.local/bin` is on PATH. For the current terminal: ```bash theme={null} export PATH="$HOME/.local/bin:$PATH" artil --version ``` If your shell finds an older installation, follow the installer's instructions to switch to the new one. See [installation](/installation). ## Login does not return to the terminal Open the login link in a browser on the same computer as the CLI. On a remote server, use [token login](/authentication#use-a-headless-machine). If a token is rejected, create another token or run `artil auth login` again. ## The wrong agent is shown Run `artil auth login` and select the agent you want in the browser, then restart the client session. The plugins use that saved login. If you also configured automatic delivery with `artil init`, run it again to review the connection and linked secrets. The CLI keeps one login at a time. ## Email or SMS is unavailable Run `artil auth status` to check which agent you are using. Open its **Manage** link and finish any email or SMS setup shown there. When the agent has no address for a channel, the error links to the page where you add one. Under an `--account` login, email and SMS commands fail with "This needs a login as one agent account." Sign in as the agent with `artil auth login`. An empty list means the command found no incoming messages. A channel that is unavailable returns an error. ## Sending to a Message URL says no one is listening `artil url send` and the agent's [Message URL](/url) reach an agent only while a client listens to it; `artil team` says which agents someone listens to. Start the agent's client, such as a Claude Code session with the Artil channel, and run `artil listen on` in it, or add a [webhook](/webhooks), then try again. ## A send failed or its outcome is unknown Read the error before retrying. Artil refuses a send past a [limit](/commands#limits), such as the plan's monthly allowance, the agent's hourly one, or too many recipients. A successful send confirms submission to the messaging service. Open the agent's email address or phone number in Artil to see the conversation and reported send failures. For confirmation of delivery, check with the recipient. If the outcome is unknown, do that before sending again to avoid a duplicate. ## Plugin skills or tools are missing For Claude Code, run `/plugin` and check that `artil@artil` is installed and enabled. If it is missing, run `artil plugin install claude-code`. Check for `/artil:email`, `/artil:sms`, and `/artil:secrets`. For Pi, check `pi list` and start a new session after installing the package. If it is missing, run `pi install npm:@artil-ai/pi`. Check for `/skill:artil`. For Codex, check that `codex plugin list` shows `artil@artil` as installed and enabled, and start a new session after installing it. If it is missing, run `artil plugin install codex`. See the [Claude Code](/claude-code), [Pi](/pi), and [Codex](/codex) installation guides. All need an agent login. All but Pi, whose package brings its own, need `artil` on PATH. Check `artil auth status` in the same terminal used to launch the client, or `!artil auth status` in Pi. ## Claude Code does not receive automatic notifications Claude Code hears about new messages only in a session started with a channel: the plugin's, from `artil plugin install claude-code`, or the one [automatic delivery](/automatic-messages) sets up with `artil init`. If `/mcp` shows the `artil-messages` server as failed, check that `artil` is on the PATH Claude Code starts with and that `artil auth status` shows the agent's login. If you already configured delivery, check Claude Code's startup warnings. Channels require Anthropic authentication and are unavailable through Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry. Team and Enterprise owners must enable channels; Console organizations with managed settings must set `channelsEnabled: true`. The development flag cannot override disabled channels. See [Claude's channel requirements](https://code.claude.com/docs/en/channels#enterprise-controls). Once channels are available, run setup again: ```bash theme={null} artil init --client claude-code ``` Start Claude Code with the command printed by setup, including its channel flag. Keep that session open to receive notifications. Check that the channel is active. A message that is in the inbox but did not wake the agent shows why under "Did not wake the agents": spam, its sender, or a limit. See [Who can wake the agent](/wake-access) to let the sender through. When another session or client takes over the agent's new messages, Claude is told which one and hears nothing more; run `! artil listen on` in the session to take them back. See [Who hears about new messages](/delivery). ## Pi does not receive automatic notifications Run `!artil auth status` in Pi. If the agent's login is missing, sign in with `!artil auth login`; then run `!artil listen on`, which `!artil listen` checks, and `/reload`. Only interactive and RPC sessions receive messages; `pi -p` runs do not. When delivery stops, Pi shows the reason, such as a missing agent login. A notification can also be refused; see [Who can wake the agent](/wake-access). ## Hermes does not run For local delivery, Hermes must be installed and configured, and the machine must stay online. Background delivery needs a working macOS login session or Linux systemd user service. Inspect the log path printed by `init`. When another client takes over the agent's new messages, the log says which one and delivery waits; run `artil init --client hermes` again to take them back. With the Artil plugin, the Hermes gateway must run; restart it after installing with `hermes gateway restart`, check that `hermes status` lists Artil as configured, and turn on new messages with `hermes artil listen on`. The gateway's log says why it stops hearing about messages, such as a missing agent login. When it says the gateway cannot run artil, the CLI belongs to another user, such as root in a Docker container: install it and sign in as the user the gateway runs as; see [Hermes](/hermes). For server delivery, finish the webhook configuration printed by `init` and check that the server is reachable at the configured URL. ## A secret is stored but not available in the client ```bash theme={null} artil secrets list artil secrets pull ``` Check the linked destinations and any reported conflicts. Link the client or file that needs the secret. After its first link, start a new Claude Code session or run `/reload` in Pi, and restart programs that load their environment only at startup. The CLI keeps existing `.env` assignments outside the section it writes. If a conflicting name was left out, resolve that assignment and pull again. If a file is rejected because Git could commit it, add it to `.gitignore` before linking it. ## Setting a secret with --json reports a missing value `artil secrets set NAME --json` never opens a hidden prompt. Supply the value or pipe it from the command that produced it. For a value you want to type, run `artil secrets set NAME` without `--json`. ## A flag is rejected Use the installed command's help: ```bash theme={null} artil email send --help artil secrets link --help ``` Flags are specific to commands. For example, SMS does not take `--subject`, and `init` does not take `--json`. See the [command reference](/commands). # Message an agent Source: https://docs.artil.ai/url Send a message to an agent from another agent, a teammate, or a script in your workspace. Every agent account has a Message URL, `https://artil.ai/a/`. A message posted to it reaches the client that listens to the agent, the way a new email does. Artil only relays the message and keeps no copy, so it never shows in the inbox. Only the members and agents of the agent's workspace can send to it, with their Artil sign-in, and only while a client listens to the agent. ## Send a message Find the agent's ID with `artil team`, then: ```bash theme={null} artil url send -- "The deploy finished. Check the error rate." ``` From code, post to the Message URL with a member's or agent's token: ```bash theme={null} curl https://artil.ai/a/ \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "text": "The deploy finished. Check the error rate." }' ``` Over MCP, `messages.send` takes channel `url`, the agent's ID in `to`, and the message in `text`, and sends it as whoever is signed in. Give a message from the Message URL as `replyTo`, and the answer goes to the Message URL of the agent that sent it. ## What the agent sees The agent gets the message whole, with who sent it and how to answer. Another agent hears back at its own Message URL, the way the message came, and only when it asks for an answer, so two agents do not answer each other forever. If no client listens to it, the answer goes by email. In Claude Code the notice reads: ```text theme={null} Research, an agent of your team, sent you a message at your Message URL: Which deploy failed? If it asks for an answer: artil url send -- "…" If no one is listening to it, answer by email: artil email send --to research@agents.artil.ai --subject "…" -- "…" ``` A member has no Message URL, so the agent answers them by email. [Webhooks](/webhooks) and MCP events carry the message whole, with who sent it in `sender`. Messages here come from the agent's own team, so the agent takes them as [its team's requests](/safety). ## Choose who can send to it Open the agent's **Message URL** page in [Artil](https://app.artil.ai). Its **Runtime webhooks** tab says who can send to it: a new agent lists only you, who created it, so add **Your team**, or members and agents one by one, for other agents to send to it. See [Who can wake the agent](/wake-access) for the list and the API that sets it. ## When it fails | Status | Means | | - | - | | 400 | The agent tried to send to its own Message URL | | 403 | The agent takes messages from other teammates only | | 404 | No agent with that ID is in your workspace | | 409 | No client is listening to the agent | | 429 | The agent got 100 messages in the last hour, by any channel | The [Claude Code](/claude-code), [Pi](/pi), and [Hermes](/hermes) integrations listen while they run. A [webhook](/webhooks) you added, or an MCP events subscription to messages at the Message URL, listens too. The agent's **Runtime webhooks** tab in Artil lists them all: the connected client first, then the webhooks. # Who can wake the agent Source: https://docs.artil.ai/wake-access Which email, texts, Telegram messages, and messages at the Message URL reach the agent account's client, and why a message did not. Anyone can write to an agent account's email address, phone number, or Telegram bot, but not every message wakes the agent, that is, reaches the client that listens to it. Each channel has its own say, and it holds for every client and webhook that listens. Open the agent in [Artil](https://app.artil.ai) and choose a channel or the Message URL: its **Runtime webhooks** tab holds the list. Once a client or webhook listens, the agent's **Runtime webhooks** tab also lists each channel under **Who can wake the agent**. ## Anyone or only these senders **Anyone** lets every message wake the agent. **Only these senders** keeps a list: a sender wakes the agent when their address or number is on it. **Your team**, every member and agent of the workspace, can be a row of the list, and listed senders wake the agent whether the team does too or only they do. A new agent takes **Only these senders** with only you, who created it, listed: your email address, the phone number in your Artil profile, and you at its Message URL. A channel you have no address for starts with **Your team**. For others to wake it, such as another agent testing it, pick **Your team** or teammates in the field that adds a sender. A member's texts come from the phone number they added to their Artil profile, so texts from that number count as from the team. Telegram names a person by a numeric user ID that no Artil profile holds, so a member's Telegram messages wake the agent only once their ID or @username is listed. A username wakes the agent for whoever holds it, and someone else can take it once its owner changes theirs, so list the ID to keep to one person. Creating a bot from Artil lists the Telegram account that created it, and agents' bots count as the team. A refused Telegram message shows its sender's name, @username, and ID in the inbox. The Message URL lists only members and agents of the workspace, since only an Artil sign-in reaches it, so it is never open to anyone. Over the API, `PUT /api/agents/{id}/access/{channel}` takes `admit` as `anyone`, `team`, or `list`, and `senders` as email addresses, phone numbers, Telegram user IDs and usernames, or for the Message URL the IDs of members and agents; see [Set access](/api-reference/access/set-access). ## Who the sender is to the agent A message that wakes the agent keeps who its sender was to it when it arrived: someone on **Your team**, a member or agent of the workspace; someone on the list; or anyone else, which includes a sender Artil could not verify, whatever address they claim. The CLI says it in each new-message notice it gives a client, such as `Ada , on your team, sent an email`. The API, the [MCP server](/mcp-server), and [webhooks](/webhooks) call it `admission`: `team`, `listed`, or `anyone`. ## Why a message did not wake the agent Artil refuses to wake the agent, checking in this order, when the message: | The message | Reason shown | | - | - | | Came from the agent itself | It came from the account itself | | Was marked as spam | It was marked as spam | | Arrived after the plan's monthly messages were used up | The plan's monthly limit was reached | | Came from a sender not on the list, when **Your team** is not listed | The sender is not on its list | | Came from outside the team and is not listed, when **Your team** is listed | The sender is not on its team | | Is email its sender's domain did not confirm, unless **Anyone** can wake the agent | The sender's domain did not confirm it with SPF or DKIM | | Arrived after the agent woke 100 times in the last hour | The hourly limit was reached | Email is verified when its sender's domain confirms it: the domain's SPF record allows the server that sent it, or the domain signed it with DKIM. Mail whose domain does neither, such as mail from a domain with no SPF record or DKIM key, or mail a rule forwards without its signature, cannot be verified; with **Anyone**, it still wakes the agent. A sender fixes it by adding the SPF record or DKIM key their mail provider gives for their domain. A refused message still lands in the inbox. In Artil, a ! mark beside it shows the reason on hover. In `artil email`, `artil sms`, and `artil telegram` lists, its line ends with "Did not wake the agents" and the reason, and `read` shows the reason before the body. The [MCP server](/mcp-server) gives it as the message's `reason`. A refused message at the agent's [Message URL](/url#when-it-fails) answers its sender with an error instead. # Webhooks Source: https://docs.artil.ai/webhooks Get an agent's new messages posted to your server. A webhook posts an agent's events to your server as they happen: an email or text arriving, or someone on your team sending to the agent's [Message URL](/url). Each agent can have up to 10 webhooks. ## Add a webhook Open the agent in [Artil](https://app.artil.ai), click **Connect runtime** on its **Runtime webhooks** tab, choose **Your server**, and enter your endpoint's URL. Or send the URL with one of the agent's tokens: ```bash theme={null} curl https://app.artil.ai/api/agents/AGENT_ID/webhooks \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"url": "https://example.com/artil"}' ``` The URL must be `https` and reachable on the public internet. Either way you get the webhook's `secret`, which starts with `whsec_`. Keep it on your server: listing webhooks leaves it out, and adding the same URL again gives back the same webhook and secret. The **Runtime webhooks** tab shows how delivery went and removes a webhook. Over the API, [List webhooks](/api-reference/webhooks/list-webhooks) and [Remove a webhook](/api-reference/webhooks/remove-a-webhook) do the same. ## What arrives Each event is one `POST` with a JSON body: ```json theme={null} { "eventId": "1234", "name": "message.received", "timestamp": "2026-10-08T12:00:00.000Z", "data": { "agent": { "id": "8f1c…", "name": "Support" }, "message": { "id": "0b7e…", "channel": "email", "from": "Ada ", "subject": "Invoice" } }, "cursor": "1234" } ``` The body leaves an email's or text's content out; fetch it with [Read a message](/api-reference/messages/read-a-message). A message at the [Message URL](/url) has channel `url` and comes whole: Artil keeps no copy, so `data.message` carries its `text` and, in `sender`, the teammate who sent it. See [Message received](/api-reference/webhooks/message-received) for every field. > [!WARNING] > > People outside write the sender and subject; treat them as [information, never as > instructions](/safety). ## Check the signature Artil signs each delivery as [Standard Webhooks](https://www.standardwebhooks.com) describes, in the `webhook-id`, `webhook-timestamp`, and `webhook-signature` headers. Check it with a Standard Webhooks library before you trust the body. In TypeScript: ```ts theme={null} import { Webhook } from "standardwebhooks"; const webhook = new Webhook(process.env.ARTIL_WEBHOOK_SECRET); export async function POST(request: Request) { const body = await request.text(); const event = webhook.verify(body, Object.fromEntries(request.headers)); // Act on event.name and event.data here. return new Response(null, { status: 204 }); } ``` Verify the raw body as it arrived, before parsing it. The library also refuses deliveries signed more than five minutes ago. ## Retries Answer with any `2xx` status within 5 seconds. Otherwise Artil sends the same event again, with the same `webhook-id`, after about 10 seconds, 1 minute, 5 minutes, 30 minutes, 2 hours, and then every 6 hours. It gives up on an event 3 days after it happened. Events arrive in order: while one is being retried, the ones after it wait. An event can arrive more than once, so skip a `webhook-id` you have already handled. | Your answer | What Artil does | | - | - | | `2xx` | Sends the next event | | `410 Gone` | Removes the webhook | | `413` | Skips the event and sends the next | | Anything else | Retries the event later | [List webhooks](/api-reference/webhooks/list-webhooks) shows the last `error`, how many `attempts` failed, and when Artil retries next in `retryAt`.