TL;DR

The full Winnr setup (buying domains, provisioning inboxes with SPF/DKIM/DMARC, starting warming, and pushing credentials into Instantly) is four API calls. Search domains with POST /v1/domains/search-bulk, purchase and queue setup with POST /v1/domains/purchase, enable warming with POST /v1/warming/enable, and export an Instantly-ready CSV with POST /v1/export. Provisioning runs in the background and is polled through the jobs API; after that, warm for at least 2 weeks (4 on a brand-new domain) before sending cold email. This guide walks through the entire pipeline with copy-paste Python and cURL.

A common developer question: can you buy domains, set up inboxes and push everything into Instantly end to end from your own code?

Yes. That's what the Winnr REST API is built for. The whole pipeline, from a list of desired domains to mailboxes loaded inside Instantly, is four API calls plus a polling loop. No DNS panel clicks, no spreadsheets of SMTP credentials, no manual CSV editing.

This guide is the runnable version of that workflow. Every code block in this article is copy-paste ready. By the end, you'll have a single Python script that takes a list of desired domain names and ends with a CSV uploaded to Instantly.

Why automate the full pipeline?

You can do all of this in the Winnr dashboard with mouse clicks. So why automate it?

Scale. Provisioning 5 domains by hand is fine. Provisioning 50 for a new agency client is a slow afternoon of clicking. Provisioning 500 for a SaaS rollout is impractical without automation. The same API calls that handle 5 domains handle 500 with a loop.

Reproducibility. When the setup lives in code, it's checked into git. When the setup lives in a person's memory of dashboard clicks, it's a liability. Code can be reviewed, replayed, and audited. Manual setup can't.

Embedding. If you're building a product on top of Winnr (an AI SDR tool, a sequencer, an agency dashboard, an internal tool), your users never touch the Winnr dashboard at all. They click a button in your product, your backend calls four Winnr APIs, and infrastructure appears. (We covered the embed pattern in detail in How to Embed Email Infrastructure in Your App.)

Reliability. Manual workflows skip steps. Did you remember to enable warming? Did you actually export the credentials before tearing down the dashboard tab? Code doesn't forget. The pipeline either runs to completion or it errors out somewhere you can see.

The four-call architecture

Here's the entire pipeline at a glance:

1
POST /v1/domains/search-bulk Check availability and pricing for up to 100 candidate domains
2
POST /v1/domains/purchase Charge your card, register the domains, configure DNS, and create mailboxes in one call. Returns a job_id per domain (or one job in async mode).
3
POST /v1/warming/enable Start built-in warming ($0.60 per mailbox per month) so mailboxes build sending reputation before going live
4
POST /v1/export with format: "instantly" Get a presigned CSV download URL, formatted for Instantly's bulk import

Plus one polling helper: GET /v1/jobs/{job_id} for tracking the async provisioning between steps 2 and 3.

That's the whole API surface for end-to-end setup. Everything else (reading the inbox, sending email, deleting mailboxes, swapping domains) is for after the pipeline runs.

Step 1: Get an API token

Every call needs a Bearer token. The fastest path: log into the Winnr dashboard, go to Settings → API Tokens, and click "Create Token". Save the value: the full token is shown once.

Token format: wnr_{account_id}_{secret}. Store it in your environment, never in source code.

export WINNR_TOKEN="wnr_acct123_sk_live_..."

If you'd rather create tokens programmatically (for example a separate token per environment or per service), there's a token endpoint:

curl -X POST https://api.winnr.app/v1/auth/tokens \
  -H "Authorization: Bearer $WINNR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "automation-pipeline", "permissions": ["read", "write"]}'

Step 2: Find available domains

Before buying, check availability and pricing in bulk. The search endpoint accepts up to 100 domains per call:

curl -X POST https://api.winnr.app/v1/domains/search-bulk \
  -H "Authorization: Bearer $WINNR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "domains": [
      "tryacmehq.com",
      "getacmehq.com",
      "acmehqmail.com",
      "acmehq.net",
      "acmehqteam.com"
    ]
  }'

Response (truncated):

{
  "data": {
    "results": [
      {"domain": "tryacmehq.com",  "available": true,  "price": 12.99},
      {"domain": "getacmehq.com",  "available": false, "price": null},
      {"domain": "acmehqmail.com", "available": true,  "price": 12.99},
      {"domain": "acmehq.net",     "available": true,  "price": 13.99},
      {"domain": "acmehqteam.com", "available": true,  "price": 12.99}
    ],
    "count": 5,
    "available_count": 4
  },
  "meta": { ... }
}

Prices above are illustrative; the API returns the live price for each name. Domains are a one-time purchase, priced per extension (a .com is about $13).

Filter the response down to available: true domains and you have your purchase list. If you want help generating candidate domains in the first place, there's also GET /v1/domains/suggest?keywords=acme, which returns suggestions based on a brand keyword.

TLD pricing varies

The safest TLDs for cold email are .com, .net, .org, and .co. Avoid .xyz, .click, .top, and other novelty TLDs, which carry a poor reputation with spam filters. We covered this in detail in Best TLDs for Cold Email.

Step 3: Buy domains and queue inbox setup (one call)

This is the call that does the most work. POST /v1/domains/purchase charges your card, registers each domain, configures DNS (MX, SPF, DKIM, DMARC), and creates the mailboxes you specify, all from one request. Prices are resolved server-side, so you don't pass them. In the default synchronous mode the response gives you a job_id per domain to track the provisioning that runs after the payment clears. For more than two or three domains, add "async": true: the call returns one job_id immediately and the charge and registration run in the background, which avoids a client-side timeout on a large order.

The trick is the users array nested inside each domain entry. List the mailboxes you want and they're created as part of the same workflow, so you don't need a separate /v1/email-users/bulk call.

curl -X POST https://api.winnr.app/v1/domains/purchase \
  -H "Authorization: Bearer $WINNR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "domains": [
      {
        "domain": "tryacmehq.com",
        "register": true,
        "setup_dns": true,
        "setup_email": true,
        "users": [
          {"name": "Sarah Chen", "username": "sarah"},
          {"name": "James Rivera", "username": "james"},
          {"name": "Maria Santos", "username": "maria"}
        ]
      },
      {
        "domain": "acmehqmail.com",
        "register": true,
        "setup_dns": true,
        "setup_email": true,
        "users": [
          {"name": "Alex Kim", "username": "alex"},
          {"name": "David Park", "username": "david"},
          {"name": "Priya Singh", "username": "priya"}
        ]
      }
    ]
  }'

Response:

{
  "data": {
    "payment": {
      "success": true,
      "invoice_id": "in_1QrX...",
      "amount_paid": 25.98
    },
    "domains": [
      {"domain": "tryacmehq.com",  "domain_id": "dom_abc", "job_id": "job_abc", "status": "queued"},
      {"domain": "acmehqmail.com", "domain_id": "dom_def", "job_id": "job_def", "status": "queued"}
    ]
  },
  "meta": { ... }
}

Two domains, six mailboxes, paid for and queued for full provisioning. Now you wait for the jobs to finish.

Idempotency

If you call /v1/domains/purchase twice with the same domain list, the second call will not double-charge you: domains the account has already paid for are skipped. That makes it safe to retry on network errors.

Already own the domains? Use register: false

If you already own the domains (bring your own domains) and just want Winnr to host the mailboxes, set register: false. The domain goes through checkout unbilled (setup only). For DNS you'll point nameservers to Winnr; if your domains are on Cloudflare or you want to add records yourself, POST /v1/domains/connect (with a Cloudflare token or manual_dns: true) is the better route.

{
  "domains": [
    {
      "domain": "acmecorphq.com",
      "register": false,
      "setup_dns": true,
      "setup_email": true,
      "users": [...]
    }
  ]
}

Step 4: Poll jobs until provisioning completes

Each domain in the purchase response comes with its own job_id (in async mode, poll the single job first; its result lists the per-domain jobs). Poll GET /v1/jobs/{job_id} to track progress. A domain setup job moves through these steps:

Step What's happening
RegistrationThe domain is registered
DNS zone and recordsMX, SPF, DKIM, DMARC records published
Mailbox creationMailboxes provisioned with IMAP/SMTP credentials

Timing varies with the registry and the size of the order. In larger orders, domains are registered a few minutes apart and show as pending until each one starts, so poll rather than assume.

The job response shape:

curl https://api.winnr.app/v1/jobs/job_abc \
  -H "Authorization: Bearer $WINNR_TOKEN"

# Response
{
  "data": {
    "job_id": "job_abc",
    "type": "domain_setup",
    "status": "completed",
    "progress": {
      "registration": "completed",
      "dns_zone": "completed",
      "dns_records": "completed"
    },
    "result": { ... },
    "error": null,
    "created_at": "2026-04-20T14:00:00Z"
  },
  "meta": { ... }
}

Statuses go pending → in_progress → completed or failed; on failure, error.message says why. A reasonable polling pattern: check every 5 seconds for the first 30 seconds, then back off to every 15 seconds.

Step 5: Enable warming

Don't skip this step. Sending cold email from a brand-new mailbox with zero sending reputation is the fastest way to land in spam, or to get the domain blocklisted before it's even useful. Winnr's built-in email warming exchanges realistic emails, with replies, between your mailbox and a shared warming network of real mailboxes, ramping volume up gradually (up to 20 warmup emails a day). It costs $0.60 per mailbox per month, and the first month is charged when you enable it.

curl -X POST https://api.winnr.app/v1/warming/enable \
  -H "Authorization: Bearer $WINNR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "user_ids": ["usr_1", "usr_2", "usr_3", "usr_4", "usr_5", "usr_6"],
    "settings": {"emails_per_day": 20, "rampup_speed": "normal"}
  }'

Get the mailbox IDs from GET /v1/email-users?filter[domain]=tryacmehq.com once provisioning completes. If you're enabling hundreds of mailboxes at once, use POST /v1/warming/enable-async to avoid a request timeout; it returns a job_id you poll the same way as Step 4. Plan on at least 2 weeks of warming before cold sending, 4 on a brand-new domain.

For background on how warming works and how it compares to warming inside a sequencer, read built-in warming vs sequencer warmup pools vs standalone warmup tools.

Step 6: Export to Instantly

Winnr produces a CSV in Instantly's bulk-import column format, including Daily Limit, Warmup Enabled, and per-mailbox SMTP/IMAP credentials.

curl -X POST https://api.winnr.app/v1/export \
  -H "Authorization: Bearer $WINNR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "format": "instantly",
    "domains": ["tryacmehq.com", "acmehqmail.com"]
  }'

Response:

{
  "data": {
    "download_url": "https://...presigned-url...",
    "count": 6,
    "format": "instantly",
    "expires_in": 900,
    "skipped_count": 0
  },
  "meta": { ... }
}

The download_url is a presigned URL valid for 15 minutes (expires_in is in seconds), so download it straight away. Only mailboxes that have finished provisioning are exported; any still pending are counted in skipped_count. The columns line up with Instantly's bulk upload:

Email,First Name,Last Name,IMAP Username,IMAP Password,IMAP Host,IMAP Port,
SMTP Username,SMTP Password,SMTP Host,SMTP Port,Daily Limit,
Warmup Enabled,Warmup Limit,Warmup Increment
sarah@tryacmehq.com,Sarah,Chen,sarah@tryacmehq.com,xY7q...,mail.your-host.example,993,
sarah@tryacmehq.com,xY7q...,mail.your-host.example,465,15,FALSE,35,1
james@tryacmehq.com,James,Rivera,...

Drop this file into Instantly's "Add accounts" → "Bulk upload (CSV)" flow. The daily limit is pre-set to 15 cold emails per mailbox, and Instantly's own warmup is exported switched off, because the mailboxes are already warming on Winnr and running both would double-warm them and push total sending past about 50 a day. If you'd rather warm in Instantly instead, disable Winnr warming and flip the column.

The full end-to-end Python script

Here's the entire pipeline as a single, runnable Python file. Set WINNR_TOKEN in your environment, edit the DESIRED_DOMAINS and USERS_PER_DOMAIN constants, and run it.

"""
buy_provision_warm_export.py

End-to-end Winnr API pipeline:
  1. Search candidate domains
  2. Purchase available ones + provision mailboxes in one call
  3. Poll jobs until provisioning completes
  4. Enable warming
  5. Export an Instantly-ready CSV
"""
import os
import time
import requests

API = "https://api.winnr.app/v1"
TOKEN = os.environ["WINNR_TOKEN"]
HEADERS = {
    "Authorization": f"Bearer {TOKEN}",
    "Content-Type": "application/json",
}

DESIRED_DOMAINS = [
    "tryacmehq.com",
    "getacmehq.com",
    "acmehqmail.com",
    "acmehq.net",
    "acmehqteam.com",
]

USERS_PER_DOMAIN = [
    {"name": "Sarah Chen",   "username": "sarah"},
    {"name": "James Rivera", "username": "james"},
    {"name": "Maria Santos", "username": "maria"},
]

def search_available(domains):
    r = requests.post(f"{API}/domains/search-bulk", headers=HEADERS,
                      json={"domains": domains})
    r.raise_for_status()
    return [d for d in r.json()["data"]["results"] if d["available"]]

def purchase_and_provision(available):
    payload = {
        "domains": [
            {
                "domain": d["domain"],
                "register": True,
                "setup_dns": True,
                "setup_email": True,
                "users": USERS_PER_DOMAIN,
            }
            for d in available
        ]
    }
    r = requests.post(f"{API}/domains/purchase", headers=HEADERS, json=payload)
    r.raise_for_status()
    body = r.json()["data"]
    print(f"  Charged ${body['payment']['amount_paid']}")
    return [(d["domain"], d["job_id"]) for d in body["domains"]]

def wait_for_jobs(domain_jobs, timeout=3600):
    pending = dict(domain_jobs)
    start = time.time()
    interval = 5
    while pending and time.time() - start < timeout:
        for domain, job_id in list(pending.items()):
            job = requests.get(f"{API}/jobs/{job_id}", headers=HEADERS).json()["data"]
            if job["status"] == "completed":
                print(f"  ✓ {domain} ready")
                pending.pop(domain)
            elif job["status"] == "failed":
                raise RuntimeError(f"{domain} failed: {(job.get('error') or {}).get('message')}")
        if pending:
            time.sleep(interval)
            interval = min(interval + 2, 15)
    if pending:
        raise TimeoutError(f"Still pending: {list(pending)}")

def mailbox_ids(domains):
    ids = []
    for domain in domains:
        r = requests.get(f"{API}/email-users", headers=HEADERS,
                         params={"filter[domain]": domain})
        r.raise_for_status()
        ids += [u["id"] for u in r.json()["data"]]
    return ids

def enable_warming(domains):
    r = requests.post(f"{API}/warming/enable", headers=HEADERS,
                      json={"user_ids": mailbox_ids(domains),
                            "settings": {"emails_per_day": 20,
                                         "rampup_speed": "normal"}})
    r.raise_for_status()

def export_for_instantly(domains):
    r = requests.post(f"{API}/export", headers=HEADERS,
                      json={"format": "instantly", "domains": domains})
    r.raise_for_status()
    data = r.json()["data"]
    print(f"  Exported {data['count']} mailboxes")
    return data["download_url"]

if __name__ == "__main__":
    print("1/5  Searching for available domains...")
    available = search_available(DESIRED_DOMAINS)
    print(f"     {len(available)} available out of {len(DESIRED_DOMAINS)}")

    print("2/5  Purchasing + provisioning...")
    domain_jobs = purchase_and_provision(available)

    print("3/5  Waiting for provisioning to complete...")
    wait_for_jobs(domain_jobs)

    domain_names = [d for d, _ in domain_jobs]

    print("4/5  Enabling warming...")
    enable_warming(domain_names)

    print("5/5  Exporting Instantly CSV...")
    csv_url = export_for_instantly(domain_names)

    print(f"\nDone. Download CSV: {csv_url}")
    print("Upload it via Instantly > Email Accounts > Bulk Upload.")
    print("Warm for at least 2 weeks (4 on a new domain) before cold sending.")

Run it:

$ export WINNR_TOKEN="wnr_acct123_sk_live_..."
$ python buy_provision_warm_export.py

1/5  Searching for available domains...
     4 available out of 5
2/5  Purchasing + provisioning...
  Charged $52.96
3/5  Waiting for provisioning to complete...
  ✓ tryacmehq.com ready
  ✓ acmehqmail.com ready
  ✓ acmehq.net ready
  ✓ acmehqteam.com ready
4/5  Enabling warming...
5/5  Exporting Instantly CSV...
  Exported 12 mailboxes

Done. Download CSV: https://...presigned-url...
Upload it via Instantly > Email Accounts > Bulk Upload.
Warm for at least 2 weeks (4 on a new domain) before cold sending.

Once written, the script runs unattended. Wall-clock time depends on registration and the size of the order, which is why it polls instead of sleeping. For orders of more than two or three domains, switch the purchase call to "async": true and poll the purchase job first.

Other sequencer formats (Smartlead, Reply, etc.)

The format parameter on /v1/export determines the column layout. If your sequencer is in this list, the CSV imports without column mapping.

Sequencer format value Notes
InstantlyinstantlyDaily Limit pre-set to 15; Warmup Enabled exported as FALSE
SmartleadsmartleadDefault format, also matches Smartlead bulk import
Reply.ioreply.ioReply-specific column names
Snov.iosnov
Saleshandysaleshandy
EmailBisonemailbison
Woodpeckerwoodpecker
Mailshakemailshake
Salesforgesalesforge
ManyReachmanyreach
Reachinboxreachinbox
Smartreachsmartreach
Plusvibeplusvibe
Warmywarmy / warmysender
Masterinboxmasterinbox
Leadengineleadengine
Mailleadmaillead
AICloseraicloser
Sendkitsendkit
Pipelimepipelime
Generic CSVdefaultStandard 12-column SMTP/IMAP layout

For the full current list, call GET /v1/export/formats. Need a sequencer that isn't here? Email info@winnr.app to request a format, or use default, which has standard SMTP/IMAP columns.

Real-world patterns: agencies, AI SDR, sequencer platforms

Lead-gen agencies

An agency takes on a new client, runs buy_provision_warm_export.py with the client's brand keyword, and gets a CSV ready to drop into Instantly under the client's workspace. Run it per client and the provisioning work stops scaling with headcount.

Keep every client on their own domains so one client's sending never affects another's reputation, and tag each client's domains with domain tags. Exports, warming and reporting can then be filtered per client.

AI SDR platforms

AI SDR products call this same pipeline when a user signs up, but they don't stop at the export. They keep the credentials inside their own database and have their AI agent send through the mailboxes via Winnr's POST /v1/email-users/{id}/inbox/send endpoint and read replies via GET /v1/email-users/{id}/inbox. Instantly never enters the picture; the agent is the sequencer. See How to build an AI SDR pipeline with Winnr for the deeper agent integration.

Sequencer platforms

A sequencer platform that wants to offer mailboxes inside its own product can call the same four endpoints when a user clicks "add mailboxes", without operating mail servers or managing DKIM keys itself. (See how to embed email infrastructure in your app for that pattern.)

Internal automations

Not every integration is a SaaS product. Teams also run this script as an internal cron job: every Monday, buy N new domains for outbound campaigns scheduled to start in 3 or 4 weeks, so there are always warmed mailboxes ready. The pipeline runs unattended and can post results to Slack.

Gotchas and best practices

Warm for at least 2 weeks before sending cold email (4 on a brand-new domain). The CSV is ready to import the moment provisioning completes, but the mailboxes are not ready to send. Importing into Instantly on day one is fine; just don't activate cold campaigns until warming has built reputation, and don't also turn on Instantly's warmup for the same mailboxes. When you start, send 10 to 20 cold emails per mailbox per day and keep the total, cold plus warming, at about 50. The cold email without warmup article explains why this matters.

Use register: true only when you actually want Winnr to buy the domain. If you already own the domain at a different registrar, set register: false (setup only, no registration charge), or use POST /v1/domains/connect for the Cloudflare and manual-DNS flows.

Retry safely. The purchase endpoint is idempotent: domains already paid for are skipped, so a retry does not charge again. Before retrying after a timeout, check GET /v1/jobs to see whether the first attempt went through.

Spread mailboxes across domains. Don't put 30 mailboxes on one domain. A few mailboxes per domain (2 to 5) is the usual practice for cold email; this script uses 3. Use 10 domains for 30 mailboxes, not 1. The USERS_PER_DOMAIN constant in the script enforces this if you keep it at 3.

Generate realistic usernames. Use POST /v1/email-users/generate-names if you need plausible names without hardcoding them. Avoid generic patterns like user1, user2, info, contact, noreply; these are spam tells.

Poll, don't sleep blindly. Don't time.sleep(120) and assume jobs are done. Poll /v1/jobs/{id} until status is completed. Some domains finish quickly and some take longer, and the export only includes mailboxes that actually exist.

Store the Winnr IDs. Save domain_id and email user IDs in your own database. You'll need them for warming controls, sending, and eventual deletion when a customer churns.

Prefer natural language?

If you're using Claude Code, Cursor, or another AI coding assistant, the same operations are exposed as MCP tools. You can ask "buy 5 cold email domains for client Acme, set up 3 mailboxes per domain, enable warming, and export to Instantly" and the AI calls the underlying API, showing you a quote to confirm before anything is charged. See the Winnr MCP server docs for the 55 available tools.

Frequently asked questions

How much does API access cost?

API access is included in both Winnr plans at no extra cost. You pay for domains and mailboxes exactly as in the dashboard: Startup is $69/mo for 50 mailboxes, Enterprise $189/mo for 200, extra mailboxes are $1 per mailbox per month, domains are a one-time purchase (about $13 for a .com), and warming is $0.60 per mailbox per month. There are no per-call API fees and no separate API tier. See the pricing page for details.

Can I really buy domains, set up inboxes, and export to Instantly with one script?

Yes. That's the script in the full end-to-end Python script section above: four API calls plus a polling loop. Once written, it runs unattended; how long provisioning takes depends on registration and the size of the order.

How long until the mailboxes are usable in Instantly?

The mailboxes are connectable in Instantly as soon as provisioning completes. They are ready to send cold email after at least 2 weeks of warming (4 on a brand-new domain). You can import them into Instantly straight away, but don't activate outbound campaigns until warming is done, and don't run Instantly's warmup on mailboxes that are already warming on Winnr.

Does Winnr's CSV format actually work with Instantly's bulk upload?

Yes. The CSV uses Instantly's bulk upload columns: Email, First Name, Last Name, IMAP/SMTP credentials, Daily Limit, Warmup Enabled, Warmup Limit, and Warmup Increment. No column mapping is required. Drop the file into Instantly → Email Accounts → Bulk Upload.

What if I want to use Smartlead instead of Instantly?

Pass "format": "smartlead" instead of "format": "instantly" on the export call. Same workflow, different column layout. See the sequencer formats table for the full list. There's also a default format with standard SMTP/IMAP columns that works with most tools.

Can I bring my own domains instead of buying through Winnr?

Yes. Set register: false on the purchase request and you won't be charged a registration fee, or use POST /v1/domains/connect, which supports nameserver delegation, a Cloudflare API token, or manual DNS records. Connecting your own domains is free. The rest of the pipeline (mailbox creation, warming, export) is identical.

What happens if the purchase API call fails partway through?

The purchase endpoint is idempotent. If your card was charged but the connection dropped before you got the response, calling the same endpoint again will not double-charge, because domains already paid for are skipped. Check GET /v1/jobs first to see what the original call started, and use async mode for larger orders so the request can't time out mid-charge.

Can I do this from Node.js / TypeScript / Go / Ruby?

Yes. The Winnr API is plain REST, so any language with an HTTP client works. The Python examples in this guide translate line-for-line to fetch in Node, net/http in Go, or Net::HTTP in Ruby. There are no language-specific SDKs you need to install.

What's the difference between this and using the Winnr MCP server?

The MCP server wraps the same REST API in a tool-calling protocol designed for AI agents (Claude, Cursor, Windsurf). If you're writing application code, use the REST API directly; it's simpler. If you're driving the workflow from an AI assistant in natural language ("buy 5 domains for client Acme and export to Instantly"), use the MCP server. Both hit the same endpoints. See winnr.app/mcp for the MCP details.

Where's the full API reference?

The complete OpenAPI spec lives at app.winnr.app/docs. Every endpoint, request schema, response shape, and error code is documented there. This guide covers the four endpoints you need for end-to-end provisioning; the docs cover the full surface, including inbox, sending, warming controls and webhooks.

Ready to run the pipeline? Create a Winnr account (no card needed to sign up), choose a plan for the SMTP mailboxes built for cold email, generate an API token, and the script in this article runs as written. The full API reference covers every endpoint if you need to go beyond the four-call setup.