> ## 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.

# Buying domains & ordering

> Create a bundle, check domains, place an order, and listen for webhooks. Peeker buys the domains and runs DNS.

Use this flow when Peeker should buy the domains and run DNS. Your customer picks available domains; Peeker registers them, provisions the inboxes, and sends webhooks as work completes.

## Steps in detail

<AccordionGroup>
  <Accordion title="1. Create your bundles" defaultOpen>
    A bundle is a saved order size. Send `name` and `monthly_sending_volume`; optionally add `google_percent` and `microsoft_percent`. Bundles default to weekday-only sending (`send_on_weekends: false`) and 2 Google inboxes/domain. Use the returned `bun_…` ID on orders. The response also tells you how many Google and Microsoft domains the customer must pick.

    ```bash cURL theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
    curl -X POST "https://api.peeker.ai/partner/v1/bundles" \
      -H "Authorization: Bearer pk_test_<your-key>" \
      -H "Content-Type: application/json" \
      --data '
    {
      "name": "Starter - 25k emails/month",
      "monthly_sending_volume": 25000,
      "send_on_weekends": false,
      "google_inboxes_per_domain": 2
    }'
    ```

    ```json 201 Created theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
    {
    	"data": {
    		"id": "bun_01HZX0BU1A2B3C4D5E6F7G8H",
    		"name": "Starter - 25k emails/month",
    		"monthly_sending_volume": 25000,
    		"google_percent": null,
    		"microsoft_percent": null,
    		"send_on_weekends": false,
    		"google_inboxes_per_domain": 2,
    		"effective": {
    			"google_percent": 50,
    			"microsoft_percent": 50
    		},
    		"required_domains": {
    			"google": 15,
    			"microsoft": 4,
    			"total": 19
    		},
    		"status": "active",
    		"created_at": "2026-05-08T12:00:00Z"
    	}
    }
    ```
  </Accordion>

  <Accordion title="2. Check domain availability">
    Generate domain candidates yourself, or let the customer search. Send up to 50 per call. Available rows include price, renewal price, and `usable_for`; unavailable rows include a `reason`.

    ```bash cURL theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
    curl -X POST "https://api.peeker.ai/partner/v1/domains/availability" \
      -H "Authorization: Bearer pk_test_<your-key>" \
      -H "Content-Type: application/json" \
      --data '
    {
      "domains": [
        "acme-mail.com",
        "team-acme.com",
        "premium-acme.com"
      ]
    }'
    ```

    ```json 200 OK theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
    {
    	"data": {
    		"domains": [
    			{
    				"domain": "acme-mail.com",
    				"available": true,
    				"price_cents": 1300,
    				"renewal_price_cents": 1500,
    				"currency": "usd",
    				"usable_for": ["google", "microsoft"]
    			},
    			{
    				"domain": "team-acme.com",
    				"available": true,
    				"price_cents": 1300,
    				"renewal_price_cents": 1500,
    				"currency": "usd",
    				"usable_for": ["google"]
    			},
    			{
    				"domain": "premium-acme.com",
    				"available": false,
    				"premium": true,
    				"reason": "premium"
    			}
    		]
    	}
    }
    ```

    <Warning>
      Microsoft domains must include `"microsoft"` in `usable_for`. Match the bundle's `required_domains.microsoft` count before checkout. Filter or flag Google-only rows when the customer still needs Microsoft-capable domains.
    </Warning>
  </Accordion>

  <Accordion title="3. Submit the order">
    `POST /orders` provisions everything in one call. It accepts one `user` string, plus Smartlead routing and credentials in `sequencer`. You pass:

    * **`bundle_id`** - the bundle from step 1; resolves the license mix and domain split.
    * **`user`** - an email to create/reuse, or an existing `usr_…` user ID.
    * **`sequencer`** - Smartlead routing plus the login credentials Peeker uses internally for provider submission.
    * **`forwarding_url`** - the default redirect for every domain in the order.
    * **`domains`** - the available domains you picked, selected to satisfy the bundle's Microsoft split.
    * **`personas`** - names and required `profile_picture_url` keys used to generate inbox names.

    Smartlead orders require both `sequencer.login_email` and `sequencer.login_password`; a Smartlead client ID or API key alone is not enough for Peeker to submit the provider work.

    Re-sending the exact same body returns the original order - no second charge, no duplicate provisioning. Orders dedupe for 24 hours.

    ```bash cURL theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
    curl -X POST "https://api.peeker.ai/partner/v1/orders" \
      -H "Authorization: Bearer pk_test_<your-key>" \
      -H "Content-Type: application/json" \
      --data '
    {
      "user": "alex@acme.com",
      "bundle_id": "bun_01HZX0BU…",
      "sequencer": {
        "provider": "smartlead",
        "client_id": 366903,
        "login_email": "peeker@yourcompany.com",
        "login_password": "<your-dedicated-login>"
      },
      "forwarding_url": "https://acme.com",
      "domains": [
        "acme-mail.com",
        "team-acme.com"
      ],
      "personas": [
        {
          "first_name": "Alex",
          "last_name": "Rivera",
          "profile_picture_url": ""
        },
        {
          "first_name": "Sam",
          "last_name": "Lee",
          "profile_picture_url": ""
        }
      ]
    }'
    ```

    ```json 200 OK theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
    {
    	"data": {
    		"id": "ord_01HZX0OR1A2B3C4D5E6F7G8H",
    		"user_id": "usr_01HZX0C6Z3K4M5N6P7Q8R9S0",
    		"email": "alex@acme.com",
    		"bundle_id": "bun_01HZX0BU…",
    		"status": "in_progress",
    		"domain_count": 2,
    		"costs": {
    			"total_cents": 6800,
    			"currency": "usd"
    		},
    		"created_at": "2026-05-08T12:00:00Z"
    	}
    }
    ```

    If a domain cannot be registered at order time, the order rejects and you are not charged.
  </Accordion>

  <Accordion title="4. Listen for the webhooks">
    Configure your endpoint in the [Partner portal → Webhooks](https://app.peeker.com/partner/webhooks) **before** placing the order. Registrar-sourced domains skip the nameserver step (Peeker controls DNS), so events fire in this sequence:

    ```
    order.in_progress  →  domain.connected (one per domain)  →  order.completed
    ```

    The `order.completed` payload is the full order. [Verify the signature](/webhooks#verify-the-signature) before trusting any payload.

    ```json Webhook · order.completed theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
    {
    	"id": "evt_01HZX0EV2A2B3C4D5E6F7G8H",
    	"type": "order.completed",
    	"created_at": "2026-05-08T12:14:33Z",
    	"data": {
    		"id": "ord_01HZX0OR1A2B3C4D5E6F7G8H",
    		"status": "completed",
    		"domain_count": 2
    	}
    }
    ```
  </Accordion>
</AccordionGroup>

## What's next

<CardGroup cols={2}>
  <Card title="Importing customer domains" href="/guides/importing-domains">
    The separate flow for domains the customer already owns at another registrar.
  </Card>

  <Card title="Change forwarding URLs" href="/guides/forwarding">
    Repoint up to 25 domains at a new URL after the order is live.
  </Card>
</CardGroup>
