# Cold email infrastructure, end to end

Most outbound stacks buy the domain in one place, host DNS in another, send from a third, and discover the authentication was wrong when the reply rate is already zero. The pieces are not hard. The seams between them are.

Eight capabilities cover the whole path here: find a domain, register it, write the DNS, verify it for sending, check the authentication independently, create the inbox, send, and read what comes back. A ninth, free one lists what you already own, so an agent picking the work up later finds the inbox instead of recreating it.

## The pipeline

```
domain.search        →  what is available and what it costs
domain.register      →  buy it, with you as registrant
email.domain         →  verify it for sending, get the DNS records
domain.dns           →  write those records
email.inbox          →  the sending address, on that domain
email.inboxes        →  what you already own (free)
email.send           →  send, or reply in a thread
email.messages       →  replies, bounces and opens
```

## Step 1: a domain that is not your main one

Never send cold outbound from your primary domain. A deliverability problem on a sending domain costs you a campaign; the same problem on the domain your invoices and password resets go through costs you the company's email.

```bash
curl -X POST https://api.jell.run/v1/run \
  -H "Authorization: Bearer $JELL_API_KEY" \
  -d '{"capability": "domain.search", "input": {"name": "acme", "tlds": ["com", "io", "co"]}}'
```

Availability and price per TLD. Pick something close to your brand and readable when a recipient checks it: `getacme.com`, `acme-hq.com`, `tryacme.com`. Avoid a TLD with a bad sending reputation, and avoid anything that looks like a throwaway, because the recipient's spam filter has an opinion about that too.

```bash
curl -X POST https://api.jell.run/v1/run \
  -H "Authorization: Bearer $JELL_API_KEY" \
  -d '{
    "capability": "domain.register",
    "input": {
      "domain": "getacme.com", "years": 1, "privacy": true, "auto_renew": true,
      "registrant": {"first_name": "Alex", "last_name": "Rivera",
                     "email": "alex@acme.com", "country": "FR"}
    }
  }'
```

You are the registrant contact. The response carries the order, the expiry and the nameservers.

## Step 2: authentication, which is the whole game

```bash
curl -X POST https://api.jell.run/v1/run \
  -H "Authorization: Bearer $JELL_API_KEY" \
  -d '{"capability": "email.domain", "input": {"domain": "getacme.com"}}'
```

This returns a `domain_id`, a `status`, the exact DNS `records` to create and a `setup_url`. Write the records with `domain.dns`:

```bash
curl -X POST https://api.jell.run/v1/run \
  -H "Authorization: Bearer $JELL_API_KEY" \
  -d '{"capability": "domain.dns",
       "input": {"domain": "getacme.com", "records": [ ... from email.domain ... ]}}'
```

Three records decide whether your mail is read or filed:

- **SPF** lists which servers may send for the domain. One record, and only one. Two SPF records is a permanent fail, and it is the single most common misconfiguration.
- **DKIM** signs each message so the receiver can verify it was not altered. Missing DKIM is a soft fail at most providers and a hard one at some.
- **DMARC** tells receivers what to do when SPF or DKIM fails, and where to send reports. Start at `p=none` to collect reports without rejecting your own mail, then tighten to `quarantine` once the reports are clean.

Google and Yahoo both require authentication for bulk senders, and enforcement has only tightened. Unauthenticated cold mail does not land in spam, it frequently does not arrive at all.

## Step 3: inbox, send, read

Every inbox sits on a domain you verified in step 2. There is no shared-domain inbox: the domain is what carries your sending reputation, and it has to be yours.

```bash
curl -X POST https://api.jell.run/v1/run \
  -H "Authorization: Bearer $JELL_API_KEY" \
  -d '{"capability": "email.inbox",
       "input": {"name": "alex", "domain": "getacme.com", "display_name": "Alex Rivera"}}'
```

Later, in another session or from another agent, `email.inboxes` returns every domain and inbox the workspace owns with their `inbox_id`, free, so nothing gets created twice.

The `display_name` is the From name recipients see on every email the inbox sends. To change it later, call `email.inbox` with `inbox_id` and the new `display_name` (no `name` or `domain`): the inbox is renamed in place, free. There is no per-email sender name on `email.send`; rename the inbox, then send.

Then send, with an unsubscribe URL, because you are the sender of record:

```bash
curl -X POST https://api.jell.run/v1/run \
  -H "Authorization: Bearer $JELL_API_KEY" \
  -H "Idempotency-Key: campaign-q4-lead-4821" \
  -d '{
    "capability": "email.send",
    "input": {
      "inbox_id": "inb_...", "to": "alex.rivera@acme.com",
      "subject": "3 lifecycle roles in 45 days",
      "text": "Saw you posted three lifecycle marketing roles...",
      "unsubscribe_url": "https://getacme.com/u/abc123"
    }
  }'
```

`email.messages` polls the inbox for replies, bounces and opens as labels. Bounces matter more than replies in week one: a hard bounce rate above 2% is a sign the list was not verified properly, and it is the fastest way to lose a new domain.

## Warm-up, honestly

A new domain has no sending history, which means no reputation, which means receivers are suspicious by default. The pattern that works is boring:

| Week | Volume per day | What you are watching |
| --- | --- | --- |
| 1 | 10 to 20 | Bounces. Anything above 2% means stop and re-verify the list. |
| 2 | 30 to 50 | Replies. Any engagement at all is reputation. |
| 3 to 4 | 75 to 150 | Spam complaints. Above 0.1% and you back off. |
| 5+ | Scale gradually | Reply rate by segment, not aggregate volume. |

Verify every address with `contact.verify` before it enters this. The [waterfall enrichment guide](/use-cases/waterfall-email-enrichment) covers that half of the pipeline.

## Where it breaks

- **DNS propagation is not instant.** Run `email.domain` again until it reports the domain verified before you send, not immediately after writing records.
- **Two SPF records is a hard failure.** If the domain had any prior mail setup, look before you add.
- **Volume is not the constraint people think it is.** Reputation is earned per recipient domain. A thousand sends a day to one large provider is a different risk from a thousand spread across many.
- **You are the sender of record.** Consent, unsubscribe handling and the applicable rules (CAN-SPAM, GDPR, and whatever applies where your recipient is) are yours. The API sends what you tell it to.

## Run it as an agent

```bash
jell run -c domain.search -i '{"name":"acme","tlds":["com","io"]}'
jell run -c email.domain -i '{"domain":"getacme.com"}'
```

An agent can run the whole setup and re-check verification on a schedule, which is the part humans stop doing after month two.

## FAQ

### Why not just send from my company domain?

Because reputation is attached to the domain, and cold outbound is the highest-risk mail you send. A spike in complaints or bounces on a sending domain costs you that campaign. The same spike on your primary domain can affect password resets, invoices and every internal thread. Separate domains contain the blast radius, and they let you retire one without consequence.

### How long does warm-up actually take?

Four to six weeks to reach meaningful volume, and the number that decides it is engagement, not days. A domain sending 20 verified emails a day that get replies warms faster than one sending 200 to a cold, unverified list. If bounces exceed 2% or complaints exceed 0.1%, the correct move is to reduce volume, not to wait longer at the same volume.

### Do I need DMARC at p=reject?

Not to start, and starting there is a common way to block your own mail. Begin at `p=none`, which collects reports and rejects nothing, and read the reports for a few weeks to see everything legitimately sending as your domain. Move to `quarantine`, then `reject`, once that list is empty of surprises.

### Can I use my own domain instead of registering a new one?

Yes. `email.domain` takes any domain you control, returns the records to add and reports verification status. You write the records wherever your DNS lives. Registering through `domain.register` is a convenience so that the whole path is one API, not a requirement.
### What API lets me create cold email inboxes on new domains without a dashboard?

`email.inbox` creates a sending inbox on a domain you verified with `email.domain`, from one call, idempotent on name plus domain, owned by your organization. Run `email.inboxes` first (free) to see what already exists. The whole chain, domain search, registration, DNS, authentication, inbox, is API calls, so an agent provisions the sending stack without opening a mailbox admin console.

### How can my agent register a domain and set up DNS records programmatically?

`domain.search` checks availability and price, `domain.register` buys the domain with you as the registrant contact and WHOIS privacy on, and `domain.dns` writes the records, including the SPF, DKIM and DMARC records that `email.domain` returns. Registration is a paid, hard-to-reverse call, so an agent should quote it with `/v1/inspect` and confirm with a human before running it.

### How do I send cold email from an API with inboxes that are already warmed up?

You do not, here. Jell creates inboxes on domains you own and sends from them, and a new domain has no reputation to borrow. The table above is the honest schedule: 10 to 20 a day in week one, watching bounces, then volume in steps while watching replies and complaints. Verify every address with `contact.verify` before it enters that schedule. Services that sell pre-warmed inboxes are renting you someone else's reputation, and it leaves with them.

