Partner API
Provision and manage end-customers entirely server-to-server. The Partner API is for resellers and platform integrators who want to embed Warm AI’s identification under their own brand — without sending their customers into the Warm AI dashboard.
Looking for the standard customer-facing API? See Identification API. The Partner API is only enabled for accounts flagged as partners — contact us if you need partner status enabled.
When to use it
Use the Partner API when:
- You sell a product that bundles visitor identification (cold-email tools, sales-intel platforms, agency dashboards).
- Your customers should never log into a Warm AI URL — you control the UX end-to-end.
- You want per-customer quotas, billing, and reporting that you mirror into your own backend.
You don’t need the Partner API if you’re just identifying visitors on your own site — use the Identification API directly.
How the model works
┌──────────────────────────────────────────────────────────────┐
│ Partner account (you) │
│ ─────────────── │
│ • is_partner = true │
│ • one master api_key (the warm_… you authenticate with) │
│ │
│ ├── Child customer A (parent_api_key_id = master) │
│ │ • own api_key, own quota, own webhook │
│ │ • own tracking_script_id, own domain │
│ │ │
│ ├── Child customer B │
│ └── Child customer C │
└──────────────────────────────────────────────────────────────┘- Each child is a phantom user under your account. They have no login, no email inbox, no dashboard access. They exist purely as a billing + tracking unit you manage on their behalf.
- Each child has its own
api_key(which you can hand back to the customer for server-side calls), its owntracking_script_id(forpulse.jsinstall), its ownsubscriptionrow (quota / period / topup balance), and its own optionalwebhook_url. - All
partner-*endpoints authenticate with your masterx-access-key— never the child’s. The child’s key is for the child’s own tracker traffic.
Authentication
Every Partner API request must include:
| Header | Description |
|---|---|
x-access-key | Your master partner api key (starts with warm_…) |
Content-Type | application/json for POST endpoints |
The API rejects the call with 401 if the key is missing, unknown, or revoked, and 403 if the key is valid but is not the key designated as your partner master, or the account isn’t flagged is_partner=true.
Never embed your master x-access-key in a browser, mobile app, or any client-side bundle. It has full provisioning authority over every customer you’ve created — treat it like a Stripe secret key.
Billing model
Each child has its own subscription row with:
| Field | Meaning |
|---|---|
monthly_id_quota | IDs the child gets each period (resets on roll-over). Set this when creating or updating the child. |
ids_used_this_period | Counter, resets to 0 at period roll-over. Changing monthly_id_quota does not reset it. |
topup_balance | Non-expiring pool consumed after monthly_id_quota is exhausted. |
current_period_end | When the period rolls over and the counter resets. Defaults to 30 days from creation. |
id_price_pence_override | Your per-ID rate for this child, in pence. Informational — used for partner-side billing math. |
The partner is invoiced for the IDs their children consume, aggregated across all customers, on whatever commercial agreement you have with us. You bill your end-customers however you like — the per-child id_price_pence_override is the unit cost you’ve agreed for that customer, so you can build margin into your own pricing.
How your own pool is billed
Above the per-child rows sits your pool. Every identification any of your children consumes draws from it. Which way you pay for that pool is set by pool_billing_mode, returned by partner-pool-state.
| Mode | pool_billing_mode | How it works |
|---|---|---|
| Prepaid | prepaid | You buy IDs up front. They land in topup_balance and are drawn down as your children consume. Optionally refills itself, see auto-reload below. |
| Monthly invoice | invoice_monthly | You consume first and we invoice in arrears on your billing anchor day. No balance to keep topped up. |
Both modes price at partner_per_id_pence, your agreed rate per identification. That rate is set by Warm as part of your commercial agreement and is not self-serve. Until it is set, top-ups are rejected, so if you are onboarding and partner-pool-state returns partner_per_id_pence: null, that is the thing to chase before anything else.
On prepaid you can have the pool refill itself: save a card, set a threshold and a reload amount, and we charge that card whenever available drops below the threshold. If auto-reload looks enabled but the pool is not refilling, check auto_reload.stripe_payment_method_id and auto_reload.consecutive_declines in the pool-state response before contacting support.
Endpoint surface
These endpoints cover the full customer lifecycle:
| Endpoint | Purpose |
|---|---|
POST partner-create-child-key | Mint a brand-new managed customer (key + tracker + subscription) |
POST partner-update-child | Change a child’s name, quota, rate, status, or period |
GET partner-list-children | Enumerate your customers with their current quota / usage / status |
POST partner-adopt-existing-key | Convert one of your existing direct trackers into a managed customer |
POST partner-release-child | Reverse an adoption — child’s tracker returns to your direct account |
GET partner-pool-state | Read your pool balance, auto-reload config and recent top-ups |
Plus the standard customer-facing endpoints, which you’ll call with the child’s api key (not yours) to fetch their visitors:
| Endpoint | Purpose |
|---|---|
GET list-visitors | Pull identified visitors for one tracker — accepts tracker_site, since, source, search, limit, offset |
GET api-account-status | Quota / topup status for one child |
What’s not in the API yet
These pieces are planned and tracked on the roadmap. Until they ship, work around them as noted.
| Feature | Workaround | Tracking |
|---|---|---|
| Real-time events for period roll-over, quota exhaustion, topup balance changes | See Webhooks: child.period_rolled, child.suspended, child.reactivated and child.topup_changed are all pushed. Use partner-pool-state for your own balance. | ✅ Live |
| Programmatic pool balance | See partner-pool-state — balance, auto-reload config and last 10 top-ups in one read | ✅ Live |
| Paginated top-up history beyond the last 10 | partner-pool-state returns the most recent 10. Ask support if you need a fuller export. | Planned |
Partner → child topup transfer (gift IDs from your balance to one child’s topup_balance) | See partner-transfer-topup — atomic transfer with insufficient-balance protection and child.topup_changed(reason="gift") event | ✅ Live |
| Rotate a lost child api key plaintext | See partner-rotate-child-key — rate-limited to 10/child/24h, audit-logged | ✅ Live |
| CDN whitelabel for the tracker script | See separate CDN whitelabel docs (coming soon) | KAN-38 |
Next steps
- Quick Start — end-to-end TypeScript walkthrough: create a child, install the tracker, fetch visitors, release.
- Endpoints — full reference for every
partner-*endpoint. - Webhooks — real-time partner-side state-change events.