# cold-email-salesblink

Adopt this skill: https://proskills.md/skills/other/open-salesblink-skill-skills-cold-email-salesblink

Page: https://proskills.md/skills/other/open-salesblink-skill-skills-cold-email-salesblink
This document is the public skill bundle for that page. It contains SKILL.md and every helper file the catalog carries. No login is required.

## File: skills/cold-email-salesblink/SKILL.md
size: 13831
sha: 564d9d622104aa5dec7853b016827b60b62d00ac
upstream: https://github.com/open-salesblink/skill/blob/main/skills/cold-email-salesblink/SKILL.md

````text
---
name: cold-email-salesblink
description: >
  Run cold email sequences on autopilot and manage full sales outreach campaigns via the SalesBlink API.
  Use this skill to build automated multi-step email campaigns (sequences), manage leads and email lists,
  create reusable templates with merge variables and spintax, connect Gmail/Outlook sending accounts via OAuth
  and SMTP/IMAP senders directly or by CSV,
  handle inbox replies, and track campaign analytics (opens, clicks, replies, sent).
  Also supports bulk contact imports, email deliverability testing (inbox placement / spam checks),
  sender warmup links, workspace/team management, and any HTTP request to the SalesBlink platform.
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.).
  No prior knowledge of SalesBlink is needed: the skill guides you through connecting email accounts,
  importing leads, writing templates, building sequences, launching campaigns, and monitoring deliverability.
metadata:
  openclaw:
    requires:
      env:
        - SALESBLINK_API_KEY
    primaryEnv: SALESBLINK_API_KEY
---

# 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 `https://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.
- **GET `/lists/:id` returns a single object**: The API returns the list object directly under `data`, including a `fields` array of its column names in snake_case.
- **Sequence status filters differ by endpoint**: `GET /sequences` uses status values `running`, `paused`, `completed`, `needs-attention`. `POST /sequences/:id/status` uses `ACTIVE`, `PAUSED`, `STOPPED`, `ARCHIVED`. Do not mix them up.
- **GET `/sequences/:id` returns the internal flowchart**: The response includes the full internal `flowchart` object, not a simplified step list. Use `flowchart` to inspect steps.
- **POST `/sequences/:id/clone` returns `clonedSequenceId`**: The response shape is `{ success, data: { clonedSequenceId }, message }`, not `data.id` or `data.name`.
- **OAuth `redirectUrl` is ignored**: The API does not use a custom `redirectUrl` for `/oauth/google` or `/oauth/outlook`. The response field is `data.auth_url`, not `data.url`.
- **messageId** is the RFC822 Message-ID (e.g. `<id@domain.com>`) 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.
- **Email steps accept `send_as: "new" | "followup"`**: `"followup"` replies in the lead's existing thread (`Re: <original subject>`); it is ignored on the first email step, which always starts a new thread. The stored value reads back as `flowchart.nodes[].data.send_as`.
- **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**.
- **GET `/senders/connect-link` returns a login link; it does not create a sender**. For Gmail and Outlook, use `/oauth/google` or `/oauth/outlook`, which return an `auth_url` the user must open in a browser; the sender is created automatically after OAuth completion. SMTP/IMAP senders can be added with `POST /senders/add-sender` (JSON) or `POST /senders/add-bulk-senders` (CSV, `multipart/form-data`, field `csvFile`); they connect in the background, so wait for `sendingEnabled: true` before using them in a sequence.
- **Forward content is optional**: `POST /inbox/:messageId/forward` uses the original email body when `content` is omitted.
- **Archiving is done via dedicated archive routes**: `PATCH /lists/:id`, `PATCH /templates/:id`, and `PATCH /sequences/:id` ignore the `archived` field. Use `PUT /lists/:id/archive`, `PUT /templates/:id/archive`, and `PUT /sequences/:id/archive` instead.
- **Folder types matter**: General folders (created without a `type`) hold lists, templates, and sequences. `type: "email-sender"` folders hold email senders only. The API rejects a folder assignment to the wrong resource type.

## Base URL

`https://run.salesblink.io/api/public/v1.0.0`

## Authentication

This API uses a SalesBlink API key. Get or create an API key at `https://run.salesblink.io/account/integration/api`.

Pass the key in every request as the `Authorization` header (no "Bearer" prefix):

**Header:** `Authorization: key-****`

## Rate Limits

| Method        | Limit | Window     | Applies To |
| ------------- | ----- | ---------- | ---------- |
| GET           | 30    | per minute | Most GET endpoints |
| POST / PATCH  | 15    | per minute | POST and PATCH endpoints |
| PUT (archive) | 10    | per minute | PUT and DELETE endpoints |
| GET /signup-link | 250   | per day    | Public signup link (per IP) |

On `429 Too Many Requests`: wait at least 60 seconds before retrying. For batch operations, insert a 4-second delay between requests.

## Public Signup

**GET** `/signup-link`

Get the public SalesBlink sign-up link. This is a public endpoint and does not require an API key. It returns a sign-up link to the SalesBlink web UI:

**Response Body:**
```json
{
  "success": true,
  "message": "Please sign up through the SalesBlink web UI.",
  "data": {
    "login_link": "https://run.salesblink.io/signup",
    "destination": "/signup",
    "purpose": "signup"
  }
}
```

**Rate Limit**: 5 requests per day per IP.

## 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).

Some endpoints support higher limits:
- `/sequences/:id/leads` and `/leads/:id/activity` — max 500
- `/sequences/:id/export` — max 50,000 (default 10,000)

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_name, job_title, and custom fields. All contact field names sent to the API must be snake_case; requests with other casings are rejected. 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 & warmup links** → [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. Warmup links are used in email warmup processes to improve deliverability.

- **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. Also includes per-lead activity history.

- **Analytics** → [references/analytics.md](references/analytics.md)
  - Use these endpoints when the user wants aggregated campaign analytics: overall stats across all sequences, day-wise breakdowns, lead-level stats, and per-mailbox/sender stats.

- **Blocklist / unsubscribes** → [references/blocklist.md](references/blocklist.md)
  - Use these endpoints when the user wants to manage account-level unsubscribes and blocked emails/domains. The public API uses `/unsubscribe` routes for all blocklist operations.

- **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 (`general` or `email-sender`) and group related resources together for easier management.

- **Domains & signatures** → [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 (returns both verified and unverified domains with DNS setup instructions and "not verified" tags). Signatures are appended to outgoing emails.

- **DFY domains & mailboxes** → [references/dfy.md](references/dfy.md)
  - Use these endpoints when the user wants to purchase domains and provision mailboxes through the Done-For-You service. Start with `/domains/search` to find available domains, then place an order with Google Workspace, Outlook, or Azure mailboxes. Supports buying new domains or connecting existing ones.

- **Billing & payment methods** → [references/billing.md](references/billing.md)
  - Use these endpoints when the user wants to add or remove a saved payment card. Returns magic login links to the billing page.

- **API Key Management** → [references/api-keys.md](references/api-keys.md)
  - Use these endpoints when the user wants to manage API keys. Users can list all keys, create new keys, refresh an existing key (which generates a new one and revokes the old one), or delete a key.

- **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: skills/cold-email-salesblink/references/account-config.md
size: 3039
sha: 776af6c93527e14b5e228145336a533d6295d6ff
upstream: https://github.com/open-salesblink/skill/blob/main/skills/cold-email-salesblink/references/account-config.md

````text
# Account Config — Domains & Signatures

## Endpoints

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/account/verify` | GET | Verify API key and return account info |
| `/domains` | GET | List custom tracking domains (both verified and unverified with DNS setup instructions) |
| `/signatures` | GET | List email signatures |

## Verify Account

**GET** `/account/verify`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

Returns account info and confirms the API key is valid.

## Domains

**GET** `/domains`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

Query Parameters:
- `owned_by` (optional string): Filter by owner email (admin/owner only). Also accepts `ownedby`, `userID`, `uid`, or `user`.

List custom tracking domains for the account. Both verified and unverified domains are returned.

### Verification Status & Tag
- **Verified domains**: have `verified: true`, `status: "verified"`, `tag: "verified"`, and `instructions: null`.
- **Unverified domains**: have `verified: false`, `status: "not verified"`, `tag: "not verified"`, and include DNS setup `instructions` (`record_type`, `host`, `value`, `message`) to guide DNS configuration.

### DNS Instructions Details
- **Root/Apex domains** (e.g. `example.com`): require an `A` record with Host `@` pointing to `143.244.213.24`.
- **Subdomains** (e.g. `track.example.com`): require a `CNAME` record with Host (e.g. `track`) pointing to `cname.salesblink.io` (for click-tracker), `meet.salesblink.io` (for meet-link), or `run.salesblink.io` (for white-label).

### Response Example

```json
{
  "success": true,
  "message": "Custom domains retrieved successfully",
  "data": [
    {
      "id": "64f1a2b3c4d5e6f7a8b9c0d1",
      "_id": "64f1a2b3c4d5e6f7a8b9c0d1",
      "domain": "track.mycompany.com",
      "type": "click-tracker",
      "verified": false,
      "status": "not verified",
      "tag": "not verified",
      "instructions": {
        "record_type": "CNAME",
        "host": "track",
        "value": "cname.salesblink.io",
        "message": "Add a CNAME record for \"track.mycompany.com\" with Host \"track\" pointing to \"cname.salesblink.io\". It can take up to 24 hours for DNS changes to propagate."
      },
      "uid": "user_123",
      "owned_by": "alex@mycompany.com"
    },
    {
      "id": "64f1a2b3c4d5e6f7a8b9c0d2",
      "_id": "64f1a2b3c4d5e6f7a8b9c0d2",
      "domain": "clicks.mycompany.com",
      "type": "click-tracker",
      "verified": true,
      "status": "verified",
      "tag": "verified",
      "instructions": null,
      "uid": "user_123",
      "owned_by": "alex@mycompany.com"
    }
  ]
}
```

## Signatures

**GET** `/signatures`

Headers:
- `Authorization`: `SALESBLINK_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.

Note: For GET domains, GET signatures, and GET warmup-links, you can use `owned_by`, `ownedby`, `userID`, `uid`, or `user` to filter by user.
````

## File: skills/cold-email-salesblink/references/activity.md
size: 2067
sha: 20f0d42b5549257fe201acc47ae9b295dbe61bb3
upstream: https://github.com/open-salesblink/skill/blob/main/skills/cold-email-salesblink/references/activity.md

````text
# 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 |
| `/leads/:id/activity` | GET | Activity history for a specific lead |

## Query Parameters

The `/sent`, `/opens`, `/clicks`, and `/replies` 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 these activity endpoints — not `limit`/`skip`.
> `/leads/:id/activity` uses `limit`/`skip` instead (see Lead Activity section below).

## Response Format

Each event includes:
```json
{
  "id": "...",
  "time": 1715000000000,
  "message": "Sent",
  "type": "outreach",
  "sequence": "sequence-uuid",
  "email": "lead@example.com",
  "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`: `SALESBLINK_API_KEY`

**GET** `/replies?since=TIMESTAMP_30_DAYS_AGO&per_page=100`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

## Lead Activity

**GET** `/leads/:id/activity`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

Query params:

| Param | Type | Description |
|-------|------|-------------|
| `sequence_id` | string | Filter by sequence UUID |
| `from` | integer | Start of date range (timestamp, ms) |
| `to` | integer | End of date range (timestamp, ms) |
| `limit` | integer | Maximum number of results (max 500) |
| `skip` | integer | Offset for pagination |

Returns the activity history for a specific lead across sequences.
````

## File: skills/cold-email-salesblink/references/analytics.md
size: 2075
sha: a067ad5023bc91081a1a557648047589f678bcb2
upstream: https://github.com/open-salesblink/skill/blob/main/skills/cold-email-salesblink/references/analytics.md

```text
# Analytics

Cross-campaign and account-level analytics endpoints.

## Endpoints

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/analytics/overall` | GET | Overall stats across all sequences |
| `/analytics/daily` | GET | Day-wise overall stats |
| `/analytics/lead-stats` | GET | Lead-level stats |
| `/analytics/mailbox-stats` | GET | Overall mailbox stats |

## Overall Analytics

**GET** `/analytics/overall`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

Query params:

| Param | Type | Description |
|-------|------|-------------|
| `from` | integer | Start date timestamp (milliseconds) |
| `to` | integer | End date timestamp (milliseconds) |

Returns aggregated stats (sent, opens, clicks, replies, bounces, unsubscribes) across all sequences.

## Daily Analytics

**GET** `/analytics/daily`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

Query params:

| Param | Type | Description |
|-------|------|-------------|
| `from` | integer | Start date timestamp (milliseconds) |
| `to` | integer | End date timestamp (milliseconds) |
| `timezone` | string | IANA timezone (e.g. `America/New_York`) |

Returns day-wise aggregated stats.

## Lead Stats

**GET** `/analytics/lead-stats`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

Query params:

| Param | Type | Description |
|-------|------|-------------|
| `from` | integer | Start date timestamp (milliseconds) |
| `to` | integer | End date timestamp (milliseconds) |
| `limit` | integer | Maximum number of results (default: 100) |
| `skip` | integer | Offset for pagination |

Returns per-lead activity statistics.

## Mailbox Stats

**GET** `/analytics/mailbox-stats`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

Query params:

| Param | Type | Description |
|-------|------|-------------|
| `from` | integer | Start date timestamp (milliseconds) |
| `to` | integer | End date timestamp (milliseconds) |
| `limit` | integer | Maximum number of results (default: 100) |
| `skip` | integer | Offset for pagination |

Returns per-sender/mailbox performance statistics.
```

## File: skills/cold-email-salesblink/references/api-keys.md
size: 2100
sha: 4fe42e98f6733a2bb3438da6beac64b03fab4429
upstream: https://github.com/open-salesblink/skill/blob/main/skills/cold-email-salesblink/references/api-keys.md

````text
# API Key Management

## Endpoints

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/keys` | GET | List all API keys for the account |
| `/keys/create-link` | GET | Get a magic login link to the API keys page in the SalesBlink web UI |
| `/keys/:id/refresh-link` | GET | Get a magic login link to refresh an API key in the SalesBlink web UI |
| `/keys/:id` | DELETE | Delete an API key |

## Get API Keys

**GET** `/keys`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

Returns a list of all API keys associated with the account.

## Get API Key Creation Link

**GET** `/keys/create-link`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

> **Important**: Creating an API key through this API gateway is no longer supported. **GET** `/keys/create-link` returns a magic login link to the API keys page in the SalesBlink web UI:

```json
{
  "success": true,
  "message": "Please manage API keys through the SalesBlink web UI.",
  "data": {
    "login_link": "https://run.salesblink.io/magic?token=...&redirect=%2Faccount%2Fintegration%2Fapi",
    "destination": "/account/integration/api",
    "purpose": "manage_api_keys"
  }
}
```

## Get API Key Refresh Link

**GET** `/keys/:id/refresh-link`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

> **Important**: Refreshing an API key through this API gateway is no longer supported. **GET** `/keys/:id/refresh-link` returns a magic login link to the API keys page in the SalesBlink web UI:

```json
{
  "success": true,
  "message": "Please refresh API keys through the SalesBlink web UI.",
  "data": {
    "login_link": "https://run.salesblink.io/magic?token=...&redirect=%2Faccount%2Fintegration%2Fapi",
    "destination": "/account/integration/api",
    "purpose": "manage_api_keys"
  }
}
```

## Delete API Key

**DELETE** `/keys/:id`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

Deletes the specified API key.

> [!IMPORTANT]
> - You cannot delete the API key you are currently using.
> - At least one API key is required per account. If you want to replace your only key, use the Refresh endpoint instead.
````

## File: skills/cold-email-salesblink/references/billing.md
size: 1115
sha: 1da2348fba614a0d6f392832d118212f13b11b58
upstream: https://github.com/open-salesblink/skill/blob/main/skills/cold-email-salesblink/references/billing.md

````text
# Billing & Payment Methods

## Add Card Login Link

**GET** `/billing/add-card-link`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

Generates a time-limited magic login link that redirects the user to the billing page where they can add a payment card.

Response:
```json
{
  "success": true,
  "message": "Add card login link generated successfully",
  "data": {
    "login_link": "https://run.salesblink.io/magic?token=...&redirect=%2Faccount%2Fbilling%3Ftab%3Dcard",
    "destination": "/account/billing?tab=card",
    "purpose": "add_card"
  }
}
```

## Remove Card Login Link

**GET** `/billing/remove-card-link`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

Generates a time-limited magic login link that redirects the user to the billing page where they can remove their saved payment card.

Response:
```json
{
  "success": true,
  "message": "Remove card login link generated successfully",
  "data": {
    "login_link": "https://run.salesblink.io/magic?token=...&redirect=%2Faccount%2Fbilling%3Ftab%3Dcard",
    "destination": "/account/billing?tab=card",
    "purpose": "remove_card"
  }
}
```
````

## File: skills/cold-email-salesblink/references/blocklist.md
size: 2428
sha: 3412259318823821f1dec948ac66ebf3ab80d46e
upstream: https://github.com/open-salesblink/skill/blob/main/skills/cold-email-salesblink/references/blocklist.md

````text
# Blocklist / Unsubscribe Management

Manage account-level unsubscribes and blocklisted emails/domains.

## Endpoints

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/unsubscribe` | GET | List blocked emails and domains |
| `/unsubscribe` | POST | Add emails/domains to the blocklist |
| `/unsubscribe/remove` | POST | Remove emails/domains from the blocklist |
| `/unsubscribe/:id` | DELETE | Remove a single blocklist entry by ID |
| `/unsubscribe` | DELETE | Delete all blocklist entries |
| `/unsubscribe/check` | GET | Check if an email is blocked |

## List Blocklist

**GET** `/unsubscribe`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

Query params:

| Param | Type | Description |
|-------|------|-------------|
| `limit` | integer | Maximum number of results (default: 50, capped at 100) |
| `skip` | integer | Offset for pagination — must be a multiple of `limit` (e.g. with `limit=50` use `skip=0`, `50`, `100`, …); non-multiples are rejected with a 400 error |
| `search` | string | Search query |
| `type` | string | Filter by entry type: `email` or `domain` |

## Add to Blocklist

**POST** `/unsubscribe`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`
- `Content-Type`: `application/json`

Body:
```json
{
  "emails": ["unwanted@example.com", "competitor.com"]
}
```

| Field | Type | Req | Description |
|-------|------|-----|-------------|
| `emails` | string[] | ✅ | Array of emails or domains to block |

## Remove from Blocklist

**POST** `/unsubscribe/remove`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`
- `Content-Type`: `application/json`

Body:
```json
{
  "emails": ["unwanted@example.com", "competitor.com"]
}
```

| Field | Type | Req | Description |
|-------|------|-----|-------------|
| `emails` | string[] | ✅ | Array of emails or domains to unblock |

## Delete Blocklist Entry by ID

**DELETE** `/unsubscribe/:id`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

## Delete All Blocklist Entries

**DELETE** `/unsubscribe`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

> Use with caution — this removes every entry in the account blocklist.

## Check Blocklist

**GET** `/unsubscribe/check?email=foo@example.com`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

Query params:

| Param | Type | Description |
|-------|------|-------------|
| `email` | string | Email address to check |

Returns whether the email is currently blocked.
````

## File: skills/cold-email-salesblink/references/contacts.md
size: 6029
sha: 6c78fee6ab63d340180fb9df18cfcdee9c9233b8
upstream: https://github.com/open-salesblink/skill/blob/main/skills/cold-email-salesblink/references/contacts.md

````text
# Contacts & Leads

## Endpoints

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/lists/:id/leads` | GET | Get leads in a list (paginated, supports `sequence` + `status` filters) |
| `/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 |
| `/leads/activity` | GET | Get activity history for a lead by email query parameter |
| `/leads/:id/activity` | GET | Get activity history for a lead by ID |
| `/leads/:id/unsubscribe` | POST | Globally unsubscribe a lead from all sequences |

## Get Lead Activity by ID

**GET** `/leads/:id/activity`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

Query params: `sequence_id`, `from`, `to` (Unix ms), `limit` (max 500), `skip`

Response: `{ success, data: [...], lead, count, skip, limit }`

## Get Lead Activity by Email

**GET** `/leads/activity?email=lead@example.com`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

Query params: `email` (required), `sequence_id`, `from`, `to` (Unix ms), `limit` (max 500), `skip`

> Use this endpoint when you have the lead's email address but not the internal lead ID. Response shape is the same as `/leads/:id/activity`.

## Globally Unsubscribe Lead

**POST** `/leads/:id/unsubscribe`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

Adds the lead to the global unsubscribe list and cancels pending tasks across all sequences.

## Get Leads

**GET** `/lists/:id/leads?limit=100&skip=0`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

Query params: `limit` (max 100), `skip`, `search`, `archived`, `sort` (`asc`/`desc`), `sequence`, `status`

The `status` filter (`not_contacted`, `contacted`, `opened`, `clicked`, `replied`, `bounced`, `unsubscribed`) works with or without `sequence`. Without it, the status matches across all sequences (e.g. `replied` = replied in any sequence); `not_contacted` matches leads with no activity in any sequence. Omit `status` (or use `all`) to return the full list.

## Add Contacts

**POST** `/contacts`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`
- `Content-Type`: `application/json`

Body:
```json
{
  "list_id": "a1b2c3d4-e5f6-7890-abcd-abcdef123456",
  "contacts": [
    {
      "email": "john@example.com",
      "first_name": "John",
      "last_name": "Doe",
      "phone": "+1234567890",
      "company_name": "Acme Inc",
      "job_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_name` | string | | Company name |
| `job_title` | string | | Job title |
| `starred` | boolean | | Mark/unmark the contact as starred. This is a contact setting, not a data field/column. |
| _(any key)_ | string | | Custom fields are supported |

> **Field naming**: All field names **must be snake_case** (`first_name`, `last_name`, `email`, `company_name`, `lead_score`). Requests with non-snake_case field names are rejected with a 400 error. New custom fields are automatically added to the list's columns. The `starred` boolean is a contact setting and is NOT added as a list column. Use `PUT /contacts/:id/archive` to archive/unarchive contacts.

## Remove Contact

**POST** `/contacts/remove`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`
- `Content-Type`: `application/json`

Body:
```json
{
  "list_id": "a1b2c3d4-e5f6-7890-abcd-abcdef123456",
  "email": "john@example.com"
}
```

| 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`: `SALESBLINK_API_KEY`
- `Content-Type`: `application/json`

Body:
```json
{
  "first_name": "Updated",
  "last_name": "Name",
  "job_title": "CTO",
  "starred": true
}
```

Any standard or custom contact fields can be updated. All field names **must be snake_case**; requests with non-snake_case field names are rejected with a 400 error. 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.

Use the dedicated `starred` boolean property to mark/unmark the lead as starred; `starred` is a contact setting and is NOT added as a list column. Use `PUT /contacts/:id/archive` to archive/unarchive contacts.

If updating `email`, it is automatically lowercased.

## Move Lead

**PUT** `/leads/:id/move` (UUID)

Headers:
- `Authorization`: `SALESBLINK_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`: `SALESBLINK_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: skills/cold-email-salesblink/references/dfy.md
size: 3827
sha: 9c216ee55f30518a06d6b2333aaa6b29ccca3388
upstream: https://github.com/open-salesblink/skill/blob/main/skills/cold-email-salesblink/references/dfy.md

````text
# Done-For-You (DFY) — Domains & Mailboxes

Use these endpoints to search domains and view existing Done-For-You orders.

> **Important**: Placing new DFY orders and adding mailboxes to an existing order must be done through the SalesBlink web UI. The API supports searching domains, listing orders, and canceling a mailbox (which returns a billing management link).

## Endpoints

| Endpoint                                    | Method | Description                                        |
| ------------------------------------------- | ------ | -------------------------------------------------- |
| `/domains/search`                           | GET    | Search available .com domains for DFY purchase     |
| `/dfy/orders`                               | GET    | List all DFY orders                                |
| `/dfy/order-link`                           | GET    | Get a magic login link to place a DFY order        |
| `/dfy/orders/:orderId/mailbox-link`         | GET    | Get a magic login link to add mailboxes to an order |
| `/dfy/orders/:orderId/mailboxes/:mailboxId/cancel-link` | GET | Get a magic login link to cancel a mailbox (billing management) |

## Placing Orders and Adding Mailboxes

`GET /dfy/order-link` and `GET /dfy/orders/:orderId/mailbox-link` return magic login links to the relevant page in the SalesBlink web UI:

**GET** `/dfy/order-link` response:

```json
{
  "success": true,
  "message": "Please place Done-For-You orders through the SalesBlink web UI.",
  "data": {
    "login_link": "https://run.salesblink.io/magic?token=...&redirect=%2Foutreach%2Femail-senders%3Faddsenders%3Dtrue",
    "destination": "/outreach/email-senders?addsenders=true",
    "purpose": "place_dfy_order"
  }
}
```

**GET** `/dfy/orders/:orderId/mailbox-link` response:

```json
{
  "success": true,
  "message": "Please manage mailbox subscriptions through the SalesBlink web UI.",
  "data": {
    "login_link": "https://run.salesblink.io/magic?token=...&redirect=%2Faccount%2Fbilling%3Ftab%3Dsubscriptions",
    "destination": "/account/billing?tab=subscriptions",
    "purpose": "manage_subscriptions"
  }
}
```

## Search Domains

**GET** `/domains/search`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

Query params:
- `keyword` (required) — domain name to search (e.g. `mybrand` or `mybrand.com`). Only `.com` domains are supported.

Returns up to 10 available `.com` domains with pricing and workspace availability.

Response:
```json
{
  "success": true,
  "message": "Domain search completed successfully",
  "data": [
    {
      "domain": "mybrand.com",
      "price": 15.00,
      "status": "available",
      "google_workspace_available": true,
      "ms365_workspace_available": true
    }
  ]
}
```

## List DFY Orders

**GET** `/dfy/orders`

Headers:

- `Authorization`: `SALESBLINK_API_KEY`

Response:

```json
{
  "success": true,
  "message": "DFY orders retrieved successfully",
  "data": [
    {
      "id": "...",
      "type": "google",
      "status": "paid",
      "amount": 23.00,
      "domains": [...],
      "nameservers": null
    }
  ]
}
```

## Get Mailbox Cancel Link

**GET** `/dfy/orders/:orderId/mailboxes/:mailboxId/cancel-link`

Headers:

- `Authorization`: `SALESBLINK_API_KEY`

Returns a billing management login link because subscription changes must be managed from the web UI.

Response:

```json
{
  "success": true,
  "message": "Please manage your mailbox subscription from the billing page.",
  "data": {
    "login_link": "https://run.salesblink.io/magic?token=...&redirect=%2Faccount%2Fbilling%3Ftab%3Dsubscriptions",
    "destination": "/account/billing?tab=subscriptions",
    "purpose": "manage_subscriptions"
  }
}
```

To place a new DFY order or add mailboxes to an order, open the SalesBlink app and go to the email-sender connection flow.
````

## File: skills/cold-email-salesblink/references/folders.md
size: 2400
sha: 997a728f0960070a33fd47db353a6c584458386a
upstream: https://github.com/open-salesblink/skill/blob/main/skills/cold-email-salesblink/references/folders.md

````text
# Folders

SalesBlink has two kinds of folders:

- **General folders** — used for lists, templates, and sequences.
- **Email-sender folders** — used only for email senders.

Only include `type` when creating an email-sender folder. For general folders (lists, templates, sequences), do not include the `type` field.

## Endpoints

| Endpoint   | Method | Description     |
| ---------- | ------ | --------------- |
| `/folders` | GET | List folders — supports `type` filter: `all`, `general`, or `email-sender` |
| `/folders` | POST | Create a folder — returns `{ success: true }` only |

## Get Folders

**GET** `/folders`

Headers:

- `Authorization`: `SALESBLINK_API_KEY`

Query params: `limit` (max 100), `skip`, `search`, `type`

| `type` value | Result |
| ------------ | ------ |
| `all` | All folders (default if omitted) |
| `general` | Non-email-sender folders only |
| `email-sender` | Email-sender folders only |

## Create Folder

**POST** `/folders`

Headers:

- `Authorization`: `SALESBLINK_API_KEY`
- `Content-Type`: `application/json`

Body:

```json
{
  "name": "Q1 Campaigns"
}
```

For an email-sender folder:

```json
{
  "name": "Warm Senders",
  "type": "email-sender"
}
```

| Field  | Type   | Req | Description                                               |
| ------ | ------ | --- | --------------------------------------------------------- |
| `name` | string | ✅  | Folder name                                               |
| `type` | string |     | Only include this field when creating an email-sender folder. For general folders, do not include this field. |

> **Response:** The API returns `{ success: true }` only. It does not return the created folder object.

> Only `"email-sender"` is accepted as a `type` value. Any other value is rejected.

## Folder assignment rules

- Lists, templates, and sequences can only be placed in **general folders** (folders created without a `type`).
- Email senders can only be placed in **email-sender folders** (folders created with `type: "email-sender"`).
- Use `PATCH /lists/:id`, `PATCH /templates/:id`, or `PATCH /sequences/:id` with `folder` to move a list, template, or sequence into a general folder (or pass an empty string to remove it from its folder).
- Use `PATCH /senders/:id` with `folder` to move an email sender into an email-sender folder (or pass an empty string to remove it from its folder).
````

## File: skills/cold-email-salesblink/references/inbox-placement.md
size: 6171
sha: 12641df70519f7292ea81f736b433876784e8d9d
upstream: https://github.com/open-salesblink/skill/blob/main/skills/cold-email-salesblink/references/inbox-placement.md

````text
# 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`: `SALESBLINK_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`: `SALESBLINK_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"` |
| `email_senders` | array | ✅* | Array of senders/folders to test from (see format below). Use `sender_id` for a legacy single sender instead. |
| `sender_id` | string | ✅* | Legacy single sender UUID. Do not use together with `email_senders`. |
| `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"`) |
| `plainText` | boolean | | Send as plain text instead of HTML. |
| `tracking_uuid` | string | | Optional UUID for `from-outside` tests. Auto-generated if omitted. |

#### `email_senders` array format

Each item in `email_senders` must include these fields:

| Sub-field | Type | Description |
|-----------|------|-------------|
| `key` | string | UUID of the folder or sender. For folders this is the folder UUID; for senders this is the sender UUID. Same as `value`. |
| `value` | string | UUID of the folder or sender (same as `key`). The UUID used by the server. |
| `label` | string | Human-readable label, e.g. folder name or sender email address. |
| `serviceName` | string | Selection type. **MUST be `"folder"`** for folders. For individual senders use any value other than `"folder"` (commonly the provider name). |

You can mix folders and individual senders in the same array. Folders are expanded server-side into all senders they contain, and duplicates are automatically removed.

> *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 with multiple senders

**POST** `/inbox-placement`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`
- `Content-Type`: `application/json`

Body:
```json
{
  "name": "Gmail Deliverability Check",
  "mode": "one-time",
  "source": "from-salesblink",
  "email_senders": [
    {
      "key": "folder-uuid-here",
      "value": "folder-uuid-here",
      "label": "Sales Team Senders",
      "serviceName": "folder"
    },
    {
      "key": "sender-uuid-here",
      "value": "sender-uuid-here",
      "label": "faizan.shaikh.test@gmail.com",
      "serviceName": "gmail"
    }
  ],
  "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`: `SALESBLINK_API_KEY`
- `Content-Type`: `application/json`

Body:
```json
{
  "name": "Weekly Sequence Check",
  "mode": "recurring",
  "source": "from-salesblink",
  "email_senders": [
    {
      "key": "sender-uuid-here",
      "value": "sender-uuid-here",
      "label": "faizan.shaikh.test@gmail.com",
      "serviceName": "gmail"
    }
  ],
  "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`: `SALESBLINK_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": ["seed1@test.com", "seed2@test.com", ...]
}
```

## Pause Test

**PUT** `/inbox-placement/:id/pause`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

Only works on **recurring** tests. Sets status to `stopped` and clears scheduling.

## Delete Test

**DELETE** `/inbox-placement/:id`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

Deletes the test and all associated tracking tasks.
````

## File: skills/cold-email-salesblink/references/inbox.md
size: 5868
sha: d8a921fabc36c7b2c862cb5e3bc0d7607dc1cf9b
upstream: https://github.com/open-salesblink/skill/blob/main/skills/cold-email-salesblink/references/inbox.md

````text
# 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/forward` | POST   | Forward an email to another recipient             |
| `/inbox/:messageId`         | PATCH  | Mark as read/unread, set outcome classification   |

## Get Inbox

**GET** `/inbox`

Headers:

- `Authorization`: `SALESBLINK_API_KEY`

Query params:

| Param      | Type    | Description                                               |
| ---------- | ------- | --------------------------------------------------------- |
| `type`     | string  | `draft`, `scheduled`, `sent`, or omit for replies (default) |
| `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                                       |

> **Note:** Query filtering is limited on this endpoint. `owned_by` and several documented advanced filters are not implemented in the current API. Use the response client-side for precise filtering where needed.

```
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`: `SALESBLINK_API_KEY`

Returns all messages in a conversation thread, sorted newest first.

## Forward Email

**POST** `/inbox/:messageId/forward`

Headers:

- `Authorization`: `SALESBLINK_API_KEY`
- `Content-Type`: `application/json`

Body:

```json
{
  "email": "colleague@company.com",
  "content": "<p>Please see below.</p>"
}
```

| Field     | Type    | Req | Description                                                         |
| --------- | ------- | --- | ------------------------------------------------------------------- |
| `email`   | string  |     | Recipient email address (defaults to the original contact if omitted) |
| `content` | string  |     | Optional additional message content                                 |
| `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 |

> If `email` is omitted, the email is forwarded to the original contact. If `content` is omitted, the original email body is forwarded as-is.

## Reply to Email

**POST** `/inbox/:messageId/reply`

Headers:

- `Authorization`: `SALESBLINK_API_KEY`
- `Content-Type`: `application/json`

Body:

```json
{
  "content": "<p>Thanks for getting back to me! Let's schedule a call.</p>",
  "cc": "manager@company.com"
}
```

| 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`: `SALESBLINK_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"` |

> **Note:** The underlying API applies any body fields directly to the task document. Only `unread` and `outcome` are documented/stable; use other fields with caution.
````

## File: skills/cold-email-salesblink/references/lists.md
size: 3242
sha: bc0fc8e8fa34e91264f6204a3edfe53c22dd0297
upstream: https://github.com/open-salesblink/skill/blob/main/skills/cold-email-salesblink/references/lists.md

````text
# Lists

## Endpoints

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/lists` | GET | Retrieve all lists. Query: `limit` (max 100), `skip`, `owned_by`, `folder` |
| `/lists/:id` | GET | Get a specific list by UUID — returns a single object `{ ... }` |
| `/lists/:id/leads` | GET | Get leads in a list — `total` reflects current page size |
| `/lists` | POST | Create a new list |
| `/lists/:id` | PATCH | Update a list |
| `/lists/:id/archive` | PUT | Archive or unarchive a list |

## Get List by ID

**GET** `/lists/:id` (UUID)

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

> **Note:** The API returns a single list object `{ ... }` for this endpoint.

> **List responses include `fields`**: an array of the list's columns (from its Complete View) in snake_case, e.g. `["first_name", "last_name", "email", "lead_score"]`. This applies to `GET /lists`, `GET /lists/:id`, and `POST /lists` responses.

## Get Leads

**GET** `/lists/:id/leads?limit=100&skip=0`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

Query params: `limit` (max 100), `skip`

> **Note:** `total` in the response reflects the number of leads in the current page, not the total count across all pages.

## Create List

**POST** `/lists`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`
- `Content-Type`: `application/json`

Body:
```json
{
  "name": "Q1 Prospects"
}
```

Required fields marked with ✅:

| Field | Type | Req | Description |
|-------|------|-----|-------------|
| `name` | string | ✅ | List name |
| `folder` | string | | General folder ID (UUID). Must be a folder created without a `type` (i.e., not an email-sender folder). |
| `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** |

## Update List

**PATCH** `/lists/:id` (UUID)

Headers:
- `Authorization`: `SALESBLINK_API_KEY`
- `Content-Type`: `application/json`

Body:
```json
{
  "name": "Q2 Prospects Restructured",
  "folder": "folder_uuid",
  "starred": true,
  "archive_invalid": true
}
```

| Field | Type | Description |
|-------|------|-------------|
| `name` | string | New name |
| `folder` | string | Move list to a general folder (a folder created without a `type`, not an email-sender folder) |
| `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`: `SALESBLINK_API_KEY`
- `Content-Type`: `application/json`

Body:
```json
{ "archived": true }
```

Set `"archived": false` to unarchive.
````

## File: skills/cold-email-salesblink/references/organization.md
size: 2342
sha: e0d723d3c7ca3bb2b702870ab794486564775515
upstream: https://github.com/open-salesblink/skill/blob/main/skills/cold-email-salesblink/references/organization.md

````text
# 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`: `SALESBLINK_API_KEY`
- `Content-Type`: `application/json`

Body:
```json
{
  "email": "newmember@company.com",
  "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`: `SALESBLINK_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`: `SALESBLINK_API_KEY`
- `Content-Type`: `application/json`

Body:
```json
{ "name": "Sales Team" }
```

| Field | Type | Req | Description |
|-------|------|-----|-------------|
| `name` | string | ✅ | Workspace name (min 4 characters) |

> **Note:** The API returns HTTP `201 Created`, not `200 OK`.

### Update Workspace

**PATCH** `/workspaces/:id` (UUID)

Headers:
- `Authorization`: `SALESBLINK_API_KEY`
- `Content-Type`: `application/json`

Body:
```json
{ "name": "New Workspace Name" }
```

| Field | Type | Req | Description |
|-------|------|-----|-------------|
| `name` | string | ✅ | New workspace name (min 4 characters) |

Note: For POST /workspaces, you can also optionally pass `workspaceName` in the body.
````

## File: skills/cold-email-salesblink/references/reports.md
size: 862
sha: cb622b4635dc934703e3c0e0fb53ac9aa8774557
upstream: https://github.com/open-salesblink/skill/blob/main/skills/cold-email-salesblink/references/reports.md

```text
# Reports

## Endpoints

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/reports` | GET | Retrieve activity reports with aggregated data |

## Get Reports

**GET** `/reports`

Headers:
- `Authorization`: `SALESBLINK_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 | Page number (0-based). The server converts this to skip × limit as the database 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`: `SALESBLINK_API_KEY`
```

## File: skills/cold-email-salesblink/references/senders.md
size: 18499
sha: 09fd16792f3f4d59f8b15c431b3f1080a8629f88
upstream: https://github.com/open-salesblink/skill/blob/main/skills/cold-email-salesblink/references/senders.md

````text
# Email Senders & OAuth

## Endpoints

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/senders` | GET | List all connected senders (flat list, not grouped by folder) |
| `/senders/connect-link` | GET | Get a magic login link to the email-sender connection page in the SalesBlink web UI. |
| `/senders/bulk-connect-link` | GET | Get a magic login link to the bulk CSV sender upload page in the SalesBlink web UI. |
| `/senders/add-sender` | POST | Add one SMTP/IMAP sender directly from JSON credentials |
| `/senders/add-bulk-senders` | POST | Add or update many SMTP/IMAP senders from a CSV file (`multipart/form-data`) |
| `/senders/:id` | PATCH | Update sender settings (warmup, inbox, signature, tracking, folder, etc.) |
| `/senders/:id/reconnect` | POST | Reconnect a failed sender |
| `/senders/:id/health` | GET | Get sender health/reputation score |
| `/senders/:id/warmup-stats` | GET | Date-wise warmup stats |
| `/senders/:id/fetch-messages` | POST | Retrieve sent emails and received replies for a sender (max 50 messages, use `?skip` for pagination) |
| `/senders/multi/fetch-messages` | POST | Bulk retrieve sent emails and received replies across senders (max 50 messages, use `?skip` for pagination) |
| `/warmup-links` | GET | List warmup link configurations |
| `/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`: `SALESBLINK_API_KEY`

Query params: `limit` (max 100), `skip`, `folder` (email-sender folder UUID), `filter` (legacy filter string)

> **Note:** The response is a flat array of senders. It is **not** grouped by folder. The `owned_by` query parameter is not supported; use the `folder` (email-sender folder UUID) and `filter` query parameters if needed.

> Use `PATCH /senders/:id` with `folder` to move a sender into an email-sender folder, or pass an empty string to remove it from its folder. The `folder` field is **not** accepted when creating a sender.

## Get Single Sender Connection Link

**GET** `/senders/connect-link`

This endpoint does not create a sender. It returns a magic login link to the email-sender connection page in the SalesBlink web UI. Ways to connect a sender:
- **Gmail / Google Workspace**: use **POST** `/oauth/google` to get an authorization URL. The sender is created automatically after OAuth completion.
- **Microsoft 365 / Outlook**: use **POST** `/oauth/outlook` to get an authorization URL. The sender is created automatically after OAuth completion.
- **SMTP/IMAP, via the API**: use **POST** `/senders/add-sender` (one sender, JSON) or **POST** `/senders/add-bulk-senders` (CSV upload).
- **SMTP/IMAP, in the web UI**: request this endpoint and give the user the `login_link`. Prefer this when the user would rather not share mailbox passwords in the conversation.

Response (`LoginLinkResponse`):

```json
{
  "success": true,
  "message": "Please connect your email sender through the SalesBlink web UI.",
  "data": {
    "login_link": "https://run.salesblink.io/magic?token=eyJ...&redirect=%2Foutreach%2Femail-senders%3Faddsenders%3Dtrue",
    "destination": "/outreach/email-senders?addsenders=true",
    "purpose": "connect_sender"
  }
}
```

For existing senders, use **PATCH** `/senders/:id` to update settings such as warmup, inbox, signature, tracking, or folder.

## Add Sender (SMTP/IMAP)

**POST** `/senders/add-sender`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`
- `Content-Type`: `application/json`

Creates one SMTP/IMAP sender. The credentials are tested in the background, not during the request (see [After adding](#after-adding-a-sender)).

Minimal body:
```json
{
  "from_email": "alex@mycompany.com",
  "password": "app-password",
  "smtp_host": "smtp.mycompany.com",
  "smtp_port": 465,
  "imap_host": "imap.mycompany.com",
  "imap_port": 993
}
```

> **Handle credentials carefully**: `password` / `imap_password` are mailbox secrets. Never echo them back to the user or write them into logs or files. Recommend an app password where the provider supports one. If the user prefers not to share a password, use `GET /senders/connect-link` instead.

### Connection fields

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `from_email` | string | ✓ | Sender address. Stored lowercased |
| `password` | string | ✓ | SMTP password. Also used for IMAP unless `imap_user_name` + `imap_password` are both given |
| `smtp_host` | string | ✓ | SMTP server host |
| `smtp_port` | integer | ✓ | SMTP port (e.g. `465`, `587`) |
| `from_name` | string | | Display name. Defaults to `"Admin User"` |
| `user_name` | string | | SMTP username. Defaults to `from_email` |
| `imap_host` | string | | IMAP server host. **Omit for an SMTP-only sender**: an SMTP-only sender can send, but it cannot receive replies, monitor the inbox or warm up |
| `imap_port` | integer | | IMAP port (e.g. `993`) |
| `imap_user_name` | string | | Separate IMAP username. Only used when `imap_password` is also set; otherwise IMAP uses `from_email` / `password` |
| `imap_password` | string | | Separate IMAP password. Only used together with `imap_user_name` |

### Warmup & inbox fields

Warmup only applies to senders with IMAP. If `imap_host` is set, inbox monitoring is always enabled.

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `warmup_enabled` | boolean | `false` | Start warmup. Ignored for SMTP-only senders |
| `auto_ramp_up_enabled` | boolean | `true` | Ramp warmup volume up daily from the starting value to the max |
| `starting_warmup_frequency` | integer | `5` | Warmup emails on day one (1–50, capped at `max_daily_frequency`) |
| `total_warmup_per_day` | integer | `5` | Older name for the starting value; used only when `starting_warmup_frequency` is omitted |
| `ramp_up_frequency` | integer | `1` | Daily warmup increase (1–50, capped at `max_daily_frequency`) |
| `max_daily_frequency` | integer | `30` | Max warmup emails per day (1–50) |
| `warmup_tag` | string | `""` | Warmup keyword/tag |
| `inbox_path` | string | `"INBOX"` | IMAP inbox folder |
| `spam_path` | string | `""` | IMAP spam folder |

### Sequence sending fields

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `sequence_auto_ramp_up_enabled` | boolean | `false` | Ramp sequence volume up from the initial value to the max |
| `sequence_initial_daily_frequency` | integer | `30` | Starting daily sequence sends (min 1, capped at the max) |
| `sequence_ramp_up_frequency` | integer | `3` | Daily sequence-send increase (min 1, capped at the max) |
| `sequence_max_daily_frequency` | integer | `30` | Max daily sequence sends (min 1). `max_emails_per_day` is accepted as an alias |
| `pause_cold_emails_when_health_low` | boolean | `false` | Pause sequence sends when health drops below the threshold |
| `pause_cold_emails_health_threshold` | integer | `80` | Health score threshold |

The sender's daily sending limit starts at `sequence_initial_daily_frequency` when sequence ramp-up is on, otherwise at `sequence_max_daily_frequency`.

### Other fields

| Field | Type | Description |
|-------|------|-------------|
| `signature_id` | string | Signature ID **or** name (case-insensitive). Silently ignored if no signature matches |
| `custom_tracking_url` | string | Click-tracking domain, e.g. `track.mycompany.com`. A verified tracking domain in the account is used right away. Any other domain is registered as a tracking domain during connection and its DNS is checked (see `account-config.md`). If the domain belongs to another SalesBlink account, the connection fails |
| `dkim_identifier` | string | DKIM selector/identifier |
| `reply_to_email` | string | Reply-to address |

Notes:
- `folder` is not accepted here. Move the sender afterwards with `PATCH /senders/:id`.
- Booleans may be sent as `true` or `"true"`.
- camelCase aliases are also accepted: `email`, `senderName`, `smtpHost`, `smtpPort`, `smtpPassword`, `smtpUsername`, `imapHost`, `imapPort`, `imapUsername`, `imapPassword`, `autoRampUpEnabled`, `rampUpFrequency`, `maxDailyFrequency`, `startingWarmupFrequency`.

Response (`200`); `data` has the same shape as a `GET /senders` item and never includes passwords:
```json
{
  "success": true,
  "message": "Email Sender will be connected in a few minutes.",
  "data": {
    "id": "6c1f0b8e-3a5d-4e0f-9a51-2b7d8e4c1a90",
    "smtpImapEmail": "alex@mycompany.com",
    "senderType": "smtp",
    "serviceName": "smtpimap",
    "sendingEnabled": false,
    "receivingEnabled": false,
    "inboxEnabled": true
  }
}
```

`serviceName` is `"smtpimap"`, or `"smtponly"` when no `imap_host` was given.

Errors:

| Status | `message` | Cause |
|--------|-----------|-------|
| 400 | `from_email is required` (likewise `password`, `smtp_host`, `smtp_port`) | Missing required field |
| 403 | `Email sender limit reached. Limit: N` | The plan's sender limit is used up |
| 409 | `Sender already in use` | That address is already connected to SalesBlink, in this or another account, including as a Gmail/Outlook OAuth sender |

### After adding a sender

The connection is tested in the background, usually within a few minutes:
- **Success**: `sendingEnabled` becomes `true` (and `receivingEnabled` too, when IMAP is set). Check with `GET /senders`.
- **Failure** (wrong credentials, host or port): the sender stays disabled and the user is notified of the sender error. Fix the settings with `PATCH /senders/:id`, or have the user fix them in the web UI, then call `POST /senders/:id/reconnect`.

Only add a sender to a sequence once `sendingEnabled` is `true`.

## Add Senders in Bulk (CSV)

**POST** `/senders/add-bulk-senders`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`
- `Content-Type`: `multipart/form-data`

Upload a CSV file in the form field **`csvFile`**. Each row is matched by `from_email` against the account's existing senders:
- **No match**: a new sender is created.
- **Match with an existing SMTP/IMAP sender**: that sender's settings are updated from the row.
- **Match with a Gmail/Outlook OAuth sender**: the row is skipped.

> **Requires `multipart/form-data`.** The JSON-only MCP gateway cannot upload this file. In that case, use `GET /senders/bulk-connect-link`, or the CLI: `salesblink senders add-bulk-senders --csv-file senders.csv`.

```bash
curl -X POST "https://run.salesblink.io/api/public/v1.0.0/senders/add-bulk-senders" \
  -H "Authorization: $SALESBLINK_API_KEY" \
  -F "csvFile=@senders.csv"
```

Example CSV:
```csv
from_email,from_name,user_name,password,smtp_host,smtp_port,imap_host,imap_port,warmup_enabled
alex@mycompany.com,Alex,alex@mycompany.com,app-password-1,smtp.mycompany.com,465,imap.mycompany.com,993,true
sam@mycompany.com,Sam,sam@mycompany.com,app-password-2,smtp.mycompany.com,465,,,false
```

The second row has an empty `imap_host`, so it is added as SMTP-only.

### CSV columns

The CSV uses the same column names as the single-sender body, **except for the warmup columns**, which use `warmup_*` names:

| Group | Columns |
|-------|---------|
| Connection | `from_email`, `password`, `smtp_host`, `smtp_port`, `from_name`, `user_name`, `imap_host`, `imap_port`, `imap_user_name`, `imap_password` |
| Warmup | `warmup_enabled`, `warmup_auto_ramp_up_enabled` (default `true`), `warmup_initial_daily_frequency` (default `5`), `warmup_ramp_up_frequency` (default `1`), `warmup_max_daily_frequency` (default `30`); frequencies 1–50 |
| Inbox | `inbox_enable`, `warmup_tag`, `inbox_path`, `spam_path` |
| Sequence | `sequence_auto_ramp_up_enabled`, `sequence_initial_daily_frequency`, `sequence_ramp_up_frequency`, `sequence_max_daily_frequency` (or `max_emails_per_day`), `pause_cold_emails_when_health_low`, `pause_cold_emails_health_threshold`; same defaults as the single-sender body |
| Other | `signature_id`, `custom_tracking_url`, `dkim_identifier`, `reply_to_email` |

CSV vs. the single-sender endpoint:
- **No per-row validation, and fewer defaults.** Fill in `from_email`, `password`, `smtp_host`, `smtp_port`, `from_name` and `user_name` on every row; `from_name` and `user_name` are **not** filled in from `from_email`.
- **SMTP-only rows**: include the `imap_host` column and leave it empty.
- **Inbox monitoring** is only enabled when the row sets `inbox_enable` to `true`. `warmup_enabled` only applies when IMAP is set.
- **Skipped rows**: blank `from_email`, rows whose `from_email` contains `Email address` (a template's example row), and later duplicates of the same `from_email` within the file.
- **Missing `from_email` header**: the whole upload fails.

Response (`200`):
```json
{
  "success": true,
  "data": null,
  "message": "Bulk senders uploaded successfully"
}
```

The response does not list the senders it created. Call `GET /senders` to see them. Each sender connects in the background as described in [After adding](#after-adding-a-sender).

Errors:

| Status | `message` |
|--------|-----------|
| 400 | `csvFile is required (multipart/form-data)` |
| 403 | `Bulk sender upload is not available on your plan.` |
| 403 | `Email sender limit reached. Limit: N` (checked before the upload) |

## Sender Lifecycle & Diagnostics

**POST** `/senders/:id/reconnect`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`
- `Content-Type`: `application/json`

Re-queues a failed SMTP/IMAP sender for connection, or refreshes OAuth tokens for Gmail/Outlook senders.

**GET** `/senders/:id/health`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

Returns sender health/reputation score and diagnostic info.

**GET** `/senders/:id/warmup-stats`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

Query params: `days` (integer, max 90, default 30)

Returns date-wise warmup statistics for the sender.

**POST** `/senders/:id/fetch-messages`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`
- `Content-Type`: `application/json`

Schedules an inbox fetch for the sender.

**POST** `/senders/multi/fetch-messages`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`
- `Content-Type`: `application/json`

Body:
```json
{ "ids": ["sender-id-1", "sender-id-2"] }
```

Bulk fetch messages across multiple senders.

## Get Bulk Sender Upload Link

**GET** `/senders/bulk-connect-link`

This endpoint returns a magic login link for uploading a sender CSV in the SalesBlink web UI. Use it when the CSV can't be sent as `multipart/form-data` (for example through the MCP gateway), or when the user prefers the UI. To upload the CSV through the API, use **POST** `/senders/add-bulk-senders`.

Calling **GET** `/senders/bulk-connect-link` returns a magic login link to the email-sender connection page:

```json
{
  "success": true,
  "message": "Please upload senders in bulk through the SalesBlink web UI.",
  "data": {
    "login_link": "https://run.salesblink.io/magic?token=...&redirect=%2Foutreach%2Femail-senders%3Faddsenders%3Dtrue",
    "destination": "/outreach/email-senders?addsenders=true",
    "purpose": "bulk_upload_senders"
  }
}
```

## Update Sender

**PATCH** `/senders/:id`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`
- `Content-Type`: `application/json`

Pass any of the fields below to update specific sender settings. Only provided fields are updated.

### Warmup fields

| Field | Type | Description |
|-------|------|-------------|
| `warmup_enabled` | boolean | Enable/disable warmup |
| `warmup_urls` | array | Warmup link URLs |
| `warmup_templates` | array | Warmup template IDs |
| `auto_ramp_up_enabled` | boolean | Enable auto ramp-up |
| `ramp_up_frequency` | integer | Ramp-up increment |
| `max_daily_frequency` | integer | Max daily warmup emails |
| `starting_warmup_frequency` | integer | Starting warmup frequency |
| `open_rate` | integer | Target open rate % |
| `spam_protection` | integer | Spam protection level |
| `read_emulation` | integer | Read emulation level |
| `warmup_keyword` | string | Warmup keyword/tag |

### Sequence / sending fields

| Field | Type | Description |
|-------|------|-------------|
| `sequence_auto_ramp_up_enabled` | boolean | Enable sequence auto ramp-up |
| `sequence_initial_daily_frequency` | integer | Initial daily sequence sends |
| `sequence_ramp_up_frequency` | integer | Sequence ramp-up increment |
| `sequence_max_daily_frequency` | integer | Max daily sequence sends |
| `pause_cold_emails_when_health_low` | boolean | Pause cold emails when health is low |
| `pause_cold_emails_health_threshold` | integer | Health threshold to pause at |

### Inbox fields

| Field | Type | Description |
|-------|------|-------------|
| `inbox_enabled` | boolean | Enable inbox monitoring |
| `inbox_path` | string | Inbox folder path |
| `spam_path` | string | Spam folder path |

### Other fields

| Field | Type | Description |
|-------|------|-------------|
| `signature` | string | Signature ID or name |
| `reply_to` | string | Reply-to email address |
| `dkim_identifier` | string | DKIM identifier |
| `use_custom_tracking_domain` | boolean | Use custom tracking domain |
| `tracking_domain` | string | Tracking domain ID |
| `folder` | string | Email-sender folder ID to move the sender into. Must be a folder created with `type: "email-sender"`. Pass an empty string to remove from folder. |

Response:
```json
{
  "success": true,
  "message": "Sender settings updated successfully",
  "data": { ... }
}
```

## Warmup Links

**GET** `/warmup-links`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

Query params: `limit` and `skip` are accepted for compatibility but currently have no effect — all matching links are returned.

List warmup link configurations. These URLs are used in sender warmup campaigns to improve deliverability.

## Google OAuth

**POST** `/oauth/google`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

Body is optional. A custom `redirectUrl` in the body is ignored by the API.

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`: `SALESBLINK_API_KEY`

Body is optional. A custom `redirectUrl` in the body is ignored by the API.

Returns an `auth_url` for Microsoft Outlook authorization.

Response:
```json
{
  "success": true,
  "data": { "auth_url": "https://login.microsoftonline.com/..." }
}
```
````

## File: skills/cold-email-salesblink/references/sequences.md
size: 11150
sha: 839c7db407423aed01c6241a8bda182e6c9c15b9
upstream: https://github.com/open-salesblink/skill/blob/main/skills/cold-email-salesblink/references/sequences.md

````text
# Sequences

## Endpoints

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/sequences` | GET | List all sequences. Query: `limit`, `skip`, `owned_by`, `status` (`running`, `paused`, `completed`, `needs-attention`), `folder`, `sendAll`, `sortBy`, `sortType`, `search` |
| `/sequences/:id` | GET | Get sequence details including the internal `flowchart` object |
| `/sequences/:id/stats` | GET | Get performance analytics. Query: `from`, `to`, `sender` (filter ignored by current API) |
| `/sequences/:id/leads` | GET | List leads in a sequence (supports `status` filter) |
| `/sequences/:id/leads/:leadId/messages` | GET | Message history for a lead in a sequence |
| `/sequences/:id/leads/:leadId/unsubscribe` | POST | Remove a lead from a sequence |
| `/sequences/:id/export` | GET | Export sequence leads as CSV |
| `/sequences/:id/status` | POST | Update sequence status (ACTIVE, PAUSED, STOPPED, ARCHIVED) |
| `/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 |

## Get Sequences

**GET** `/sequences`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

Query params: `limit` (max 100), `skip`, `owned_by`, `status`, `folder`, `sendAll`, `sortBy`, `sortType`, `search`

| `status` value | Meaning |
|----------------|---------|
| `running` | Active sequences |
| `paused` | Paused sequences |
| `completed` | Sequences that have completed all sends |
| `needs-attention` | Sequences with issues (e.g. bounces, errors) |

## Get Sequence by ID

**GET** `/sequences/:id` (UUID)

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

> **Note:** The response exposes the internal `flowchart` object. Step data must be read from `flowchart`, not from a simplified `steps` array.

## Create Sequence

**POST** `/sequences`

Headers:
- `Authorization`: `SALESBLINK_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", "send_as": "followup" }
  ],
  "paused": false,
  "launchTimingMode": "schedule",
  "scheduledAt": 1751983200000,
  "timezone": "Asia/Calcutta",
  "delayEnabled": true,
  "delayFrom": 10,
  "delayTo": 20,
  "stopWhenReplyRecieved": true,
  "stopWhenReplyRecievedWhen": "contact",
  "emailSendingHours": [
    { "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" }
  ]
}
```

Required fields marked with ✅:

| Field | Type | Req | Description |
|-------|------|-----|-------------|
| `name` | string | ✅ | Sequence name |
| `senders` | string | ✅ | **Comma-separated string** of sender/folder IDs (NOT an array). Folder IDs here must be email-sender folders. |
| `lists` | string[] | ✅ | Array of list UUIDs |
| `steps` | Step[] | ✅ | Ordered array of email and delay steps |
| `folder` | string | | General folder ID (UUID). Must be a folder created without a `type` (i.e., not an email-sender folder). |
| `starred` | boolean | | Star the sequence |
| `paused` | boolean | | Create in paused state (default: **true**) |
| `launchTimingMode` | string | | `"now"` (starts in 5 mins) or `"schedule"` (requires `scheduledAt` and `timezone`) |
| `scheduledAt` | integer \| string | | UTC timestamp in **milliseconds** (recommended). ISO 8601 strings with timezone offset are documented but are not parsed correctly by the current API. Convert the desired time to Unix milliseconds before sending. (required if `launchTimingMode = "schedule"`) |
| `timezone` | string | | IANA timezone for sending, e.g. `"Asia/Calcutta"` or `"America/New_York"`. Required when scheduling to avoid defaulting to `"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", "send_as": "followup" }
]
```

- `type: "email"` → MUST include `template_id` (the template's MongoDB ObjectId). MAY include `send_as`: `"new"` (default) sends a fresh thread; `"followup"` sends the email as a reply in the lead's existing thread (`"Re: <original subject>"`).
- `send_as: "followup"` is ignored on the **first email step** of the sequence — there is no previous thread to reply to, so it always sends as a new thread. Only steps after the first email can follow up.
- `type: "delay"` → MUST include `days` (integer, number of days to wait). `send_as` is not valid on delay steps.
- Omitting `type` or misspelling it will fail or create a broken sequence.
- The stored value is readable back on `GET /sequences/:id` as `flowchart.nodes[].data.send_as` (`{ "label": "RE: Follow Up", "value": "followup" }`); nodes without `send_as` send as new threads.

**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`: `SALESBLINK_API_KEY`
- `Content-Type`: `application/json`

Body:
```json
{
  "folder": "folder_uuid",
  "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. `folder` must be a folder created without a `type` (i.e., not an email-sender folder). All other field updates do.

## Clone Sequence

**POST** `/sequences/:id/clone` (UUID)

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

Creates a paused duplicate of an existing sequence.

Response:
```json
{
  "success": true,
  "data": { "clonedSequenceId": "new-sequence-uuid" },
  "message": "Sequence cloned successfully"
}
```

## Archive Sequence

**PUT** `/sequences/:id/archive` (UUID)

Headers:
- `Authorization`: `SALESBLINK_API_KEY`
- `Content-Type`: `application/json`

Body:
```json
{ "archived": true }
```

Archiving a sequence automatically pauses it and removes pending email tasks.

## Sequence Lead Lifecycle

**GET** `/sequences/:id/leads`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

Query params: `limit` (max 500), `skip`, `status` (`not_contacted`, `contacted`, `opened`, `clicked`, `replied`, `bounced`, `unsubscribed`)

The status filter works in two scopes: `not_contacted` (first email not yet sent) and `contacted` (first email sent) are evaluated on the lead's first-outreach task. `opened`, `clicked`, `replied`, `bounced` and `unsubscribed` match engagement recorded on any step of the sequence, so leads who engaged with a follow-up step are included.

Response: `{ success, data: [...], count, total, skip, limit }`

**GET** `/sequences/:id/leads/:leadId/messages`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

Query params: `from`, `to` (Unix ms), `limit` (max 500), `skip`

Response: `{ success, data: [...], count, skip, limit }`

**POST** `/sequences/:id/leads/:leadId/unsubscribe`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

Removes the lead from the sequence (same behavior as the internal remove-from-sequence action). Returns `{ success: true }`.

## Sequence Export & Status

**GET** `/sequences/:id/export`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`

Query params: `limit` (max 50000, default 10000)

Returns a CSV download of sequence leads.

**POST** `/sequences/:id/status`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`
- `Content-Type`: `application/json`

Body:
```json
{ "status": "PAUSED" }
```

Allowed statuses: `ACTIVE`, `PAUSED`, `STOPPED`, `ARCHIVED`.
````

## File: skills/cold-email-salesblink/references/templates.md
size: 2869
sha: 7a2e6b57afb0d936ad404bf579a618b59e20d12c
upstream: https://github.com/open-salesblink/skill/blob/main/skills/cold-email-salesblink/references/templates.md

````text
# 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`: `SALESBLINK_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 | | General folder ID (UUID). Must be a folder created without a `type` (i.e., not an email-sender folder). |
| `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`: `SALESBLINK_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 |
| `folder` | string | Move template to a general folder (a folder created without a `type`, not an email-sender folder). Pass an empty string to remove from folder. |
| `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`: `SALESBLINK_API_KEY`
- `Content-Type`: `application/json`

Body:
```json
{ "archived": true }
```
````

## File: skills/cold-email-salesblink/references/workflows.md
size: 3735
sha: 73353a6c6a2e456db5b1639a62d7b9837a6fb02d
upstream: https://github.com/open-salesblink/skill/blob/main/skills/cold-email-salesblink/references/workflows.md

````text
# Workflow Examples

## End-to-End Campaign Setup

Goal: Build a complete outreach campaign from scratch.

### Step 1 — Create a list

**POST** `/lists`

Headers:
- `Authorization`: `SALESBLINK_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`: `SALESBLINK_API_KEY`
- `Content-Type`: `application/json`

Body:
```json
{
  "list_id": "LIST_ID_HERE",
  "contacts": [
    { "email": "alice@corp.com", "first_name": "Alice", "company_name": "Corp Inc" },
    { "email": "bob@startup.io", "first_name": "Bob", "company_name": "Startup IO" }
  ],
  "remove_duplicates": true
}
```

### Step 3 — Create email templates

**POST** `/templates`

Headers:
- `Authorization`: `SALESBLINK_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`: `SALESBLINK_API_KEY`

Extract target `SENDER_ID` (UUID).

### Step 5 — Create the sequence

**POST** `/sequences`

Headers:
- `Authorization`: `SALESBLINK_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", "send_as": "followup" }
  ],
  "paused": false,
  "launchTimingMode": "schedule",
  "scheduledAt": 1751983200000,
  "timezone": "America/New_York"
}
```

## 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`: `SALESBLINK_API_KEY`

Extract `NEW_SEQ_ID` from `response.data.clonedSequenceId`.

### 2. Update the cloned sequence with new settings

**PATCH** `/sequences/:id`

Headers:
- `Authorization`: `SALESBLINK_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", "send_as": "followup" }
  ]
}
```

### 3. Launch when ready

**PATCH** `/sequences/:id`

Headers:
- `Authorization`: `SALESBLINK_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`: `SALESBLINK_API_KEY`
- `Content-Type`: `application/json`

Body:
```json
{ "archived": true }
```

### Archive the associated list

**PUT** `/lists/:id/archive`

Headers:
- `Authorization`: `SALESBLINK_API_KEY`
- `Content-Type`: `application/json`

Body:
```json
{ "archived": true }
```

### Archive templates

**PUT** `/templates/:id/archive`

Headers:
- `Authorization`: `SALESBLINK_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: README.md
upstream: https://github.com/open-salesblink/skill/blob/main/README.md

````text
# SalesBlink Cold Email Outreach Skill

[![GitHub release](https://img.shields.io/github/v/release/open-salesblink/skill)](https://github.com/open-salesblink/skill/releases)
[![Claude Desktop one-click install](https://img.shields.io/badge/Claude%20Desktop-one--click%20install-d97757)](https://github.com/open-salesblink/skill/releases/latest/download/cold-email-salesblink.mcpb)
[![GitHub stars](https://img.shields.io/github/stars/open-salesblink/skill)](https://github.com/open-salesblink/skill)
[![Last commit](https://img.shields.io/github/last-commit/open-salesblink/skill)](https://github.com/open-salesblink/skill/commits/main)
[![Release downloads](https://img.shields.io/github/downloads/open-salesblink/skill/total)](https://github.com/open-salesblink/skill/releases)
[![Website](https://img.shields.io/badge/website-salesblink.io-orange)](https://salesblink.io)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

Run cold email sequences and sales outreach on autopilot via the [SalesBlink](https://salesblink.io) API. Works with any AI agent — Claude, Claude Code, Claude Desktop, ChatGPT, OpenClaw, Cursor, Codex, Hermes, and more. Install it as an agent skill, or use the one-click `.mcpb` bundle for MCPB-compatible hosts.

---

## Install in 30 seconds

### 1. Get your API key (or sign up)

Grab your key from [`run.salesblink.io/account/integration/api`](https://run.salesblink.io/account/integration/api), or use the `salesblink_signup` tool to get the public sign-up link.

### 2. Install the skill

**Recommended: let your agent install it (copy-paste prompt)**

Paste this into your AI agent and it will set everything up for you:

```text
Set up the SalesBlink Cold Email agent skill from https://github.com/open-salesblink/skill
for me. Read the README and SKILL.md in that repo, then install the skill in this
environment using the method that fits you best.

Ask me for my SalesBlink API key (from
https://run.salesblink.io/account/integration/api), or use the salesblink_signup
tool to get the sign-up link if I don't have an account yet. Configure the API key
as SALESBLINK_API_KEY. Verify the setup works, then help me launch my first cold
email sequence: ask what product I'm selling and who I'm targeting, create the
list, contacts, templates, and sequence, and walk me through launching it.
```

**Or install manually**

**Any agent (npx)**

```bash
npx skills add open-salesblink/skill
```

**Claude Code**

```bash
/plugin marketplace add https://github.com/open-salesblink/skill
/plugin install cold-email-salesblink
```

**Hermes**

```bash
hermes skills tap add open-salesblink/skill
hermes skills install cold-email-salesblink
```

**OpenClaw / ClawHub**

```bash
openclaw skills install cold-email-salesblink
```

### 3. Set your API key

```bash
export SALESBLINK_API_KEY="key-****"
```

### 4. Start working

- _"Create a cold email sequence for my SaaS product"_
- _"Add these leads to a new list and launch a campaign"_
- _"Check analytics for opens and replies from last week"_
- _"Run an inbox placement test for my template"_

---

## What you can do

- **Cold email campaigns** — build sequences, write templates, and launch outreach
- **Lead management** — create lists, import contacts in bulk, move and update leads
- **Sender management** — connect Gmail/Outlook via OAuth; add SMTP/IMAP senders directly or in bulk from a CSV
- **Inbox & replies** — read threads, reply to prospects, mark conversations
- **Analytics** — track opens, clicks, replies, and sent events
- **Deliverability testing** — run inbox placement tests across providers
- **Workspace & team management** — users, roles, folders, and account config

---

## Other platforms

### Codex

```bash
codex plugin marketplace add https://github.com/open-salesblink/skill
```

Invoke by referencing the plugin namespace.

### Cursor

```bash
git clone https://github.com/open-salesblink/skill.git
```

Load the plugin from the cloned directory in Cursor's plugin settings.

### Gemini CLI

```bash
gemini extensions install https://github.com/open-salesblink/skill
```

Gemini CLI prompts for your `SALESBLINK_API_KEY` and stores it in the system keychain.

### ChatGPT

ChatGPT connects via MCP — visit https://mcp.salesblink.io and add SalesBlink as an MCP connector, or use the `.mcpb` bundle below in an MCPB-compatible host.

### MCP

Visit https://mcp.salesblink.io to connect via MCP.

### Claude Desktop / MCP Bundle (`.mcpb`)

[![Download MCPB](https://img.shields.io/badge/download-latest_.mcpb-blue)](https://github.com/open-salesblink/skill/releases/latest/download/cold-email-salesblink.mcpb)

**[⬇ Download cold-email-salesblink.mcpb](https://github.com/open-salesblink/skill/releases/latest/download/cold-email-salesblink.mcpb)** — one-click install of this skill in MCPB-compatible hosts (e.g. Claude for macOS/Windows). Open the downloaded file and enter your `SALESBLINK_API_KEY` when prompted.

The bundle runs a local Node.js MCP server with hardened gateway tools plus the full bundled API reference. Source lives in [`mcpb/cold-email-salesblink/`](mcpb/cold-email-salesblink/). To build it yourself instead of downloading:

```bash
cd mcpb/cold-email-salesblink
npm install --omit=dev
npm test                            # protocol smoke test (no API key needed)
npx @anthropic-ai/mcpb pack .       # produces cold-email-salesblink.mcpb
```

See [`mcpb/cold-email-salesblink/README.md`](mcpb/cold-email-salesblink/README.md) for tools, configuration, and security details.

> **Note:** All platforms above require the same `SALESBLINK_API_KEY` environment variable shown in Step 3.

---

## API Quick Reference

**Base URL**

```
https://run.salesblink.io/api/public/v1.0.0
```

**Authentication**
Pass your API key in the `Authorization` header with no "Bearer" prefix:

```
Authorization: key-****
```

**Pagination**

- Most list endpoints: `limit` (max 100) and `skip`
- Activity endpoints (`/sent`, `/opens`, `/clicks`, `/replies`): `per_page` (max 100) and `page` (1-indexed)

Always paginate — never assume a single request returns all data.

**Error Handling**

| 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                                  |

---

## Quick Start: End-to-End Campaign

See [`skills/cold-email-salesblink/references/workflows.md`](skills/cold-email-salesblink/references/workflows.md) for full examples. High-level flow:

1. **Create a list** → `POST /lists`
2. **Add contacts** → `POST /contacts` (batch up to 500, snake_case fields)
3. **Create templates** → `POST /templates` (merge vars like `{{first_name}}`)
4. **Fetch senders** → `GET /senders`
5. **Create sequence** → `POST /sequences` (steps: email → delay → email …)
6. **Launch** → `PATCH /sequences/:id` with `paused: false` & `launchTimingMode: "now"`

---

## Evaluations

The [`evals/evals.json`](evals/evals.json) file contains test scenarios covering list creation, bulk contact import, sequence creation, large CSV ingestion, inbox replies with CC, and paginated activity tracking.

---

## Compatibility

- Requires network access to `run.salesblink.io`
- Supports any HTTP client (curl, Node.js fetch, Python requests, PowerShell, etc.)

## License

MIT — © FUTUREBLINK Inc. See [LICENSE](LICENSE).
````
