catchall.in for AI agents

You are setting up registrations, trials or monitoring for a person, and every service on the way wants an email address it can verify. This page is the walkthrough: real mailboxes on your human's own domains, unlimited addresses that need no creating, and confirmation mail read over POP3S — protocols your runtime's standard library already speaks.

What you get

Every address at the issued subdomain already exists. Invent a fresh one per registration — signup-7f2q@mail.example.com, billing-x1@mail.example.com — nothing to create, and it all lands in one mailbox you poll.

The domain is your human's own zone, so sender trust and control stay with them; we take one subdomain and never touch the apex. Credentials are issued once at creation — no OAuth dance, no browser session to keep alive.

There is deliberately no HTTP endpoint for reading mail — messages never enter the API layer at all. Pickup is plain POP3S (995) or IMAPS (993). The management API is documented in full at /api.

Set up in three calls

1. Create the domain. Two DNS paths. manual works with any DNS: the answer returns mailbox credentials plus the TXT challenge and MX record to set. cf_token writes the records for you — use it when your human connected their Cloudflare account on the Integrations page (omit the token field) or handed you a scoped bootstrap token (the exact three permissions are listed at /api).

curl -X POST https://catchall.in/v1/domains \
  -H "Authorization: Bearer ck_your_key" \
  -H "Idempotency-Key: create-example-com-1" \
  -H "Content-Type: application/json" \
  -d '{"zone_name": "example.com", "path": "manual"}'

2. Set the records, then verify. Relay the TXT and MX to whoever runs the DNS — possibly you. Then POST /v1/domains/{id}/verify and poll GET /v1/domains/{id} until state is active. DNS moves in minutes, not milliseconds: poll at tens of seconds, not in a tight loop. On the cf_token path there is nothing to set — the domain activates on its own.

3. Fetch the mail. Host is mx_host on the domain object (also the MX target). There is no parser on our side — the message reaches you verbatim, extracting the code is your regex:

import poplib, re, time

HOST = "mx1.catchall.in"                    # mx_host from GET /v1/domains/{id}
USER, PASSWORD = "<username>", "<password>" # from the create response - shown once

deadline = time.time() + 180
while time.time() < deadline:
    box = poplib.POP3_SSL(HOST, 995)
    box.user(USER); box.pass_(PASSWORD)
    count = len(box.list()[1])
    if count:
        body = b"\n".join(box.retr(count)[1]).decode(errors="replace")
        m = re.search(r"\b\d{4,8}\b", body)
        if m:
            print(m.group()); box.quit(); break
    box.quit()
    time.sleep(5)   # one POP3 session per source IP - poll sequentially

Ground rules

Retries are safe by construction — use that. Every POST that returns a secret takes an Idempotency-Key; repeating the same key and body replays the original answer for 15 minutes, afterwards it answers 410 and only a rotation issues a new secret. Rate limits answer 429 with Retry-After in seconds — honor it.

One POP3 session per mailbox from one source IP. Parallel streams come from more mailboxes, not more connections: the Pro tier issues five mailboxes on five subdomains under one parent domain.

Mail lives 72 hours, then it is deleted on our side. Fetch promptly; what you downloaded is yours.

Mailboxes only receive. Sending is not part of the service at all, and the acceptable-use policy applies to the traffic your registrations create elsewhere. If a check fails or a domain degrades, the panel and GET /v1/domains/{id} show the state and the reason — surface them to your human instead of retrying blind.

Cheat sheet

Dense on purpose — keep it in your context:

base_url:     https://catchall.in
auth:         Authorization: Bearer <key from your human>
mail_pickup:  POP3S :995 / IMAPS :993 (no HTTP mail endpoint - by design)
addresses:    any local part @ <label>.<zone> - nothing to pre-create
mail_ttl:     72 hours, then deleted
money:        usd_micro, integer ($10 = 10000000); errors: {"error": "machine_code"}
pricing:      standard $10 / 30 days - pro $15, five mailboxes on five subdomains
idempotency:  Idempotency-Key on domain create / verify / rotate-password / key create
full_docs:    https://catchall.in/api