@clawhub-sheksushant-19d2708430
Interact with the SalesBlink for cold email and sales outreach automation. Use when the user needs to send cold emails, manage email lists, sequences, templa...
--- name: cold-email-salesblink description: > Interact with the SalesBlink for cold email and sales outreach automation. Use when the user needs to send cold emails, manage email lists, sequences, templates, senders, leads, inbox replies, or campaign analytics via the SalesBlink API. Also use for bulk contact imports, workspace management, or deliverability testing via HTTP requests. compatibility: > Requires network access to run.salesblink.io and a SalesBlink API key. Supports any HTTP client (curl, Node.js fetch, Python requests, PowerShell, etc.). --- # SalesBlink Public REST API v1.0.0 ## When to use this skill Use this skill when the user wants to: - Create, update, or manage email lists, sequences, templates, or senders - Add, update, move, or remove contacts/leads - Send or reply to emails via the inbox - Check campaign analytics (opens, clicks, replies, sent) - Set up outreach campaigns end-to-end - Manage workspaces, users, folders, or deliverability tests - Make any HTTP request to `run.salesblink.io/api/public/v1.0.0` ## Gotchas - **ID types matter**: Templates and contact archive use MongoDB ObjectId (24-char hex). All other entities use UUID v4. - **messageId** is the RFC822 Message-ID (e.g. `<[email protected]>`) or Microsoft Graph ID. **Crucial:** Always URL-encode this ID when using it as a path parameter (e.g. in `/inbox/:messageId/thread`). This is distinct from the internal UUID `id`. - **`senders` is a comma-separated string**, not an array. It can mix sender IDs and folder IDs — the server auto-detects each. - **Sequence `steps` fully replace on PATCH**. Send the complete desired array. - **Verification flags are IRREVERSIBLE**: `verification`, `archive_invalid`, `archive_risky` on lists can only be turned ON, never OFF. - **Sequences default to paused**: If `paused` is omitted on create, it defaults to `true`. - **`launchTimingMode: "now"` starts in 5 minutes**, not instantly. - **Template attachments use FormData field `attachment`** (not `attachments`). Max 3 per template. - **Remove template attachments via `remove_attachments`** array of file **names**. - **Adding SMTP sender requires `from_email`**, not `email`. - **If an endpoint for a specific task is not mentioned then tell the user that the endpoint is not available** - **If user does not have a list, ask them for a CSV file, or list of lead emails with data.** - **If email sender is not connected, help them connect one using APIs.** - **When asked to create a sequence or campaign for cold email outreach, first ask them about their ICP, Offer, and other details.** ## Base URL `https://run.salesblink.io/api/public/v1.0.0` ## Authentication Ask the user for their SalesBlink API key: `https://run.salesblink.io/account/integration/api` Pass it in every request as the `Authorization` header (no "Bearer" prefix): **Header:** `Authorization: YOUR_API_KEY` ## Rate Limits | Method | Limit | Window | | ------------- | ----- | ---------- | | GET | 30 | per minute | | POST / PATCH | 15 | per minute | | PUT (archive) | 10 | per minute | On `429 Too Many Requests`: wait at least 60 seconds before retrying. For batch operations, insert a 4-second delay between requests. ## Pagination Most list endpoints use `limit` (max 100) and `skip`. Activity endpoints (`/sent`, `/opens`, `/clicks`, `/replies`) use `per_page` (max 100) and `page` (1-indexed). Always paginate. Never assume a single request returns all data. ## Endpoint Categories Read the relevant reference file before performing operations in that domain: - **Lists & contacts/leads** → [references/lists.md](references/lists.md) and [references/contacts.md](references/contacts.md) - Use these endpoints when the user wants to fetch or manage lists that contain leads/contacts. A list is a container for contacts/leads. Each contact/lead contains fields like Email, First_Name, Last_Name, Phone, Company, Title, and custom fields. Contacts are added to lists in batches (up to 500 per request), can be moved between lists, updated, or removed. - **Email templates** → [references/templates.md](references/templates.md) - Use these endpoints when the user wants to create or manage reusable email templates. A template has a name, subject_line, and HTML content that supports merge variables like {{first_name}} and {{company}}. Templates can have up to 3 attachments and are referenced by sequences when building outreach steps. - **Sequences & email campaigns** → [references/sequences.md](references/sequences.md) - Use these endpoints when the user wants to create or manage automated email campaigns (sequences). A sequence connects lists (who to email), senders (which accounts send), and templates (what to send) into a timed step-by-step workflow. Steps alternate between email sends and delay periods. Sequences can be launched, paused, resumed, cloned, or archived. - **Senders & OAuth** → [references/senders.md](references/senders.md) - Use these endpoints when the user wants to connect or manage email sending accounts. A sender is an email account (SMTP/IMAP or OAuth-connected Gmail/Outlook) that sends emails on behalf of sequences. Multiple senders can be assigned to a sequence. Senders can also be organized into folders. - **Inbox & replies** → [references/inbox.md](references/inbox.md) - Use these endpoints when the user wants to view or interact with email conversations. The inbox contains reply threads, sent emails, scheduled emails, and drafts. Each thread has a messageId. The user can reply to a lead's email, mark messages as read/unread, or classify outcomes. - **Activity tracking** → [references/activity.md](references/activity.md) - Use these endpoints when the user wants to query engagement events. The system tracks four event types: sent (emails sent), opens (emails opened), clicks (links clicked), and replies (responses received). Events can be filtered by sequence, recipient email, and date range. - **Users & workspaces** → [references/organization.md](references/organization.md) - Use these endpoints when the user wants to manage team membership or workspaces. A workspace is an account boundary. Users have roles (client, user, admin, developer). Only owners and admins can invite users or create workspaces. - **Folders** → [references/folders.md](references/folders.md) - Use these endpoints when the user wants to organize resources into folders. Folders have a type (list, template, sequence, or email-sender) and group related resources together for easier management. - **Domains, signatures & warmup links** → [references/account-config.md](references/account-config.md) - Use these endpoints when the user wants to view account-level configuration. Custom tracking domains are used for click tracking in emails. Signatures are appended to outgoing emails. Warmup links are used in email warmup processes. - **Reports** → [references/reports.md](references/reports.md) - Use these endpoints when the user wants to fetch aggregated activity reports over a date range. Reports combine data across campaigns into summary views. - **Inbox placement tests** → [references/inbox-placement.md](references/inbox-placement.md) - Use these endpoints when the user wants to test email deliverability. An inbox placement test sends a test email to seed email addresses across providers (Gmail, Outlook, etc.) and reports whether the email landed in inbox, spam, promotions, or other tabs. Tests can be one-time or recurring. - **End-to-end workflow examples** → [references/workflows.md](references/workflows.md) - Use this reference when the user wants to set up a complete outreach campaign from scratch. It shows the full chain: create list → add contacts → create templates → fetch senders → create sequence → launch. ## Error Handling Always check the `success` boolean in the response body. A `200` status can still return `{ success: false, message: "..." }`. | Status | Meaning | Action | | ------ | ------------ | ----------------------------------------------------- | | 200 | Success | Check `success` field | | 400 | Bad request | Re-check payload structure against the reference file | | 401 | Unauthorized | Verify API key | | 403 | Forbidden | Insufficient permissions (role too low) | | 404 | Not found | Verify the ID / endpoint | | 409 | Conflict | Resource already exists or connection failed | | 429 | Rate limited | Wait 60s, then retry | | 500 | Server error | Retry once after 10s | FILE:references/workflows.md # Workflow Examples ## End-to-End Campaign Setup Goal: Build a complete outreach campaign from scratch. ### Step 1 — Create a list **POST** `/lists` Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "name": "Q1 Prospects", "removeDuplicates": { "inThisList": true, "inOtherLists": true } } ``` Extract `LIST_ID` from the response. ### Step 2 — Add leads to the list **POST** `/contacts` Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "list_id": "LIST_ID_HERE", "contacts": [ { "Email": "[email protected]", "First_Name": "Alice", "Company": "Corp Inc" }, { "Email": "[email protected]", "First_Name": "Bob", "Company": "Startup IO" } ], "remove_duplicates": true } ``` ### Step 3 — Create email templates **POST** `/templates` Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "name": "Intro Email", "subject_line": "Quick question, {{first_name}}", "content": "<p>Hi {{first_name}},</p><p>I noticed {{company}} is scaling fast...</p>" } ``` Extract `TEMPLATE_1_ID` (MongoDB ObjectId). Repeat for follow-up templates. ### Step 4 — Fetch available senders **GET** `/senders` Headers: - `Authorization`: `YOUR_API_KEY` Extract target `SENDER_ID` (UUID). ### Step 5 — Create the sequence **POST** `/sequences` Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "name": "Q1 Outbound Campaign", "senders": "SENDER_ID_HERE", "lists": ["LIST_ID_HERE"], "steps": [ { "type": "email", "template_id": "TEMPLATE_1_ID" }, { "type": "delay", "days": 3 }, { "type": "email", "template_id": "TEMPLATE_2_ID" } ], "paused": false, "launchTimingMode": "now" } ``` ## Clone and Modify Workflow Goal: Duplicate an existing sequence and tweak it. ### 1. Clone the sequence (creates paused copy) **POST** `/sequences/:id/clone` Headers: - `Authorization`: `YOUR_API_KEY` Extract `NEW_SEQ_ID` from response. ### 2. Update the cloned sequence with new settings **PATCH** `/sequences/:id` Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "name": "Q1 Campaign — Variant B", "steps": [ { "type": "email", "template_id": "NEW_TEMPLATE_ID" }, { "type": "delay", "days": 5 }, { "type": "email", "template_id": "FOLLOW_UP_TEMPLATE_ID" } ] } ``` ### 3. Launch when ready **PATCH** `/sequences/:id` Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "paused": false, "launchTimingMode": "now" } ``` ## Archive Cleanup Workflow Goal: Clean up old campaigns, lists, and templates. ### Archive a sequence (pauses it and removes pending tasks) **PUT** `/sequences/:id/archive` Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "archived": true } ``` ### Archive the associated list **PUT** `/lists/:id/archive` Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "archived": true } ``` ### Archive templates **PUT** `/templates/:id/archive` Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "archived": true } ``` > Archiving a sequence automatically pauses it and removes pending email tasks. Archiving a list pauses it if active. Archiving a contact removes its pending tasks from all sequences. FILE:references/reports.md # Reports ## Endpoints | Endpoint | Method | Description | |----------|--------|-------------| | `/reports` | GET | Retrieve activity reports with aggregated data | ## Get Reports **GET** `/reports` Headers: - `Authorization`: `YOUR_API_KEY` Query params: | Param | Type | Description | |-------|------|-------------| | `from` | integer | Start date timestamp (milliseconds) | | `to` | integer | End date timestamp (milliseconds) | | `limit` | integer | Max 100 (default enforced server-side) | | `skip` | integer | Offset | > The endpoint maps `from`/`to` to a date range filter internally. Both are optional; omitting them returns all available report data. **GET** `/reports?from=1715000000000&to=1717600000000` Headers: - `Authorization`: `YOUR_API_KEY` FILE:references/inbox.md # Inbox & Outreach ## Endpoints | Endpoint | Method | Description | | -------------------------- | ------ | ----------------------------------------------- | | `/inbox` | GET | Retrieve inbox threads | | `/inbox/:messageId/thread` | GET | Get all messages in a specific thread | | `/inbox/:messageId/reply` | POST | Reply to a lead's email | | `/inbox/:messageId` | PATCH | Mark as read/unread, set outcome classification | ## Get Inbox **GET** `/inbox` Headers: - `Authorization`: `YOUR_API_KEY` Query params: | Param | Type | Description | | ---------- | ------- | --------------------------------------------------------- | | `type` | string | `all` (replies, default), `draft`, `scheduled`, or `sent` | | `limit` | integer | Max 100 (default: 10) | | `skip` | integer | Offset (default: 0) | | `sequence` | string | Filter by sequence UUID | | `outcome` | string | Filter by outcome classification | | `search` | string | Search in body, subject, or email address | | `date` | string | Date range as `startTimestamp-endTimestamp` | | `sender` | string | Filter by sender ID | | `owned_by` | string | Filter by user email (owners/admins only) | ``` GET /inbox?type=all&limit=50&skip=0 GET /inbox?type=sent&sequence=SEQ_ID&limit=100 GET /inbox?search=acme&limit=20 ``` Response includes `data.result` (thread array), `totalCount`, `count`, and `messageIDs`. ## Get Thread **GET** `/inbox/:messageId/thread` Headers: - `Authorization`: `YOUR_API_KEY` Returns all messages in a conversation thread, sorted newest first. ## Reply to Email **POST** `/inbox/:messageId/reply` Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "content": "<p>Thanks for getting back to me! Let's schedule a call.</p>", "cc": "[email protected]" } ``` | Field | Type | Req | Description | | ------------------ | ------- | --- | ------------------------------------------------------- | | `content` | string | ✅ | HTML content of the reply | | `cc` | string | | Optional CC email address | | `bcc` | string | | Optional BCC email address | | `scheduled_time` | integer | | Schedule at this timestamp (ms). Defaults to ~20s delay | | `tzMode` | string | | Timezone mode: `"sequence"` or `"custom"` | | `selectedTimezone` | string | | Timezone identifier if tzMode is custom | > Attachments are supported via FormData field `attachment`. Base64 images in HTML are automatically uploaded to S3. > The reply is automatically sent from the same sender that originally contacted the lead. ## Update Mail State **PATCH** `/inbox/:messageId` Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "unread": false, "outcome": "interested" } ``` | Field | Type | Description | | --------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `unread` | boolean | Mark as read (`false`) or unread (`true`) | | `outcome` | string | Classify the reply: `"interested"`, `"not-interested"`, `"automatic-response"`, `"meeting-request"`, `"out-of-office"`, `"do-not-contact"`, `"wrong-person"`, `"closed"` | FILE:references/senders.md # Email Senders & OAuth ## Endpoints | Endpoint | Method | Description | |----------|--------|-------------| | `/senders` | GET | List all connected senders (grouped by folder) | | `/senders` | POST | Add a single SMTP/IMAP sender | | `/senders/bulk` | POST | Bulk add senders via CSV upload | | `/oauth/google` | POST | Get Google OAuth URL for connecting Gmail | | `/oauth/outlook` | POST | Get Microsoft OAuth URL for connecting Outlook | ## Get Senders **GET** `/senders` Headers: - `Authorization`: `YOUR_API_KEY` Query params: `limit` (max 100), `skip`, `owned_by` ## Add Single Sender (SMTP/IMAP) **POST** `/senders` Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "from_email": "[email protected]", "from_name": "Sales Team", "password": "your_password", "smtp_host": "smtp.yourprovider.com", "smtp_port": 587, "user_name": "[email protected]", "imap_host": "imap.yourprovider.com", "imap_port": 993 } ``` | Field | Type | Req | Description | |-------|------|-----|-------------| | `from_email` | string | ✅ | Sender email address | | `password` | string | ✅ | SMTP/IMAP password | | `smtp_host` | string | ✅ | SMTP server hostname | | `smtp_port` | integer/string | ✅ | SMTP port (e.g., 587) | | `from_name` | string | | Display name | | `user_name` | string | | SMTP username (defaults to `from_email`) | | `imap_host` | string | | IMAP hostname (omit for SMTP-only) | | `imap_port` | integer/string | | IMAP port (e.g., 993) | | `imap_user_name` | string | | IMAP username if different from SMTP | | `imap_password` | string | | IMAP password if different from SMTP | | `total_warmup_per_day` | integer | | Warmup emails per day (default: 5) | | `warmup_enabled` | boolean | | Enable warmup (default: false) | | `inbox_enable` | boolean | | Enable inbox (default: false) | | `warmup_tag` | string | | Warmup keyword/tag | | `inbox_path` | string | | Inbox folder path (default: "INBOX") | | `spam_path` | string | | Spam folder path | | `signature_id` | string | | Signature ID or name to attach | | `custom_tracking_url` | string | | Custom tracking domain (must be verified) | | `sequence_auto_ramp_up_enabled` | boolean | | Enable sequence ramp-up | | `sequence_initial_daily_frequency` | integer | | Initial daily send limit (default: 30) | | `sequence_ramp_up_frequency` | integer | | Ramp-up increment (default: 3) | | `max_emails_per_day` | integer | | Max daily send limit (default: 30) | | `dkim_identifier` | string | | DKIM identifier | | `reply_to_email` | string | | Reply-to email address | > If `imap_host` is omitted or empty, the sender is created as **SMTP-only** (`serviceName: "smtponly"`). ## Bulk Add Senders **POST** `/senders/bulk` Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `multipart/form-data` Upload a CSV file via FormData with field name `csvFile`. ## Google OAuth **POST** `/oauth/google` Headers: - `Authorization`: `YOUR_API_KEY` Returns an `auth_url` that the user must visit to authorize Gmail access. Response: ```json { "success": true, "data": { "auth_url": "https://accounts.google.com/o/oauth2/v2/auth?..." } } ``` ## Outlook OAuth **POST** `/oauth/outlook` Headers: - `Authorization`: `YOUR_API_KEY` Returns an `auth_url` for Microsoft Outlook authorization. FILE:references/templates.md # Email Templates ## Endpoints | Endpoint | Method | Description | |----------|--------|-------------| | `/templates` | GET | List all templates (includes `cold_email_score`) | | `/templates/:id` | GET | Get full template details + score breakdown | | `/templates` | POST | Create a template | | `/templates/:id` | PATCH | Update template content, attachments | | `/templates/:id/archive` | PUT | Archive or unarchive a template | ## Create Template **POST** `/templates` Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "name": "Cold Outreach V1", "subject_line": "Quick question, {{first_name}}", "content": "<p>Hi {{first_name}},</p><p>I noticed {{company}} is scaling fast...</p>", "folder": "folder_id", "starred": false } ``` | Field | Type | Req | Description | |-------|------|-----|-------------| | `name` | string | ✅ | Template name | | `subject_line` | string | ✅ | Email subject (supports `{{variables}}`) | | `content` | string | ✅ | HTML body (supports `{{first_name}}`, `{{company}}`, etc.) | | `folder` | string | | Folder ID (UUID) | | `starred` | boolean | | Star the template | | `attachments` | file[] | | Files to attach (**max 3 total**). Use FormData field name `attachment` | Response includes `cold_email_score`: ```json { "score": 72.5, "rating": "Good", "details": { "word_count": 85, "personalization_count": 3, "link_count": 1, "image_count": 0, "question_count": 1, "spam_word_count": 0 } } ``` ## Update Template **PATCH** `/templates/:id` (ObjectId) Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "name": "Updated Template", "subject_line": "New subject {{first_name}}", "content": "<p>Updated content here</p>", "remove_attachments": ["old_file.pdf"] } ``` | Field | Type | Description | |-------|------|-------------| | `name` | string | New template name | | `subject_line` | string | New subject line | | `content` | string | New HTML body | | `starred` | boolean | Star/unstar | | `attachments` | file[] | New files to **append** (total must not exceed 3). Use FormData field name `attachment` | | `remove_attachments` | string[] | Names of existing attachments to **remove** | > **IMPORTANT**: To remove attachments, use the `remove_attachments` array with file **names** — not the `attachments` field. ## Archive Template **PUT** `/templates/:id/archive` (ObjectId) Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "archived": true } ``` FILE:references/inbox-placement.md # Inbox Placement Tests ## Endpoints | Endpoint | Method | Description | |----------|--------|-------------| | `/inbox-placement` | GET | List deliverability tests | | `/inbox-placement` | POST | Create a new deliverability test | | `/inbox-placement/:id/pause` | PUT | Pause an active **recurring** test | | `/inbox-placement/:id` | DELETE | Delete a test | ## Get Tests **GET** `/inbox-placement` Headers: - `Authorization`: `YOUR_API_KEY` Query params: | Param | Type | Description | |-------|------|-------------| | `search` | string | Filter by test name (case-insensitive) | | `status` | string | Filter by status: `pending`, `running`, `completed`, `stopped` | | `mode` | string | Filter by mode: `one-time`, `recurring` | | `ownedBy` | string | Filter by user ID | | `limit` | integer | Page size (default: 10) | | `skip` | integer | Offset (default: 0) | | `sortBy` | string | Sort field (default: `created_at`) | | `sortType` | integer | `-1` for descending, `1` for ascending | > Note: filtering by `status=completed` excludes recurring tests since they never truly complete. ## Create Test **POST** `/inbox-placement` Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` | Field | Type | Req | Description | |-------|------|-----|-------------| | `name` | string | ✅ | Test name (min 3 characters) | | `mode` | string | ✅ | `"one-time"` or `"recurring"` | | `source` | string | ✅ | `"from-salesblink"` or `"from-outside"` | | `sender_id` | string | ✅* | Sender UUID (required when `source="from-salesblink"` or `mode="recurring"`) | | `content_type` | string | ✅* | `"custom"`, `"sequence"`, or `"template"` (required when `source="from-salesblink"` or `mode="recurring"`) | | `subject` | string | ✅* | Email subject (required when `content_type="custom"`) | | `body` | string | ✅* | Email HTML body (required when `content_type="custom"`) | | `sequence_id` | string | ✅* | Sequence UUID (required when `content_type="sequence"`) | | `template_id` | string | ✅* | Template ObjectId (required when `content_type="sequence"` or `"template"`) | | `schedule_day` | integer | ✅* | Day of week: `0`=Sunday through `6`=Saturday (required when `mode="recurring"`) | | `tracking_uuid` | string | | Optional UUID for `from-outside` tests. Auto-generated if omitted. | > *Field requirement depends on `source`, `mode`, and `content_type` values. **Behavior:** - **One-time tests** run ~2 minutes after creation by default. - **Recurring tests** run weekly on the specified `schedule_day` at 09:00 UTC. - **`from-outside` tests** return `seed_emails` in the response — these are the addresses the user must send to. - **`from-salesblink` tests** send automatically using the selected sender. **V1 Wrapper behavior:** When `source="from-salesblink"` is provided without `content_type`: - If `subject` and `body` are present → `content_type` becomes `"custom"` - Otherwise → `content_type` becomes `"sequence"` with `sequence_id: "from_api"` ### Example: One-time custom content test **POST** `/inbox-placement` Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "name": "Gmail Deliverability Check", "mode": "one-time", "source": "from-salesblink", "sender_id": "sender-uuid-here", "content_type": "custom", "subject": "Hello from SalesBlink", "body": "<p>This is a test email.</p>" } ``` ### Example: Recurring sequence-based test **POST** `/inbox-placement` Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "name": "Weekly Sequence Check", "mode": "recurring", "source": "from-salesblink", "sender_id": "sender-uuid-here", "content_type": "sequence", "sequence_id": "sequence-uuid-here", "template_id": "507f1f77bcf86cd799439011", "schedule_day": 1 } ``` ### Example: From-outside test (returns seed emails) **POST** `/inbox-placement` Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "name": "External Send Test", "mode": "one-time", "source": "from-outside" } ``` Response for `from-outside`: ```json { "success": true, "data": { "id": "...", "tracking_uuid": "...", ... }, "seed_emails": ["[email protected]", "[email protected]", ...] } ``` ## Pause Test **PUT** `/inbox-placement/:id/pause` Headers: - `Authorization`: `YOUR_API_KEY` Only works on **recurring** tests. Sets status to `stopped` and clears scheduling. ## Delete Test **DELETE** `/inbox-placement/:id` Headers: - `Authorization`: `YOUR_API_KEY` Deletes the test and all associated tracking tasks. FILE:references/folders.md # Folders ## Endpoints | Endpoint | Method | Description | | ---------- | ------ | --------------- | | `/folders` | GET | List folders | | `/folders` | POST | Create a folder | ## Get Folders **GET** `/folders` Headers: - `Authorization`: `YOUR_API_KEY` ## Create Folder **POST** `/folders` Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "name": "Q1 Campaigns", "type": "sequence" } ``` | Field | Type | Req | Description | | ------ | ------ | --- | --------------------------------------------------------- | | `name` | string | ✅ | Folder name | | `type` | string | ✅ | `"list"`, `"template"`, `"sequence"`, or `"email-sender"` | > If `type` contains `"sender"`, it is automatically converted to `"email-sender"`. FILE:references/contacts.md # Contacts & Leads ## Endpoints | Endpoint | Method | Description | |----------|--------|-------------| | `/lists/:id/leads` | GET | Get leads in a list (paginated) | | `/contacts` | POST | Add up to 500 leads to a list | | `/contacts/remove` | POST | Remove a single lead by email from a list | | `/leads/:id` | PATCH | Update lead fields | | `/leads/:id/move` | PUT | Move a lead to a different list | | `/contacts/:id/archive` | PUT | Archive or unarchive a contact | ## Get Leads **GET** `/lists/:id/leads?limit=100&skip=0` Headers: - `Authorization`: `YOUR_API_KEY` Query params: `limit` (max 100), `skip` ## Add Contacts **POST** `/contacts` Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "list_id": "a1b2c3d4-e5f6-7890-abcd-abcdef123456", "contacts": [ { "Email": "[email protected]", "First_Name": "John", "Last_Name": "Doe", "Phone": "+1234567890", "Company": "Acme Inc", "Title": "VP Sales", "Custom_Field": "any value" } ], "remove_duplicates": true } ``` | Field | Type | Req | Description | |-------|------|-----|-------------| | `list_id` | string | ✅ | List UUID to add leads to | | `contacts` | object[] | ✅ | Array of lead objects (**max 500 per request**) | | `remove_duplicates` | boolean | | Remove duplicate emails after insert | Each contact object: | Field | Type | Req | Description | |-------|------|-----|-------------| | `Email` | string | ✅ | Lead's email address | | `First_Name` | string | | First name | | `Last_Name` | string | | Last name | | `Phone` | string | | Phone number | | `Company` | string | | Company name | | `Title` | string | | Job title | | _(any key)_ | string | | Custom fields are supported | > **Field naming**: Use **PascalCase with underscores** (`First_Name`, `Last_Name`, `Email`). ## Remove Contact **POST** `/contacts/remove` Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "list_id": "a1b2c3d4-e5f6-7890-abcd-abcdef123456", "email": "[email protected]" } ``` | Field | Type | Req | Description | |-------|------|-----|-------------| | `list_id` | string | ✅ | List UUID | | `email` | string | ✅ | Email address of the lead to remove | ## Update Lead **PATCH** `/leads/:id` (UUID) Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "First_Name": "Updated", "Last_Name": "Name", "Title": "CTO" } ``` Any standard or custom contact fields can be updated. System fields (`_id`, `id`, `list_id`, `account_id`, `user_id`, `accuracy`, `provider`, `custom_fields`, `removed_sequences`, `verification_required`, `archive_invalid_contacts`, `archive_risky_contacts`, `processing`, `completed`, `completedAt`, `last_modified`, `created_date`, `verification_blocked`, `didOpen`, `didClick`, `didReply`, `contactStats`, `retryCount`, `esg_name`, `archived`, `deleted`) **cannot** be modified. If updating `Email`, it is automatically lowercased. ## Move Lead **PUT** `/leads/:id/move` (UUID) Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "list_id": "destination_list_uuid" } ``` | Field | Type | Req | Description | |-------|------|-----|-------------| | `list_id` | string | ✅ | Destination list UUID | ## Archive Contact **PUT** `/contacts/:id/archive` Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "archived": true } ``` > ⚠️ **The `:id` here is a MongoDB ObjectId** (24-char hex), NOT a UUID. This is the only contact endpoint that uses ObjectId. FILE:references/lists.md # Lists ## Endpoints | Endpoint | Method | Description | |----------|--------|-------------| | `/lists` | GET | Retrieve all lists. Query: `limit` (max 100), `skip`, `owned_by` | | `/lists/:id` | GET | Get a specific list by UUID | | `/lists/:id/leads` | GET | Get leads in a list. Query: `limit` (max 100), `skip` | | `/lists` | POST | Create a new list | | `/lists/:id` | PATCH | Update a list | | `/lists/:id/archive` | PUT | Archive or unarchive a list | ## Create List **POST** `/lists` Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "name": "Q1 Prospects", "removeDuplicates": { "inThisList": true, "inOtherLists": true } } ``` Required fields marked with ✅: | Field | Type | Req | Description | |-------|------|-----|-------------| | `name` | string | ✅ | List name | | `folder` | string | | Folder ID (UUID) | | `starred` | boolean | | Star the list (default: false) | | `verification` | boolean | | Enable email verification ⚠️ **IRREVERSIBLE** | | `archive_invalid` | boolean | | Auto-archive invalid emails ⚠️ **IRREVERSIBLE** | | `archive_risky` | boolean | | Auto-archive risky emails ⚠️ **IRREVERSIBLE** | | `removeDuplicates.inThisList` | boolean | | Remove duplicate emails within this list | | `removeDuplicates.inOtherLists` | boolean | | Remove contacts that exist in other lists | | `removeDuplicates.inTeamMembersLists` | boolean | | Remove contacts that exist in team members' lists | ## Update List **PATCH** `/lists/:id` (UUID) Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "name": "Q2 Prospects Restructured", "starred": true, "archive_invalid": true } ``` | Field | Type | Description | |-------|------|-------------| | `name` | string | New name | | `starred` | boolean | Star or unstar | | `duplicate_removal` | boolean | Remove duplicates from this list | | `duplicate_removal_other_list` | boolean | Remove contacts in other lists | | `duplicate_removal_team_list` | boolean | Remove contacts in team members' lists | | `verification` | boolean | Enable verification ⚠️ **IRREVERSIBLE** | | `archive_invalid` | boolean | Archive invalid emails ⚠️ **IRREVERSIBLE** | | `archive_risky` | boolean | Archive risky emails ⚠️ **IRREVERSIBLE** | > Use `PUT /lists/:id/archive` for archiving — not this endpoint. ## Archive List **PUT** `/lists/:id/archive` Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "archived": true } ``` Set `"archived": false` to unarchive. FILE:references/sequences.md # Sequences ## Endpoints | Endpoint | Method | Description | |----------|--------|-------------| | `/sequences` | GET | List all sequences. Query: `limit`, `skip`, `owned_by` | | `/sequences/:id` | GET | Get sequence details including steps and settings | | `/sequences/:id/stats` | GET | Get performance analytics. Query: `from`, `to`, `sender` | | `/sequences` | POST | Create a full sequence with steps, senders, lists | | `/sequences/:id` | PATCH | Update settings, pause/resume, rewrite steps | | `/sequences/:id/clone` | POST | Duplicate an existing sequence (created paused) | | `/sequences/:id/archive` | PUT | Archive or unarchive a sequence | ## Create Sequence **POST** `/sequences` Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "name": "Q1 Outbound Campaign", "senders": "a1b2c3d4-e5f6-7890-abcd-000000000001,a1b2c3d4-e5f6-7890-abcd-000000000002", "lists": ["b2c3d4e5-f6a7-8901-bcde-000000000001"], "steps": [ { "type": "email", "template_id": "507f1f77bcf86cd799439011" }, { "type": "delay", "days": 3 }, { "type": "email", "template_id": "507f1f77bcf86cd799439012" } ], "paused": false, "launchTimingMode": "now", "timezone": "America/New_York", "delayEnabled": true, "delayFrom": 10, "delayTo": 20, "stopWhenReplyRecieved": true, "stopWhenReplyRecievedWhen": "contact" } ``` Required fields marked with ✅: | Field | Type | Req | Description | |-------|------|-----|-------------| | `name` | string | ✅ | Sequence name | | `senders` | string | ✅ | **Comma-separated string** of sender/folder IDs (NOT an array) | | `lists` | string[] | ✅ | Array of list UUIDs | | `steps` | Step[] | ✅ | Ordered array of email and delay steps | | `folder` | string | | Folder ID (UUID) | | `starred` | boolean | | Star the sequence | | `paused` | boolean | | Create in paused state (default: **true**) | | `launchTimingMode` | string | | `"now"` (starts in 5 mins) or `"schedule"` (requires `scheduledAt`) | | `scheduledAt` | integer | | UTC timestamp in **milliseconds** (required if mode = `"schedule"`) | | `timezone` | string | | Timezone for sending (default: `"America/New_York"`) | | `delayEnabled` | boolean | | Enable random delay between emails (default: true) | | `delayFrom` | integer | | Minimum delay in minutes (default: 10) | | `delayTo` | integer | | Maximum delay in minutes (default: 20) | | `stopWhenReplyRecieved` | boolean | | Stop sequence when lead replies (default: true) | | `stopWhenReplyRecievedWhen` | string | | `"contact"` or `"contact-with-same-domain"` (default: `"contact"`) | | `evergreen` | boolean | | Enable evergreen mode — continuously running (default: false) | | `bounceThreshold` | integer | | Bounces before pausing (default: 2) | | `bouncePause` | boolean | | Pause sequence on bounce threshold (default: false) | | `autoPause` | boolean | | Auto-pause on high bounce rate (default: true) | | `autoTagReplies` | boolean | | Auto-tag reply outcomes (default: false) | | `plainText` | boolean | | Send as plain text email (default: true) | | `auto_reply` | boolean | | Enable auto-reply detection (default: true) | | `matchProvider` | boolean | | Match sender email provider with recipient (default: true) | | `skip_esg` | boolean | | Skip ESG detection (default: true) | | `sendToOnlyVerifiedEmail` | boolean | | Only send to verified emails (default: false) | | `validEmail` | boolean | | Send to contacts with valid email status (default: true) | | `riskyEmail` | boolean | | Send to contacts with risky email status (default: true) | | `invalidEmail` | boolean | | Send to contacts with invalid email status (default: true) | | `checkEmailOpen` | boolean | | Check if recipient opened previous email before sending next (default: false) | | `checkEmailClick` | boolean | | Check if recipient clicked link before sending next (default: false) | | `checkEmailReply` | boolean | | Check if recipient replied before sending next (default: true) | | `checkEmailBeforeSending` | boolean | | Verify email before sending (default: true) | | `bcc` | string | | BCC email address for all outgoing emails (default: `""`) | | `emailSendingHours` | array | | Sending hours per day of the week (see default below) | **Steps structure** — ordered array mixing `email` and `delay` types: ```json "steps": [ { "type": "email", "template_id": "507f1f77bcf86cd799439011" }, { "type": "delay", "days": 3 }, { "type": "email", "template_id": "507f1f77bcf86cd799439012" } ] ``` - `type: "email"` → MUST include `template_id` (the template's MongoDB ObjectId). - `type: "delay"` → MUST include `days` (integer, number of days to wait). - Omitting `type` or misspelling it will fail or create a broken sequence. **Default `emailSendingHours`:** ```json [ { "enabled": true, "name": "Monday", "fromTime": "09:00", "toTime": "17:00" }, { "enabled": true, "name": "Tuesday", "fromTime": "09:00", "toTime": "17:00" }, { "enabled": true, "name": "Wednesday", "fromTime": "09:00", "toTime": "17:00" }, { "enabled": true, "name": "Thursday", "fromTime": "09:00", "toTime": "17:00" }, { "enabled": true, "name": "Friday", "fromTime": "09:00", "toTime": "17:00" }, { "enabled": false, "name": "Saturday", "fromTime": "09:00", "toTime": "17:00" }, { "enabled": false, "name": "Sunday", "fromTime": "09:00", "toTime": "17:00" } ] ``` ## Update Sequence **PATCH** `/sequences/:id` (UUID) Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "paused": true } ``` Accepts the same fields as create (all optional). Additionally used to **pause/resume**. > **Critical**: When updating `steps`, the entire array is **replaced** — send the full desired step list. > Updates to `name`, `starred`, or `folder` do **not** trigger task rescheduling. All other field updates do. ## Clone Sequence **POST** `/sequences/:id/clone` (UUID) Headers: - `Authorization`: `YOUR_API_KEY` Creates a paused duplicate of an existing sequence. ## Archive Sequence **PUT** `/sequences/:id/archive` (UUID) Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "archived": true } ``` Archiving a sequence automatically pauses it and removes pending email tasks. FILE:references/activity.md # Activity Tracking ## Endpoints | Endpoint | Method | Description | |----------|--------|-------------| | `/sent` | GET | Log of all sent emails | | `/opens` | GET | Email open events | | `/clicks` | GET | Link click events | | `/replies` | GET | Reply events | ## Query Parameters All activity endpoints support: | Param | Type | Description | |-------|------|-------------| | `per_page` | integer | Max 100 | | `page` | integer | 1-indexed | | `sequence_id` | string | Filter by sequence UUID | | `recipient_email_address` | string | Filter by email address | | `since` | integer | Filter events after this timestamp (ms) | | `from` | integer | Start of date range (timestamp, ms) | | `to` | integer | End of date range (timestamp, ms) | > Use `per_page` and `page` for activity endpoints — not `limit`/`skip`. ## Response Format Each event includes: ```json { "id": "...", "time": 1715000000000, "message": "Sent", "type": "outreach", "sequence": "sequence-uuid", "email": "[email protected]", "sequence_name": "Campaign Name" } ``` For clicks and replies, `template_name` is also included. ## Examples **GET** `/opens?sequence_id=SEQ_ID&per_page=100&page=1` Headers: - `Authorization`: `YOUR_API_KEY` **GET** `/replies?since=TIMESTAMP_30_DAYS_AGO&per_page=100` Headers: - `Authorization`: `YOUR_API_KEY` FILE:references/account-config.md # Account Config — Domains, Signatures & Warmup Links ## Domains **GET** `/domains` Headers: - `Authorization`: `YOUR_API_KEY` List custom tracking domains for the account. ## Signatures **GET** `/signatures` Headers: - `Authorization`: `YOUR_API_KEY` List email signatures. > Signature IDs can be referenced when adding senders via the `signature_id` field. You can pass either the signature ID or its name. ## Warmup Links **GET** `/warmup-links` Headers: - `Authorization`: `YOUR_API_KEY` List warmup link configurations. FILE:references/organization.md # Organization — Users & Workspaces ## Users | Endpoint | Method | Description | |----------|--------|-------------| | `/users` | GET | List workspace users | | `/users` | POST | Invite a user | | `/users/:id` | PATCH | Update user name or role | ### Create User **POST** `/users` Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "email": "[email protected]", "role": "user", "url": "https://run.salesblink.io/dashboard" } ``` | Field | Type | Req | Description | |-------|------|-----|-------------| | `email` | string | ✅ | Email address of the new user | | `role` | string | | `"client"`, `"user"`, `"admin"`, or `"developer"`. Default: `"user"` | | `url` | string | | Optional redirect URL after accepting invitation | > Only **owners and admins** can add users. Returns 403 otherwise. ### Update User **PATCH** `/users/:id` (UUID) Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "name": "Updated Name", "role": "admin" } ``` | Field | Type | Description | |-------|------|-------------| | `name` | string | New display name | | `role` | string | One of: `client`, `user`, `admin`, `developer` | --- ## Workspaces | Endpoint | Method | Description | |----------|--------|-------------| | `/workspaces` | GET | List all accessible workspaces | | `/workspaces` | POST | Create a new workspace | | `/workspaces/:id` | PATCH | Update workspace name | > **Owner only.** Returns 403 for non-owners. ### Create Workspace **POST** `/workspaces` Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "name": "Sales Team" } ``` | Field | Type | Req | Description | |-------|------|-----|-------------| | `name` | string | ✅ | Workspace name (min 4 characters) | ### Update Workspace **PATCH** `/workspaces/:id` (UUID) Headers: - `Authorization`: `YOUR_API_KEY` - `Content-Type`: `application/json` Body: ```json { "name": "New Workspace Name" } ``` | Field | Type | Req | Description | |-------|------|-----|-------------| | `name` | string | ✅ | New workspace name (min 4 characters) |