---
title: "Quickstart"
url: "https://coldmeetings.com/docs/quickstart.md"
canonical_url: "https://coldmeetings.com/docs/quickstart"
last_updated: "2026-10-28"
type: "how-to"
summary: "Register a sending domain, check its DNS, create a campaign, import prospects and run preflight with cURL, Node.js or Python."
related: ["https://coldmeetings.com/docs/authentication.md", "https://coldmeetings.com/docs/api/campaigns.md", "https://coldmeetings.com/docs/errors.md"]
---

# Quickstart

> **For AI agents:** the complete index of ColdMeetings pages is [llms.txt](https://coldmeetings.com/llms.txt). Every page is also served as markdown at its URL plus .md.

Register a sending domain, check its DNS, create a campaign, import prospects and run preflight with cURL, Node.js or Python.

This walkthrough registers a sending domain, checks its DNS, creates a campaign, imports prospects and runs preflight. Each step shows cURL, Node.js and Python.

## Before you start

- An API key that starts with `cm_live_`. Email hello@coldmeetings.com and we set up your account and send your first key. See [Authentication](https://coldmeetings.com/docs/authentication.md).
- A plan with prospect credits. See [Credits and billing](https://coldmeetings.com/docs/credits-and-billing.md).
- A domain you own, separate from your company website domain, and access to its DNS.

## 1. Set up a client

Export your key. The Node.js and Python samples below use the small `api` helper, which sends the key and turns an error envelope into an exception. The cURL samples are complete as they are.

**cURL**

```bash
export COLDMEETINGS_API_KEY="cm_live_..."
```

**Node.js**

```js
const base = 'YOUR_API_BASE_URL';

async function api(method, path, body) {
  const res = await fetch(base + path, {
    method,
    headers: {
      Authorization: `Bearer ${process.env.COLDMEETINGS_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: body === undefined ? undefined : JSON.stringify(body),
  });
  const data = await res.json();
  if (!res.ok) throw new Error(`${data.error.code}: ${data.error.message}`);
  return data;
}
```

**Python**

```python
import os
import requests

BASE = "YOUR_API_BASE_URL"

def api(method, path, body=None):
    res = requests.request(
        method,
        BASE + path,
        json=body,
        headers={"Authorization": f"Bearer {os.environ['COLDMEETINGS_API_KEY']}"},
    )
    data = res.json()
    if not res.ok:
        raise RuntimeError(f"{data['error']['code']}: {data['error']['message']}")
    return data
```

## 2. Choose a plan

Email hello@coldmeetings.com with the plan you want. Enterprise is bought on a call. We set up your plan and its prospect credits and tell you when they are active.

An account with no payment has no plan. Creating a sending domain or a mailbox needs a paid plan: until one is active those requests answer `subscription_required` and create nothing. Campaigns and prospect credits need a paid plan as well. A plan starts when your payment is confirmed. You can ask for a refund of your first paid month within 7 days of paying. See [Credits and billing](https://coldmeetings.com/docs/credits-and-billing.md).

## 3. Register a sending domain

Register a domain you already own. The response lists the DNS records to publish. Pass `dkim_value` if your mailbox host has already issued the DKIM key.

**cURL**

```bash
curl -X POST YOUR_API_BASE_URL/v1/domains \
  -H "Authorization: Bearer $COLDMEETINGS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"domain":"acme-mail.example"}'
```

**Node.js**

```js
const domain = await api('POST', '/v1/domains', {
  "domain": "acme-mail.example"
});
console.log(domain);
```

**Python**

```python
domain = api("POST", "/v1/domains", {
    "domain": "acme-mail.example",
})
print(domain)
```

```json
{
  "id": "dom_4kT9xQp2Wm7ZsLr1VbN8cE",
  "domain": "acme-mail.example",
  "status": "pending_dns",
  "dkim_selector": "google",
  "created_at": "2026-10-17T12:00:00.000Z",
  "required_records": [
    {
      "type": "TXT",
      "name": "acme-mail.example",
      "content": "v=spf1 include:_spf.google.com ~all"
    },
    {
      "type": "TXT",
      "name": "_dmarc.acme-mail.example",
      "content": "v=DMARC1; p=none; rua=mailto:dmarc@acme-mail.example"
    }
  ],
  "dkim_note": "The DKIM record is issued by your mailbox host after the domain is added there. Re-register with dkim_value to see it listed, or publish it yourself."
}
```

Keep the `id`. The next samples read it from the `domain` variable, or from `$DOMAIN_ID` in cURL.

## 4. Publish the records and check DNS

Publish the SPF and DMARC records at your DNS host, or ask the API to publish them with `POST /v1/domains/{id}/dns` if your DNS host is connected. Then run the check. It reads live DNS, so repeat it until `authenticated` is `true`.

**cURL**

```bash
export DOMAIN_ID="dom_4kT9xQp2Wm7ZsLr1VbN8cE"
curl -X POST "YOUR_API_BASE_URL/v1/domains/$DOMAIN_ID/check-dns" \
  -H "Authorization: Bearer $COLDMEETINGS_API_KEY"
```

**Node.js**

```js
const check = await api('POST', `/v1/domains/${domain.id}/check-dns`);
console.log(check);
```

**Python**

```python
check = api("POST", f"/v1/domains/{domain["id"]}/check-dns")
print(check)
```

```json
{
  "domain": "acme-mail.example",
  "spf": true,
  "dkim": true,
  "dmarc": true,
  "authenticated": true,
  "status": "authenticated"
}
```

Only mailboxes on an authenticated domain may send. Create them with [POST /v1/mailboxes](https://coldmeetings.com/docs/api/mailboxes.md). That step answers 503 when the mailbox provider cannot be reached.

## 5. Create a campaign

A sequence has up to 6 steps and up to 4 variants per step. Every body must contain `{{unsubscribe}}` and `{{sender_address}}`, or the request is refused.

**cURL**

```bash
curl -X POST YOUR_API_BASE_URL/v1/campaigns \
  -H "Authorization: Bearer $COLDMEETINGS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Q4 outbound to software teams",
  "timezone": "America/New_York",
  "steps": [
    {
      "delay_days": 0,
      "variants": [
        {
          "subject": "Outbound without the busywork",
          "body": "Hello,\n\nWe run outbound for B2B software teams, from sending domains to booked calls. Would a short call about your pipeline be useful?\n\n{{sender_address}}\n{{unsubscribe}}"
        }
      ]
    },
    {
      "delay_days": 3,
      "variants": [
        {
          "subject": "Following up",
          "body": "Hello,\n\nA quick follow up on my note. If outbound is not a priority this quarter, tell me and I will stop writing.\n\n{{sender_address}}\n{{unsubscribe}}"
        }
      ]
    }
  ]
}'
```

**Node.js**

```js
const campaign = await api('POST', '/v1/campaigns', {
  "name": "Q4 outbound to software teams",
  "timezone": "America/New_York",
  "steps": [
    {
      "delay_days": 0,
      "variants": [
        {
          "subject": "Outbound without the busywork",
          "body": "Hello,\n\nWe run outbound for B2B software teams, from sending domains to booked calls. Would a short call about your pipeline be useful?\n\n{{sender_address}}\n{{unsubscribe}}"
        }
      ]
    },
    {
      "delay_days": 3,
      "variants": [
        {
          "subject": "Following up",
          "body": "Hello,\n\nA quick follow up on my note. If outbound is not a priority this quarter, tell me and I will stop writing.\n\n{{sender_address}}\n{{unsubscribe}}"
        }
      ]
    }
  ]
});
console.log(campaign);
```

**Python**

```python
campaign = api("POST", "/v1/campaigns", {
    "name": "Q4 outbound to software teams",
    "timezone": "America/New_York",
    "steps": [
        {
            "delay_days": 0,
            "variants": [
                {
                    "subject": "Outbound without the busywork",
                    "body": "Hello,\n\nWe run outbound for B2B software teams, from sending domains to booked calls. Would a short call about your pipeline be useful?\n\n{{sender_address}}\n{{unsubscribe}}",
                },
            ],
        },
        {
            "delay_days": 3,
            "variants": [
                {
                    "subject": "Following up",
                    "body": "Hello,\n\nA quick follow up on my note. If outbound is not a priority this quarter, tell me and I will stop writing.\n\n{{sender_address}}\n{{unsubscribe}}",
                },
            ],
        },
    ],
})
print(campaign)
```

```json
{
  "id": "cmp_2LmQx7RtVw3ZbN1pEs9fKd",
  "name": "Q4 outbound to software teams",
  "status": "draft",
  "timezone": "America/New_York",
  "pause_reason": null,
  "pause_origin": null,
  "steps": [
    {
      "delay_days": 0,
      "variants": [
        {
          "subject": "Outbound without the busywork",
          "body": "Hello,\n\nWe run outbound for B2B software teams, from sending domains to booked calls. Would a short call about your pipeline be useful?\n\n{{sender_address}}\n{{unsubscribe}}"
        }
      ]
    },
    {
      "delay_days": 3,
      "variants": [
        {
          "subject": "Following up",
          "body": "Hello,\n\nA quick follow up on my note. If outbound is not a priority this quarter, tell me and I will stop writing.\n\n{{sender_address}}\n{{unsubscribe}}"
        }
      ]
    }
  ],
  "created_at": "2026-10-17T12:00:00.000Z",
  "launched_at": null
}
```

## 6. Import prospects

Each prospect needs an email address and a two letter `country`, which decides region gating. Each prospect that is imported costs one credit. Skipped prospects cost nothing.

**cURL**

```bash
export CAMPAIGN_ID="cmp_2LmQx7RtVw3ZbN1pEs9fKd"
curl -X POST "YOUR_API_BASE_URL/v1/campaigns/$CAMPAIGN_ID/prospects" \
  -H "Authorization: Bearer $COLDMEETINGS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "allow_risky": false,
  "prospects": [
    {
      "email": "jordan@northwind.example",
      "first_name": "Jordan",
      "company": "Northwind",
      "country": "US"
    },
    {
      "email": "sam@contoso.example",
      "first_name": "Sam",
      "company": "Contoso",
      "country": "CA"
    },
    {
      "email": "lena@fabrikam.example",
      "first_name": "Lena",
      "company": "Fabrikam",
      "country": "DE"
    }
  ]
}'
```

**Node.js**

```js
const imported = await api('POST', `/v1/campaigns/${campaign.id}/prospects`, {
  "allow_risky": false,
  "prospects": [
    {
      "email": "jordan@northwind.example",
      "first_name": "Jordan",
      "company": "Northwind",
      "country": "US"
    },
    {
      "email": "sam@contoso.example",
      "first_name": "Sam",
      "company": "Contoso",
      "country": "CA"
    },
    {
      "email": "lena@fabrikam.example",
      "first_name": "Lena",
      "company": "Fabrikam",
      "country": "DE"
    }
  ]
});
console.log(imported);
```

**Python**

```python
imported = api("POST", f"/v1/campaigns/{campaign["id"]}/prospects", {
    "allow_risky": False,
    "prospects": [
        {
            "email": "jordan@northwind.example",
            "first_name": "Jordan",
            "company": "Northwind",
            "country": "US",
        },
        {
            "email": "sam@contoso.example",
            "first_name": "Sam",
            "company": "Contoso",
            "country": "CA",
        },
        {
            "email": "lena@fabrikam.example",
            "first_name": "Lena",
            "company": "Fabrikam",
            "country": "DE",
        },
    ],
})
print(imported)
```

```json
{
  "imported": 2,
  "skipped": [
    {
      "email": "lena@fabrikam.example",
      "reason": "region_blocked"
    }
  ],
  "credits_charged": 2,
  "credits_remaining": 9848
}
```

Verification needs an outside service. If it cannot be reached, this call answers 503 and charges nothing.

## 7. Run preflight

Preflight lists everything that still blocks a launch. When `ready` is `true`, launch with `POST /v1/campaigns/{id}/launch`. Launching answers 503 and leaves the campaign a draft when the sending engine cannot be reached.

**cURL**

```bash
export CAMPAIGN_ID="cmp_2LmQx7RtVw3ZbN1pEs9fKd"
curl "YOUR_API_BASE_URL/v1/campaigns/$CAMPAIGN_ID/preflight" \
  -H "Authorization: Bearer $COLDMEETINGS_API_KEY"
```

**Node.js**

```js
const preflight = await api('GET', `/v1/campaigns/${campaign.id}/preflight`);
console.log(preflight);
```

**Python**

```python
preflight = api("GET", f"/v1/campaigns/{campaign["id"]}/preflight")
print(preflight)
```

```json
{
  "ready": false,
  "blockers": [
    "No mailbox sits on a domain with verified SPF, DKIM and DMARC."
  ]
}
```

## Next steps

- Read the [API reference](https://coldmeetings.com/docs/api.md) for every operation.
- Report delivery events and replies with [Events](https://coldmeetings.com/docs/api/events.md) and [Replies](https://coldmeetings.com/docs/api/replies.md).
- Plan for the limits in [Rate limits](https://coldmeetings.com/docs/rate-limits.md) and the codes in [Errors](https://coldmeetings.com/docs/errors.md).

## Related

- [Authentication](https://coldmeetings.com/docs/authentication.md)
- [API reference: Campaigns](https://coldmeetings.com/docs/api/campaigns.md)
- [Errors](https://coldmeetings.com/docs/errors.md)
