Custom email domains
By default your agent's email lives on a Caspian address. Bring your own subdomain and it sends and receives as kernel@agents.example.com instead, DKIM-signed as your domain. You register the subdomain, add a few DNS records, and connect once it verifies.
Use a dedicated subdomain (e.g. agents.example.com), never your root domain. Root domains are rejected, and pointing an active mail domain at Caspian would disrupt your existing email.
How it works
- Register the subdomain with
add_domain(). Caspian returns the DNS records to add: three DKIMCNAMEs and oneMX. - Add the records at your DNS provider, exactly as returned.
- Poll
get_domain(id)untilstatusisactive. DNS propagation can take a few minutes. - Connect with
connect_email(domain=…, username=…)to get an address on your domain.
1. Register the subdomain
add_domain() registers the subdomain and returns its id plus the DNS records you need to add.
from caspian_sdk import CommClient
client = CommClient()
domain = client.add_domain("agents.example.com")
print("Domain id:", domain["id"])
print("Status:", domain["status"]) # "pending" until DNS verifies
for record in domain["dns_records"]:
# three DKIM CNAMEs + one MX
print(record["type"], record["name"], "->", record["value"])import { CommClient } from "caspian-sdk";
const client = new CommClient();
const domain = await client.addDomain("agents.example.com");
console.log("Domain id:", domain.id);
console.log("Status:", domain.status); // "pending" until DNS verifies
for (const record of domain.dns_records as any[]) {
// three DKIM CNAMEs + one MX
console.log(record.type, record.name, "->", record.value);
}A root domain such as example.com is rejected. Pass a subdomain you control and that isn't already handling mail.
2. Add the DNS records
Add every record exactly as returned at your DNS provider. You get four in total:
| Type | Purpose | Count |
|---|---|---|
CNAME | DKIM signing keys: authenticate outbound mail as your domain | 3 |
MX | Routes inbound mail for the subdomain to Caspian | 1 |
Prefer a bulk import? The full zone file is available for the domain id:
curl -s https://api.trycaspianai.com/v1/domains/{id}/zone-file \
-H "Authorization: Bearer $COMM_API_KEY"3. Poll until active
DNS changes take time to propagate. Poll get_domain(id) until status is active before connecting.
import time
while True:
d = client.get_domain(domain["id"])
if d["status"] == "active":
print("Domain verified.")
break
print("Still", d["status"], "- waiting for DNS...")
time.sleep(15)while (true) {
const d = await client.getDomain(domain.id);
if (d.status === "active") {
console.log("Domain verified.");
break;
}
console.log("Still", d.status, "- waiting for DNS...");
await new Promise((r) => setTimeout(r, 15000));
}Propagation is out of Caspian's hands; it depends on your DNS provider's TTLs. Verification usually completes within minutes but can take longer.
4. Connect email on your domain
Once the domain is active, connect email with domain= to put the address on your domain, and username= to pick the exact local part.
inbox = client.connect_email(
domain="agents.example.com",
username="kernel",
)
print(inbox["address"]) # kernel@agents.example.comconst inbox = await client.connectEmail({
domain: "agents.example.com",
username: "kernel",
});
console.log(inbox.address); // kernel@agents.example.comOutbound mail is now DKIM-signed as your domain, and replies route back through your handler like any other channel. username= works only on custom domains, and a taken local part returns a 409.
List your domains
list_domains() returns every domain registered under your project, with each one's current status.
for d in client.list_domains():
print(d["domain"], d["status"])for (const d of await client.listDomains()) {
console.log(d.domain, d.status);
}