← all tools
Sequencers

SmartLead

Cold email at scale, across as many mailboxes as you run.

Cost per callBYOK · vendor billed
Your own keyrequired
CategorySequencers

Smartlead is built for volume: many sending mailboxes, rotation between them, analytics per campaign. Your agent creates the campaign, drafts the sequence and loads the leads, then stops and waits for your approval before a single email goes.

Smartlead is activation for high volume. Everything upstream feeds it a validated list.

things you can ask for

  • Build a three-step sequence for this role url and load the leads
  • Load these 500, but only the addresses Enrichley passed
  • Have Hyreflow Agent write a first line for each one before we start
  • Push anyone who replies into Bullhorn as a new contact

works well with SmartLead

  • Enrichley

    volume sending punishes a bad list hardest

  • Hyreflow Agent

    personalisation at the scale volume sending needs

  • FullEnrich

    fill a large list before it goes near a mailbox

  • Bullhorn

    replies land on the record automatically

Ask for the outcome and your agent composes the run itself. Or start from a skill, a whole pipeline it already knows end to end.

browse all skills →

What your agent reads before it touches SmartLead

Every tool ships with a written playbook, and the agent loads it before the first call. Auth, rate limits, what each call costs, which actions need your approval, and the mistakes worth avoiding. It is the difference between an agent that knows the tool and one that guesses at it.

Read the SmartLead docs →
Show the raw playbookwritten for the agent

HOW in lib/smartlead.py. Docs cached at reference/docs/smartlead/raw/llms.txt.

Auth & config

  • Base URL: https://server.smartlead.ai/api/v1 · Auth: API key as query param ?api_key= (env SMARTLEAD_API_KEY; never hardcode). The client injects it automatically.

Approval gates (sending)

  • update_campaign_status(id, "START") begins sending. add_leads_to_campaign into an active campaign feeds live sending. Approval-gate both: build in a paused campaign / lead list, pilot, confirm, then start.

Operations

Campaigns: list_campaigns, create_campaign, get_campaign, update_campaign_status, save_campaign_sequence, get_campaign_analytics. Leads: add_leads_to_campaign, list_leads_by_campaign. Email accounts: list_email_accounts, add_email_account_to_campaign. Else → generic request().

Handoff

Activation layer: enriched + validated leads → add_leads_to_campaign → review → START. Mailboxes via InboxKit feed list_email_accounts.

Field notes (production experience)

Field-shape note: these are vendor-native operational notes. The client returns the raw vendor JSON and uses the method names in this file — read field shapes accordingly (no normalized-wrapper / result.data. prefix).

Activation order: create/list campaign -> add email accounts -> save sequences -> set schedule -> set settings -> push leads -> start -> monitor. Always resolve the campaign ID before any write.

Gotchas (vendor-native):

  • Campaign status valid values: PAUSED, STOPPED, START - it's START, not STARTED/RUNNING.
  • Lead batches: max 400 per push; duplicate emails within a batch are rejected; emails auto-lowercased for dedup.
  • Sequences: seq_number must be contiguous from 1 (no gaps); step 1 must have a subject; delay_in_days is days (0 = immediate) - never hours/minutes.
  • Schedule: IANA timezone (America/New_York); start_hour/end_hour 24h HH:MM, start strictly before end; days_of_the_week 0=Sun..6=Sat, >=1 required. min_time_btw_emails is canonical (min_time_btwn_emails legacy alias).
  • Track settings (string or array): DONT_TRACK_EMAIL_OPEN, DONT_TRACK_LINK_CLICK, DONT_TRACK_REPLY_TO_AN_EMAIL. Stop-lead: REPLY_TO_AN_EMAIL, CLICK_ON_A_LINK, OPEN_AN_EMAIL.
  • Analytics by date: max 30-day window; dates YYYY-MM-DD.
  • Webhook event types: EMAIL_SENT, EMAIL_OPEN, EMAIL_LINK_CLICK, EMAIL_REPLY, LEAD_UNSUBSCRIBED, LEAD_CATEGORY_UPDATED.
  • Block list: entries must be a bare domain or email (no protocol/path).
  • Edge cases: fetch-lead-by-email returns HTTP 200 {} when missing (treat as no-result); unsubscribe can return 200 {"ok":false} for unknown lead (treat as failure); lead export returns CSV text, not JSON.
  • Rate limit: ~60 req / 60s per API key. (Our adapter passes the key as a query param, matching Smartlead.)

Callable surface — lib/smartlead.py

Import: from lib.smartlead import Smartlead → instantiate Smartlead() (reads key from env). Base: https://server.smartlead.ai/api/v1. Generic passthrough: request(method, path, *, params, json).

  • add_email_account_to_campaign(campaign_id: str, email_account_ids: list[int]) -> Any
  • add_leads_to_campaign(campaign_id: str, lead_list: list[dict], **settings) -> Any — POST /campaigns/{id}/leads — add leads. Feeds an active campaign into sending — approval-gate.
  • create_campaign(name: str, **opts) -> Any — POST /campaigns/create — create a campaign.
  • get_campaign(campaign_id: str) -> Any
  • get_campaign_analytics(campaign_id: str) -> Any
  • list_campaigns() -> Any — GET /campaigns — all campaigns.
  • list_email_accounts(**params) -> Any — GET /email-accounts/ — sending mailboxes.
  • list_leads_by_campaign(campaign_id: str, **params) -> Any
  • save_campaign_sequence(campaign_id: str, sequences: list[dict]) -> Any — POST /campaigns/{id}/sequences — define the email steps/A-B variants.
  • update_campaign_status(campaign_id: str, status: str) -> Any — POST /campaigns/{id}/status — status: PAUSED | STOPPED | START. START begins sending (outreach!).