> ## Documentation Index
> Fetch the complete documentation index at: https://developers.telnyx.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Dedicated IPs

> Purchase dedicated sending IP addresses for your Telnyx email traffic, with automated warm-up and per-IP pools.

Dedicated IPs give your email traffic its own sending IP address, isolated from
other Telnyx customers. Use them when your sending reputation requirements
demand full control of the IP's history — deliverability-critical workloads,
shared-pool reputation sensitivity, or compliance needs that name a specific IP.

## Pricing and eligibility

* **Pricing:** \$14 per IP per month, charged as a monthly recurring charge (MRC)
  that starts at purchase.
* **Billing:** the charge continues while the IP is `provisioning`,
  `warming_up`, `active`, or `suspended`. It stops only when you release the
  IP. The `billing` object on the IP reports the charge state.
* **Eligibility:** purchases require a **verified** or **enterprise** account.
  Other accounts receive `403`.
* **Selection:** the optional `region` (supported value: `ewr-nj1`) and
  `ip_version` (supported value: `ipv4`) fields on `POST /email/dedicated_ips`
  each restrict which stock IP is allocated; they never change the allocated
  IP's metadata. An omitted field applies no restriction for that field.

## Lifecycle

A dedicated IP moves through these statuses:

| Status | Meaning |
| - | - |
| `provisioning` | Purchased and billed. Carries no mail until you call `start_warmup`. |
| `warming_up` | Warm-up ramp in progress. Sends up to the day's cap. |
| `active` | Ramp complete. Sends without a warm-up cap. `warmup.today.cap` reports `100000000`, the ramp-complete cap, including on the day of a [manual completion](#warm-up); a day's allowance created before the IP became `active` can keep its stored cap until the next first-attempt send refreshes it. |
| `suspended` | Suspended by Telnyx. Carries no mail and is still billed. Only Telnyx can lift a suspension; you can still release the IP. |
| `released` | Returned to Telnyx. Carries no mail; the MRC stop is requested (see [Releasing an IP](#releasing-an-ip)); a 30-day quarantine applies. |

### Provisioning and reverse DNS

Dedicated IPs ship with a Telnyx-managed reverse DNS (PTR) record,
`mta-<a-b-c-d>.msgtelnyx.com` — for example, `mta-64-16-239-165.msgtelnyx.com`
for `64.16.239.165`. The hostname already resolves forward to the IP, and
Telnyx uses it as the EHLO hostname when sending from the IP. There is no
customer step to request or author the PTR record, and the hostname cannot be
changed: a `PATCH /email/dedicated_ips/{id}` request that includes
`ptr_hostname` (including `null`) returns `422` (code `10015`), and the IP's
hostname and DNS confirmation are unchanged. When the same request carries
`pool_id` or a warm-up pause field, that change applies and `ptr_hostname` is
ignored.

A purchased IP stays `provisioning` until you call
`POST /email/dedicated_ips/{id}/actions/start_warmup`. That call requires the
IP to be in `provisioning` status; `warming_up`, `active`, and `released` IPs
return `422`. A `suspended` IP returns `409` (code `40901`): only Telnyx can
lift a suspension. The call does not check DNS or pool membership.

The `provisioning` object is read-only status information:

| Field | Meaning |
| - | - |
| `provisioning.dns_status` | `dns_confirmed` when the PTR is forward-confirmed and `dns_confirmed_at` is set. Other values: `pending_ptr`, `pending_dns`, `dns_lost` (a confirmed PTR no longer confirms, or the PTR request was abandoned before it ever confirmed), and `not_applicable` (released IPs). |
| `provisioning.checklist` | Informational progress flags for the Telnyx-managed PTR. No customer action completes them. |

An IP sends only while `dns_confirmed_at` is set. Without it, the IP is
excluded from sending, and mail that depends on it is held — it never spills
over to the shared pool.

### DNS for your sending domains

Dedicated IPs need no SPF change for domains whose SPF record already uses
`include:spf.telnyx.com`, the record Telnyx asks you to publish for every
sending domain. Telnyx lists its dedicated sending IPs in `spf.telnyx.com`. See
[Domains](/docs/messaging/email/domains.md) for the required records.

## Warm-up

Every dedicated IP follows an **automated warm-up ramp** before reaching its
full daily volume — the same principle as [sender-domain graduation](/docs/messaging/email/deliverability.md),
applied per-IP. One warm-up engine per IP: per-IP and per-domain scopes never
stack.

* **Start:** the ramp starts only when you call
  `POST /email/dedicated_ips/{id}/actions/start_warmup`. It never starts
  automatically, including after DNS is confirmed.
* **Progression:** the ramp advances one stage for each UTC day on which the IP
  sent mail. Days without sends do not count, so an idle IP holds its stage.
* **Completion:** after 28 send-days, the IP moves to `active` automatically.
* **Pause/resume:** `POST /email/dedicated_ips/{id}/actions/pause_warmup` and
  `.../resume_warmup` hold the ramp at its current stage and continue it.
  You can also PATCH the resource with `warmup_paused: true|false` or
  `warmup.paused`; a Boolean `warmup_paused` takes precedence.
  **Pausing stops sending, not just progression:** while paused, the IP
  releases no first-attempt sends from hold (see below) until you resume.
  Pause and resume apply only to `warming_up` IPs; other statuses return
  `409`.
* **Manual completion:** `POST /email/dedicated_ips/{id}/actions/complete_warmup`
  moves a `warming_up` IP to `active` immediately, bypassing the schedule — use
  only when you have independently warmed the IP elsewhere. Suspended IPs
  return `409`; other statuses return `422`. Completion sets
  `warmup.progress` to `100`, clears `warmup.daily_cap`, and applies the
  ramp-complete allowance to the current UTC day: `today.cap` becomes
  `100000000`, `today.granted` keeps the sends already released today, and
  `today.remaining` becomes `cap − granted`. Once completion applies this
  allowance, an exhausted stage allowance does not block the next eligible
  first-attempt send. On an `active` IP, `warmup.progress` and
  `warmup.daily_cap` are informational: they can keep earlier values after a
  suspension is lifted and do not limit sending.

The default schedule sets these daily caps:

| Send-day | Daily cap |
| - | - |
| 1 | 1,000 |
| 2 | 2,000 |
| 3 | 4,000 |
| 4 | 8,000 |
| 5 | 12,000 |
| 6 | 16,000 |
| 7 | 24,000 |
| 8 | 32,000 |
| 9 | 48,000 |
| 10 | 64,000 |
| 11 | 96,000 |
| 12 | 128,000 |
| 13 | 192,000 |
| 14 | 256,000 |
| 15 | 384,000 |
| 16–28 | 512,000 |

An IP under warm-up sends only up to its current stage cap. Do not attempt to
send around the cap: once the day's allowance is spent, further first-attempt
sends are **held in queue**, not rejected, and retried about once a minute
until allowance is available — normally when the next UTC day arms a new
allowance. A held message waits at most 7 days: if it is still held after
7 days, it expires and is not delivered. Once released from hold, a message
gets the standard 72-hour delivery-retry window.

### Checking today's allowance

The `warmup` object on every dedicated IP carries a `today` block — the
live report card for the current UTC day (caps reset at UTC midnight):

| Field | Meaning |
| - | - |
| `today.day` | The UTC day the snapshot describes. |
| `today.cap` | The day's allowance cap. `active` IPs report `100000000`, the ramp-complete cap; manual completion applies it to the current day immediately. A day's allowance created before the IP became `active` can keep its stored cap and `remaining` until the next first-attempt send refreshes them. Null while the ramp hasn't opened (day 0, before the scheduler arms a stage). |
| `today.granted` | First-attempt sends released from warm-up hold so far today. Delivery retries never lease allowance and are not counted. |
| `today.remaining` | Headroom left today: `cap − granted`. A queued send while `remaining == 0` is held by the daily cap — see [warm-up](#warm-up) semantics above — unless the IP is `active` with a `cap` below `100000000` (see below). |

`today` is `null` outside the warm-up window: before `start_warmup`, and after
release or suspension. A spent day reports the cap with `remaining: 0` — that
combination is the deliberate "your mail is held for today" signal, distinct
from day-0's `cap: null` (ramp not yet opened). The exception is an `active`
IP whose `cap` is below `100000000`: that is a stored allowance awaiting
refresh, and it does not block the next eligible first-attempt send.

## Failure semantics

* **Unsupported purchase selector (422):** a `region` or `ip_version` value
  other than the supported one — including `ipv6`, an empty string, or
  `null` — returns `422` (code `10015`). `source.pointer` names the field and
  `detail` lists the supported values. Nothing is allocated or waitlisted.
* **Out of stock / over capacity (429):** when no dedicated IPs are available
  for purchase (including none matching your `region` and `ip_version`), or
  your account's holding capacity is exceeded, the API returns `429` with a
  `meta` object carrying `retry_after_seconds` (and `waitlist: true` when your
  request was recorded on the waitlist). Nothing was allocated — retry after
  the indicated interval.
* **Billing unavailable (503):** if billing could not be verified at purchase
  time — or its admission decision could not be read — the API returns `503`
  and nothing is allocated. A release returns `503` when billing is
  unavailable, and the IP is not released. Retry shortly.
* **No sendable IP holds traffic (no spillover):** mail routed to a dedicated
  pool is **held** when no IP in that pool can send — for example, when every
  member is `provisioning`, `suspended`, or not DNS-confirmed, or the pool is
  empty. Held mail never spills over to the shared pool, and it expires
  undelivered if it is still held after the 7-day limit described in
  [Warm-up](#warm-up).

## IP pools

Group dedicated IPs into **pools** to control which addresses carry your
traffic:

* Pools are account-scoped; optionally scope one to a specific email domain
  (`domain_id`). An account has at most one default pool (`is_default: true`).
* Add/remove members with `POST /email/ip_pools/{id}/ips` and
  `DELETE /email/ip_pools/{id}/ips/{ip_id}`; each membership carries a
  `weight` (positive integer, default `100`) for traffic distribution.
* An IP belongs to at most one pool; moving it (`PATCH
  /email/dedicated_ips/{id}` with `pool_id`) removes it from the previous
  pool's memberships.
* **Last-member protection:** removing a pool's last IP, moving it to another
  pool, releasing it, or deleting a pool that still has IPs returns `409`
  (code `40901`) unless you pass both `force=true` and `drain=true`. A routed
  pool left without a sendable IP holds its mail.

### Pool routing

A dedicated IP carries mail only through a pool. For each message, Telnyx
selects one of the sending account's pools:

1. The pool scoped to the message's sending domain (`domain_id`).
2. Otherwise, the account's default pool (`is_default: true`).
3. Otherwise, no dedicated pool: the account sends on the shared Telnyx pool.

A pool that is neither the default nor scoped to a domain carries no mail, and
an IP outside every pool carries no mail. Scope at most one pool to each
domain; when several pools share a `domain_id`, which one is used is not
defined. Deleting a pool changes routing for subsequently resolved messages.
It does not reroute messages already assigned to the deleted pool.

To send from dedicated IPs:

1. Call `start_warmup` for each IP.
2. Create a pool with `is_default: true` (all sending domains) or with
   `domain_id` (one domain), or set either field on an existing pool.
3. Add the IPs to that pool.

Start warm-up before adding an IP to a routed pool: while every member of the
selected pool is still `provisioning`, that pool's mail is held.

## Releasing an IP

`DELETE /email/dedicated_ips/{id}` releases the IP:

* The IP stops sending immediately and leaves its pool.
* `status` becomes `released`; `released_at` is set, and `quarantine_until` is
  set to 30 days later. The address is not reassigned before then.
* Release requests that the MRC stop asynchronously. Check `billing`:
  `stopping` means the stop is pending, `stopped` means it completed, and
  `stop_failed` means the charge remains. If the charge was never created or
  its creation has not completed, `billing` keeps showing the creation status
  (`not_started`, `pending`, `in_progress`, or `failed`).
* Any status except `released` can be released, including `suspended`.
  Releasing a released IP returns `422`. A released IP cannot be restarted.

Pass these options as query parameters or in a JSON body:

| Parameter | Type | Meaning |
| - | - | - |
| `force` | boolean | With `drain=true`, allows releasing the last IP in a pool. |
| `drain` | boolean | With `force=true`, acknowledges that the emptied pool holds its mail until it has a sendable IP again. |
| `reason` | string | Free-text reason recorded with the release. |

Releasing a pool's last IP without both `force=true` and `drain=true` returns
`409` (code `40901`).

## Next steps

* [Deliverability overview](/docs/messaging/email/deliverability.md)
* [Rate limits](/docs/messaging/email/rate-limits.md)
