Channels
Msg9
OpenClaw channel plugin for msg9.io — turns a msg9 agent inbox into an OpenClaw channel (poll-based inbound, reply_to auto-close, multi-account)
Install
npm install
npm
README
# openclaw-msg9
OpenClaw channel plugin for [msg9.io](https://msg9.io) agent inboxes. Turns a
msg9 agent inbox into an OpenClaw channel: inbound mail drives the agent,
agent replies are delivered back on the original msg9 thread.
- **Channel id:** `msg9` (package / plugin id: `openclaw-msg9`)
- **Transport:** poll-based inbound (`agent.unread()`), direct outbound (`agent.send()`)
- **Content:** text only — msg9 has no attachments, so no `sendMedia`
## Configuration
Single account (`channels.msg9`):
```json5
{
channels: {
msg9: {
enabled: true,
address: "[email protected]", // your agent inbox address
apiKey: "msg9_sk_...", // the inbox's agent key — a secret
// baseUrl: "https://api.msg9.io", // optional, this is the default
pollIntervalMs: 3000, // inbox poll interval (default 3000)
allowFrom: ["[email protected]"], // optional sender whitelist
},
},
}
```
Multi-account:
```json5
{
channels: {
msg9: {
enabled: true,
address: "[email protected]",
apiKey: "msg9_sk_...",
accounts: {
side: {
address: "[email protected]",
apiKey: "msg9_sk_...",
pollIntervalMs: 1000,
},
},
},
},
}
```
Or via environment variables (default account only):
`MSG9_ADDRESS`, `MSG9_API_KEY`, `MSG9_BASE_URL`.
CLI-style setup tokens follow the same shape as other channels:
`--token "address,apiKey[,baseUrl]"`, or `--use-env` to read the env vars above.
## Addressing
Outbound targets use the `msg9:` prefix:
```
msg9:[email protected]
```
`normalizeTarget` strips the prefix; a bare address containing `@` is also
recognized as a msg9 id (`<user>@<pod>.msg9.io`).
## Inbound semantics
Each poll sweep drains the inbox's unread folder:
1. **Dedupe** by `message_id` (bounded in-memory set, reset at 1000 entries).
The platform is at-least-once; the set covers a gateway run.
2. **allowFrom** — when the list is non-empty and the sender is not listed,
the message is `markRead` and skipped: it never reaches the agent, but
stays in the owner's unprocessed folder for manual review. Empty allowFrom
allows everyone — configure it before enabling text commands.
3. **markProcessed** — allowed messages are marked processed (implies read,
idempotent) *before* dispatch, so the agent is driven at-most-once even if
dispatch or delivery fails.
4. **Dispatch** — the standard channel pipeline
(`activity.record` → `resolveAgentRoute` → envelope → `finalizeInboundContext`
→ `dispatchReplyWithBufferedBlockDispatcher`). The `deliver` callback sends
via `agent.send({ ..., reply_to: <original message_id> })`.
### reply_to auto-close
Sending a reply with `reply_to` set makes the server close the original
message automatically. Combined with the mark-processed-first policy, replied
threads are closed exactly once, and messages that never get a reply remain
visible in the unprocessed folder.
## Outbound
`outbound.sendText` resolves the account, strips the `msg9:` prefix, and calls
`agent.send`. A core-provided `replyToId` (the inbound `MessageSid`) is mapped
to msg9 `reply_to`. Returns `{ channel: "msg9", messageId }`. There is no
`sendMedia` — msg9 carries text bodies only.
## Credential hygiene
`apiKey` is a `msg9_sk_...` agent key: anyone holding it can read and send as
that inbox. Keep it in OpenClaw's config/secret stores with restricted file
permissions (0600), never in a repo or a chat log. If a key leaks in
plaintext, rotate it at the platform before reusing the inbox.
## Requirements
- Node.js >= 18
- OpenClaw plugin API `>=2026.3.23-1` (built against SDK `2026.4.26`)
## Development
```sh
npm install
npm run build # tsc → dist/
npm test # vitest (no network — the client factory is mocked)
npm run pack:dry-run
```
## Roadmap
- **Webhook inbound** — replace polling with tenant-key webhook subscriptions
(`registerHttpRoute` + signature verification) once a webhook is provisioned.
- **Inbox tools** — expose `list_inbox` / `mark_processed` style tools to the
agent via `registerTool`.
- **ClawHub publish** — publishing pipeline (`openclaw.plugin.json` already
carries the required `compat`/`build` metadata; schema is fully flat, no
`$ref`/`$defs`).
channels
Comments
Sign in to leave a comment