Architecture
The whole system on one page. Read top-to-bottom: where requests enter, what gates they cross, what gets logged, and what providers we sit on top of.
┌────────────────────────────┐
│ Your Agent Code / LLM │
│ (uses SDK or MCP tools) │
└─────────────┬──────────────┘
│ Bearer ak_*
│ Idempotency-Key
│ x-correlation-id
▼
┌───────────────────────────────────────┐
│ Anima API (api.useanima.sh) │
│ ┌─────────────────────────────────┐ │
│ │ Auth → Rate-limit → Versioning │ │
│ │ Idempotency → Audit → Errors │ │
│ └─────────────────────────────────┘ │
└────┬───────┬───────┬───────┬──────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌────┐ ┌────┐ ┌────┐ ┌────────┐
│Mail│ │SMS │ │Vox │ │ Vault │
└─┬──┘ └─┬──┘ └─┬──┘ └───┬────┘
│ │ │ │
│ │ │ │
┌───────┘ │ └─────┐ │
│ │ │ │
▼ ▼ ▼ ▼
┌─────────┐ ┌──────────┐ ┌──────────────┐
│ AWS │ │ Twilio │ │ bw-serve + │
│ SES │ │ / │ │ Vaultwarden │
│ │ │ Telnyx │ │ + envelope │
└─────────┘ └────┬─────┘ └──────────────┘
│
│ TCPA + RND + ToD + cap-check
▼
┌─────────────────┐
│ Outbound dial │
│ (server-side │
│ gates passed) │
└─────────────────┘
Cross-cuts (every domain):
audit_events ← every action emits one row keyed by
correlation_id (workflow chain) +
request_id (per-call ID)
hard-cap ← MTD spend per meter checked before write;
block at 100% (or alert-only per setting)
trust-score ← signup gate: domain age / ASN / Stripe CVC
Out-of-band:
/v1/events/stream — SSE feed of every audit_events row,
consumed by 'am tail' and the dashboard
/v1/audit/events — REST query by correlation_id
mcp.useanima.sh — MCP server, 190+ tools across 6 domains
The API surface (api.useanima.sh)
One Fastify-based service handles every customer-facing API call. The middleware chain is fixed and runs in this order on every request:
auth— resolves theak_*/mk_*token to an org + agent.rate-limit— per-tier per-minute (Redis-backed sliding window) plus per-org per-second soft cap.X-RateLimit-*headers on every response.api-versioning— pins to v1; emitsX-API-Version.idempotency— POST/PUT/PATCH/DELETE replay cache forIdempotency-Keyretries.audit— every action emits a row toaudit_eventstagged withcorrelation_id.error-handler— RFC 7807 problem+json on failure.
See /api-reference for the wire contract.
Domain boundaries
Five domain modules behind the API surface:
- Mail — outbound SES, inbound parsing, DKIM/DMARC config. Custom domains via the dashboard.
- SMS — Twilio 10DLC + toll-free. Per-tier daily caps; opt-out keyword handling server-side.
- Voice (Vox) — Telnyx PSTN dial pipeline behind TCPA + RND + time-of-day gates. Tier-based cap on per-day calls.
- Vault — bw-serve + Vaultwarden under an encryption envelope. Server-side masking before responses leave the API.
- Platform — admin, audit, billing, trust-score. Cross-cutting concerns.
Server-side safety gates
Three voice gates run before the dial:
- TCPA — requires
consent_sourceon every outbound call. Missing or malformed → 403. - RND — Twilio Lookup against the FCC Reassigned Numbers Database. 30-day Postgres cache. Reassigned numbers never dial.
- Time-of-day — 8am–9pm local enforced from destination area code. Per-state stricter windows configurable.
Plus the cross-cutting hard cap with 80% / 90% / 100% notifications.
MCP surface (mcp.useanima.sh)
One URL, path-routed by domain. Same Postgres + provider stack as the REST API; a different protocol surface. 190+ tools across email, voice, vault, phone, platform, agent. anima_discover({intent}) for tool routing when the LLM doesn't know the exact name.
Audit chain (the data moat)
Every action emits one row to audit_events tagged with correlation_id (workflow ID) and request_id (per-call ID). Inbound email gets a synthetic correlation ID so reply chains link back to the trigger.
/audit renders the chain; /v1/events/stream feeds am tail via SSE.
What's NOT in this diagram
- Consolidated MCP routing — Cloud Run service
mcp-serverconsumes the same Anima API behind it. - Async workers (audit retention, baseline computation, anomaly detection, Stripe webhook ingest, auto-pay). They read/write the same Postgres but don't sit on the synchronous request path.
- Console (dashboard.useanima.sh) — separate Next.js app, calls the same API via oRPC.
- WebSocket bridge (used by the Chrome extension) — separate auth, separate channel; bypasses the REST middleware chain.