# OfferBanner for AI agents

> OfferBanner is an MCP server. An assistant can register a site, design a promotional bar, book it on a calendar, create a real Stripe discount code and read the numbers back — fifteen tools, no dashboard visit required. The server itself is included on the free plan, and so are dated campaigns; country targeting, triggers, several live campaigns at once and created codes need Pro, and every tool says so when it meets the limit.

## Connect

**Endpoint:** `https://offerbanner.com/mcp` — Streamable HTTP.

| Client | How |
| --- | --- |
| Claude Code | `claude mcp add --transport http offerbanner https://offerbanner.com/mcp` |
| Claude.ai / Claude Desktop | Settings → Connectors → Add custom connector → `https://offerbanner.com/mcp` |
| Anything else that speaks MCP | Streamable HTTP server URL: `https://offerbanner.com/mcp` |

### OAuth (the normal path)

OAuth 2.1 Authorization Code + PKCE, completed in the user's browser. Nothing has to be copied by hand. The consent screen asks for two scopes:

- `banners:read` — see your sites, banners and their performance.
- `banners:write` — change them.

`banners:write` does not imply `banners:read`: a grant covers exactly what the consent screen listed. Discovery metadata lives at `https://offerbanner.com/.well-known/oauth-protected-resource`; the machine-readable server card is at `https://offerbanner.com/.well-known/mcp.json`.

### API key (for things with no browser)

Create one at <https://offerbanner.com/agents> and send it as `Authorization: Bearer ob_…`. Every call acts on the account that owns the key; there is no way to reach anyone else's sites. Keys are rate-limited per key. Keep them out of prompts, repositories and logs.

## The fifteen tools

| Tool | | Purpose |
| --- | --- | --- |
| `list_sites` | read | Every website on the account: host, site code, install snippet, and what it is showing right now. **Start here** — every other tool takes a `siteId`. |
| `get_site` | read | One site in depth: the snippet, every banner belonging to it, its whole schedule, and whether Stripe is connected. |
| `create_site` | write | Register a host and create its site code, so the merchant has a tag to install. Idempotent per host. |
| `check_install` | read | Fetch a page of the merchant's site and report whether the tag is working. |
| `get_site_stats` | read | Daily traffic for a whole site over a window of days, plus the per-banner breakdown. |
| `schedule_site` | write | Add, move, list or remove bookings. The only tool that changes what a visitor sees. |
| `create_banner` | write | Design a banner for one site — copy, button, colours, position, countdown, effect. It is not shown to anyone until it is booked. |
| `update_banner` | write | Change only the fields named, including which site the banner belongs to and the offer plan behind it. |
| `get_banner` | read | One banner in full, including where it is booked and whether its code is backed by a real Stripe promotion code. |
| `get_banner_stats` | read | Daily traffic for one banner: views, clicks, CTR, dismissals, code copies, and redemptions read from Stripe. |
| `create_offer` | write | Create a real discount code on the merchant's own Stripe account and attach it to a banner. |
| `retire_offer` | write | Turn off the code attached to a banner, in the merchant's own Stripe account. Use it when a campaign ends. |
| `delete_banner` | write | Delete a banner and every version of it, permanently. Its stats go with it. |
| `connect_stripe` | write | Hand the merchant a one-time link that connects a Stripe account to one site. |
| `list_activity` | read | Everything that changed on the account, newest first: what, to what, by whom, from where. |

The read/write column is the scope the call needs. `schedule_site` is a write tool with a read inside it — `action: "list"` needs only `banners:read`.

## The typical flow

```
list_sites          →  get a siteId (or create_site if the host is new)
create_banner       →  design the banner for that site
schedule_site       →  action "add": a banner, a window, a country list — this is what shows it
create_offer        →  create the Stripe code and attach it (needs a key on that site)
get_site_stats      →  answer "is it working?"
```

`check_install` belongs after the merchant has pasted the tag; `connect_stripe` belongs before the first `create_offer` on a site that has no key.

## Rules an agent must know

**The site is the unit.** One host, one script tag, installed once and never edited again. Every tool takes a `siteId`. Banners belong to a site, and there is only ever one snippet — the site's. There is no per-banner snippet and no `delete_site` tool: deleting a site is a click the merchant makes at `https://offerbanner.com/sites/{siteId}/settings`.

**Booking is showing.** `schedule_site` with `action: "add"` is what makes a banner visible. There is no separate activate step and no status to set. Removing a banner's last booking stops it showing. `showing` is a *list*, not a single winner: the edge picks per country, so a country-targeted booking and a worldwide one can both be reaching real visitors.

**Codes are real Stripe objects.** `create_offer` writes a coupon and a promotion code into the merchant's own Stripe account, using the restricted key connected to that banner's site. Without a key it fails with `stripe_key_missing`. Replacing a code turns the old one off. A code keeps working after the banner that advertised it stops, unless `retire_offer` is called or the code was created with an expiry — taking a banner off the schedule does **not** stop a code.

**Creating the Stripe key is the merchant's step.** A restricted key can only be created inside Stripe's own dashboard. `connect_stripe` returns links for the merchant to follow; never ask for the key itself, and never expect to read one back — `get_site` reports `stripeKeyConnected` as a yes or no and nothing more.

**Installing the tag is the merchant's step.** Send them to their site's Install screen at `https://offerbanner.com/sites/{siteId}/settings`, which has the snippet and nine guided ways to get it onto a page, rather than describing where in their HTML it goes. Then call `check_install`.

**Failures are structured.** Every refusal carries `code`, `message` and `hint`, and the hint names an action that can be taken next. A Free-plan refusal names the price and the alternative; a scope refusal names the scope that is missing.

**Plan limits an agent will meet.** The line is *which visitor, and how many at once*: Free plays one booking at a time per site, shown worldwide, from the moment the page loads, on whatever dates the booking carries, with the "Powered by OfferBanner" badge. Anything that decides *which* visitor sees it is Pro, which is one payment of $49.

- **Country-targeted bookings** are written and never served on Free. This is **not** an error: `schedule_site` returns the booking, plus `planHold` — a sentence saying it is booked and goes live with Pro. Relay that sentence, and never report such a booking as showing; it will not appear in `showing`, whatever its dates say. A dated worldwide booking carries no `planHold` and serves normally.
- **Triggers.** Every type except `immediate` — delay, scroll, exit-intent, click — comes back as `premium_trigger` and nothing is written.
- **Creating codes** is refused, not held: `create_offer` fails with `pro_required`, `schedule_site` returns `offerCode: "pro_required"`, and nothing is created on the merchant's Stripe account and nothing is waiting to. The free alternative is `update_banner` with `offerPlan.selfManaged: true` and `offerPlan.codePrefix` set to a code the merchant made themselves.
- **Effects.** The thirteen premium effects, including the gift, come back as `premium_effect`.

Dates are the way around the one-at-a-time limit, and they are free: book the second campaign for a window that starts after the first one ends, and both serve on their own days.

## See also

- Index of everything machine-readable: <https://offerbanner.com/llms.txt>
- Summary for AI systems: <https://offerbanner.com/ai-info.md>
- Human page: <https://offerbanner.com/ai-info>
- Features: <https://offerbanner.com/features.md>
- Contact: <https://offerbanner.com/help> — a form, read by a person. There is no support email address.
