Skip to main content

Postbox

Email for agents. Inbound and outbound flow through postbox.* tables and the agent-postbox Cloud Function.

Tables

TablePurpose
postbox.domainsVerified sending domains (SPF/DKIM/DMARC state).
postbox.addressesagent_id → address mapping with aliasing.
postbox.messagesEvery inbound and outbound row.
postbox.inbound_rulesPer-agent move/forward/auto-reply rules.
postbox.outbound_suppressionsHard-bounce block list.
postbox.send_quotasPer-agent per-window rate limits.
postbox.inbound_webhooksSigned callback endpoints.

Inbound flow

  1. MTA POSTs parsed MIME to /ingest with x-agentpack-key.
  2. postbox.ingest() resolves to → agent_id, scores for prompt injection, inserts, and applies matching rules.
  3. Raw MIME is uploaded to the postbox-mime bucket; the row is stamped via stamp_body_ref().
  4. Registered webhooks fire through Cloud Functions fetch with an HMAC signature and an x-agentpack-webhook-timestamp header.

Outbound flow

  1. Agent calls /send. postbox.enqueue_outbound() checks quotas and suppressions, writes a row with direction='out', status='queued'.
  2. MTA polls postbox.next_outbound_batch(), ships, and calls record_send_result or record_bounce.
  3. is_hard_bounce(reason) auto-suppresses addresses on terminal codes.

Prompt injection scoring

postbox.detect_prompt_injection(body) returns a 0–1 float. Every inbound row carries injection_score and quarantined = (score > 0.8). Agents that auto-act on mail should gate behavior on the score.

Routes

MethodPathPurpose
POST/ingestIngest parsed MIME (bridge key).
POST/allocAllocate an address for an agent.
POST/sendSend outbound mail.
POST/listList messages (direction, limit, since).
POST/mark_readMark ids read.
POST/rule/putCreate/update an inbound rule.
POST/webhook/registerRegister a signed inbound webhook.
POST/webhook/rotateDual-secret rotation.

Bounce handling

Postmark (or any bounce-emitting provider) POSTs to the postboxBounce webhook with an HMAC-SHA256 signature of the raw body using the shared POSTBOX_BOUNCE_SECRET. The handler:

  1. Verifies the signature in constant time and rejects mismatches with 401 — never an explanation that would aid forgery.
  2. Looks up the outbound row by messageId (or MessageID) and writes a bouncedAt / bounceType / bounceDetail triple.
  3. Updates the recipient's reputation in owners/{uid}/emailReputation/{address}. HardBounce and SpamComplaint block future sends to that address for 30 days.
  4. Appends a postbox.bounce audit row.

This is the single source of truth for "don't email this person again." Deliverability dashboards read from emailReputation; the sender-side send call checks it before debiting the quota.

Retention

agentpack_postbox_retention (03:17 UTC daily) hard-deletes rows past postbox_retention_days (default 90). MIME blobs are reaped alongside.