Email, iMessage, Linq & automation
Email (IMAP/SMTP & Gmail Push), macOS iMessage, Linq SMS/RCS, and webhook/MQTT/Notion automation channels.
This page is the reference for the channels that connect Revka to email, native messaging, and automation systems rather than to chat apps: Email over IMAP/SMTP, Gmail Push over Google Cloud Pub/Sub, native iMessage on macOS, Linq for iMessage/RCS/SMS, the Generic Webhook adapter, MQTT as a Standard Operating Procedure (SOP) event trigger, and the Notion task-queue channel. Reach for these when your agent needs to live in an inbox, drive a phone number, fan in IoT events, or be wired into an arbitrary external system.
Every channel here is configured under [channels_config] in ~/.revka/config.toml. Edit the file directly or run revka onboard --channels-only, then apply changes by restarting the daemon. For the shared trait model, delivery modes, and allowlist semantics, see Channels overview. If this is your first channel, read Connect a messaging channel first.
At a glance
Section titled “At a glance”| Channel | Receive mode | Public inbound port? | Allowlist field |
|---|---|---|---|
| Email (IMAP/SMTP) | IMAP IDLE | No | allowed_senders |
| Gmail Push | Pub/Sub webhook | Yes | allowed_senders |
| iMessage | macOS AppleScript bridge | No (macOS only) | allowed_contacts |
| Linq | Webhook (push) | Yes | allowed_senders |
| Generic Webhook | HTTP server | Usually yes | — |
| MQTT | Broker subscriber | No (outbound connection) | — |
| Notion | Polling | No | — (access controlled by Notion integration sharing) |
The webhook-based channels (Gmail Push, Linq) need your gateway to be reachable over public HTTPS — see Expose your gateway with a tunnel. The Generic Webhook channel runs its own HTTP listener on a configurable port. The rest connect outbound and work behind NAT.
Email (IMAP/SMTP)
Section titled “Email (IMAP/SMTP)”The Email channel listens with IMAP IDLE for real-time push and sends over SMTP. Both legs use TLS (validated with rustls). It parses multipart MIME, gates senders by address or domain, and tracks replies through the Message-ID and In-Reply-To headers. Per RFC 2177, the IDLE connection is recycled on a ~29-minute cycle so it stays alive behind middleboxes. No public inbound port is required.
Configure email
Section titled “Configure email”[channels_config.email]imap_host = "imap.example.com"imap_port = 993imap_folder = "INBOX"smtp_host = "smtp.example.com"smtp_port = 465smtp_tls = trueusername = "[email protected]"password = "email-password"from_address = "[email protected]"idle_timeout_secs = 1740allowed_senders = ["*"]default_subject = "Revka Message"| Field | Type | Default | Meaning |
|---|---|---|---|
imap_host | string | — | IMAP server hostname. Required. |
imap_port | number | 993 | IMAP port (implicit TLS). |
imap_folder | string | "INBOX" | Mailbox folder to watch. |
smtp_host | string | — | SMTP server hostname. Required. |
smtp_port | number | 465 | SMTP port (implicit TLS). |
smtp_tls | bool | true | Use TLS for the SMTP connection. |
username | string | — | Account username for IMAP and SMTP auth. Required. |
password | string | — | Account password. Required. Use an app password for Gmail/Outlook. |
from_address | string | — | Address used in the From header on outbound mail. Required. |
idle_timeout_secs | number | 1740 | IDLE reconnect interval (1740 s = 29 min). Alias: poll_interval_secs. |
allowed_senders | array | — | Email addresses or domains allowed to interact. [] denies all; ["*"] allows everyone. |
default_subject | string | "Revka Message" | Subject line used when the agent starts a new thread. |
Gmail Push (Pub/Sub)
Section titled “Gmail Push (Pub/Sub)”The Gmail Push channel replaces IMAP polling with Google Cloud Pub/Sub push: when a watched mailbox changes, Google posts a notification to your gateway, and Revka uses the Gmail History API to fetch the new messages. It watches the labels you configure (default INBOX) and auto-renews the Gmail watch subscription before its 7-day expiry.
This is a webhook channel: Google must reach POST /webhook/gmail on your gateway, so you need a public HTTPS URL.
Configure Gmail Push
Section titled “Configure Gmail Push”[channels_config.gmail_push]enabled = truetopic = "projects/my-project/topics/gmail-topic"oauth_token = "" # or GMAIL_PUSH_OAUTH_TOKEN env varlabel_filter = ["INBOX"]allowed_senders = ["*"]webhook_url = "https://example.com/webhook/gmail"webhook_secret = "" # or GMAIL_PUSH_WEBHOOK_SECRET env var| Field | Type | Default | Meaning |
|---|---|---|---|
enabled | bool | false | Master switch for the channel. |
topic | string | — | Full GCP Pub/Sub topic path (projects/<id>/topics/<name>). Required. |
oauth_token | string | — | Gmail API OAuth2 token. Falls back to GMAIL_PUSH_OAUTH_TOKEN. |
label_filter | array | ["INBOX"] | Gmail labels to watch. |
allowed_senders | array | — | Email addresses or domains allowed to interact. [] denies all; ["*"] allows everyone. |
webhook_url | string | — | Public URL registered as the Pub/Sub push subscription endpoint. |
webhook_secret | string | — | Bearer token used to authenticate incoming push requests. Falls back to GMAIL_PUSH_WEBHOOK_SECRET. |
Google Cloud setup
Section titled “Google Cloud setup”To wire up the push path:
- Create a GCP project and a Pub/Sub topic (its path is your
topicvalue). - Grant the Gmail push service account
[email protected]the Pub/Sub Publisher role on that topic, so Gmail is allowed to publish change notifications. - Create a push subscription on the topic whose endpoint is your
webhook_url(https://<gateway>/webhook/gmail). - Provide an
oauth_tokenwith Gmail API scope so Revka can register the watch and read message history.
Revka registers the Gmail watch and auto-retries renewal as it nears expiry. You can run Gmail Push to supplement or replace the IMAP Email channel for Gmail accounts.
iMessage (macOS AppleScript bridge)
Section titled “iMessage (macOS AppleScript bridge)”The iMessage channel is macOS only. It polls the local Messages database at ~/Library/Messages/chat.db for new messages and sends replies by driving the Messages app through osascript (AppleScript). It reads both the modern attributedBody typedstream blobs used on Ventura and later, and the legacy text column on older macOS.
Configure iMessage
Section titled “Configure iMessage”[channels_config.imessage]allowed_contacts = ["*"]| Field | Type | Default | Meaning |
|---|---|---|---|
allowed_contacts | array | — | Contact identifiers (phone numbers / Apple IDs) allowed to interact. [] denies all; ["*"] allows everyone. |
Full Disk Access requirement
Section titled “Full Disk Access requirement”Reading ~/Library/Messages/chat.db is protected by macOS privacy controls. The process that runs Revka must be granted Full Disk Access, or the channel cannot see incoming messages.
- Open System Settings → Privacy & Security → Full Disk Access.
- Add the binary or app that launches Revka. If you run Revka from a terminal, grant Full Disk Access to that terminal application (for example Terminal or iTerm); if you run it as a background service, grant it to the Revka binary itself.
- Quit and relaunch the granted application so the new permission takes effect, then restart the daemon.
Linq (iMessage/RCS/SMS)
Section titled “Linq (iMessage/RCS/SMS)”Linq routes iMessage, RCS, and SMS through the Linq Partner V3 API, so a single phone number can reach Apple, Android, and carrier-SMS recipients without a Mac. It runs in webhook mode: Linq posts inbound messages to POST /linq on your gateway, and Revka sends replies to https://api.linqapp.com/api/partner/v3. Inbound requests are authenticated with an HMAC-SHA256 signature plus a 300-second timestamp-freshness check. You need a public HTTPS URL for the webhook.
Configure Linq
Section titled “Configure Linq”[channels_config.linq]api_token = "linq-partner-api-token"from_phone = "+15551234567"signing_secret = "optional-signing-secret"allowed_senders = ["*"]| Field | Type | Default | Meaning |
|---|---|---|---|
api_token | string | — | Linq Partner API token. Required. |
from_phone | string | — | Sender phone number in E.164 format. Required. |
signing_secret | string | — | HMAC signing secret for inbound webhook verification. Overridden by REVKA_LINQ_SIGNING_SECRET. |
allowed_senders | array | — | Sender phone numbers (E.164) allowed to interact. [] denies all; ["*"] allows everyone. |
Webhook ingress and signing
Section titled “Webhook ingress and signing”Set your Linq dashboard’s webhook URL to the gateway ingress path:
POST /linqContent-Type: application/jsonWhen a signing secret is set, the gateway verifies the HMAC-SHA256 signature on each request and rejects payloads whose timestamp is more than 300 seconds old (replay protection). The secret resolves from the environment first, then config:
export REVKA_LINQ_SIGNING_SECRET="your-signing-secret"Linq prevents self-echo by dropping messages where is_from_me is set or direction is "outbound"; both the legacy payload shape and the newer sender_handle.is_me form are recognized. Inbound media parts with an image/* MIME type are converted to [IMAGE:url] markers so the agent sees the attachment. For the full set of gateway ingress endpoints, see Webhook ingress.
Generic Webhook
Section titled “Generic Webhook”The Generic Webhook channel is a universal adapter for any system that can POST JSON and (optionally) receive a callback. It runs its own HTTP listener on a configurable port, receives messages at a configurable path, and posts replies to an outbound URL. Inbound requests can be authenticated with an HMAC-SHA256 signature.
Configure the webhook
Section titled “Configure the webhook”[channels_config.webhook]port = 8080path = "/webhook"send_url = "https://target.example.com/callback"send_method = "POST"auth_header = "Bearer my-token"secret = "my-hmac-secret"| Field | Type | Default | Meaning |
|---|---|---|---|
port | number | — | Port the webhook HTTP server listens on. Required. |
path | string | "/webhook" | Path that accepts inbound message POSTs. |
send_url | string | — | Outbound callback URL for replies. Omit for receive-only operation. |
send_method | string | "POST" | HTTP method used for outbound replies (e.g. POST, PUT). |
auth_header | string | — | Value sent as the Authorization header on outbound requests. |
secret | string | — | Shared HMAC-SHA256 secret for verifying inbound signatures. No verification is performed when unset. |
Payload shapes
Section titled “Payload shapes”Inbound POST body:
{"sender": "user@example", "content": "hello", "thread_id": "opt-thread"}Outbound reply body (sent to send_url):
{"content": "reply text", "thread_id": "opt", "recipient": "user@example"}The optional thread_id carries through the round trip, letting external systems thread a conversation. If you do not set secret, no signature is required — always use HTTPS and a secret for any production deployment.
MQTT (SOP event trigger)
Section titled “MQTT (SOP event trigger)”MQTT is different from the other channels on this page: it is not a chat channel. It subscribes to an MQTT broker and routes incoming messages into the Standard Operating Procedure (SOP) engine rather than the agent chat loop. Use it to fan IoT and automation events into SOP workflows. It connects outbound to the broker, so no inbound port is needed, and it supports TLS (mqtts://), authentication, and per-topic QoS.
Trigger an SOP from MQTT
Section titled “Trigger an SOP from MQTT”In an SOP’s SOP.toml, declare an MQTT trigger with the topic to match and an optional JSONPath condition:
[[triggers]]type = "mqtt"topic = "sensors/pressure"condition = "$.value > 85"The topic supports the same + and # wildcards as a subscription, and condition is a JSONPath expression that fails closed — a missing or invalid payload does not match. For the full SOP trigger model (webhook, cron, peripheral, manual) and execution modes, see SOP reference: syntax, triggers & execution.
Notion (task queue)
Section titled “Notion (task queue)”The Notion channel is a task queue, not a real-time chat channel. It polls a Notion database for rows whose status property is set to a pending value, dispatches each row’s prompt to the agent, and writes the result back into the row. On startup it can reset stale “running” rows so a crash mid-task is recoverable, and it caps how many tasks run concurrently.
Configure Notion
Section titled “Configure Notion”[channels_config.notion]api_key = "secret_..."database_id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"poll_interval_secs = 5status_property = "Status"input_property = "Input"result_property = "Result"max_concurrent = 4recover_stale = true| Field | Type | Default | Meaning |
|---|---|---|---|
api_key | string | — | Notion integration secret. Required. |
database_id | string | — | Target database ID. Required. |
poll_interval_secs | number | 5 | How often the database is polled for pending rows. |
status_property | string | "Status" | Property holding task status. Both select and status property types are supported. |
input_property | string | "Input" | Property holding the prompt text. |
result_property | string | "Result" | Property the agent’s result is written into. |
max_concurrent | number | 4 | Maximum tasks processed in parallel. |
recover_stale | bool | true | On boot, reset rows stuck in “running” so they are reprocessed. |
Allowlist semantics
Section titled “Allowlist semantics”The sender-gating field differs per channel, but the rule is the same everywhere:
| Channel | Field |
|---|---|
| Email, Gmail Push, Linq | allowed_senders |
| iMessage | allowed_contacts |
- An empty list (
[]) denies all senders. - A single
"*"entry allows all senders. - Otherwise only listed identities are accepted. For email, an entry can be a full address or a bare domain.
The Generic Webhook and MQTT channels have no allowlist field: the webhook is gated by its HMAC secret, and MQTT trust comes from the broker connection and (optionally) username/password. The Notion channel has no sender allowlist — access is controlled by which Notion database the integration is shared with.
Start with the permissive ["*"] to confirm delivery end to end, then tighten to explicit identities.
Related pages
Section titled “Related pages”- Channels overview — the channel trait, delivery modes, and allowlist model.
- Connect a messaging channel — the channel model and
revka channelcommands. - Webhook ingress — all gateway ingress endpoints (
/linq,/webhook/gmail,/webhook) and their signing rules. - Expose your gateway with a tunnel — give Gmail Push and Linq a public HTTPS URL.
- SOP reference: syntax, triggers & execution — SOP triggers (including MQTT) and execution modes.
- Variables, expressions & triggers — workflow triggers that pair with these channels.
- Config: channels, tools & integrations — every channel config key in one place.
- macOS update & uninstall — managing the Revka process on macOS where iMessage runs.