docs: add the AI variables guide and update the campaigns API reference, endpoints scope map, AI credits, and guides nav for per-recipient AI blocks

This commit is contained in:
Matthew Meszaros
2026-07-22 17:05:08 +02:00
parent 14587b9cd1
commit 58c166c073
5 changed files with 82 additions and 0 deletions
+6
View File
@@ -51,12 +51,16 @@ All paths below are relative to the versioned base URL `https://api.warmbly.com/
| GET | `/campaigns/:id/logs` | `READ_CAMPAIGNS` |
| GET/POST/PATCH/DELETE | `/campaigns/:id/steps[/:sid]` | `READ_CAMPAIGNS` for GET, `WRITE_CAMPAIGNS` otherwise |
| PATCH | `/campaigns/:id/step-layout` | `WRITE_CAMPAIGNS` |
| POST | `/generation/write` | `WRITE_CAMPAIGNS` |
| POST | `/generation/edit` | `WRITE_CAMPAIGNS` |
| POST | `/generation/ai-variable` | `WRITE_CAMPAIGNS` |
### Contacts
| Method | Path | API Permission |
|--------|------|----------------|
| POST | `/contacts/search` | `READ_CONTACTS` |
| GET | `/contacts/custom-fields` | `READ_CONTACTS` |
| POST | `/contacts` | `WRITE_CONTACTS` |
| DELETE | `/contacts` | `BULK_CONTACTS` |
| PATCH | `/contacts` | `BULK_CONTACTS` |
@@ -72,6 +76,8 @@ All paths below are relative to the versioned base URL `https://api.warmbly.com/
| GET | `/contacts/:id/research` | `AI_RESEARCH` |
| POST | `/contacts/research/batch` | `AI_RESEARCH` |
`GET /contacts/custom-fields` lists the distinct custom-field keys used across your contacts (frequency-ranked), for building personalization pickers. It returns a flat string array under `data`, capped at 200 keys.
AI contact research charges credits (2 per run, billable even when it finds nothing) and only saves cited findings. See the [AI contact research](/guides/ai-contact-research/) guide. The batch endpoint accepts up to 500 contact ids and drains in the background.
### Unibox
@@ -935,3 +935,39 @@ Generate outreach copy with the AI writing assistant. Gated to paid and free-tri
```
When the organization is out of credits the endpoint returns `402` with `code: "insufficient_credits"` and the standard envelope. A depleted balance is checked before any provider call, so no completion is ever burned on a `402`.
## Preview an AI variable
`POST /generation/ai-variable`
Generate the recipient-specific snippet a per-recipient AI variable block would produce, for the campaign editor's preview. The prompt is rendered against a supplied contact (must belong to the organization) or a sample contact, then generated with the same framing the send path uses, including the surrounding email so the fragment fits. The charge is metered by usage (the model and tokens the snippet uses, plus any web lookup), refunded if the provider call fails, and `Idempotency-Key` is honored so a retried request is not double-charged. See the [AI variables](/guides/ai-variables/) guide. **Scope** `WRITE_CAMPAIGNS` · **Org permission** `manage_campaigns` and `use_ai`.
### Request body
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `mode` | string | no | `instant` (default) for a single fast completion, or `research` for the deeper, higher-cost path. |
| `prompt` | string | yes | The block instruction, itself a template rendered against the contact (max 8000 characters). |
| `tone` | string | no | Desired tone. |
| `web_search` | boolean | no | Allow one bounded web lookup about the contact's company to enrich context. Implied by `research`. |
| `contact_id` | string | no | A contact in the organization to render the prompt against. Omit to use a sample contact. |
| `context_before` | string | no | The email text immediately before the block, so the fragment fits the sentence it lands in. |
| `context_after` | string | no | The email text immediately after the block. |
```json
{ "mode": "instant", "prompt": "One line noting something specific about {{company}}.", "contact_id": "4f6c..." }
```
### Response
```json
{
"text": "Saw Acme just shipped its new billing API.",
"credits_remaining": 246,
"credits_charged": 1,
"tokens_used": 180,
"model": "claude-..."
}
```
A `contact_id` that is not in the organization returns `404`. When the organization is out of credits the endpoint returns `402` with `code: "insufficient_credits"`, checked before any provider call.
+2
View File
@@ -35,6 +35,8 @@ Credits are charged from what each call actually uses. Every action has a flat *
| AI switch in an automation with the AI decider (the value decider is free) | 1 |
| Ask AI branch in an automation (per evaluation) | 1 |
| Switch step with the AI decider in a campaign sequence (per contact; the value decider is free) | 1 |
| Per-recipient AI variable in a campaign email, instant mode (per recipient) | 1 |
| Per-recipient AI variable in a campaign email, research mode (per recipient) | 2 |
| Inbox agent handled thread | 5 |
| Web search made by an AI step (only when results are found) | 1 |
+37
View File
@@ -0,0 +1,37 @@
---
title: AI variables
description: Drop AI blocks into a campaign email that write a unique, personalized snippet for each recipient at send time.
---
An AI variable is a block you place inside a campaign email that generates its own copy for every recipient. Where a normal variable like `{{first_name}}` substitutes a stored field, an AI variable runs a short instruction against each contact and writes a snippet tailored to them, right where you dropped it. The rest of the email is yours; the AI variable fills one spot with something specific to the person receiving it.
Because it runs per recipient, an AI variable costs credits for each contact it resolves. Plan for that the same way you plan send volume.
## Adding an AI variable
In the campaign email editor, place your cursor where you want the personalized copy and insert an AI variable. Give it a short instruction, for example "one line noting something specific about their company" or "a friendly opener that references {{company}}". The instruction is itself a template, so you can reference contact fields inside it and they render per contact before the model runs.
By default a block writes instantly from the contact's own fields. That is the right choice for most blocks. One optional add-on personalizes deeper:
- **Web search** runs a bounded lookup about the contact's company before writing, so the snippet can reference something beyond their stored fields. It adds the cost of that lookup.
The block always uses the model configured for your workspace; there is no separate model setting to choose. Keep instructions tight: an AI variable is meant to fill a phrase or a sentence or two, not write the whole email.
## Previewing
Use the editor's preview to see the block written for a built-in sample contact, shown inside the whole message so you can check it reads naturally where it lands. A preview costs the same as a send-time resolution, because it runs the same generation, and the editor shows what that example actually cost.
## How it resolves at send time
When the campaign sends to a contact, each AI variable in that email is generated for that contact just before tracking and signature are added, so the AI copy is tracked and styled like the rest of the email. The result is cached per contact and step: if a send is retried, the same snippet is reused instead of generating (and charging) again. Different contacts always get their own copy.
A block whose instruction renders empty for a contact resolves to nothing and costs nothing. A block with a malformed configuration is skipped rather than blocking the send. If the AI provider fails, that recipient's send is retried later with the cached result, so you are never charged for output you did not receive.
## Cost and safety
- A block is billed by usage: the model and the tokens each snippet uses, plus the web search when it is on. See [AI credits](/guides/ai-credits/) for how usage maps to credits.
- Costs multiply by recipients, not by email: a block that resolves for 500 contacts is charged 500 times over the campaign's run.
- The contact's fields and any web results are treated as untrusted input, so text a contact controls cannot change the block's instruction.
- Web lookups are derived only from the contact's company and name, never from anything a contact wrote to you.
AI variables gather and write copy; they do not decide who to send to or when. Your campaign sending rules, warmup, and deliverability guards apply exactly as they would to any other email.
+1
View File
@@ -19,6 +19,7 @@
"ai-steps-in-automations",
"---AI---",
"ai-credits",
"ai-variables",
"ai-assistant",
"ai-contact-research",
"inbox-agent",