Overview

The AllternitBus API provides the runtime surface agents use to send and receive messages, store encrypted secrets, resolve connector credentials, and manage identity channels such as email, phone, and wallet addresses. All authenticated routes are mounted under /api/v1 and require a valid bearer token via the platform auth middleware. The public inbound webhook is mounted at /webhooks/photon and is called server-to-server by Photon.codes.
This namespace is served by the Allternit API data-plane node (http://127.0.0.1:8013 on the same machine, or your paired or provisioned node). The hosted Cloud API at https://api.allternit.com does not route it; it relays only agent sessions, canvases, office and beta research/sessions. See Cloud API.

Base URL

Authenticated surface:
Public webhook surface:
For local development:

Authentication

All authenticated routes require a valid bearer token in the Authorization header. The inbound Photon webhook is authenticated server-to-server by Photon.codes and does not carry a Clerk session. See Authentication for details.

Endpoints

Message inbox

Send a message

Delivers a message to the agent’s inbox. The route path is kept for backward compatibility with the Photon.codes integration.

Request body

Example request

Example response

List inbox messages

Returns messages for the agent, newest first.

Query parameters

Example request

Example response

Inbound webhook

Receive an inbound message

Public webhook called by Photon.codes when an inbound SMS or message arrives. The payload is routed to the agent whose identity channel matches the to phone number. No bearer token is required.

Request body

Example request

Example response

Secrets

Set a secret

Stores an encrypted secret value for the agent. Values are sealed at rest via token_crypto. If a secret with the same key already exists, it is overwritten.

Request body

Example request

Example response

Resolve secrets

Decrypts and returns values for the requested secret references. Required secrets that are missing or cannot be decrypted are reported in missing and errors.

Request body

Each reference:

Example request

Example response

Connectors

Resolve connector credentials

Resolves live credentials for the requested connector bindings. The route first looks up a connected connector_connections row for the caller, then falls back to legacy allternit_vault_credentials that belong to the agent and user.

Request body

Each binding:

Example request

Example response

Identity channels

Get identity channels

Returns the agent’s bound identity channels: email, phone, and wallet. Channels that have not been provisioned are returned as null.

Example request

Example response

Provision email

Provisions an email address for the agent on ALLTERNIT_BOT_EMAIL_DOMAIN (default bots.allternit.com): through Allternit’s cloud on a signed-in computer, or directly when the runtime has Allternit Mail’s admin key. See Agent email rail. When the mailflare agent-email rail is configured (ALLTERNIT_MAILFLARE_URL + ALLTERNIT_MAILFLARE_ADMIN_KEY), this creates a real mailbox on the mailflare worker, mints a mailbox-scoped send/read API key, seals it server-side, and stores the channel with provider "mailflare". The agent can then genuinely receive and (subject to approval) send internet email. When mailflare is not configured, the endpoint falls back to minting an address record only (provider "commrails", a stored value) with no send/receive transport. Provisioning is idempotent: re-provisioning an agent returns its existing channel. A platform address (provider mailflare) is owned by the server: a PATCH /api/v1/agents/:id that sends a different identityChannels.email.address keeps the stored one. To change it, release it and provision again (below). Switching the bot to its own mail account (provider other than mailflare / commrails) still saves the new address. To put the bot on a verified company domain, send { "localPart": "support", "domain": "acme.com" } (see Company domains). A domain whose records haven’t all checked returns 409 domain_not_verified; a bot that already has an address elsewhere returns 409 email_address_exists (release it first). The part before the @ comes from the optional body { "localPart": "ledger-bot" }: 3-40 lowercase letters, numbers and single hyphens (invalid values return 400 invalid_local_part; role names such as postmaster return 400 reserved_local_part). A requested name that is already taken returns 409 email_local_part_taken. Without localPart, the address is a slug of the bot’s name (ledger-bot), with -2, -3 and so on added only when that address is taken. The mailbox’s display name, which becomes the From name on outbound mail, is the bot’s name.

Example request

Example response

Change an agent’s address

Removes the agent’s mailbox (on a cloud-provisioned runtime, cloud-api also removes its key, its webhook and the relay route), then clears the address and the mailbox credentials from the agent. Reply settings (replyMode, replyAllowlist) stay. Mail sent to the old address stops arriving. Follow it with POST …/identity/email {"localPart": "<new name>"}. If the mailbox can’t be removed, the call returns 502 mailbox_not_removed and the agent keeps its address. The app’s email wizard and the bot’s Identity settings do both steps; when the new name is taken they provision the default address again and say which address the bot is on.

Agent email rail

Real inbound/outbound email for agents, backed by a per-installation mailflare worker (see services/mailflare/). Inbound mail arrives as an HMAC-signed webhook and is bridged into the agent’s Factory mail threads; outbound mail is held for human approval before it is sent.

Two ways a runtime gets bot email

Inbound mail is verified with the receiving mailbox’s own secret (cloud-provisioned) or the runtime-wide one (admin), before anything in the body is trusted; the bot is found by the event’s mailboxId, then the delivered-to address, then to. A cloud-provisioned bot’s direct replies (auto, auto_known) are sent with its own key and approved by the runtime itself once its review and autonomy rules allow them. A mailbox’s mail is never delivered to another runtime: each webhook is scoped to one mailbox. ALLTERNIT_BOT_EMAIL_BROKERED=0 turns the cloud mode off. cloud-api answers 503 bot_email_not_configured until ALLTERNIT_MAILFLARE_URL and ALLTERNIT_MAILFLARE_ADMIN_KEY are set on it.

Company domains

Bots can use a customer’s own domain ([email protected]) instead of bots.allternit.com. The domain’s mail runs through Allternit’s mail host, mx.allternit.com (Postfix plus services/mail-relay), not through Cloudflare: the customer’s domain never needs to be in Allternit’s Cloudflare account. The records (each comes back with ok: true | false after a check): Pointing a domain’s MX at mx.allternit.com sends all of that domain’s mail to the bots, so use a domain or subdomain (bots.acme.com) that doesn’t already receive mail elsewhere. A domain belongs to one Allternit account (409 domain_taken for anyone else); allternit.com and its subdomains are refused. On a signed-in computer the runtime calls cloud-api (PUT/GET/DELETE /api/v1/runtime-devices/me/bot-email/domains[/:domain], device credential), which records ownership in bot_email_domains (migration 062, applied by hand to prod) and calls Allternit Mail’s /api/v1/relay/domains/:host with the admin key. A runtime with the admin key calls that route itself. Allternit Mail (D1 migration 0012_relay_domains.sql, column domains.transport = 'relay') asks the relay to make the domain’s DKIM key and check DNS, marks the domain active once it verifies, and from then on:
  • Inbound: Postfix on mx.allternit.com accepts mail only for verified domains and existing bot mailboxes (live socketmap lookups to the relay, which asks the worker’s GET /api/v1/relay/recipient), and the relay posts it to the worker’s POST /api/v1/relay/inbound. From there it is the same path as any bot mail.
  • Outbound: mail from a relay domain goes to the relay’s POST /relay/send, which signs it with the domain’s DKIM key and queues it in Postfix (retries for up to two days).
Worker env: MAIL_RELAY_URL (https://mx.allternit.com/relay) and MAIL_RELAY_SECRET (32+ characters, the same value as the relay’s RELAY_SECRET). Without them, adding a domain answers 503 custom_domains_unavailable. The relay’s install script is services/mail-relay/deploy/install.sh.

Send email (approval-gated)

The message is not sent immediately. It is recorded as pending_approval and a review is requested on a Factory mail thread (mail:email-out-<id>). A human approves or rejects via POST /api/factory/mail/decide with { "thread": "<thread_id>", "approve": true|false }; approving triggers the actual send and writes a receipt with the provider message id. The decide response includes an email object for email threads.

Email rail status

Reports whether the mailflare rail is configured, the agent email domain, and whether the worker is reachable. Use this to diagnose installations.

Inbound webhook (server-to-server)

Public route called by the mailflare worker, authenticated by an X-Email-Platform-Signature HMAC-SHA256 header against ALLTERNIT_MAILFLARE_WEBHOOK_SECRET. message.inbound events are stored and bridged into the receiving agent’s Factory mail thread (mail:email-in-<agent>). Unknown recipients return 202 and are logged.

Provision phone

Assigns a phone number to the agent from the comma-separated pool in ALLTERNIT_BOT_PHONE_POOL. The first number not already assigned to another agent is chosen.

Example request

Example response

Cross-surface bridge

Bridge a session

Requests a cross-surface bridge for a session. The route path is kept for backward compatibility. The payload is echoed back with a bridged status.

Request body

Example request

Example response

Error codes