Skip to main content

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.

The default flow for selling email infrastructure: set up a bundle once, then for each customer check domain availability, place an order, and listen for webhooks. Peeker buys the domains from a partner registrar, runs DNS on our Cloudflare, and provisions the inboxes — the customer never touches a registrar or sets a nameserver.

Steps in detail

1. Create your bundles

A bundle is a saved order template sized by monthly sending volume. Create the bundles you want to offer customers once — POST /bundles needs a name and monthly_sending_volume. Optionally set a google_percent / microsoft_percent split; skip it to inherit Peeker’s recommended mix.You reference the returned bun_… ID on every order — the bundle resolves the Google/Microsoft license counts and the domain split for you.
cURL
curl -X POST "https://api.peeker.ai/api/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
}'
201 Created
{
	"data": {
		"id": "bun_01HZX0BU1A2B3C4D5E6F7G8H",
		"name": "Starter — 25k emails/month",
		"monthly_sending_volume": 25000
	}
}
Generate domain candidates yourself, or let the customer type them in a search box. Send up to 25 per call to POST /domains/availability. Each available: true row returns the domain name, price_cents (first-year registration cost), renewal_price_cents (yearly renewal cost), and usable_for (Google, Microsoft, or both). Unavailable rows return available: false with a reason.
cURL
curl -X POST "https://api.peeker.ai/api/partner/v1/domains/availability" \
  -H "Authorization: Bearer pk_test_<your-key>" \
  -H "Content-Type: application/json" \
  --data '
{
  "domains": [
    "acme-mail.com",
    "team-acme.com"
  ]
}'
200 OK
{
	"data": {
		"domains": [
			{
				"domain": "acme-mail.com",
				"available": true,
				"price_cents": 1300,
				"renewal_price_cents": 1500,
				"usable_for": ["google", "microsoft"]
			},
			{
				"domain": "team-acme.com",
				"available": true,
				"price_cents": 1300,
				"renewal_price_cents": 1500,
				"usable_for": ["google"]
			}
		]
	}
}
A domain can only be used as a Microsoft domain if its usable_for includes "microsoft". Rows that come back usable_for: ["google"] work for Google inboxes but can’t fill a Microsoft slot — filter them out or show them as unavailable for Microsoft in your UI.Your bundle’s split determines how many Microsoft domains the order needs. If a customer is selecting 10 domains and the bundle’s split calls for 3 Microsoft domains, at least 3 of those 10 must have "microsoft" in their availability response. A ["google", "microsoft"] row can fill either slot.
POST /orders provisions everything in one call — Peeker buys the domains, runs DNS, and creates the inboxes. It accepts either a user object or an existing user_id, plus Smartlead routing and credentials in sequencer. You pass:
  • bundle_id — the bundle from step 1; resolves the license mix and domain split.
  • user or user_id — create/reuse the customer by email, or attach the order to an existing Peeker user.
  • 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.
  • users — the personas (first/last names) used to generate inbox names.
Re-sending the exact same body returns the original order — no second charge, no duplicate provisioning. Orders dedupe for 24 hours.
cURL
curl -X POST "https://api.peeker.ai/api/partner/v1/orders" \
  -H "Authorization: Bearer pk_test_<your-key>" \
  -H "Content-Type: application/json" \
  --data '
{
  "user": {
    "email": "alex@acme.com",
    "first_name": "Alex",
    "last_name": "Rivera"
  },
  "sequencer": {
    "provider": "smartlead",
    "client_id": 366903,
    "login_email": "peeker@yourcompany.com",
    "login_password": "<your-dedicated-login>"
  },
  "bundle_id": "bun_01HZX0BU…",
  "forwarding_url": "https://acme.com",
  "domains": [
    "acme-mail.com",
    "team-acme.com"
  ],
  "users": [
    {
      "first_name": "Alex",
      "last_name": "Rivera"
    },
    {
      "first_name": "Sam",
      "last_name": "Lee"
    }
  ]
}'
200 OK
{
	"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 the registrar can’t sell us a domain at place-order time, the whole order rejects with domain_already_in_active_order or internal_error. You won’t be charged.
Configure your endpoint in the Partner portal → 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 provisioned order — drop it straight into your UI. Verify the signature before trusting any payload.
Webhook · order.completed
{
	"id": "evt_01HZX0EV2A2B3C4D5E6F7G8H",
	"type": "order.completed",
	"created_at": "2026-05-08T12:14:33Z",
	"data": {
		"order": {
			"id": "ord_01HZX0OR1A2B3C4D5E6F7G8H",
			"status": "completed",
			"domain_count": 2
		}
	}
}

What’s next

Importing customer domains

The separate flow for domains the customer already owns at another registrar.

Change forwarding URLs in bulk

Repoint many domains at a new URL after the order is live.
Last modified on May 14, 2026