Go from zero to a fully provisioned domain and mailboxes.
This guide walks through the complete flow (i.e, creating a user, checking availability, placing an order, and handling the webhook).
INFO
You need a CMR partner API key before making any requests. Sign up and generate one at https://partners.coldmailreseller.com.
You'll also need a publicly reachable webhook URL to receive the order confirmation. If you're testing locally, use a tool like ngrok to expose a local port — or use the Sandbox to test the full flow without a live endpoint.
Create user → Check availability → Place order
↓
{ orderId: "ord_..." }
↓
10–90 seconds
↓
domain.order.success ✓
↓
mailbox.order.success ✓
↓
mailbox.created (one per mailbox)
You can also poll GET /orders/status?userId=&orderId= at any point instead of (or alongside) listening for webhooks.
The HTTP 200 on the order request confirms it was accepted — not completed. Wait for mailbox.created events before treating the order as done.
Every domain and mailbox in CMR belongs to a user. Create one first and store the returned id — you'll pass it as userId on every subsequent request for this customer.
POST /users HTTP/1.1
cmr-x-api-key: your_partner_api_key
Content-Type: application/json
{
"email": "alice@company.com",
"firstName": "Alice",
"lastName": "Johnson",
"company": "Acme Corp",
"languagePreference": "en",
"addressLineOne": "123 Main Street",
"city": "New York",
"state": "New York",
"country": "US",
"postalCode": "10001",
"phoneCc": "1",
"phone": "2125551234"
}
Response:
{
"status": 200,
"message": "User created",
"data": { "id": "usr_abc123" }
}
Save data.id as userId.
Always check availability before placing an order. Attempting to register an unavailable domain results in a failed order.
```http POST /domains/available-single HTTP/1.1 cmr-x-api-key: your_partner_api_key Content-Type: application/json{ "domainName": "alicesender.com", "years": 1 }
:::warning
Both `domainName` and `years` are required. A body like `{ "domain": "alicesender.com" }` (wrong key, missing `years`) returns `422 Unprocessable Entity`.
:::
**Response:**
```json
{
"status": 200,
"message": "Domain availability status",
"data": {
"status": "AVAILABLE",
"domainName": "alicesender.com",
"domainPrice": "12.99",
"renewPrice": "14.99"
}
}
`POST /domains/available` checks **one base name across multiple TLDs** — it does not take a list of full domain names. To check several unrelated full domain names, call `/domains/available-single` once per domain instead.
POST /domains/available HTTP/1.1
cmr-x-api-key: your_partner_api_key
Content-Type: application/json
{
"domainName": "alicesender",
"tlds": ["com", "io", "net"],
"years": 1
}
Note domainName here is the base name without a TLD (e.g. alicesender, not alicesender.com).
Response:
{
"status": 200,
"data": {
"availableDomains": [
{ "status": "AVAILABLE", "domainName": "alicesender.com", "domainPrice": "12.99", "renewPrice": "14.99" },
{ "status": "UNAVAILABLE", "domainName": "alicesender.io", "domainPrice": "9.99", "renewPrice": "11.99" }
],
"restrictedDomains": []
}
}
Filter for status: "AVAILABLE" and present the options to your customer.
POST /orders/json?userId=usr_abc123 HTTP/1.1
cmr-x-api-key: your_partner_api_key
Content-Type: application/json
{
"billingCycle": "MONTHLY",
"domains": [
{ "domain": "alicesender.com", "years": 1 }
],
"mailboxes": {
"alicesender.com": [
{ "username": "alice", "firstName": "Alice", "lastName": "Johnson" },
{ "username": "alice.j", "firstName": "Alice", "lastName": "Johnson" },
{ "username": "hello", "firstName": "Alice", "lastName": "Johnson" }
]
}
}
Response:
{
"status": 200,
"message": "Order created and queued for processing",
"data": {
"orderId": "ord_R8K2LQPZA9XM",
"metadata": { "alicesender.com": "Success" }
}
}
Store data.orderId — pass it as the orderId query param on GET /orders/status to poll progress, and it's how incoming webhooks for this order can be correlated.
| Field | Required | Description |
|---|---|---|
billingCycle | Yes | MONTHLY (only supported value currently) |
domains | No | Array of { domain, years } objects to register. Omit entirely for mailbox-only orders on an existing domain. |
mailboxes | Yes | Object keyed by domain name → array of mailbox objects (username, firstName, lastName, optional onWarmup). Each mailbox's firstName/lastName is set per mailbox, not once for the whole order. |
There is no mailboxesPerDomain field and no top-level firstName/lastName — a request in that shape will fail validation ("mailboxes must be an object").
WARNING
Never resubmit an order while waiting for webhooks. Duplicates create duplicate domain registrations and charges with no automatic way to reverse them.
A successful order produces a sequence of events — not a single webhook. Listen for all of them.
Domain is registered. Mailbox provisioning begins immediately after.
{
"event": "domain.order.success",
"eventId": "6SSHBXWR3N4JEKGRQSG3KW3D76N6",
"data": {
"userId": "usr_abc123",
"orderId": "NQ682GAVFTMYC2H6C87PPKPG5WXV",
"domain": "alicesender.com"
}
}
Fires once per mailbox when it's fully provisioned and ready. If you ordered 3 mailboxes, you receive 3 separate mailbox.created events.
{
"event": "mailbox.created",
"eventId": "7TTIBYXS4O5KFLHSRTG4LX4E87O7",
"data": {
"userId": "usr_abc123",
"orderId": "NQ682GAVFTMYC2H6C87PPKPG5WXV",
"mailboxId": "mbx_001",
"email": "alice@alicesender.com"
}
}
{
"event": "domain.order.failed",
"eventId": "8UUJCZYT5P6LGMITSTG5MY5F98P8",
"data": {
"userId": "usr_abc123",
"orderId": "NQ682GAVFTMYC2H6C87PPKPG5WXV",
"reason": "Workspace Validation"
}
}
TIP
Respond with HTTP 200 immediately when you receive any webhook — before processing. If your handler throws before responding, CMR will retry the delivery.
Once you have an active domain, add more mailboxes without a new domain order:
POST /orders/create-mailbox-order/json?userId=usr_abc123 HTTP/1.1
cmr-x-api-key: your_partner_api_key
Content-Type: application/json
{
"domainId": "dom_xyz456",
"billingCycle": "MONTHLY",
"mailboxes": {
"acmecorp.com": [
{ "username": "bob", "firstName": "Bob", "lastName": "Smith" },
{ "username": "bob.s", "firstName": "Bob", "lastName": "Smith" }
]
}
}
There is no count field — list every mailbox you want created explicitly under mailboxes, keyed by the domain's actual domain name (not domainId). Same pattern — mailbox.created fires per mailbox when each is ready.
Test the full flow without real charges:
POST /sandbox/orders/json?userId=usr_abc123 HTTP/1.1
cmr-x-sandbox-api-key: your_sandbox_api_key
Content-Type: application/json
{
"billingCycle": "MONTHLY",
"domains": [
{ "domain": "testdomain.com", "years": 1 }
],
"mailboxes": {
"testdomain.com": [
{ "username": "test", "firstName": "Test", "lastName": "User" }
]
}
}
Sandbox mirrors the production request shape exactly — see Place the Order above.
Control the outcome via the cmr-x-sandbox-scenario header:
| Header value | Behavior |
|---|---|
simulate:order-success | domain.order.success → mailbox.order.success → mailbox.created events fire |
simulate:order-domain-failed | domain.order.failed fires |
simulate:order-partial-failure | Some domains succeed, some fail |
See the full Sandbox API reference for every sandboxed endpoint and scenario key.