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

# How to implement domain swaps

> Replace a degrading domain inside an active order - standard cuts over immediately, premium keeps the old one warm 14 days alongside the new one.

When a domain's deliverability is deteriorating, a **swap** replaces it with a fresh one inside the existing order - keeping the workspace, the customer, and the rest of the order intact.

Two flavors:

* **Standard.** Old domain retired immediately. Lower cost, fastest cutover, brief send-pause on that one domain.
* **Premium.** Old domain stays warm **14 days** alongside the new one. No sending interruption; smoother handoff during warmup.

To rename the *people* on a domain (not the domain itself), use [Changing user names on active orders](/guides/user-name-swaps) instead.

## Steps in detail

<AccordionGroup>
  <Accordion title="1. Choose standard or premium" defaultOpen>
    \| You want to… | Use | Why | | -------------------------------------------------- | -------- |
    \-------------------------------------------------- | | Replace one domain, no sending gap |
    Premium | Old domain stays warm 14 days; smoother handoff. | | Replace one domain, can absorb a
    brief pause | Standard | Lower cost, immediate cutover. | | Replace many domains at once | Cancel
    order, place new | Swaps are one domain at a time. |
  </Accordion>

  <Accordion title="2. Submit the swap">
    `POST /swaps` takes `type` (`standard` or `premium`), `domain_id`, and `replacement_domain`. Same response shape for both types - only `type` differs. The `replacement_domain` must be a domain Peeker can either buy from a partner registrar (registrar source) or one the customer has already imported (imported source).

    ```bash cURL theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
    curl -X POST "https://api.peeker.ai/partner/v1/swaps" \
      -H "Authorization: Bearer pk_test_<your-key>" \
      -H 'Content-Type: application/json' \
      -d '{
        "type":               "premium",
        "domain_id":          "dom_01HZX0D01…",
        "replacement_domain": "acme-fresh.com"
      }'
    ```

    ```json 201 Created theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
    {
    	"data": {
    		"id": "swp_01HZX0SW1A2B3C4D5E6F7G8H",
    		"type": "premium",
    		"domain_id": "dom_01HZX0D01A2B3C4D5E6F7G8H",
    		"replacement_domain": "acme-fresh.com",
    		"status": "created",
    		"created_at": "2026-05-08T12:00:00Z"
    	}
    }
    ```
  </Accordion>

  <Accordion title="3. Track swap status">
    Poll `GET /swaps/{id}` or listen for the webhooks below.

    ```bash cURL theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
    curl -X GET "https://api.peeker.ai/partner/v1/swaps/swp_01HZX0SW…" \
      -H "Authorization: Bearer pk_test_<your-key>"
    ```

    ```
    created  →  in_progress  →  completed
                           ↘  failed
                           ↘  action_required
    ```

    | Event                  | Fires when                                                                           |
    | ---------------------- | ------------------------------------------------------------------------------------ |
    | `swap.created`         | Immediately after `POST /swaps` accepts.                                             |
    | `swap.in_progress`     | Workers picked it up.                                                                |
    | `swap.completed`       | New domain is live; old one detached (or kept warm for premium).                     |
    | `swap.failed`          | Swap hit an unrecoverable error. Read the next `swap` payload via `GET /swaps/{id}`. |
    | `swap.action_required` | Swap is blocked on partner input (e.g. replacement domain isn't imported).           |
  </Accordion>

  <Accordion title="4. Recovering from `swap.action_required`">
    The most common reason: the replacement domain isn't imported yet. Run [Importing domains & ordering](/guides/importing-domains) for the replacement (just the import portion - no need for a new order), then call `POST /swaps` again. The original swap will resolve automatically once the replacement is verified, or you can cancel it and start fresh.

    ```json Webhook · swap.action_required theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
    {
    	"id": "evt_01HZX0EVFA2B3C4D5E6F7G8H",
    	"type": "swap.action_required",
    	"created_at": "2026-05-08T12:30:00Z",
    	"data": {
    		"id": "swp_01HZX0SW1A2B3C4D5E6F7G8H",
    		"type": "premium",
    		"domain_id": "dom_01HZX0D01A2B3C4D5E6F7G8H",
    		"replacement_domain": "acme-fresh.com",
    		"status": "action_required",
    		"created_at": "2026-05-08T12:00:00Z"
    	}
    }
    ```
  </Accordion>
</AccordionGroup>

## What's next

<CardGroup cols={2}>
  <Card title="Changing user names on active orders" href="/guides/user-name-swaps">
    Same domain, different people on the inboxes - for team rotations.
  </Card>

  <Card title="Best practices" href="/best-practices">
    Pending actions, webhook hygiene, idempotent retries.
  </Card>
</CardGroup>
