From an API key to a running campaign.
Four requests: connect a mailbox, import contacts, create a campaign, then check, test and launch. Create your key in Settings, then API & MCP.
Authorization: Bearer gtm_.... Replace api.yourdomain.com with your API address. Times are ISO 8601 in UTC.// A tiny helper used in the JavaScript examples
const BASE = "https://api.yourdomain.com/v1";
async function api(method, path, body) {
const res = await fetch(BASE + path, {
method,
headers: { Authorization: "Bearer " + process.env.GTM_KEY, "content-type": "application/json" },
body: body ? JSON.stringify(body) : undefined,
});
const data = await res.json();
if (!res.ok) throw new Error(data.error); // errors are plain sentences
return data;
} -
Connect a mailbox
Use an app password for Google Workspace or Zoho, or any SMTP/IMAP server. We test the login before saving. New mailboxes start with a slow start; add "slowStart": false if it's already warmed up.
API=https://api.yourdomain.com/v1 KEY="Authorization: Bearer gtm_..." curl -X POST $API/mailboxes/smtp -H "$KEY" -H 'content-type: application/json' \ -d '{"email":"sam@acme.com","name":"Sam","password":"app-password","provider":"google"}'await api("POST", "/mailboxes/smtp", { email: "sam@acme.com", name: "Sam", password: "app-password", provider: "google", }); -
Import contacts
Send rows directly, or a Google Sheets link. Every address is checked. The response tells you how many were imported and what was left out.
curl -X POST $API/contacts/import -H "$KEY" -H 'content-type: application/json' \ -d '{"listName":"SaaS founders","rows":[ {"email":"ana@startup.io","first_name":"Ana","company":"Startup"}, {"email":"raj@fintech.in","first_name":"Raj","company":"Fintech"}]}' # {"listId":"...","imported":2,"verification":{"valid":2,"role":0,"disposable":0,"no_mail_server":0}, ...}const { listId, imported, verification } = await api("POST", "/contacts/import", { listName: "SaaS founders", rows: [{ email: "ana@startup.io", first_name: "Ana", company: "Startup" }], }); -
Create the campaign
A campaign starts as a draft. Steps with an empty subject go out as replies in the same thread.
curl -X POST $API/campaigns -H "$KEY" -H 'content-type: application/json' -d '{ "name": "Founders Q4", "listIds": ["LIST_ID"], "mailboxIds": ["MAILBOX_ID"], "dailyLimit": 30, "window": {"start":"10:00","end":"17:00","tz":"Asia/Kolkata","days":["Mon","Tue","Wed","Thu","Fri"]}, "steps": [ {"waitDays":0,"subject":"{Quick|Short} question, {{first_name|there}}","body":"Hi {{first_name|there}},\n\n...","format":"plain"}, {"waitDays":3,"subject":"","body":"Bumping this up, {{first_name|there}}.","format":"plain"} ]}'const { campaign } = await api("POST", "/campaigns", { name: "Founders Q4", listIds: [listId], mailboxIds: [mailboxId], dailyLimit: 30, window: { start: "10:00", end: "17:00", tz: "Asia/Kolkata", days: ["Mon","Tue","Wed","Thu","Fri"] }, steps: [ { waitDays: 0, subject: "{Quick|Short} question, {{first_name|there}}", body: "Hi {{first_name|there}},\n\n...", format: "plain" }, { waitDays: 3, subject: "", body: "Bumping this up, {{first_name|there}}.", format: "plain" }, ], }); -
Check, test, then launch
The check lists anything that would block the launch. Send yourself every step as a test, then launch. An account's first campaign gets a short content review before it starts.
curl $API/campaigns/CAMPAIGN_ID/check -H "$KEY" curl -X POST $API/campaigns/CAMPAIGN_ID/test -H "$KEY" -H 'content-type: application/json' \ -d '{"to":"you@yourcompany.com","allSteps":true}' curl -X POST $API/campaigns/CAMPAIGN_ID/launch -H "$KEY"const check = await api("GET", `/campaigns/${id}/check`); if (check.blocking.length) throw new Error(check.blocking.join(", ")); await api("POST", `/campaigns/${id}/test`, { to: "you@yourcompany.com", allSteps: true }); await api("POST", `/campaigns/${id}/launch`);
GET /inbox or the email.replied webhook. Answer with POST /inbox/:id/reply. Check quotas and rate-limit headroom with GET /limits. The full reference is at /v1/docs on your API.
Try it on your own mailbox.
Your first month is free. Create a key and run the four steps above.