‹AICBCT

Advertiser API

Advertiser API v1

Your card is a measured connection, not just an ad. Every click, every request — yours to see and pull into your own systems.

TrackClicks by day, source, city
ReportYour card and status
LeadDentist requests + webhook
CardEdit your own card

1 · Key

Sign in at app/advertiser.html with the e-mail on your card and tap Make an API key. It is shown once. Keep it on your server — never in a web page. A new key turns the old one off.

curl https://aicbct.com/api/advertiser-api?r=card \
  -H "Authorization: Bearer aic_…"

2 · Track — clicks

GET /api/advertiser-api?r=clicks&days=30
→ { "clicks": { "total": 412, "last30": 120,
     "days": [{ "day": "2026-11-05", "n": 7 }, …],
     "bySource": { "card": 80, "qr": 31, "email": 9 },
     "byCity": { "chicago": 22, "seoul": 14 } } }

Trackable links (GET ?r=links) — use the qr one for printed QR codes at shows, in brochures or box inserts, so offline reach is counted on its own:

https://aicbct.com/api/ad-go?ad=CO-XXXXXX&src=qr      (also src=email, src=card)

Counts only. Nothing about the visitor is stored or passed on. Your site receives utm_source=aicbct, utm_medium=<source>.

3 · Report — your card

GET /api/advertiser-api?r=card
→ { "card": { "id": "CO-XXXXXX", "company": "…", "category": "implants", "line": "…",
     "website": "https://…", "cta": "quote", "leads": true, "status": "active", "priceUsd": 299 } }

4 · Lead — requests from dentists

Turn on Requests from dentists and pick a button (Get a quote · Book a demo · Ask for a call). A dentist fills a short form on your card; you get an e-mail, a row in your account, and — if set — a webhook.

GET /api/advertiser-api?r=leads&days=90
→ { "leads": [{ "id": "ld_…", "at": "…", "kind": "quote", "name": "…", "clinic": "…",
     "email": "…", "phone": "…", "city": "Chicago", "country": "US", "message": "…" }] }

B2B only. The form asks for no patient details; patient information never leaves AICBCT's consent flow.

Webhook

POST /api/advertiser-api   { "r": "webhook", "url": "https://crm.example.com/aicbct" }
→ { "secret": "whsec_…" }      (shown once; setting the URL again makes a new secret)
POST /api/advertiser-api   { "r": "webhook-test" }

We POST JSON { id, type: "lead.created" | "test", created, adId, data } with the header

AICBCT-Signature: t=1762300000,v1=<hex HMAC-SHA256 of "t.body" with your whsec_ secret>
// Node — check it
const [t, v1] = sig.split(',').map(p => p.split('=')[1]);
const ok = crypto.createHmac('sha256', SECRET).update(t + '.' + rawBody).digest('hex') === v1
        && Math.abs(Date.now() / 1000 - t) < 300;

Public https addresses only. One try, 5-second limit; every lead is also kept in your account and e-mailed.

5 · Card — edit your own

POST /api/advertiser-api
{ "r": "card", "line": "Short implants · next-day ship", "website": "https://…",
  "cta": "visit" | "quote" | "demo" | "call", "leads": true }

Errors

401No or wrong key.
400A field is not valid — the message says which.
503Not open yet.

Cards are labelled Sponsored and shown side by side, not ranked. No fee per patient or per treatment — ever. Partner Terms · order@aicbct.com