Bring your own bot
Any outside AI bot can join your island as a bot agent. It stands on the island, shows in the @ menu, and answers when someone messages or @mentions it in Sessions.
Bots run on your subscription or API key. ACRE pays for no model and never resells one.
There are three ways in, and there’s no special setup per brand:
| Kind | For | You give ACRE |
|---|---|---|
| API bot | Grok, OpenAI, OpenRouter, Groq, Together, Mistral, or any OpenAI-compatible API | A base URL, a model, and your API key |
| Webhook bot | Your own bot service (“Muse”, “Dots”, anything) | A URL that ACRE POSTs signed messages to |
| MCP agent | Claude Code, Codex, Cursor, or your own MCP client | Nothing. It connects itself (see Connect your chats) |
To add one, open the Agents desk, then Bring a bot, then pick a card. Only a host or co-host can add, change or remove bots. Teammates can see them and message the ones that are open to the team.
Worked example: Grok
Section titled “Worked example: Grok”- Make an API key at console.x.ai.
- In ACRE, go to Agents desk, then Bring a bot, then API bot. Pick xAI Grok as the provider.
- The preset fills in the base URL
https://api.x.ai/v1and the modelgrok-4.7. You can type any model your key can use instead. - Paste your key. Leave the name as Grok or rename it, then pick a colour.
- Optionally add a persona, for example “Dry wit. Answers in two lines.”
- Click Add bot. ACRE sends Grok a hello and shows you the reply. That’s the Test.
- In Sessions, type
@grok what ships friday?. Grok answers in the session, labelled as a bot.
Behind the scenes, ACRE makes this call on your key:
POST https://api.x.ai/v1/chat/completionsAuthorization: Bearer <your key>Content-Type: application/json
{ "model": "grok-4.7", "max_tokens": 512, "stream": false, "messages": [ { "role": "system", "content": "You are Grok, a bot agent on the ACRE island …" }, { "role": "user", "content": "what ships friday?" } ] }Presets
Section titled “Presets”Each preset fills in the base URL and a default model. These were checked against each provider’s own docs on 2026-10-13. Models change often, so the model field is always yours to edit.
| Preset | Base URL | Default model |
|---|---|---|
| xAI Grok | https://api.x.ai/v1 |
grok-4.7 |
| OpenAI | https://api.openai.com/v1 |
gpt-6-luna (sends max_completion_tokens) |
| OpenRouter | https://openrouter.ai/api/v1 |
~openai/gpt-sol-latest |
| Groq | https://api.groq.com/openai/v1 |
llama-3.3-70b-versatile |
| Together AI | https://api.together.ai/v1 |
MiniMaxAI/MiniMax-M3 |
| Mistral | https://api.mistral.ai/v1 |
mistral-large-latest |
| Other | any https://…/v1 |
you type it |
Local models (Ollama, LM Studio)
Section titled “Local models (Ollama, LM Studio)”Ollama (http://localhost:11434/v1) and LM Studio (http://localhost:1234/v1) run on your own computer. ACRE’s server never calls addresses on your machine or your network; that’s the SSRF rule below. For now, local models aren’t available as API bots on a hosted ACRE. A relay through the desktop app is planned.
Until then, you have two options:
- Expose the local server over your own
httpstunnel and use Other. - Run an MCP agent on that machine instead.
A self-hosted ACRE can reach local models by setting ALLOW_PRIVATE_AGENT_ENDPOINTS=true. Only do this if you run the server yourself.
Webhook bots
Section titled “Webhook bots”Give ACRE an https URL. ACRE makes a signing secret and a bot token and shows each of them once. Copy both into your bot.
What ACRE sends
Section titled “What ACRE sends”POST https://your-bot.example.com/acreContent-Type: application/jsonX-Acre-Event: messageX-Acre-Delivery: dlv_inv_… (unique per delivery)X-Acre-Timestamp: 1791900000 (unix seconds)X-Acre-Signature: v1=<hex HMAC-SHA256(secret, "<timestamp>.<raw body>")>{ "type": "message", "version": 1, "id": "dlv_inv_…", "createdAt": "2026-10-13T15:04:05.000Z", "island": { "id": "isl_…", "name": "Kiln" }, "bot": { "id": "muse", "name": "Muse" }, "from": { "id": "usr_…", "name": "Bo Chen" }, "session": { "thread": "ask-muse" }, "message": { "runId": "inv_…", "text": "hi muse" }, "history": [ { "prompt": "earlier question", "reply": "earlier answer" } ], "context": "About the island: … (only when the host turned on shared notes)", "reply": { "inline": "Answer 200 with {\"reply\": \"text\"}", "later": { "url": "https://useacre.dev/api/bots/reply", "method": "POST", "auth": "Bearer <bot token>", "body": { "runId": "inv_…", "reply": "text" } } }}How your bot replies
Section titled “How your bot replies”- Right away: answer
200with{ "reply": "your text" }. You have 30 seconds. - Later: answer
202, or200with{ "deferred": true }. Then send the reply once it’s ready:
POST https://useacre.dev/api/bots/replyAuthorization: Bearer <bot token>Content-Type: application/json
{ "runId": "inv_…", "reply": "Thought about it: yes." }Send { "runId": "…", "error": "what went wrong" } instead to fail the message. Each message takes one reply. A second reply gets 409.
Verify every delivery (Node)
Section titled “Verify every delivery (Node)”import { createHmac, timingSafeEqual } from 'node:crypto';
const seen = new Set(); // keep delivery ids for at least 5 minutesexport function verify(secret, rawBody, headers) { const ts = headers['x-acre-timestamp'], sig = headers['x-acre-signature'], id = headers['x-acre-delivery']; if (!ts || !sig || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false; // stale const want = Buffer.from(`v1=${createHmac('sha256', secret).update(`${ts}.${rawBody}`).digest('hex')}`); const got = Buffer.from(sig); if (want.length !== got.length || !timingSafeEqual(want, got)) return false; // forged if (seen.has(id)) return false; // replayed seen.add(id); return true;}Sign over the raw body bytes, not re-serialised JSON. A host can rotate the secret or the token from the bot in the Agents desk (New signing secret, New bot token); the old one stops working at once.
Safety and cost
Section titled “Safety and cost”- Your key stays yours. API keys, webhook URLs, signing secrets and bot tokens are encrypted at rest with the server’s
ISLAND_TOKEN_SECRET. ACRE never sends them back to your browser. You only ever see the last four characters. - Only the address you gave. Every call is
https, goes only to public internet addresses, and pins the address it checked. That blocks private networks, loopback, cloud metadata and DNS rebinding. Redirects aren’t followed. - Timeouts and rate limits. Each call times out after 30 seconds. A bot answers up to 6 messages a minute, and an island’s bots together answer up to 30.
- Cost caps. You set the max tokens per reply (16 to 4,096, default 512) and messages a day (default 100). The daily count resets at 00:00 UTC. When a bot reaches its cap, it stops answering until the next day.
- Who can message it. New bots start as Only me. Switch to The whole team to share one.
- Privacy. Bots read nothing from the island unless a host turns on Read the island’s shared notes. Even then, a bot only gets notes shared with the whole island. Nobody’s “only me” notes reach a bot.
- Always labelled. A bot is always shown as a bot, and its instructions tell it never to claim to be a person.