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 end-to-end flow most resellers ship as their first integration. Your customer is buying email infrastructure on domains they already own at GoDaddy / Namecheap / Squarespace / etc. — Peeker provisions the inboxes on their existing domains and pushes them into Smartlead.

Full workflow

Save this as a sandbox script to verify the chain end-to-end before you ship.
cURL · full workflow
BASE="https://api.peeker.ai/api/partner/v1"
AUTH="Authorization: Bearer pk_test_<your-key>"

# Step 1: Search for available domains (your customer typed candidates).
curl -X POST "$BASE/domains/availability" \
  -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{ "domains": ["acme-mail.com", "team-acme.com"] }'

# Step 2: Submit the import job (after the customer picks).
curl -X POST "$BASE/domains/import" \
  -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{
    "domains":    ["acme-mail.com", "team-acme.com"]
  }'

# Step 3: Poll until the import completes (or listen for the webhook).
curl -X GET "$BASE/domains/import/imp_01HZX0IM…" -H "$AUTH"

# Step 4: Submit the order — provisions every inbox, routes to Smartlead,
#         runs forwarding, queues the webhook stream.
curl -X POST "$BASE/orders" \
  -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{
    "user": {
      "email": "alex@acme.com",
      "first_name": "Alex",
      "last_name": "Rivera"
    },
    "sequencer": {
      "provider": "smartlead",
      "client_id": 366903,
      "login_email": "ops@acme.com",
      "login_password": "their-smartlead-password"
    },
    "bundle_id":          "bun_01HZX0BU…",
    "forwarding_url":     "https://acme.com",
    "domains":            ["acme-mail.com", "team-acme.com"],
    "users": [
      { "first_name": "Alex", "last_name": "Rivera", "profile_picture_url": "https://cdn.acme.com/alex.jpg" },
      { "first_name": "Sam",  "last_name": "Lee",    "profile_picture_url": "https://cdn.acme.com/sam.jpg"  }
    ]
  }'

Steps in detail

1. Search for available domains

Your customer types domain candidates in your app’s search box. Debounce 250–400 ms and batch the latest input into one call to POST /domains/availability (up to 25 candidates). Each available row comes back with available, price_cents, renewal_price_cents, usable_for, and premium.In your UI, render available: true rows that include the customer’s chosen provider in usable_for. Hide premiums (they aren’t orderable via API) or surface them as “contact us.”
200 OK · response shape
{
	"data": {
		"domains": [
			{
				"domain": "acme-mail.com",
				"available": true,
				"price_cents": 1300,
				"renewal_price_cents": 1500,
				"usable_for": ["google", "microsoft"],
				"premium": false
			},
			{
				"domain": "team-acme.com",
				"available": true,
				"price_cents": 1300,
				"renewal_price_cents": 1500,
				"usable_for": ["google", "microsoft"],
				"premium": false
			},
			{ "domain": "premium-domain.com", "available": false, "premium": true, "reason": "premium" }
		]
	}
}
After the customer picks. POST /domains/import returns quickly with the nameserver_groups your customer must paste at their registrar. The domains are imported into your default org; they attach to the customer later when you place the order.
202 Accepted · nameserver groups
{
	"data": {
		"id": "imp_01HZX0IM1A2B3C4D5E6F7G8H",
		"status": "in_progress",
		"submitted_count": 2,
		"completed_count": 0,
		"failed_count": 0,
		"nameserver_groups": [
			{
				"nameserver_one": "helena.ns.cloudflare.com",
				"nameserver_two": "idris.ns.cloudflare.com",
				"domains": ["acme-mail.com", "team-acme.com"]
			}
		],
		"failed": [],
		"created_at": "2026-05-08T12:00:00Z"
	}
}
Send the customer those nameservers in your UI:
Open your registrar’s DNS settings for acme-mail.com and team-acme.com. Replace the existing nameservers with helena/idris. Most registrars apply the change within a few hours.
Poll GET /domains/import/{id} every 60 seconds, or subscribe to the domain_import.completed webhook. Status moves from in_progresscompleted once nameservers propagate.If you get domain_import.action_required, surface “Acme — waiting on registrar” in your UI and re-show the same nameservers. Peeker keeps re-checking; you’ll get domain_import.completed automatically when the customer flips DNS.
{
	"id": "evt_01HZX0EVBA2B3C4D5E6F7G8H",
	"type": "domain_import.completed",
	"created_at": "2026-05-08T12:08:43Z",
	"data": {
		"id": "imp_01HZX0IM1A2B3C4D5E6F7G8H",
		"status": "completed",
		"submitted_count": 2,
		"completed_count": 2,
		"failed_count": 0,
		"nameserver_groups": [
			{
				"nameserver_one": "helena.ns.cloudflare.com",
				"nameserver_two": "idris.ns.cloudflare.com",
				"domains": ["acme-mail.com", "team-acme.com"]
			}
		],
		"failed": [],
		"created_at": "2026-05-08T12:00:00Z",
		"completed_at": "2026-05-08T12:08:43Z"
	}
}
One call. Pick a bundle_id (resolves the license mix from monthly volume), pass the Smartlead route and login credentials, list the imported domains, and list the personas (first/last names + optional profile picture URLs).If the email already exists, Peeker reuses that user and updates their saved Smartlead routing for future orders. Re-sending the same body returns the original order — orders dedupe for 24 hours.
200 OK · order
{
	"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"
	}
}
Use GET /orders/{id} or order webhooks when you need the full detail response with imported vs registered domains, line items, and generated users.
The progression for a Smartlead-backed order:
order.in_progress
  → domain.connected (×N)   (one per domain as DNS + provider land)
  → order.completed         (every inbox is live in Smartlead)
When order.completed fires, the inboxes are already pushed into Smartlead. They can start warming and sending. No additional API call from your side.
{
	"id": "evt_01HZX0EV7A2B3C4D5E6F7G8H",
	"type": "domain.connected",
	"created_at": "2026-05-08T12:08:00Z",
	"data": {
		"domain": {
			"id": "dom_01HZX0D01A2B3C4D5E6F7G8H",
			"domain": "acme-mail.com",
			"provider": "google",
			"source": "imported",
			"status": "active",
			"usable_for": ["google", "microsoft"]
		}
	}
}

Things to handle in production

A few real-world cases that show up once you’re past sandbox:
  • Customer hasn’t flipped nameservers yet. You’ll see domain_import.action_required after ~30 minutes. Re-show the nameservers in your UI and surface “Acme — waiting on registrar.” Peeker keeps re-checking; you’ll get domain_import.completed when they propagate.
  • Profile picture URL is private/invalid. You’ll see order.action_required between in_progress and completed with reason: "profile_picture_public_link_required" or "profile_picture_resubmit". Get a fresh public URL from the customer and PATCH /users/{id} (or place a corrected order). Provisioning resumes automatically.
  • Smartlead credentials are wrong. You’ll see order.action_required with reason: "provider_workspace_problem", and the order stalls. Submit a corrected order or update the user’s Smartlead route before future orders.

What’s next

Buying domains from your registrar

Same flow, but Peeker buys the domains and runs DNS — no nameserver step for the customer.

Change forwarding URLs in bulk

Repoint many domains at a new forwarding URL after the order is live.

How to implement domain swaps

Replace a degrading domain mid-flight without losing warmup.

Best practices

Errors, paging, idempotent retries, pending actions, webhook hygiene.
Last modified on May 14, 2026