Offers
Read, create and change the plays your agent delivers — the copy, the call to action, who sees them on arrival, and what converting means.
An offer is a play the agent delivers: a line of copy, optional quick replies, an optional button bound to a real action, and a contract for what converting collects. The agent uses offers in conversation. An offer that also carries a deploy presents itself on the site — a toast, a top banner, or a full takeover — to the visitors the gates allow.
The smallest useful offer is a name and a line of copy:
curl -X POST "https://api.showpilot.com/v1/offers" \
-H "Authorization: Bearer $SHOWPILOT_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"name": "SOC 2 one-pager",
"message": "Want the SOC 2 summary? I can send it over or walk you through it.",
"goal": "lead_captured",
"chips": ["Send it", "Walk me through it"]
}'That offer is live for the agent immediately. It will not appear on the site until you give it a deploy.
Who sees it
Two different questions, answered in two places.
On arrival — deploy holds the gates. Every gate narrows, and an absent gate matches everything, so a deploy of { "live": true, "presentation": "toast" } shows to everyone.
{
"deploy": {
"live": true,
"presentation": "toast",
"includePages": ["/pricing", "/security"],
"referrerHosts": ["linkedin.com"],
"afterSeconds": 8,
"crm": { "knownContact": false }
}
}The campaign and timing gates are matched in the browser, because the server never sees the query string. Pages, sites, the date window, CRM and industry are matched server-side. The CRM and industry gates fail closed: a visitor ShowPilot cannot resolve never sees the offer.
In conversation — audienceIds names the audiences the agent may offer it to. An empty list means any audience it matches. Sending audienceIds replaces the whole set; send [] to clear it.
What converting means
goal is what counts: meeting_booked, lead_captured, content_viewed, or engaged. conversion says what happens when the visitor accepts:
{
"conversion": {
"collect": ["email", "company"],
"agentNote": "Send the SOC 2 summary, then ask whether their security review has a deadline.",
"destination": { "kind": "hubspot_list", "listId": "482" }
}
}The agent skips anything it already knows about the visitor, so collect is the ceiling, not a script. formId runs one of your agent forms instead, and its own after-steps fire — including a CRM relay, so an imported registration form still triggers your own workflow.
Retiring an offer
There is no delete. Send { "isActive": false }. An offer carries its lifetime stats — how many times it was shown and accepted — and its targeting rows; retiring keeps the record and stops the offer.
Changes are live
Nothing here needs publishing. The selector reads current rows, so an edit reaches the next visitor. That is the one way offers differ from tours, agent scripts and skills, which freeze a revision when you publish them.
Letting an agent write them
draft_offer on the MCP server turns a sentence about who an offer is for into the gates, and explains what it set and what to confirm. It writes nothing — you read the draft, then create or change the offer yourself.
Segments
Save a filter over what visitors asked and did, read its members with the identity your systems need, and hear when someone enters or leaves.
What your assistant can see
Conversations carry what a visitor asked. By default the API masks who they were, and reading them verbatim is a separate scope nobody gets by accident.