Email Signal Routing

Route trade signals into email-worker via Mailgun, Cloudflare® Email Routing, or an authenticated JSON post. There is no IMAP inbox poll.

This page

The email-worker turns an inbound email or a JSON body into a trade signal and forwards it to trade-worker. It does not log into a mailbox. Cloudflare® Workers cannot open IMAP sockets, and EMAIL_HOST, EMAIL_USER, and EMAIL_PASS are reserved names that this worker does not read.

Three ingress paths:

PathRouteAuth
MailgunPOST /webhookHMAC-SHA256. Headers Mailgun-Signature, Mailgun-Timestamp, Mailgun-Token. Secret MAILGUN_API_KEY.
Cloudflare® Email Routingthe worker email handlerCloudflare® delivers the message to the binding.
Internal JSONPOST /email-signalX-Internal-Auth-Key only. This route does not check a Mailgun signature.
bash
hoox secrets set email-worker MAILGUN_API_KEY
hoox secrets set email-worker INTERNAL_KEY_BINDING

Worker name is required. The CLI prompts for each value. INTERNAL_KEY_BINDING is normally generated by hoox setup — only set it here if you are wiring email-worker by hand.

Point the Mailgun route at https://<email-worker-host>/webhook. For Email Routing, bind the destination address to this worker in the Cloudflare® dashboard.

Warning

Use a dedicated Mailgun route or routing address for signals. The scheduled handler only drops the in-memory pattern cache. It does not scan an inbox.


🔎 2. Configuring Regex Parsing Rules in KV

Plain-text bodies are matched against patterns in CONFIG_KV. JSON bodies that already contain exchange, action, and symbol skip the regex.

bash
# Coin symbols pulled out of a plain-text body
hoox config kv set email:coin_pattern "(BTC|ETH|SOL|LINK|AVAX)"

# Buy / sell words in that body
hoox config kv set email:action_pattern "(LONG|SHORT|CLOSE|BUY|SELL)"

The Parsing Mechanism

When a Mailgun webhook or Email Routing message arrives:

  1. The worker reads the plain body (body-plain / stripped-text for Mailgun).
  2. If the body is JSON with exchange, action, and symbol, that object is the signal.
  3. Otherwise the regex keys above extract the coin and the action. LONG / BUY become LONG; SHORT / SELL become SHORT.
  4. The worker forwards the signal to trade-worker over the TRADE_SERVICE binding.

JSON body with testnet mode

If the email body is structured JSON (instead of free-text), optional fields are forwarded to trade-worker:

json
{
  "exchange": "bybit",
  "action": "LONG",
  "symbol": "BTCUSDT",
  "quantity": 0.001,
  "test": true
}

"test": true requests exchange testnet execution (Binance/Bybit). Plaintext extraction never sets test — always live. See Test Trading.


🛡️ 3. What actually authenticates the message

Mailgun calls are rejected unless MAILGUN_API_KEY is set and the HMAC of timestamp + token matches Mailgun-Signature, inside a ±15 minute window. POST /email-signal ignores that signature and requires the mesh internal key. There is no SPF or DKIM check inside the worker, and there is no sender allowlist key.


🧪 4. Testing the JSON path

POST /email-signal is the path you can call without a Mailgun account. Replace the host and key with your local hoox dev port (unpinned workers start at 8800) and mesh key:

bash
curl -sS -X POST http://localhost:8800/email-signal \
  -H "content-type: application/json" \
  -H "X-Internal-Auth-Key: $INTERNAL_KEY_BINDING" \
  -d '{"exchange":"bybit","action":"LONG","symbol":"BTCUSDT","quantity":0.001,"test":true}'

"test": true requests exchange testnet execution where the exchange allows it. Plain-text extraction never sets test. See Test Trading.

Tail the isolate while you send:

bash
hoox monitor logs email-worker

🔗 Next Steps