Skip to main content
The email-worker ingests trading signals from email sources, parses them, and forwards them to trade-worker for execution. It receives emails via Mailgun webhook (POST /webhook) or direct JSON POST (POST /email-signal). No IMAP/SMTP polling — Cloudflare Workers edge runtime does not support the Node.js net/tls modules required for IMAP. The worker also tracks signal ingestion metrics by forwarding event data to analytics-worker via service binding.

⚡ 1. Endpoints

Note: For the canonical endpoint directory with full request/response examples across all workers, see /docs/devops/api/endpoints.
The Mailgun webhook endpoint is /webhook, not /webhook/mailgun. The direct JSON endpoint is /email-signal, not /process.

🔐 2. Mailgun Signature Verification

When a POST arrives at /webhook, the worker validates the Mailgun signature before processing the email body:
  1. Reads Mailgun-Signature, Mailgun-Timestamp, Mailgun-Token from request headers
  2. Computes HMAC-SHA256 hex digest of timestamp + token using MAILGUN_API_KEY as the signing key
  3. Compares computed digest against the Mailgun-Signature header value
  4. Returns 401 Unauthorized if headers are missing or signature does not match
On success, the worker extracts the email body from the body-plain or stripped-text field of the Mailgun form-data payload and passes it to the signal parser.

📨 3. Signal Extraction

Two-phase parsing: JSON first, plaintext fallback.

Phase 1: JSON Parsing

parseEmailSignal() calls JSON.parse() on the body text. If the result contains exchange, action, and symbol fields, it returns a structured signal immediately:

Phase 2: Plaintext Fallback

If JSON parsing fails or required fields are missing, extractFromPlaintext() uses keyword matching:
  • extractField() scans for keyword: prefixes (e.g. exchange:, symbol:, action:)
  • Coin symbols and action keywords matched via regex from KV config (coinPattern, actionPattern)
  • normalizeExchange() resolves exchanges: binance, mexc, bybit
  • normalizeAction() maps: buy/longbuy, sell/shortsell
Example plaintext email body:

Zod Validation

Incoming JSON payloads are validated using Zod schemas:
  • EmailSignalSchema validates the signal structure (exchange, action as enum, symbol, quantity with default 100, optional price/leverage)
  • WebhookPayloadSchema validates the wrapper payload (subject, text, body — all optional)
  • Invalid payloads return 400 Bad Request with structured error
  • Extra fields are stripped via .strip()

Forwarding

Once parsed, the signal is sent to trade-worker via:
Analytics events are sent non-blocking via ctx.waitUntil():

Cloudflare Email Routing

The worker also exposes an email() handler that processes incoming emails via Cloudflare Email Routing. When an email arrives, postal-mime parses the raw MIME content, extracts the text body, and feeds it through the same parseEmailSignal() pipeline. Validated signals are forwarded to trade-worker with analytics tracking.

🔗 4. Bindings

Service Bindings

Send Email Binding

KV Namespaces


🔑 5. Secrets

All secrets are set via wrangler secret put <name>:

⚙️ 6. Environment Variables (Vars)


🗄️ 7. KV Configuration Keys

The worker loads signal parsing patterns from CONFIG_KV via loadSignalPatterns():

📊 8. Observability

Full observability enabled with 100% head sampling:
  • Head sampling rate: 1 (all requests sampled)
  • Log persistence: Enabled — logs stored for debugging and audit
  • Invocation logs: Enabled — full invocation records available
  • Smart Placement: Enabled for 30-60% latency reduction

🛠️ 9. Configuration (wrangler.jsonc)

Secrets (INTERNAL_KEY_BINDING, MAILGUN_API_KEY, EMAIL_HOST_BINDING, EMAIL_USER_BINDING, EMAIL_PASS_BINDING) are set via wrangler secret put <name> and are not present in the checked-in wrangler.jsonc.

🧪 10. Development

Config tracking: wrangler.jsonc is tracked in git.

🏗️ 11. Architecture Context

The email-worker is one of 10 workers in the Hoox service binding mesh:
  • Mailgun flow: Inbound email → Mailgun webhook POST → email-worker /webhook → trade-worker
  • Direct flow: Internal tooling → POST /email-signal (authenticated) → email-worker → trade-worker
  • Email Routing flow: Cloudflare Email Routing → email() handler → email-worker → trade-worker
  • Analytics: Every parsed signal sends { source, type, symbol, confidence } to analytics-worker
All active signal ingestion is via webhook, direct POST, or Cloudflare Email Routing.

Next Steps

Last modified on June 17, 2026