Help
Developers

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.

On this page