co
toa://contact
Embeddable contact forms, a per-site inbox.
OVERVIEW
contact turns any site’s contact form into a tidy, per-site inbox. Create a workspace for each site you run, drop a one-line embed on it, and every spam-filtered submission lands in that workspace — ready to read, archive and reply to. Access is single sign-on via toa://auth with per-user isolation; new messages can ping a Discord channel, replies go out over email, and an API-key path stays open for sites that prefer to post programmatically.
FEATURES
Drop-in embed
Paste one <iframe> on any site — no per-site backend, no CORS, nothing to install.
Themed to fit
Per-workspace embed designs — Console or Minimal, dark or light, transparent to inherit the host page.
Spam-filtered
Honeypot, rate limiting and Cloudflare Turnstile before anything is stored.
Manage, reply & alert
Per-site inbox to read, archive and reply, with Discord notifications on every new message.
WHAT'S NEW
v2.3.0
- Make the **Minimal** embed design visually distinct from Console — borderless, underline inputs, pill button and airy spacing (it read too close to the boxed Console look).
- The embed is now **transparent when framed** on another site, so it inherits the host page's background colour. Direct visits (e.g. the preview link) keep a solid background.
- Pin Node to 24.16.0 (`.node-version`) and add an `engines.node` floor, so builds and typechecks are reproducible across machines.
- Frontend: `pnpm typecheck` crashed under Node 24 — vue-tsc/@vue/language-core pinned at 2.x used the old Volar plugin API while Vue Router 5 ships 3.x-API volar plugins (`plugin is not a function`). Bumped `vue-tsc` to ^3.3.5.
v2.2.0
- Per-workspace embed appearance: pick a **Console** (terminal) or **Minimal** (clean) form design, each in **dark** or **light**, from the workspace's edit dialog. A public config endpoint lets existing `<iframe>` embeds adopt the chosen look automatically — no re-embedding needed.
v2.1.1
- Discord webhook notifications never fired: a workspace's `notify_webhook_url` was silently dropped when saved (it was missing from the update field allow-list), so it stayed unset and no notification could be sent. It now persists correctly.
API
tor@toa: ~ /contact --routes
/api/v1/messages/public
/api/v1/messages
/api/v1/workspaces