> ## Documentation Index
> Fetch the complete documentation index at: https://docs.truthlocks.com/llms.txt
> Use this file to discover all available pages before exploring further.

# AI CMO

> Run autonomous outbound campaigns that discover leads, generate personalized emails, classify replies, and sync hot leads to your CRM.

The AI CMO is an autonomous campaign engine for enterprise outbound. It uses specialized AI agents that reason, plan, and adapt in real time — handling lead discovery, AI scoring, personalized email generation, reply classification, follow-up sequencing, and CRM sync — so you can launch targeted outreach without manual effort.

You manage campaigns from the **Growth** section of the [console](https://console.truthlocks.com) or through the platform API. Once a campaign is activated, the AI CMO orchestrator runs continuously, processing leads and sending emails within the safety limits you configure.

## How it works

The AI CMO processes each active campaign through a repeating loop:

1. **Discover leads.** When a campaign has fewer than ten pending leads, the orchestrator automatically discovers new prospects matching your ICP through [multiple enrichment providers](#multi-source-lead-discovery) with automatic failover.
2. **Score leads.** Each lead receives an AI-generated qualification score (0–100) with a fit level of high, medium, or low.
3. **Generate emails.** Personalized outbound emails are composed by the AI writing model in the campaign's [salesperson persona](#salesperson-personas) voice, based on the campaign strategy, lead profile, and product knowledge. Each email is signed with the persona's name and title.
4. **Decide whether to follow up.** Before every follow-up email, the AI evaluates the lead individually — considering campaign strategy, prior contact history, and engagement signals — and skips the send if a follow-up is unlikely to be productive. See [AI-powered follow-up decisions](#ai-powered-follow-up-decisions).
5. **Send within safety limits.** Every send passes through a seven-layer safety check before delivery. Daily caps, per-minute caps, bounce thresholds, and complaint thresholds are enforced automatically. Emails are also held until the lead's [local business hours](#timezone-aware-outreach) (9 am–6 pm, weekdays).
6. **Capture and classify replies.** Inbound replies are [automatically captured](#reply-capture) via a dedicated reply address, matched to the original lead, and classified by sentiment — positive, negative, neutral, out-of-office, auto-reply, request for more information, or not interested.
7. **Sync hot leads.** Leads that reply positively or request more information are automatically pushed to your [CRM](#crm-integration). When HubSpot is connected, the AI CMO creates a deal, logs email activity on the contact timeline, and updates the lead's lifecycle stage to sales-qualified — giving your sales team a complete handoff.
8. **Learn and adjust.** The system synthesizes what worked — effective [salesperson personas](#salesperson-personas), messaging angles, subject lines — every 20 emails and again when a campaign completes. Active campaigns also receive mid-flight strategy adjustments based on bounce and reply rates.

Every step of this loop is visible in the [agent activity viewer](#agent-activity-viewer), where you can inspect the full reasoning trail for each agent.

## Agentic architecture

The AI CMO uses four specialized AI agents that collaborate on each campaign. Each agent follows a reason-act loop — it plans what to do, executes the action, observes the result, and adjusts its approach before moving to the next step.

| Agent                 | Role                                                                                                                                                                                                                                                                              |
| :-------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Campaign director** | Coordinates the overall campaign. Decides when to discover leads, when to generate emails, and when to adjust strategy based on performance signals.                                                                                                                              |
| **Prospector**        | Discovers and qualifies leads. Queries enrichment providers, scores prospects against the ICP, and handles [multi-source failover](#multi-source-lead-discovery).                                                                                                                 |
| **Writer**            | Generates personalized outbound emails in the campaign's [salesperson persona](#salesperson-personas) voice. Uses the campaign strategy, lead profile, and [knowledge base](#knowledge-base) entries to craft warm, human-sounding messaging. Produces A/B subject line variants. |
| **Strategist**        | Analyzes campaign performance and synthesizes learnings. Recommends mid-flight adjustments to messaging, cadence, and targeting based on bounce and reply rates.                                                                                                                  |

The agents run autonomously once a campaign is activated. You do not need to configure or manage them directly — the orchestrator assigns work to each agent based on the campaign's current state.

<Info>
  The agentic architecture replaces the previous single-model orchestrator. Campaigns created before this update continue to work without changes — the new agents handle the same lifecycle steps with improved reasoning and adaptability.
</Info>

## Salesperson personas

Every campaign automatically generates a **salesperson persona** — a persistent fictional identity that signs all outbound emails for that campaign. The persona gives your outreach a consistent human voice instead of generic corporate messaging.

When you create a campaign, the AI CMO generates a persona tailored to your target audience. A campaign targeting financial services compliance officers gets a persona with a regulatory background; a campaign targeting engineering leaders gets one with an enterprise SaaS background. The persona includes a first name, last name, title, personality style, and a short backstory that establishes credibility with your ICP.

### What the persona controls

| Element             | How the persona is used                                                                                                                                                   |
| :------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Email voice**     | All emails are written in the persona's personality style — warm-consultative, direct-strategic, analytical-advisory, or relationship-builder                             |
| **Email signature** | Every email is signed with the persona's name, title, and "Truthlocks"                                                                                                    |
| **From header**     | The SES `From` display name shows the persona's name (e.g. "Sarah Mitchell \<[growth@truthlocks.com](mailto:growth@truthlocks.com)>")                                     |
| **Writing rules**   | Emails use contractions, natural rhythm, and specificity. No marketing buzzwords, no AI tells, and a maximum of 150 words for initial outreach or 80 words for follow-ups |

### How personas are generated

The persona is generated automatically during campaign creation — you do not need to configure it manually. The AI considers your campaign's `audience_description` to produce a persona that would be credible and relatable to your target audience:

* **Financial services audience** → persona with a compliance or risk management background
* **Healthcare audience** → persona with health-tech or regulatory experience
* **Legal audience** → persona with legal-tech or document management expertise
* **Technology audience** → persona with enterprise SaaS or security credentials

The persona is stored on the campaign record and persists for the campaign's lifetime. Every email in the campaign uses the same persona for consistency — recipients who receive multiple follow-ups always hear from the same person.

### Viewing a campaign's persona

You can see the persona assigned to a campaign by retrieving the campaign via the API. The `salesperson_persona` field on the campaign object contains the persona details:

```json theme={null}
{
  "id": "campaign_abc123",
  "name": "Q2 SaaS Engineering Outreach",
  "status": "active",
  "salesperson_persona": {
    "first_name": "Sarah",
    "last_name": "Mitchell",
    "title": "Senior Account Executive",
    "personality": "warm-consultative",
    "backstory": "After 8 years leading document compliance programs at two Fortune 500 banks, Sarah joined Truthlocks to help engineering teams solve the same integrity challenges she spent her career navigating."
  }
}
```

The persona is also visible in the campaign detail view in the console, on the **Overview** tab.

### Persona and template campaigns

When you clone a campaign from a [template](/guides/campaign-templates), the template's persona is copied to the new campaign. This means template-based campaigns start with a consistent persona immediately, with no additional AI generation step.

<Info>
  If persona generation fails during campaign creation (for example, due to a transient AI model error), the campaign is still created successfully. Emails fall back to a generic senior account executive voice without a named persona. You can check whether a persona was generated by inspecting the `salesperson_persona` field on the campaign object.
</Info>

## Strategic campaign planning

When you create a campaign, the Campaign Director performs market research and competitive analysis before generating any emails. The result is a strategic campaign plan that gives the AI deeper context to craft targeted messaging instead of generic outreach.

The planning step runs automatically during campaign creation — you provide an `audience_description` and `description`, and the AI generates a full strategic plan covering pain points, objections, competitive positioning, and industry-specific triggers.

### What the plan includes

| Field                   | Description                                                                        |
| :---------------------- | :--------------------------------------------------------------------------------- |
| `market_analysis`       | Competitive landscape — who else sells to this audience, where you differentiate   |
| `pain_points`           | Specific problems your audience faces that Truthlocks solves, with business impact |
| `objections`            | Anticipated pushback with preemptive rebuttals                                     |
| `competitor_weaknesses` | Where you win against alternatives and the status quo                              |
| `industry_triggers`     | Regulatory changes, market events, or trends creating urgency now                  |
| `engagement_sequence`   | High-level multi-touch plan describing the purpose of each email in the series     |
| `conversion_goal`       | The desired outcome — demo booking, trial signup, or meeting                       |

These fields are stored on the campaign's `ai_strategy` object alongside the core strategy fields (`icp_description`, `outbound_strategy`, `key_value_props`, `tone_guidelines`, `followup_cadence`, and `subject_line_formulas`).

### Viewing the strategic plan

Retrieve the campaign via the API and inspect the `ai_strategy` object:

```json theme={null}
{
  "id": "campaign_abc123",
  "name": "Q2 Compliance Outreach",
  "ai_strategy": {
    "icp_description": "VP Compliance and GRC leaders at mid-market financial services firms (200-2000 employees)",
    "outbound_strategy": "Lead with regulatory urgency, position Truthlocks as compliance infrastructure",
    "market_analysis": "Competing with manual audit processes and legacy document management systems. DocuSign and Adobe Sign address signing but not attestation integrity.",
    "pain_points": [
      "Audit failures from missing chain-of-custody documentation cost $50K+ per incident",
      "Manual compliance reporting takes 40+ hours per quarter"
    ],
    "objections": [
      "We already have DocuSign — Rebuttal: DocuSign signs documents; Truthlocks proves they haven't been altered after signing",
      "Budget is frozen until next fiscal year — Rebuttal: Compliance failures cost more than prevention. One audit finding pays for three years of Truthlocks"
    ],
    "competitor_weaknesses": [
      "Legacy tools verify identity but not document integrity post-signature",
      "No competitor offers cryptographic proof bundles for offline verification"
    ],
    "industry_triggers": [
      "SEC cybersecurity disclosure rules effective December 2026 require documented data integrity controls",
      "Rising deepfake fraud targeting financial documents"
    ],
    "engagement_sequence": [
      "Touch 1: Pattern interrupt — specific pain point from their industry",
      "Touch 2: Social proof — how similar firms solved this",
      "Touch 3: ROI — cost of inaction vs. cost of Truthlocks",
      "Touch 4: Breakup — final follow-up with urgency"
    ],
    "conversion_goal": "demo booking"
  }
}
```

In the console, the strategic plan is visible on the **Overview** tab of the campaign detail view.

### How the plan is used

The strategic plan flows into every stage of the campaign:

* **Email writing.** The Writer agent uses pain points, objections, and competitive positioning to craft specific, relevant messaging instead of generic value propositions.
* **Mid-flight adjustments.** The Strategist agent performs root-cause diagnosis using the full strategic context — including market analysis, objection maps, and competitive positioning — when adjusting a live campaign. If reply rates drop, the strategist considers whether the campaign is targeting the wrong pain point or using the wrong competitive angle, rather than just tweaking tone.
* **Lead scoring.** The Prospector uses the ICP description and industry triggers to evaluate lead fit more precisely.

<Info>
  Strategic planning runs during campaign creation and does not require manual input beyond your audience and campaign descriptions. If you provide an `ai_strategy` object when creating a campaign, those fields are used as-is and the AI does not overwrite them with generated values.
</Info>

## Multi-touch engagement sequences

Campaigns design structured multi-touch email sequences where each follow-up has a distinct purpose — introduction, social proof, ROI analysis, or closing. Previously, follow-up emails were generated independently without a cohesive progression. Now, the sequence strategy is planned upfront and adapts based on campaign performance.

### How sequences work

When a campaign is activated, the Campaign Director can use the `compose_sequence` tool to design a multi-touch engagement sequence of 2–5 emails. Each touch in the sequence has:

| Field          | Description                                          |
| :------------- | :--------------------------------------------------- |
| `number`       | Position in the sequence (1, 2, 3...)                |
| `delay_days`   | Days to wait after the previous touch before sending |
| `goal`         | What this touch is designed to achieve               |
| `angle`        | The messaging approach for this email                |
| `subject_hint` | Direction for the subject line                       |

### Example sequence

A typical four-touch sequence for a compliance campaign might look like:

| Touch | Delay  | Goal                      | Angle                                                          |
| :---- | :----- | :------------------------ | :------------------------------------------------------------- |
| 1     | Day 0  | Earn the right to be read | Pattern interrupt — lead with a specific regulatory pain point |
| 2     | Day 3  | Build credibility         | Social proof — show how similar firms solved this              |
| 3     | Day 7  | Make the business case    | ROI — quantify the cost of inaction vs. the cost of Truthlocks |
| 4     | Day 14 | Create urgency            | Breakup — final follow-up, offer to reconnect next quarter     |

### Sequence progression

Each email in the sequence builds on the previous one. The Writer agent knows which touch number it is composing and uses the sequence plan to select the right angle, tone, and call-to-action:

* **Touch 1** opens with a pattern interrupt — something unexpected that earns the right to be read, tied to a specific pain point from the campaign's strategic plan.
* **Touch 2** shifts to social proof — referencing how peers in the recipient's industry use Truthlocks, without name-dropping specific companies.
* **Touch 3** focuses on ROI — making the cost of inaction concrete with industry-specific data.
* **Touch 4+** uses a breakup or completely new angle — creating urgency or pivoting the approach entirely.

The sequence adapts during the campaign. If the Strategist detects low reply rates after touch 2, it may recommend changing the angle for subsequent touches based on what the performance data reveals.

### Viewing a campaign's sequence

The engagement sequence is stored in the `ai_strategy.engagement_sequence` field on the campaign object and is visible in the campaign detail view in the console. You can also inspect the full sequence design — including rationale and expected conversion — in the [agent activity viewer](#agent-activity-viewer), where the `compose_sequence` tool call shows the complete output.

## AI-powered follow-up decisions

Before sending any follow-up email, the AI CMO evaluates each lead individually and decides whether the follow-up is worth sending. Instead of relying solely on a fixed schedule, the AI considers campaign strategy, prior contact history, and engagement signals to determine if a follow-up is likely to be productive. If the AI decides a follow-up would not be effective — for example, because the lead has shown no engagement after multiple touches — it skips the send entirely.

This reduces wasted sends and protects your sender reputation by avoiding follow-ups to unresponsive leads.

### How it works

On each orchestrator cycle, the AI evaluates every lead that is due for a follow-up:

1. The AI reviews the lead's full contact history — emails sent, opens, bounces, and any replies.
2. It considers the campaign's strategic plan, the current engagement sequence stage, and broader campaign performance signals.
3. If the AI determines the follow-up is unlikely to generate a response, it skips the lead for that cycle.
4. If the AI approves the follow-up, the email is generated and enters the normal [safety controls](#safety-controls) pipeline.

Follow-up decisions are visible in the [Command Center](#agent-activity-command-center) cycle feed alongside other agent reasoning. You can inspect why the AI chose to send or skip a follow-up by opening the decision modal for any cycle.

### Controlling maximum follow-ups

You control the maximum number of follow-ups per lead with the `max_followups` setting on each campaign. The default is 3 and the maximum is 10. The AI will never send more follow-ups than this limit, but it may send fewer if it determines earlier follow-ups are unproductive.

Set `max_followups` when creating or updating a campaign:

```bash theme={null}
curl -X POST https://api.truthlocks.com/v1/platform/growth/campaigns \
  -H "X-API-Key: tl_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Q3 Enterprise Outreach",
    "max_followups": 5,
    "ai_strategy": { ... }
  }'
```

| Value          | Behavior                                                  |
| :------------- | :-------------------------------------------------------- |
| `1`            | Initial email only, no follow-ups                         |
| `3` (default)  | Up to three follow-ups per lead, subject to AI evaluation |
| `10` (maximum) | Up to ten follow-ups per lead, subject to AI evaluation   |

<Info>
  The AI follow-up strategist works alongside your existing [multi-touch engagement sequence](#multi-touch-engagement-sequences). The sequence defines the plan for each touch, while the AI decides whether each scheduled follow-up should actually be sent based on real-time signals.
</Info>

## Automatic email verification

Every lead discovered through [multi-source lead discovery](#multi-source-lead-discovery) is automatically verified for email deliverability before being imported into a campaign. Leads with email addresses that score below the verification threshold are filtered out during discovery, so they never enter your pipeline or receive outreach.

This runs automatically with no configuration on your part. The verification step:

* Checks the email address for syntax, domain validity, and mailbox existence.
* Filters out disposable, role-based, and undeliverable addresses.
* For GitHub-sourced leads with no public email, attempts to find and verify an email address automatically through a secondary provider.

The result is lower bounce rates across your campaigns without any manual list cleaning. Combined with [AI-powered follow-up decisions](#ai-powered-follow-up-decisions), fewer emails are sent to invalid addresses or unresponsive leads — improving your overall sender reputation and deliverability.

<Info>
  Email verification applies to all leads discovered through enrichment providers. Leads you import manually via CSV or the API are not automatically verified — verify your lists before importing to maintain low bounce rates.
</Info>

## Multi-source lead discovery

The prospector agent discovers leads from a four-tier enrichment pipeline — Apollo, Google search, directory scraping, and GitHub — and automatically fails over between them. If one provider is unavailable, rate-limited, or returns no results, the agent switches to the next available source without interrupting the campaign.

This means:

* **No manual intervention.** You do not need to configure which provider to use or switch providers when one is down.
* **No campaign interruption.** Lead discovery continues seamlessly even if a provider experiences an outage.
* **Broader coverage.** The prospector queries multiple sources, increasing the number and diversity of leads matching your ICP.

The enrichment providers used and the [failover order](#provider-failover-and-circuit-breakers) are managed by the platform. You can see which provider sourced each lead in the [agent activity viewer](#agent-activity-viewer), where the prospector's tool calls show the provider name and query parameters. The `source` field on each lead also records the discovery provider for [attribution tracking](#lead-source-attribution).

### GitHub as a lead source

When your campaign targets engineering personas — CTOs, VPs of Engineering, Heads of Engineering, or similar technical leadership roles — the prospector agent can discover leads directly from GitHub. The agent searches public GitHub profiles matching your ICP description, then extracts name, company, location, and job title from each profile.

GitHub lead discovery is useful when:

* Your ICP targets **engineering leadership** at software companies.
* Other enrichment providers are unavailable or rate-limited.
* You want to reach technical decision-makers who may not appear in traditional sales databases.

GitHub discovery works automatically as part of the [multi-source failover](#multi-source-lead-discovery) pipeline. If a higher-priority provider returns results, GitHub is skipped. If the primary provider is unavailable or returns no matches, the prospector falls back to GitHub without interrupting the campaign.

#### How it works

1. The prospector extracts role keywords from your campaign's ICP description (e.g. "CTO", "VP Engineering", "Head of Engineering").
2. It searches GitHub for public user profiles matching those roles, sorted by follower count.
3. For each profile, it extracts the user's name, company, bio, location, and public email.
4. Profiles without a company association and fewer than 50 followers are filtered out.
5. When a GitHub-sourced lead has no public email, the platform attempts to find the email through a secondary email verification provider.

<Info>
  GitHub-sourced leads appear with a source of `github` in the agent activity viewer. You can inspect the prospector's tool calls to see the exact search query and which profiles were evaluated.
</Info>

#### Leads that work best with GitHub discovery

| ICP keyword                        | Profiles matched                                |
| :--------------------------------- | :---------------------------------------------- |
| CTO, Chief Technology Officer      | CTOs and technical co-founders                  |
| VP Engineering, VP of Engineering  | Engineering VPs and directors                   |
| Head of Engineering                | Engineering department leads                    |
| Engineering Manager                | Engineering managers                            |
| Staff Engineer, Principal Engineer | Senior individual contributors                  |
| Founder, Co-founder                | Technical founders (matched as "Founder / CTO") |
| Security, CISO                     | Security engineers and CISOs                    |
| Engineering (general)              | Broad engineering leadership                    |

The agent maps these keywords from your campaign's `icp_description` or `audience_description` field — no additional configuration is needed.

### Google search discovery

When Apollo is unavailable or returns no results, the prospector can discover leads through intelligent web search using Google Custom Search Engine (CSE) or SerpAPI. The agent builds targeted search queries — sometimes called "dorking" — to find prospects across public sources:

| Source type        | What it finds                                                                      |
| :----------------- | :--------------------------------------------------------------------------------- |
| LinkedIn profiles  | Decision-makers matching your ICP by role and company                              |
| Company team pages | Leadership bios and contact information from company websites                      |
| PDF documents      | Regulatory filings, annual reports, and public documents that mention key contacts |
| Regulatory filings | SEC filings, FDIC records, and other public disclosures with named officers        |

The prospector automatically selects the most effective query patterns based on your campaign's ICP description and target industry. You do not need to configure search queries manually.

<Info>
  Google search discovery requires a Google CSE API key and engine ID, or a SerpAPI key. Configure these in your platform settings. If neither is configured, the prospector skips this tier and falls through to directory scraping.
</Info>

### Directory scraping

The prospector can also discover leads from open web directories and public databases. This tier is useful for industries where prospects are listed in regulatory or professional registries:

| Directory                | Contacts found                                                               |
| :----------------------- | :--------------------------------------------------------------------------- |
| SEC EDGAR                | Officers and directors named in corporate filings                            |
| Industry associations    | Members listed in professional directories (e.g. ISACA, ComplianceWeek, AFP) |
| Professional directories | Executives listed in open industry registries                                |

Directory scraping does not require API keys — it works with publicly available data.

#### Provider failover and circuit breakers

Each enrichment provider is protected by a circuit breaker. If a provider returns a hard error (such as an authentication failure or payment-required response), it is placed on a one-hour cooldown. During cooldown, the prospector skips that provider and moves to the next one in the failover chain. This prevents a single misconfigured or rate-limited provider from blocking lead discovery across your campaigns.

The failover order is:

1. **Apollo** — highest-quality results, requires a paid API key.
2. **Google search** — intelligent web search using CSE or SerpAPI to find prospects on LinkedIn, team pages, PDFs, and regulatory filings.
3. **Directory scraping** — open web sources including SEC EDGAR and professional association directories.
4. **GitHub** — free fallback, best for engineering personas. Works without authentication at 60 requests per hour, or at 5,000 requests per hour with a GitHub token.

When leads from any source are missing an email address, the platform automatically attempts to find and verify the email through a secondary email verification provider before importing the lead. All discovered leads — regardless of source — pass through [automatic email verification](#automatic-email-verification) before entering your pipeline.

### Lead source attribution

Every lead records which enrichment provider discovered it, so you can track where your pipeline is coming from. The `source` field on each lead shows the discovery provider — `apollo`, `google`, `directory`, or `github`. Leads you import manually show `manual` or `csv_import`.

Source attribution appears in the leads list in the console and in API responses. Use it to evaluate which discovery channels produce the highest-quality leads for your campaigns.

## Agent activity command center

The **Growth > Agent Activity** page in the console is a real-time command center for monitoring your AI CMO campaigns. It presents agent reasoning, delivery metrics, and operational alerts in a four-column dashboard with interactive charts.

### Command header

A sticky header at the top of the page provides at-a-glance context and quick controls:

* **Breadcrumb** — shows your position in the console: Growth > AI CMO > Activity.
* **Campaign selector** — a dropdown to switch between campaigns. Template campaigns are automatically filtered out.
* **Status indicator** — shows whether the selected campaign is **Active** (with a pulsing green dot) or in another state such as paused or completed.
* **Live clock** — updates every second so you can correlate activity timestamps.
* **New activity indicator** — a pulsing label appears when new agent entries arrive.
* **Refresh button** — click to fetch the latest data immediately. The timestamp of the last refresh is shown alongside the button.

### Four-column layout

The command center is organized into four columns on wide screens. On smaller screens the columns stack vertically.

| Column                    | Content                                                                                       |
| :------------------------ | :-------------------------------------------------------------------------------------------- |
| **1 — Agent vitals**      | Six stat cards, a campaign health card, the email pipeline chart, and the tool usage donut    |
| **2–3 — Feed and charts** | The cycle activity chart, delivery funnel chart, cycle feed list, and latest decision preview |
| **4 — Alerts**            | Synthetic alerts derived from campaign stats                                                  |

### Stat cards

Six cards with colored left-border accents and animated counters give you a numeric snapshot of the selected campaign:

| Card           | Accent color | What it shows                                                    |
| :------------- | :----------- | :--------------------------------------------------------------- |
| **Turns**      | Gray         | Total reasoning steps across all agents                          |
| **Decisions**  | Amber        | Final conclusions the agents reached                             |
| **Thoughts**   | Blue         | Internal reasoning turns                                         |
| **Tool calls** | Purple       | Tools the agents invoked                                         |
| **Sent**       | Cyan         | Emails sent for this campaign                                    |
| **Replies**    | Green        | Replies received, with the reply rate percentage displayed below |

### Charts

Five interactive charts visualize campaign performance. All charts support light and dark themes and display tooltips on hover.

#### Cycle activity chart

An area chart showing turns, thoughts, and tool calls per reasoning cycle (up to the last eight cycles). Use this to spot whether agent activity is increasing or tapering off across cycles.

| Series   | Color  | Data                       |
| :------- | :----- | :------------------------- |
| Turns    | Cyan   | Total turns in each cycle  |
| Thoughts | Blue   | Reasoning turns per cycle  |
| Tools    | Purple | Tool invocations per cycle |

#### Email pipeline chart

A bar chart showing the delivery funnel for the selected campaign:

| Bar     | Color | Data                   |
| :------ | :---- | :--------------------- |
| Sent    | Cyan  | Total emails sent      |
| Replied | Green | Total replies received |
| Bounced | Amber | Total bounced emails   |

#### Delivery funnel chart

A horizontal bar chart showing the progression from sent to delivered to replied, with the current reply rate displayed in the header. Use this to identify where emails are dropping off in the delivery pipeline.

#### Tool usage donut

A donut (pie) chart showing the distribution of tool calls by tool name. The top six tools are shown, color-coded, with a legend listing each tool and its call count. The total number of tool calls appears in the chart header.

#### Campaign health card

A compact status card showing:

* Bounce rate and reply rate as color-coded chips (green when healthy, amber or red when thresholds are approached or exceeded).
* Total bounced email count.
* A daily cap progress bar showing how many of the daily email limit have been used.

### Cycle feed

The cycle feed panel lists every reasoning cycle for the selected campaign. Each entry shows:

* A cycle number badge (the most recent cycle is highlighted in cyan with a **Latest** label and a pulsing dot).
* A count of turns, thoughts, and tool calls.
* A preview of the cycle's decision text (first 60 characters).
* Total execution time.

Click any cycle to open the [decision modal](#decision-modal).

### Latest decision preview

Below the cycle feed, a preview card shows the most recent cycle's decision text (first 200 characters) along with three health checks:

* Whether the bounce rate is within acceptable limits.
* Whether the reply rate meets the 1% threshold.
* Whether the campaign is currently active.

Click **Read Full Decision** to open the [decision modal](#decision-modal) for the latest cycle.

### Decision modal

Click any cycle in the feed or the **Read Full Decision** button to open a full-screen modal with deep-dive details for that reasoning cycle:

* **Decision badge** — the first line of the AI's decision, highlighted in an amber panel.
* **Performance grid** — four metric cards showing total turns, time spent, leads discovered, and emails written during the cycle.
* **Turn composition chart** — a bar chart breaking down the cycle's turns into thoughts, tool calls, results, and decisions.
* **Reasoning trail** — every turn in the cycle rendered as a color-coded card. Thought cards expand by default; tool call and tool result cards include collapsible JSON sections showing exact inputs and outputs.
* **AI reasoning** — the full structured reasoning text from the decision, rendered with markdown-style headings and bullet points.

Press **Escape** or click outside the modal to close it.

### Turn cards

Each turn in the reasoning trail is displayed as a color-coded card:

| Turn type       | Color  | What it shows                                                                                          |
| :-------------- | :----- | :----------------------------------------------------------------------------------------------------- |
| **Reasoning**   | Blue   | The agent's internal thought process — what it plans to do and why                                     |
| **Tool call**   | Purple | The tool the agent invoked (e.g. `discover_leads()`, `write_email()`) with expandable input parameters |
| **Tool result** | Green  | The output returned by the tool, expandable as formatted JSON                                          |
| **Decision**    | Amber  | The agent's final conclusion after reviewing tool results                                              |

Each card shows the execution duration. Click any card to expand or collapse its content.

### Alerts panel

The alerts column displays synthetic alerts derived from campaign stats. Alerts are generated automatically — no new API endpoints or configuration are required.

| Alert                    | Severity | Trigger                                         |
| :----------------------- | :------- | :---------------------------------------------- |
| **High bounce rate**     | Critical | Bounce rate reaches 5% or higher                |
| **Bounce rate elevated** | Warning  | Bounce rate is between 2% and 5%                |
| **Low reply rate**       | Warning  | Reply rate is below 1% after 20+ emails sent    |
| **Awaiting first send**  | Info     | Campaign is active but no emails have been sent |
| **Campaign paused**      | Info     | Campaign is not in an active state              |

When no alerts are active, the panel shows an **All clear** message. A badge on the alerts header shows the count of critical and warning alerts.

### When to use the command center

| Scenario                    | What to look for                                                                                                                              |
| :-------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------- |
| Campaign not sending emails | Check the alerts panel for a "Campaign paused" or "Awaiting first send" alert, then open the latest decision to read the director's reasoning |
| Low-quality emails          | Open a cycle's decision modal and review the reasoning trail — inspect the writer's tool calls and the knowledge base entries used            |
| Leads not matching your ICP | Expand tool call cards for `discover_leads` to see which providers were queried and what filters were applied                                 |
| Unexpected strategy changes | Read the strategist's decision text in the modal to understand why it recommended adjustments                                                 |
| High bounce rate            | The alerts panel flags this automatically. Review the delivery funnel chart and campaign health card for details                              |

### Live refresh

The command center refreshes automatically every 15 seconds while the page is open. Active campaigns display a pulsing green dot in the command header. A **New activity** label pulses briefly when fresh data arrives. Click **Refresh** at any time to fetch the latest data immediately.

## AI model tiers

The AI CMO routes tasks to three model tiers based on complexity:

| Tier      | Used for                                                                                                                               |
| :-------- | :------------------------------------------------------------------------------------------------------------------------------------- |
| Reasoning | Campaign planning, strategy adjustment, learning synthesis                                                                             |
| Fast      | Lead scoring, [follow-up decisions](#ai-powered-follow-up-decisions), reply classification, email verification, send-time optimization |
| Write     | Email generation, A/B subject line variants                                                                                            |

You can view which models are in use from the **Settings** page in the Growth section or via the [settings API endpoint](#settings).

## Getting started

<Steps>
  <Step title="Open the Growth section">
    Navigate to **Growth** in the [console](https://console.truthlocks.com). The dashboard shows an overview of active campaigns, emails sent today, average bounce rate, and total replies.
  </Step>

  <Step title="Choose a campaign approach">
    You can start outreach in two ways:

    * **Use a template.** Ten pre-built campaign templates target specific industry verticals with complete AI strategies. Click **Use Template** in the campaigns list to clone a template into a new campaign instantly — no AI planning step required. See [campaign templates](/guides/campaign-templates) for the full list and workflow.
    * **Create from scratch.** Define your own ICP, messaging strategy, and safety limits via the API.
  </Step>

  <Step title="Import or discover leads">
    Add leads to your campaign manually, via bulk import, or let the orchestrator discover them automatically when the campaign is active.

    To import leads via the API:

    ```bash theme={null}
    curl -X POST https://api.truthlocks.com/v1/platform/growth/leads/import \
      -H "X-API-Key: tl_live_your_api_key" \
      -H "Content-Type: application/json" \
      -d '{
        "leads": [
          {
            "email": "jane.doe@example.com",
            "first_name": "Jane",
            "last_name": "Doe",
            "company": "Acme Corp",
            "title": "VP Compliance",
            "country": "US",
            "timezone": "America/New_York"
          }
        ]
      }'
    ```

    The `country` and `timezone` fields are optional. If provided, they enable [timezone-aware outreach](#timezone-aware-outreach) so emails arrive during the lead's local business hours.
  </Step>

  <Step title="Activate the campaign">
    Activate from the console or via the API:

    ```bash theme={null}
    curl -X POST https://api.truthlocks.com/v1/platform/growth/campaigns/{id}/activate \
      -H "X-API-Key: tl_live_your_api_key"
    ```

    The orchestrator begins processing leads and generating emails immediately.
  </Step>

  <Step title="Monitor from the dashboard">
    Track campaign performance in real time from the Growth dashboard. Click into any campaign to open the detail view with six tabs:

    * **Overview** — campaign strategy, status, and configuration
    * **Leads** — all leads associated with the campaign, their status, and follow-up count
    * **Emails** — every email generated by the AI, with delivery status and content hash
    * **Pending Approval** — emails awaiting your review (when [approval mode](#approval-mode) is enabled)
    * **Analytics** — sent, bounced, and replied counts at a glance
    * **Learnings** — AI-synthesized insights on effective personas, industries, and messaging

    You can also query all of these through the [campaign detail endpoints](#campaign-detail-endpoints).
  </Step>
</Steps>

## Growth dashboard

The **Growth** section of the console opens to a dashboard that gives you an at-a-glance view of your outbound program. The dashboard displays four key metrics:

| Metric           | Description                                               |
| :--------------- | :-------------------------------------------------------- |
| Active campaigns | Number of campaigns currently in the `active` state       |
| Emails today     | Total emails sent across all campaigns in the current day |
| Avg bounce rate  | Weighted average bounce rate across active campaigns      |
| Total replies    | Sum of all replies received across active campaigns       |

Below the metrics, the dashboard shows three charts and two data panels.

### Dashboard charts

#### Emails sent by campaign

A bar chart showing the number of emails sent per campaign (up to eight campaigns). Bars are color-coded by campaign status:

| Status    | Color |
| :-------- | :---- |
| Active    | Cyan  |
| Completed | Blue  |
| Paused    | Amber |
| Other     | Gray  |

#### Reply rates

An area chart plotting the reply rate percentage for each campaign that has sent at least one email. A shaded green gradient fills the area below the line. Use this to compare reply performance across campaigns at a glance.

#### Campaign status distribution

A pie chart breaking down your campaigns by status — active, paused, completed, draft, and failed. Each status has a distinct color. Use this to see the overall composition of your campaign portfolio.

### Data panels

* **Recent campaigns** — a list of your most recent campaigns with their status, emails sent count, and reply rate. Click any campaign to open its detail view.
* **Recent activity** — an audit log of the last ten events across all campaigns, showing what happened and when. Use this to spot issues like bounces or auto-pauses without clicking into individual campaigns.

### Dashboard API

Retrieve the same data programmatically:

```bash theme={null}
curl -X GET https://api.truthlocks.com/v1/platform/growth/dashboard \
  -H "X-API-Key: tl_live_your_api_key"
```

The response includes:

```json theme={null}
{
  "stats": {
    "active_campaigns": 3,
    "emails_today": 42,
    "avg_bounce_rate": 2.1,
    "total_replies": 17
  },
  "recent_campaigns": [
    {
      "id": "campaign_abc123",
      "name": "Q2 SaaS Engineering Outreach",
      "status": "active",
      "emails_sent": 128,
      "reply_rate": 4.7
    }
  ],
  "recent_activity": [
    {
      "message": "Campaign 'Q2 SaaS Engineering Outreach' sent 12 emails",
      "timestamp": "2026-03-27T14:32:00Z"
    }
  ]
}
```

<Info>
  The dashboard refreshes in real time. The bounce rate card turns red when the average exceeds 3%, signaling that one or more campaigns may need attention.
</Info>

## Knowledge base

The knowledge base stores product information that the AI CMO uses to personalize outreach emails. Knowledge entries are injected into every AI prompt, giving the writing model context about your product, ideal customers, and competitive positioning.

Entries carry forward across campaigns — when you add or update a knowledge entry, every future campaign benefits from it automatically.

### Knowledge categories

Each entry belongs to one of these categories:

| Category            | Description                                        |
| :------------------ | :------------------------------------------------- |
| `feature`           | Product features and capabilities                  |
| `use_case`          | Specific workflows or problems your product solves |
| `icp`               | Ideal customer profile descriptions                |
| `persona`           | Target buyer personas                              |
| `pain_point`        | Customer pain points your product addresses        |
| `differentiator`    | What sets your product apart from alternatives     |
| `objection_handler` | Responses to common prospect objections            |
| `pricing`           | Pricing information and positioning                |
| `industry`          | Industry-specific context and terminology          |

### Managing entries in the console

Open **Growth > Knowledge Base** in the console. From this page you can:

* **Add entries** — click **New Entry**, choose a category, and provide a title, content, and priority (0–100).
* **Edit entries** — click any entry to update its title, content, category, or priority.
* **Toggle active/inactive** — each entry has an active switch that controls whether it is injected into AI prompts. Deactivate an entry to temporarily exclude it from AI-generated emails without deleting it.
* **Delete entries** — permanently remove entries you no longer want the AI to reference.
* **Filter by category** — use the category tabs to narrow the list.

Higher-priority entries (70+) are weighted more heavily in AI prompts. Use priority to control which product angles the AI emphasizes most.

<Tip>
  Use the active toggle to A/B test messaging angles. Deactivate a differentiator or objection handler, run a campaign, and compare reply rates against a campaign where the entry was active.
</Tip>

### Managing entries via the API

#### List all entries

```bash theme={null}
curl -X GET https://api.truthlocks.com/v1/platform/growth/knowledge \
  -H "X-API-Key: tl_live_your_api_key"
```

Returns all active knowledge entries ordered by priority (highest first).

#### Create an entry

```bash theme={null}
curl -X POST https://api.truthlocks.com/v1/platform/growth/knowledge \
  -H "X-API-Key: tl_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "category": "differentiator",
    "title": "Cryptographic proof vs. watermarking",
    "content": "Unlike digital watermarks that can be stripped, Truthlocks uses cryptographic attestations anchored to immutable ledgers. This provides tamper-evident proof that survives format conversion, re-encoding, and redistribution.",
    "priority": 80
  }'
```

| Field      | Type    | Required | Description                                                                                  |
| :--------- | :------ | :------- | :------------------------------------------------------------------------------------------- |
| `category` | string  | Yes      | One of the [knowledge categories](#knowledge-categories)                                     |
| `title`    | string  | Yes      | Short descriptive title                                                                      |
| `content`  | string  | Yes      | Full text the AI uses for context                                                            |
| `priority` | integer | No       | 0–100, defaults to 50. Higher values are weighted more in AI prompts                         |
| `active`   | boolean | No       | Defaults to `true`. Set to `false` to exclude this entry from AI prompts without deleting it |

#### Update an entry

```bash theme={null}
curl -X PATCH https://api.truthlocks.com/v1/platform/growth/knowledge/{id} \
  -H "X-API-Key: tl_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Updated content with new competitive positioning.",
    "priority": 90
  }'
```

You can update any combination of `title`, `content`, `category`, `priority`, and `active` in a single request. Toggle `active` to `false` to temporarily exclude an entry from AI prompts:

```bash theme={null}
curl -X PATCH https://api.truthlocks.com/v1/platform/growth/knowledge/{id} \
  -H "X-API-Key: tl_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"active": false}'
```

#### Delete an entry

```bash theme={null}
curl -X DELETE https://api.truthlocks.com/v1/platform/growth/knowledge/{id} \
  -H "X-API-Key: tl_live_your_api_key"
```

Deleted entries are deactivated and no longer included in AI prompts.

### Campaign learnings

The AI synthesizes what worked into campaign learnings — capturing effective personas, industries, messaging angles, subject lines, and reply rates. You can view learnings in the **Learnings** tab of any campaign detail view, or retrieve them via the API.

#### Periodic learning

The orchestrator synthesizes learnings every 20 emails while a campaign is running, so the AI CMO improves its messaging continuously rather than waiting for a campaign to complete. This is especially important for [continuous campaigns](#continuous-campaigns) that may run for weeks or months without completing. Learnings are also synthesized when a non-continuous campaign auto-completes.

To list recent learnings across all campaigns:

```bash theme={null}
curl -X GET https://api.truthlocks.com/v1/platform/growth/knowledge/learnings \
  -H "X-API-Key: tl_live_your_api_key"
```

Each learning includes:

| Field                  | Type    | Description                                        |
| :--------------------- | :------ | :------------------------------------------------- |
| `id`                   | string  | Unique learning identifier                         |
| `campaign_id`          | string  | Campaign that generated the learning               |
| `summary`              | string  | AI-generated summary of what worked                |
| `effective_personas`   | array   | Buyer personas that responded well                 |
| `effective_industries` | array   | Industry verticals with the highest engagement     |
| `ineffective_angles`   | array   | Messaging approaches that underperformed           |
| `best_subject_lines`   | array   | Subject lines with the highest open or reply rates |
| `reply_rate_pct`       | number  | Overall reply rate for the campaign                |
| `bounce_rate_pct`      | number  | Overall bounce rate for the campaign               |
| `emails_analyzed`      | integer | Number of emails included in the analysis          |

<Tip>
  Review learnings regularly to refine your knowledge base. If the AI identifies a persona or industry that performs well, consider adding a dedicated knowledge entry to reinforce that angle in future campaigns.
</Tip>

## Campaign lifecycle

Campaigns move through these states:

| Transition                         | Trigger                                                     | Effect                                                                                             |
| :--------------------------------- | :---------------------------------------------------------- | :------------------------------------------------------------------------------------------------- |
| Template → new `draft`             | [Use Template](/guides/campaign-templates#using-a-template) | Clones the template's strategy into a new campaign                                                 |
| `draft` → `active`                 | Activate                                                    | Orchestrator begins processing and sending                                                         |
| `active` → `paused`                | Pause or auto-pause                                         | Sending stops; campaign can be resumed                                                             |
| `paused` → `active`                | Resume                                                      | Sending resumes from where it left off                                                             |
| `active` → `paused`                | Kill switch                                                 | Emergency stop — sets the kill switch and pauses                                                   |
| `active` → `completed`             | Automatic                                                   | All leads have been processed and at least one email has been sent (non-continuous campaigns only) |
| `completed` or `failed` → `active` | [Reactivate](#reactivate-a-campaign)                        | Campaign resumes with all leads reset to pending                                                   |

Template campaigns cannot be activated directly. Use the **Use Template** action to clone a template into a new campaign, then activate the clone. See [campaign templates](/guides/campaign-templates) for details.

A non-continuous campaign only auto-completes when all leads have been exhausted **and** at least one email has been sent. This prevents newly activated campaigns from completing instantly before lead discovery has had a chance to run. If a campaign has zero sends, the orchestrator keeps it active until leads are discovered and processed. [Continuous campaigns](#continuous-campaigns) never auto-complete.

Campaigns auto-pause when the bounce rate or complaint rate exceeds the configured threshold.

## Continuous campaigns

By default, campaigns are **continuous** — they run indefinitely, discovering new leads and sending emails without ever auto-completing. This is useful for ongoing outbound programs where you want the AI CMO to keep finding and engaging prospects matching your ICP over time.

When a continuous campaign has no pending leads, the orchestrator waits and continues to discover new prospects rather than marking the campaign as completed. Learnings are [synthesized periodically](#periodic-learning) so the AI improves its messaging throughout the campaign's lifetime.

Set `continuous` to `false` when creating a campaign if you want it to auto-complete after all leads have been processed:

```bash theme={null}
curl -X POST https://api.truthlocks.com/v1/platform/growth/campaigns \
  -H "X-API-Key: tl_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "One-time product launch outreach",
    "continuous": false,
    "ai_strategy": { ... }
  }'
```

| Behavior           | Continuous (`true`, default)             | Non-continuous (`false`)                                          |
| :----------------- | :--------------------------------------- | :---------------------------------------------------------------- |
| Auto-completion    | Never                                    | When all leads are processed and at least one email has been sent |
| Lead discovery     | Runs indefinitely                        | Stops after all leads are exhausted                               |
| Learning synthesis | Every 20 emails and on manual completion | Every 20 emails and on auto-completion                            |

In the console, continuous campaigns display a **Continuous** badge on the campaign detail page and in the campaigns list.

<Info>
  Campaigns cloned from templates are always continuous by default.
</Info>

## Timezone-aware outreach

The AI CMO only sends emails during local business hours — **9 am to 6 pm, Monday through Friday** — in each lead's timezone. Emails are never sent on weekends. If a lead's send window is closed, the email is automatically deferred to 9 am on the next weekday in the lead's local time. The deferred send is stored on the lead record and picked up by the orchestrator when the window opens — no manual intervention is needed.

Timezone-aware sending is always active for every campaign. There is nothing to enable or configure.

### How timezones are resolved

Timezones are resolved in this order:

1. **Explicit timezone** — if you set a `timezone` value (IANA format, e.g. `America/New_York`) when importing the lead, that value is used.
2. **Country lookup** — if no timezone is set but a `country` is present (ISO 3166-1 alpha-2, e.g. `US`, `GB`), the timezone is inferred automatically from the supported country list.
3. **Auto-discovered leads** — leads found through enrichment providers have their country and timezone populated automatically during discovery.
4. **Fallback** — if neither timezone nor country is available, the system falls back to UTC.

<Info>
  For countries that span multiple timezones, the most common business timezone is used. For example, `US` maps to `America/New_York` (Eastern Time) and `AU` maps to `Australia/Sydney`.
</Info>

### Supported countries

52 countries are mapped to IANA timezones across five regions.

<Tabs>
  <Tab title="Americas">
    | Country code | Timezone                        |
    | :----------- | :------------------------------ |
    | US           | America/New\_York               |
    | CA           | America/Toronto                 |
    | MX           | America/Mexico\_City            |
    | BR           | America/Sao\_Paulo              |
    | AR           | America/Argentina/Buenos\_Aires |
    | CL           | America/Santiago                |
    | CO           | America/Bogota                  |
    | PE           | America/Lima                    |
    | VE           | America/Caracas                 |
  </Tab>

  <Tab title="Europe">
    | Country code | Timezone          |
    | :----------- | :---------------- |
    | GB           | Europe/London     |
    | IE           | Europe/Dublin     |
    | FR           | Europe/Paris      |
    | DE           | Europe/Berlin     |
    | NL           | Europe/Amsterdam  |
    | BE           | Europe/Brussels   |
    | CH           | Europe/Zurich     |
    | AT           | Europe/Vienna     |
    | ES           | Europe/Madrid     |
    | PT           | Europe/Lisbon     |
    | IT           | Europe/Rome       |
    | SE           | Europe/Stockholm  |
    | NO           | Europe/Oslo       |
    | DK           | Europe/Copenhagen |
    | FI           | Europe/Helsinki   |
    | PL           | Europe/Warsaw     |
    | CZ           | Europe/Prague     |
    | RO           | Europe/Bucharest  |
    | HU           | Europe/Budapest   |
    | GR           | Europe/Athens     |
    | TR           | Europe/Istanbul   |
    | RU           | Europe/Moscow     |
    | UA           | Europe/Kiev       |
    | LU           | Europe/Luxembourg |
    | SK           | Europe/Bratislava |
    | HR           | Europe/Zagreb     |
  </Tab>

  <Tab title="Middle East & Africa">
    | Country code | Timezone            |
    | :----------- | :------------------ |
    | AE           | Asia/Dubai          |
    | SA           | Asia/Riyadh         |
    | IL           | Asia/Jerusalem      |
    | EG           | Africa/Cairo        |
    | ZA           | Africa/Johannesburg |
    | NG           | Africa/Lagos        |
    | KE           | Africa/Nairobi      |
    | QA           | Asia/Qatar          |
    | KW           | Asia/Kuwait         |
    | BH           | Asia/Bahrain        |
  </Tab>

  <Tab title="Asia-Pacific">
    | Country code | Timezone           |
    | :----------- | :----------------- |
    | IN           | Asia/Kolkata       |
    | CN           | Asia/Shanghai      |
    | JP           | Asia/Tokyo         |
    | KR           | Asia/Seoul         |
    | SG           | Asia/Singapore     |
    | HK           | Asia/Hong\_Kong    |
    | TW           | Asia/Taipei        |
    | AU           | Australia/Sydney   |
    | NZ           | Pacific/Auckland   |
    | MY           | Asia/Kuala\_Lumpur |
    | TH           | Asia/Bangkok       |
    | ID           | Asia/Jakarta       |
    | PH           | Asia/Manila        |
    | VN           | Asia/Ho\_Chi\_Minh |
    | PK           | Asia/Karachi       |
    | BD           | Asia/Dhaka         |
    | LK           | Asia/Colombo       |
  </Tab>
</Tabs>

<Tip>
  You can override the inferred timezone for any lead by setting the `timezone` field explicitly during import. Use this when a lead is based in a different timezone than their country's default — for example, a US-based lead working from `America/Los_Angeles`.
</Tip>

### How deferral works

When the orchestrator processes a lead and the lead's local time falls outside the 9 am–6 pm weekday window, the following happens:

1. The email is **not generated or sent**.
2. The lead's next follow-up time is set to **9 am on the next weekday** in their local timezone.
3. The orchestrator skips the lead until that time arrives.

This means a lead in `Asia/Tokyo` processed at 11 pm JST on a Friday will not receive their email until 9 am JST on the following Monday.

Follow-up emails use a 48-hour interval by default. If the 48-hour mark falls outside business hours, the follow-up is deferred to the next business-hours window automatically.

## Safety controls

Every email passes through a seven-layer safety check before sending:

1. **Global kill switch** — operator-level emergency stop across all campaigns.
2. **Campaign kill switch** — stops a single campaign immediately.
3. **Campaign status** — only active campaigns can send.
4. **Daily send cap** — maximum emails per day per campaign (default: 1,000).
5. **Per-minute cap** — sliding-window rate limit per campaign (default: 5).
6. **Bounce rate threshold** — auto-pauses the campaign if exceeded (default: 5%).
7. **Complaint rate threshold** — auto-pauses the campaign if exceeded (default: 0.1%).

<Info>
  When a campaign is auto-paused due to bounce or complaint thresholds, you must investigate the cause before resuming. This protects your sender reputation.
</Info>

## Managing campaigns via the API

### Create a campaign

```bash theme={null}
curl -X POST https://api.truthlocks.com/v1/platform/growth/campaigns \
  -H "X-API-Key: tl_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Q2 SaaS Engineering Outreach",
    "description": "Target engineering leaders at B2B SaaS companies",
    "ai_strategy": {
      "icp_description": "CTOs and VP Engineering at B2B SaaS companies with 50-500 employees building document workflows",
      "outbound_strategy": "Lead with document integrity pain points, position Truthlocks as infrastructure",
      "key_value_props": [
        "Cryptographic proof of document authenticity",
        "Drop-in SDK integration in under an hour",
        "Compliance-ready audit trail for SOC 2 and ISO 27001"
      ],
      "tone_guidelines": "Technical and direct. Avoid marketing jargon. Speak as an engineer to an engineer.",
      "followup_cadence": "Day 1: Intro email. Day 4: Technical deep-dive. Day 10: Case study. Day 18: Final follow-up.",
      "subject_line_formulas": [
        "Document integrity for {company}",
        "How {company} can prove file authenticity",
        "Quick question about document workflows at {company}"
      ]
    },
    "daily_cap": 40,
    "per_minute_cap": 5,
    "max_followups": 3,
    "simulation_mode": false,
    "approval_required": false
  }'
```

The request accepts these fields:

| Field                  | Type    | Required | Description                                                                                                                                                                                                              |
| :--------------------- | :------ | :------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`                 | string  | yes      | Campaign name                                                                                                                                                                                                            |
| `description`          | string  | no       | Brief description of the campaign's purpose                                                                                                                                                                              |
| `audience_description` | string  | no       | Plain-text description of the target audience for AI lead discovery                                                                                                                                                      |
| `ai_strategy`          | object  | no       | AI strategy configuration (see below)                                                                                                                                                                                    |
| `daily_cap`            | integer | no       | Maximum emails per day (default: 1,000)                                                                                                                                                                                  |
| `per_minute_cap`       | integer | no       | Sliding-window rate limit per minute (default: 5)                                                                                                                                                                        |
| `max_followups`        | integer | no       | Maximum follow-up emails per lead (default: 3, maximum: 10). The AI may send fewer follow-ups if it determines they are unlikely to be productive. See [AI-powered follow-up decisions](#ai-powered-follow-up-decisions) |
| `simulation_mode`      | boolean | no       | Run the campaign without sending real emails (default: `false`)                                                                                                                                                          |
| `approval_required`    | boolean | no       | Hold every email for manual review before sending (default: `false`)                                                                                                                                                     |
| `continuous`           | boolean | no       | Keep the campaign running indefinitely (default: `true`). Set to `false` for campaigns that should auto-complete after all leads are processed. See [continuous campaigns](#continuous-campaigns)                        |

The `ai_strategy` object accepts these fields. All fields are optional — if you omit them, the AI generates values automatically during [strategic campaign planning](#strategic-campaign-planning):

| Field                   | Type             | Description                                                                                            |
| :---------------------- | :--------------- | :----------------------------------------------------------------------------------------------------- |
| `icp_description`       | string           | Ideal customer profile — titles, company size, industries, and buying signals                          |
| `outbound_strategy`     | string           | The strategic approach and reasoning behind the campaign's messaging                                   |
| `key_value_props`       | array of strings | Value propositions tied to specific audience pain points                                               |
| `tone_guidelines`       | string           | Voice and tone that matches the audience's communication style                                         |
| `followup_cadence`      | string           | Timing strategy with reasoning for follow-up spacing                                                   |
| `subject_line_formulas` | array of strings | Templated subject line patterns using `{company}` placeholders                                         |
| `market_analysis`       | string           | Competitive landscape — who else sells to this audience, how you differentiate                         |
| `pain_points`           | array of strings | Specific problems the audience faces, with business impact                                             |
| `objections`            | array of strings | Anticipated pushback with preemptive rebuttals                                                         |
| `competitor_weaknesses` | array of strings | Where you win against alternatives and the status quo                                                  |
| `industry_triggers`     | array of strings | Regulatory changes, market events, or trends creating urgency                                          |
| `engagement_sequence`   | array of strings | High-level [multi-touch plan](#multi-touch-engagement-sequences) — purpose of each email in the series |
| `conversion_goal`       | string           | Desired outcome — `demo booking`, `trial signup`, or `meeting`                                         |

The response returns the full campaign object:

| Field                     | Type           | Description                                                                                                                                                                                                                                          |
| :------------------------ | :------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                      | string         | Unique campaign identifier                                                                                                                                                                                                                           |
| `name`                    | string         | Campaign name                                                                                                                                                                                                                                        |
| `description`             | string         | Campaign description                                                                                                                                                                                                                                 |
| `status`                  | string         | Current state — `draft`, `active`, `paused`, `completed`, or `failed`                                                                                                                                                                                |
| `simulation_mode`         | boolean        | Whether the campaign runs in simulation mode                                                                                                                                                                                                         |
| `approval_required`       | boolean        | Whether emails require manual approval before sending                                                                                                                                                                                                |
| `ai_strategy`             | object         | The AI strategy including ICP, messaging, cadence, and [strategic campaign plan](#strategic-campaign-planning) fields (market analysis, pain points, objections, competitor weaknesses, industry triggers, engagement sequence, and conversion goal) |
| `daily_cap`               | integer        | Daily send limit                                                                                                                                                                                                                                     |
| `per_minute_cap`          | integer        | Per-minute rate limit                                                                                                                                                                                                                                |
| `max_followups`           | integer        | Maximum follow-ups per lead                                                                                                                                                                                                                          |
| `bounce_threshold_pct`    | number         | Bounce rate auto-pause threshold (percentage)                                                                                                                                                                                                        |
| `complaint_threshold_pct` | number         | Complaint rate auto-pause threshold (percentage)                                                                                                                                                                                                     |
| `continuous`              | boolean        | Whether the campaign runs indefinitely without auto-completing                                                                                                                                                                                       |
| `salesperson_persona`     | object or null | The AI-generated [salesperson persona](#salesperson-personas) for this campaign, including `first_name`, `last_name`, `title`, `personality`, and `backstory`. Null if persona generation was skipped                                                |
| `kill_switch`             | boolean        | Whether the emergency kill switch is active                                                                                                                                                                                                          |
| `activated_at`            | string or null | ISO 8601 timestamp when the campaign was activated                                                                                                                                                                                                   |
| `completed_at`            | string or null | ISO 8601 timestamp when the campaign completed                                                                                                                                                                                                       |
| `created_at`              | string         | ISO 8601 timestamp when the campaign was created                                                                                                                                                                                                     |
| `updated_at`              | string         | ISO 8601 timestamp of the last update                                                                                                                                                                                                                |

### Clone a template into a new campaign

Create a new campaign by cloning an existing template. The template's AI strategy, description, and safety limits are copied as-is — no AI planning call is made, so the operation completes instantly.

```bash theme={null}
curl -X POST https://api.truthlocks.com/v1/platform/growth/campaigns/{id}/use-template \
  -H "X-API-Key: tl_live_your_api_key"
```

The response returns the full new campaign object in `draft` status. The new campaign's name is the template name with `(Campaign)` appended. Customize it with a `PATCH` request before activating.

See [campaign templates](/guides/campaign-templates) for the full workflow.

### Pause, resume, or kill a campaign

```bash theme={null}
# Pause
curl -X POST https://api.truthlocks.com/v1/platform/growth/campaigns/{id}/pause \
  -H "X-API-Key: tl_live_your_api_key"

# Resume
curl -X POST https://api.truthlocks.com/v1/platform/growth/campaigns/{id}/resume \
  -H "X-API-Key: tl_live_your_api_key"

# Emergency kill switch
curl -X POST https://api.truthlocks.com/v1/platform/growth/campaigns/{id}/kill \
  -H "X-API-Key: tl_live_your_api_key"
```

### Reactivate a campaign

Transition a `completed` or `failed` campaign back to `active`. All leads are reset to `pending` so they re-enter the processing pipeline from the beginning.

```bash theme={null}
curl -X POST https://api.truthlocks.com/v1/platform/growth/campaigns/{id}/reactivate \
  -H "X-API-Key: tl_live_your_api_key"
```

The response confirms the campaign is active:

```json theme={null}
{
  "status": "active"
}
```

Use reactivation when you want to re-run a campaign that finished or failed — for example, after updating your knowledge base or adjusting the AI strategy. You do not need to create a new campaign from scratch.

<Info>
  Only campaigns in `completed` or `failed` status can be reactivated. Attempting to reactivate a campaign in any other state returns an error. In the console, a **Reactivate** button appears on the campaign detail page when the campaign is eligible.
</Info>

## Simulation mode

Simulation mode lets you preview the full orchestrator pipeline — lead discovery, scoring, and email generation — without delivering any emails. When `simulation_mode` is `true` on a campaign, every email the AI generates is recorded with a status of `simulated` instead of being sent through SES. Use simulation mode to validate your ICP, review generated email content, and confirm safety limits before switching to live delivery.

Set simulation mode when creating a campaign:

```bash theme={null}
curl -X POST https://api.truthlocks.com/v1/platform/growth/campaigns \
  -H "X-API-Key: tl_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Test Campaign - Simulation",
    "simulation_mode": true,
    "ai_strategy": { ... }
  }'
```

Simulation mode campaigns display a violet **SIM** badge in the campaign list and a **Simulation Mode** badge on the detail page. Individual emails show a **Simulated** indicator so you can distinguish them from real sends.

### Filtering emails by simulation status

In the console, the **Emails** tab provides a tri-state filter to view simulated emails only, real emails only, or both. Via the API, the `simulation` boolean on each [email object](#list-campaign-emails) indicates whether the email was simulated.

### Global simulation default

You can set `simulation_default` to `true` in the Growth [settings](#settings) so that every new campaign starts in simulation mode by default. Individual campaigns can still override this at creation time by explicitly setting `simulation_mode` to `false`.

## Approval mode

When `approval_required` is set to `true`, every AI-generated email is held with a status of `pending_approval` instead of being sent automatically. You review and approve each email individually from the **Pending Approval** tab in the console or via the [approve endpoint](#approve-a-pending-email). Only after approval does the email move to `queued` and enter the normal send pipeline.

This gives you full control over outbound messaging before any email leaves the system. Approval mode is useful when:

* You are launching a campaign targeting a sensitive audience and want to review every message.
* Compliance requires human review of AI-generated outbound content.
* You want to spot-check email quality before allowing autonomous delivery.

Set approval mode when creating a campaign:

```bash theme={null}
curl -X POST https://api.truthlocks.com/v1/platform/growth/campaigns \
  -H "X-API-Key: tl_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Compliance Outreach - Manual Review",
    "approval_required": true,
    "ai_strategy": { ... }
  }'
```

Approval mode campaigns display an amber **Approval Required** badge on the campaign detail page.

### Approval workflow

1. The orchestrator generates an email and sets its status to `pending_approval`.
2. The email appears in the **Pending Approval** tab in the console, or in the response from `GET /campaigns/{id}/pending`.
3. You review the subject, recipient, and email body.
4. Approve the email via the console or by calling `POST /campaigns/{id}/emails/{emailId}/approve`.
5. The email status changes to `queued` and enters the standard send pipeline — [safety controls](#safety-controls), daily caps, and [timezone-aware delivery](#timezone-aware-outreach) still apply.

<Tip>
  Combine simulation mode and approval mode during onboarding. This lets you review AI-generated emails in a safe environment before enabling real delivery — set `simulation_mode: true` and `approval_required: true` together.
</Tip>

## Campaign detail endpoints

Once a campaign is created, you can query its leads, emails, pending approvals, stats, and learnings through campaign-scoped endpoints. These are the same endpoints that power the campaign detail tabs in the console.

All endpoints below are scoped to a single campaign by ID.

| Endpoint                                   | Method | Description                           |
| :----------------------------------------- | :----- | :------------------------------------ |
| `/campaigns/{id}/leads`                    | `GET`  | List leads with pagination            |
| `/campaigns/{id}/emails`                   | `GET`  | List all generated emails             |
| `/campaigns/{id}/pending`                  | `GET`  | List emails awaiting approval         |
| `/campaigns/{id}/stats`                    | `GET`  | Get delivery and engagement metrics   |
| `/campaigns/{id}/learnings`                | `GET`  | Get AI-synthesized campaign learnings |
| `/campaigns/{id}/emails/{emailId}/approve` | `POST` | Approve a pending email               |
| `/campaigns/{id}/leads`                    | `POST` | Add a lead to the campaign            |

### List campaign leads

Retrieve leads associated with a specific campaign, with pagination support:

```bash theme={null}
curl -X GET "https://api.truthlocks.com/v1/platform/growth/campaigns/{id}/leads?limit=20&offset=0" \
  -H "X-API-Key: tl_live_your_api_key"
```

Each lead in the response includes:

| Field            | Type           | Description                                                         |
| :--------------- | :------------- | :------------------------------------------------------------------ |
| `id`             | string         | Unique lead identifier                                              |
| `email`          | string         | Lead's email address                                                |
| `status`         | string         | Current status (e.g. `pending`, `sent`, `bounced`, `replied`)       |
| `followup_count` | integer        | Number of follow-up emails sent to this lead                        |
| `last_sent_at`   | string or null | ISO 8601 timestamp of the most recent email sent, or `null` if none |

### List campaign emails

Retrieve all emails generated for a campaign:

```bash theme={null}
curl -X GET https://api.truthlocks.com/v1/platform/growth/campaigns/{id}/emails \
  -H "X-API-Key: tl_live_your_api_key"
```

Each email in the response includes:

| Field             | Type           | Description                                                                 |
| :---------------- | :------------- | :-------------------------------------------------------------------------- |
| `id`              | string         | Unique email identifier                                                     |
| `campaign_id`     | string         | Parent campaign identifier                                                  |
| `lead_id`         | string         | Associated lead identifier                                                  |
| `sequence_number` | integer        | Position in the follow-up sequence (1 = initial, 2+ = follow-ups)           |
| `subject`         | string         | Email subject line                                                          |
| `html_body`       | string         | Full HTML body of the generated email                                       |
| `status`          | string         | Delivery status — see [email statuses](#email-statuses) below               |
| `simulation`      | boolean        | `true` if this email was simulated rather than sent via SES                 |
| `content_hash`    | string         | SHA-256 hash of the email body for integrity verification                   |
| `ai_model`        | string         | Model ID used to generate the email                                         |
| `ai_prompt_hash`  | string         | Hash of the AI prompt used for generation                                   |
| `ses_message_id`  | string or null | SES message ID for delivered emails, or `null` for simulated/pending emails |
| `sent_at`         | string or null | ISO 8601 timestamp when the email was sent, or `null` if not yet delivered  |
| `created_at`      | string         | ISO 8601 timestamp when the email record was created                        |

### Email statuses

| Status             | Description                                                                            |
| :----------------- | :------------------------------------------------------------------------------------- |
| `pending_approval` | Email is waiting for manual review (when [approval mode](#approval-mode) is enabled)   |
| `queued`           | Email is approved and waiting to be sent                                               |
| `sent`             | Email was sent via SES                                                                 |
| `delivered`        | SES confirmed delivery to the recipient's mail server                                  |
| `bounced`          | Email bounced — recipient address is invalid or mailbox is full                        |
| `complained`       | Recipient marked the email as spam                                                     |
| `simulated`        | Email was generated but not sent because [simulation mode](#simulation-mode) is active |
| `failed`           | Terminal delivery failure                                                              |

### List pending approvals

Retrieve emails awaiting manual approval before sending. This endpoint only returns results when the campaign has `approval_required` set to `true`.

```bash theme={null}
curl -X GET https://api.truthlocks.com/v1/platform/growth/campaigns/{id}/pending \
  -H "X-API-Key: tl_live_your_api_key"
```

Each pending email in the response includes:

| Field        | Type   | Description                                                                             |
| :----------- | :----- | :-------------------------------------------------------------------------------------- |
| `id`         | string | Unique email identifier — pass this to the [approve endpoint](#approve-a-pending-email) |
| `subject`    | string | Email subject line                                                                      |
| `to`         | string | Recipient email address                                                                 |
| `html_body`  | string | Full HTML body so you can review the content before approving                           |
| `status`     | string | Always `pending_approval` for this endpoint                                             |
| `created_at` | string | ISO 8601 timestamp when the email was generated                                         |

### Get campaign stats

Retrieve delivery and engagement metrics for a campaign:

```bash theme={null}
curl -X GET https://api.truthlocks.com/v1/platform/growth/campaigns/{id}/stats \
  -H "X-API-Key: tl_live_your_api_key"
```

The response includes:

| Field              | Type    | Description                               |
| :----------------- | :------ | :---------------------------------------- |
| `total_sent`       | integer | Total emails successfully sent            |
| `total_bounced`    | integer | Total emails that bounced                 |
| `total_complained` | integer | Total emails marked as spam by recipients |
| `total_replied`    | integer | Total replies received                    |
| `bounce_rate_pct`  | number  | Bounce rate as a percentage of total sent |
| `reply_rate_pct`   | number  | Reply rate as a percentage of total sent  |

### Get campaign learnings

After a campaign completes, the AI synthesizes what worked. Retrieve those learnings:

```bash theme={null}
curl -X GET https://api.truthlocks.com/v1/platform/growth/campaigns/{id}/learnings \
  -H "X-API-Key: tl_live_your_api_key"
```

Each learning in the response includes:

| Field                  | Type             | Description                                         |
| :--------------------- | :--------------- | :-------------------------------------------------- |
| `id`                   | string           | Unique learning identifier                          |
| `campaign_id`          | string           | Campaign this learning was synthesized from         |
| `summary`              | string           | AI-generated summary of what worked                 |
| `effective_personas`   | array of strings | Buyer personas that responded well                  |
| `effective_industries` | array of strings | Industry verticals with the highest engagement      |
| `effective_angles`     | array of strings | Messaging angles that drove replies                 |
| `ineffective_angles`   | array of strings | Angles that performed poorly                        |
| `best_subject_lines`   | array of strings | Top-performing subject lines from the campaign      |
| `reply_rate_pct`       | number or null   | Overall reply rate for the analyzed emails          |
| `bounce_rate_pct`      | number or null   | Overall bounce rate for the analyzed emails         |
| `emails_analyzed`      | integer          | Number of emails included in the learning synthesis |
| `ai_model`             | string           | Model used to synthesize the learnings              |
| `created_at`           | string           | ISO 8601 timestamp when the learning was generated  |

The orchestrator applies these learnings automatically to future campaigns targeting similar audiences.

### Approve a pending email

If your campaign uses approval mode, approve a specific email to queue it for sending:

```bash theme={null}
curl -X POST https://api.truthlocks.com/v1/platform/growth/campaigns/{id}/emails/{emailId}/approve \
  -H "X-API-Key: tl_live_your_api_key"
```

Once approved, the email enters the normal send pipeline and is subject to [safety controls](#safety-controls) — daily caps, per-minute limits, and timezone-aware delivery still apply.

### Add a lead to a campaign

Attach an existing lead to a campaign:

```bash theme={null}
curl -X POST https://api.truthlocks.com/v1/platform/growth/campaigns/{id}/leads \
  -H "X-API-Key: tl_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"lead_id": "lead_abc123"}'
```

## Lead management

The **Growth > Leads** page in the console is your central hub for managing outbound leads across all campaigns.

### Managing leads in the console

From the leads page you can:

* **Add a lead** — click **Add Lead** and fill in email, name, company, title, and industry.
* **Import leads from CSV** — click **Import**, upload a CSV file with columns `email`, `first_name`, `last_name`, `company`, `title`, and `industry`, and optionally assign all imported leads to a campaign.
* **Score a lead with AI** — click **Score** next to any lead to trigger AI-powered qualification scoring. The AI returns a numeric score (0–100) that updates in place.
* **Add a lead to a campaign** — click **Add to Campaign** next to any lead and select the target campaign from the dropdown.

Lead statuses are color-coded in the table: `new`, `qualified`, `contacted`, `replied`, `converted`, `unsubscribed`, and `bounced`.

### List all leads

Retrieve all leads across campaigns with optional search filtering:

```bash theme={null}
curl -X GET "https://api.truthlocks.com/v1/platform/leads" \
  -H "X-API-Key: tl_live_your_api_key"
```

Each lead in the response includes:

| Field        | Type           | Description                                                                                 |
| :----------- | :------------- | :------------------------------------------------------------------------------------------ |
| `id`         | string         | Unique lead identifier                                                                      |
| `email`      | string         | Lead's email address                                                                        |
| `first_name` | string         | First name                                                                                  |
| `last_name`  | string         | Last name                                                                                   |
| `company`    | string or null | Company name                                                                                |
| `title`      | string or null | Job title                                                                                   |
| `source`     | string         | How the lead was created — `manual`, `csv_import`, `github`, or an enrichment provider name |
| `status`     | string         | Current [lifecycle status](#lead-lifecycle)                                                 |
| `created_at` | string         | ISO 8601 timestamp                                                                          |

### Get a single lead

Retrieve a specific lead by ID:

```bash theme={null}
curl -X GET "https://api.truthlocks.com/v1/platform/leads/{id}" \
  -H "X-API-Key: tl_live_your_api_key"
```

The response returns the full lead object including all fields listed above, plus the `inquiry_message` and `country` fields when available.

### Create a single lead

Add a single lead to your pipeline via the API. The lead is created in the global lead pool and can be assigned to a campaign separately.

```bash theme={null}
curl -X POST https://api.truthlocks.com/v1/platform/growth/leads \
  -H "X-API-Key: tl_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "alex.chen@dataflow.io",
    "first_name": "Alex",
    "last_name": "Chen",
    "company": "DataFlow Inc",
    "title": "CTO",
    "industry": "SaaS",
    "country": "GB"
  }'
```

| Field        | Type   | Required | Description                                                                                                               |
| :----------- | :----- | :------- | :------------------------------------------------------------------------------------------------------------------------ |
| `email`      | string | yes      | Lead's email address                                                                                                      |
| `first_name` | string | no       | First name                                                                                                                |
| `last_name`  | string | no       | Last name                                                                                                                 |
| `company`    | string | no       | Company name                                                                                                              |
| `title`      | string | no       | Job title                                                                                                                 |
| `industry`   | string | no       | Industry vertical                                                                                                         |
| `linkedin`   | string | no       | LinkedIn profile URL                                                                                                      |
| `country`    | string | no       | ISO 3166-1 alpha-2 code (e.g. `US`, `GB`). Used to infer timezone for [timezone-aware outreach](#timezone-aware-outreach) |
| `timezone`   | string | no       | IANA timezone (e.g. `America/New_York`). Overrides the country-based timezone inference                                   |
| `source`     | string | no       | How you acquired this lead. Defaults to `manual`                                                                          |

On success, the endpoint returns HTTP 201 with the full lead object. To add multiple leads at once, use the [bulk import endpoint](#import-leads-via-the-api) instead.

### Import leads via the API

```bash theme={null}
curl -X POST https://api.truthlocks.com/v1/platform/growth/leads/import \
  -H "X-API-Key: tl_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "leads": [
      {
        "email": "cto@example.com",
        "first_name": "Alex",
        "last_name": "Chen",
        "company": "DataFlow Inc",
        "title": "CTO",
        "country": "GB"
      }
    ]
  }'
```

### Score a lead

Trigger AI scoring for a specific lead with optional campaign context:

```bash theme={null}
curl -X POST https://api.truthlocks.com/v1/platform/growth/leads/{id}/score \
  -H "X-API-Key: tl_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "campaign_context": "Enterprise compliance officers at financial services firms"
  }'
```

The response includes a score (0–100), a fit level (`high`, `medium`, or `low`), and the AI's reasoning.

### Lead lifecycle

Leads move through a defined set of statuses as you engage with them. You can update a lead's status manually from the lead detail page or through the API.

| Status         | Meaning                                          |
| :------------- | :----------------------------------------------- |
| `new`          | Freshly created or discovered — no outreach yet  |
| `contacted`    | Initial outreach has been made                   |
| `qualified`    | Confirmed as a good fit and ready for conversion |
| `replied`      | Lead has responded to an outbound email          |
| `converted`    | Lead has been onboarded as a customer            |
| `unsubscribed` | Lead opted out of communications                 |
| `bounced`      | Email delivery failed                            |
| `archived`     | Manually removed from the active pipeline        |

A typical progression is `new` → `contacted` → `replied` → `qualified` → `converted`. Leads that opt out move to `unsubscribed`, leads with delivery failures move to `bounced`, and leads you no longer want in the active pipeline can be set to `archived`. The orchestrator updates `contacted`, `replied`, and `bounced` automatically based on campaign activity — you only need to set statuses manually for `qualified`, `converted`, `unsubscribed`, and `archived`.

#### Update lead status via the API

```bash theme={null}
curl -X PATCH https://api.truthlocks.com/v1/platform/leads/{id}/status \
  -H "X-API-Key: tl_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"status": "contacted"}'
```

The response returns the updated lead object.

#### Delete a lead

Permanently remove a lead from your pipeline. In the console, click **Delete** next to any lead in the leads list and confirm the prompt. Via the API:

```bash theme={null}
curl -X DELETE https://api.truthlocks.com/v1/platform/leads/{id} \
  -H "X-API-Key: tl_live_your_api_key"
```

<Warning>
  Deleting a lead is permanent and cannot be undone. Emails already sent to the lead are not affected — they remain in the campaign's email log for audit purposes.
</Warning>

### Lead detail view

Click any lead in the leads list to open its detail page. The detail view shows:

* **Identity context** — name, company, email, source, and submission date.
* **Inquiry message** — the original message submitted by the lead, if any.
* **Status badge** — the lead's current lifecycle status, color-coded for quick identification.

From the detail page you can:

* **Mark as contacted** — update the status to `contacted` when you have reached out.
* **Archive** — move the lead out of the active pipeline.
* **Onboard as customer** — convert the lead into a platform customer (see [lead conversion](#lead-conversion) below).

### Lead conversion

When a lead is ready to become a customer, you can convert them directly from the lead detail page or via the API. Conversion sends an enterprise onboarding invite to the lead's email address with a link to set up their organization account on the platform.

**From the console:** Open the lead detail page and click **Onboard Customer**. A confirmation dialog shows the recipient email and company. Click **Confirm & Send Invite** to send the onboarding invite.

**From the API:**

```bash theme={null}
curl -X POST https://api.truthlocks.com/v1/platform/leads/{id}/convert \
  -H "X-API-Key: tl_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{}'
```

On success the API returns HTTP 201 with a confirmation message. The lead's status changes to `qualified` and an enterprise onboarding invite is sent to the lead's email. The lead's detail page shows an **Onboarded** badge once converted.

Example response:

```json theme={null}
{
  "message": "Onboarding invite sent",
  "lead_id": "lead_abc123",
  "status": "qualified"
}
```

<Info>
  Lead conversion is a one-way action. Once an onboarding invite is sent, the lead cannot be reverted to a pre-conversion state. Verify the lead's email and company before confirming.
</Info>

### Export leads to CSV

You can export your full lead pipeline to a CSV file from the **Leads** page in the console. Click **Export Pipeline** to download a CSV containing every lead currently visible in the list.

The exported file is named `leads-export-YYYY-MM-DD.csv` using the current date, and includes these columns:

| Column       | Description                                                                                 |
| :----------- | :------------------------------------------------------------------------------------------ |
| `ID`         | Unique lead identifier                                                                      |
| `First Name` | Lead's first name                                                                           |
| `Last Name`  | Lead's last name                                                                            |
| `Email`      | Lead's email address                                                                        |
| `Company`    | Company name (blank if unknown)                                                             |
| `Source`     | How the lead was created — e.g. `manual`, `csv_import`, `github`, or an enrichment provider |
| `Status`     | Current [lifecycle status](#lead-lifecycle)                                                 |
| `Created At` | ISO 8601 timestamp when the lead was added                                                  |

Use the search bar to filter leads by name, company, or email before exporting — the CSV includes only the leads matching your current filter. This is useful for exporting a subset of your pipeline (e.g. all leads at a specific company) for offline analysis, CRM import, or sharing with stakeholders.

<Tip>
  The export runs client-side, so it works even for very large lead lists without hitting API timeouts.
</Tip>

## Email management

The **Growth > Emails** page in the console shows a log of all AI-generated emails across every campaign. Use it to monitor delivery status, review email content, and spot problems.

### Filtering the email log

Three filters narrow the log:

| Filter     | Options                                                                                           |
| :--------- | :------------------------------------------------------------------------------------------------ |
| Campaign   | Select a specific campaign or view all                                                            |
| Status     | `pending_approval`, `queued`, `sent`, `delivered`, `bounced`, `complained`, `simulated`, `failed` |
| Simulation | All emails, simulated only, or real only                                                          |

### Viewing email details

Click any email in the log to open a detail drawer showing:

* **Subject and status** — the email subject line with a status badge.
* **Content hash** — the SHA-256 hash of the email body, useful for integrity verification.
* **AI prompt hash** — the hash of the prompt used to generate the email.
* **SES message ID** — the Amazon SES tracking ID for delivered emails.
* **Event timeline** — a chronological list of delivery events (send, delivery, bounce) with timestamps.
* **Body preview** — the rendered HTML email body so you can review exactly what the recipient saw.

### Cross-campaign approval queue

The **Growth > Emails > Pending Approval** page shows all emails awaiting manual approval across every campaign that has [approval mode](#approval-mode) enabled. This gives you a single queue to review and approve outbound emails without opening each campaign individually.

Each entry shows the campaign name, recipient, subject line, and generation timestamp. Click **Approve** to send the email into the delivery pipeline.

## Reply capture

The AI CMO automatically captures replies to outbound emails and routes them back to the platform for classification. Every outbound email includes a `Reply-To` header pointing to your dedicated reply address (e.g. `sales@truthlocks.com`) and a tracking header (`X-CMO-Email-ID`) that links the reply back to the original email and lead.

When a prospect replies, the platform:

1. Receives the inbound email via an SES webhook.
2. Matches the sender to an existing lead in your pipeline.
3. Links the reply to the most recent outbound email sent to that lead.
4. Creates an inbound reply record with the full message body, sender, and timestamp.
5. Queues the reply for AI sentiment classification.

You do not need to monitor a shared inbox or manually forward replies. The entire flow — from inbound email to classified reply to CRM sync — runs automatically.

### How reply matching works

The platform uses the sender's email address to match inbound replies to existing leads. If a reply arrives from an email that does not match any known lead, it is logged but not classified. When a match is found, the reply is linked to the most recent outbound email sent to that lead, preserving the conversation thread.

<Info>
  Reply capture requires a verified SES domain and an active inbound email rule. The reply address is set in the `reply_to_address` field in your Growth [settings](#settings) (e.g. `sales@truthlocks.com`). This is configured at the platform level — no per-campaign setup is needed.
</Info>

## Reply classification

Once a reply is captured, the AI CMO classifies it with one of these sentiment labels:

| Sentiment           | Description                                         |
| :------------------ | :-------------------------------------------------- |
| `positive`          | Interested in learning more or scheduling a meeting |
| `negative`          | Clearly not interested or hostile                   |
| `neutral`           | Acknowledgment without clear intent                 |
| `out_of_office`     | Auto-reply indicating the contact is away           |
| `auto_reply`        | Non-human automated response                        |
| `request_more_info` | Asking for additional details                       |
| `not_interested`    | Polite decline                                      |

Classification runs automatically in the background. The reply processor polls for unclassified replies and uses AI to determine sentiment based on the full reply text.

Replies classified as `positive` or `request_more_info` are flagged as **hot leads** and trigger automatic actions:

* The lead's status is updated to `replied`.
* A deal is created in your connected [CRM](#crm-integration) with the sentiment score and reply text.
* The lead's lifecycle stage is advanced to `salesqualifiedlead` in HubSpot.
* Optional [webhooks](/guides/webhooks) fire with the reply event.

## CRM integration

Hot leads — those with a `positive` or `request_more_info` reply — are automatically pushed to your connected CRM through a full pipeline that keeps your sales team informed at every stage.

### Supported providers

| Provider    | Status          |
| :---------- | :-------------- |
| **HubSpot** | Fully supported |

### What happens automatically

When you connect HubSpot, the AI CMO performs these actions without manual intervention:

| Trigger                                                       | CRM action                                                                                                                                                                          |
| :------------------------------------------------------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Email sent to a lead                                          | Contact is created or updated in HubSpot with the lead source set to "Truthlocks AI CMO". The email send is logged as an email activity on the contact timeline.                    |
| Lead replies with `positive` or `request_more_info` sentiment | A deal is created in your HubSpot pipeline. The deal includes the campaign name, sentiment score, and reply text. The contact's lifecycle stage is updated to `salesqualifiedlead`. |
| Deal stage changes                                            | The AI CMO updates the deal stage in HubSpot as the lead progresses through your pipeline.                                                                                          |

This means your sales team sees new deals appear in their HubSpot pipeline the moment a prospect expresses interest — complete with the AI's sentiment analysis and the full reply text for context.

### Configuring HubSpot

Connect HubSpot from the Growth **Settings** page in the console, or via the [settings API](#update-settings). You need two values from your HubSpot account:

| Setting           | API field           | Description                                                                                      |
| :---------------- | :------------------ | :----------------------------------------------------------------------------------------------- |
| HubSpot API key   | `hubspot_api_key`   | A private app access token from your HubSpot account with CRM scopes (contacts, deals, timeline) |
| HubSpot portal ID | `hubspot_portal_id` | Your HubSpot account's portal ID, visible in your HubSpot account URL                            |

```bash theme={null}
curl -X PATCH https://api.truthlocks.com/v1/platform/growth/settings \
  -H "X-API-Key: tl_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "hubspot_api_key": "pat-na1-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "hubspot_portal_id": "12345678"
  }'
```

Once configured, the integration is active for all campaigns. You do not need to enable it per campaign. To disconnect HubSpot, set both fields to `null`.

### HubSpot activity timeline

Every outbound email the AI CMO sends is logged as an email activity on the corresponding HubSpot contact. This gives your sales reps full visibility into what the AI sent before they take over the conversation — including subject lines, send timestamps, and delivery status.

## Settings

### Retrieve settings

Retrieve your AI CMO configuration:

```bash theme={null}
curl -X GET https://api.truthlocks.com/v1/platform/growth/settings \
  -H "X-API-Key: tl_live_your_api_key"
```

Key settings fields:

| Field                            | Type           | Description                                                                                           |
| :------------------------------- | :------------- | :---------------------------------------------------------------------------------------------------- |
| `daily_email_cap`                | integer        | Platform-wide default daily send cap                                                                  |
| `per_minute_cap`                 | integer        | Platform-wide default per-minute cap                                                                  |
| `max_follow_ups`                 | integer        | Default maximum follow-up emails per lead                                                             |
| `bounce_rate_pause_threshold`    | number         | Default bounce rate threshold for auto-pause (percentage)                                             |
| `complaint_rate_pause_threshold` | number         | Default complaint rate threshold for auto-pause (percentage)                                          |
| `simulation_default`             | boolean        | When `true`, new campaigns start in simulation mode by default                                        |
| `global_kill_switch`             | boolean        | Emergency stop for all campaigns — no emails are sent while active                                    |
| `hubspot_api_key`                | string or null | HubSpot private app access token for [CRM integration](#crm-integration). Set to `null` to disconnect |
| `hubspot_portal_id`              | string or null | HubSpot portal ID for deal and contact sync                                                           |
| `reply_to_address`               | string or null | Dedicated reply address for [reply capture](#reply-capture) (e.g. `sales@truthlocks.com`)             |
| `ses_status`                     | string         | Current SES health — `healthy`, `degraded`, or `unavailable` (read-only)                              |
| `bedrock_model_id`               | string         | Primary AI model for campaign planning and reasoning (read-only)                                      |
| `bedrock_model_fast`             | string         | Fast model for lead scoring and reply classification (read-only)                                      |
| `bedrock_model_write`            | string         | Model used for email generation (read-only)                                                           |
| `cloudwatch_metrics_url`         | string or null | Link to the CloudWatch dashboard for delivery metrics                                                 |

### Update settings

Update one or more settings with a `PATCH` request:

```bash theme={null}
curl -X PATCH https://api.truthlocks.com/v1/platform/growth/settings \
  -H "X-API-Key: tl_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "daily_email_cap": 100,
    "per_minute_cap": 10,
    "bounce_rate_pause_threshold": 3.0,
    "simulation_default": true
  }'
```

You can update any combination of writable fields in a single request. Read-only fields like `ses_status`, `bedrock_model_id`, `bedrock_model_fast`, and `bedrock_model_write` are ignored if included.

### Global kill switch

The global kill switch immediately stops all active campaigns from sending emails. When activated, no emails are sent across any campaign until you deactivate it.

**From the console:** Open **Growth > Settings** and toggle **Global Kill Switch** under the Safety section. A confirmation dialog requires you to confirm before activation to prevent accidental use.

**From the API:**

```bash theme={null}
curl -X PATCH https://api.truthlocks.com/v1/platform/growth/settings \
  -H "X-API-Key: tl_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"global_kill_switch": true}'
```

To deactivate the kill switch and resume sending:

```bash theme={null}
curl -X PATCH https://api.truthlocks.com/v1/platform/growth/settings \
  -H "X-API-Key: tl_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"global_kill_switch": false}'
```

<Warning>
  The global kill switch affects every campaign. Use the campaign-level kill switch at `POST /campaigns/{id}/kill` if you only need to stop a single campaign.
</Warning>

## Related

<CardGroup cols={2}>
  <Card title="Campaign templates" icon="rectangles-mixed" href="/guides/campaign-templates">
    Activate pre-built campaigns for ten industry verticals.
  </Card>

  <Card title="Webhooks" icon="webhook" href="/guides/webhooks">
    Receive real-time notifications for campaign events including hot lead replies.
  </Card>

  <Card title="Authentication" icon="key" href="/security/auth">
    Set up your API key for growth API access.
  </Card>

  <Card title="Error reference" icon="circle-exclamation" href="/ops/errors">
    Troubleshoot API errors including retryable vs. terminal failures.
  </Card>
</CardGroup>
