Domains, mailboxes & aliases
Every workspace gets a <subdomain>.knobs.io mail domain at creation; admins can add a custom domain and verify its DNS.
Addresses are workspace-wide. An address is a local part that exists as a mailbox row on every verified domain — claiming jd creates jd@ at each one, and disabling it releases all of them. A local part belongs to exactly one member or group across all domains, so availability is checked workspace-wide, never per domain. When a new domain verifies, every existing address is fanned out onto it automatically.
All routes are workspace-scoped and accept a session JWT or API key. Reads are member-level; domain and mailbox mutations require admin.
Domains
Section titled “Domains”| Endpoint | Description |
|---|---|
GET /v1/workspaces/{wid}/domains |
List the workspace’s mail domains |
POST /v1/workspaces/{wid}/domains |
Add a custom domain {domain} (admin) → the DNS records to publish |
GET /v1/workspaces/{wid}/domains/{did} |
Domain + records + per-check verification status |
POST /v1/workspaces/{wid}/domains/{did}/verify |
Re-check DKIM/SPF/DMARC/MX + ownership now (admin) |
DELETE /v1/workspaces/{wid}/domains/{did} |
Remove a custom domain; disables its mailbox rows (admin) — addresses keep working at the remaining domains |
Add and verify a custom domain
Section titled “Add and verify a custom domain”curl -X POST https://api.knobs.io/v1/workspaces/{wid}/domains \ -H "Authorization: Bearer knak_..." \ -H "Content-Type: application/json" \ -d '{"domain": "acme.com"}'The 201 response includes the DNS records to publish (verification TXT, DKIM, SPF, DMARC, MX). Publish them, then:
curl -X POST -H "Authorization: Bearer knak_..." \ https://api.knobs.io/v1/workspaces/{wid}/domains/{did}/verifyThe response returns the domain with each check’s current status. Verification is also re-polled automatically. Sending and mailbox creation require a verified domain (409 failed_precondition otherwise).
checks carries two advisory fields worth reading. dmarcReporting is narrower than dmarc: it means the published DMARC record actually carries a rua= address, without which receivers are forbidden from ever sending an aggregate report. And checks.warnings is a list of {record, code, message} findings about the SPF/DMARC records — code is a stable vocabulary you may branch on (spf_multiple_records, spf_not_listed, spf_unreachable, spf_malformed, spf_too_many_lookups, dmarc_no_reporting, dmarc_multiple_records), while message is complete, human-readable remediation copy meant to be displayed as-is. Warnings never block verification, and a check that reads the zone successfully rebuilds the list from what it found — so the list is empty once the zone is fixed. If a DNS lookup itself fails, that record’s previous result and warning are carried forward rather than cleared (a lookup outage is not evidence the zone is healthy) and checks.lastError names the failure.
Calling /verify on an already-verified domain re-runs only these advisory checks — it never changes the domain’s status and never re-triggers address provisioning, so it is safe to use as a “re-check my DNS” button at any time.
Mailboxes
Section titled “Mailboxes”A mailbox row binds a local part on one domain to a member (or a group). One address = one row per verified domain, so writes are workspace-scoped and reads can still be filtered per domain.
| Endpoint | Description |
|---|---|
GET /v1/workspaces/{wid}/domains/{did}/mailboxes |
List the mailbox rows on one domain |
GET /v1/workspaces/{wid}/mailboxes |
List workspace mailbox rows; ?mine=true narrows to your active send-from addresses (the compose From picker) |
POST /v1/workspaces/{wid}/mailboxes |
Create {localPart, uid?} (admin) — claims the local part on every verified domain, atomically; returns {items}, one row per domain. uid omitted assigns it to the caller |
DELETE /v1/workspaces/{wid}/mailboxes/{mid} |
Disable the address (admin) — the whole local part, every domain. Never hard-deleted, existing messages still reference it |
POST /v1/workspaces/{wid}/addresses:reconcile |
Admin repair: re-assert that every active local part exists on every verified domain → {created}. Idempotent |
curl -H "Authorization: Bearer knak_..." \ "https://api.knobs.io/v1/workspaces/{wid}/mailboxes?mine=true"Per-user aliases
Section titled “Per-user aliases”Members can claim additional local parts as their own aliases when the workspace-wide toggle is on. An alias is not domain-scoped — there is no domainId on any of these calls.
| Endpoint | Description |
|---|---|
GET /v1/workspaces/{wid}/aliases |
{enabled, domains, aliases} — one entry per local part you own, each carrying its addresses[] across every verified domain, exactly one flagged primary |
POST /v1/workspaces/{wid}/aliases |
Claim {localPart} on every verified domain → {items}, one row per domain |
POST /v1/workspaces/{wid}/aliases:check |
Workspace-wide availability {localPart} → {status} — one of available, reserved, mine, taken, invalid (always 200) |
DELETE /v1/workspaces/{wid}/aliases/{mid} |
Remove one of your own aliases (never your primary); any of its mailbox ids releases it on every domain |
PATCH /v1/workspaces/{wid}/settings/email-aliases |
Admin toggle {enabled} for the whole workspace |
{ "enabled": true, "domains": [{ "domainId": "…", "domain": "yourcompany.com", "default": true }], "aliases": [ { "localPart": "press", "primary": false, "addresses": [ { "mailboxId": "…", "domainId": "…", "domain": "yourcompany.com", "address": "press@yourcompany.com", "status": "active" } ] } ]}Check, then claim
Section titled “Check, then claim”curl -X POST https://api.knobs.io/v1/workspaces/{wid}/aliases:check \ -H "Authorization: Bearer knak_..." \ -H "Content-Type: application/json" \ -d '{"localPart": "press"}'{ "status": "available" }curl -X POST https://api.knobs.io/v1/workspaces/{wid}/aliases \ -H "Authorization: Bearer knak_..." \ -H "Content-Type: application/json" \ -d '{"localPart": "press"}'Claiming a reserved local part returns 422 invalid_argument; claiming while the feature is off returns 409 failed_precondition. A local part held by anyone else on any domain returns 409 — the claim is atomic, so nothing is written when one domain collides.
Related
Section titled “Related”- Mail — sending and reading messages on these addresses.
- Workspaces & members — the default-domain switch lives on
PATCH /v1/workspaces/{wid}.