API

A job change feed your stack can consume

Every time someone in your contact list moves, Champions emits a structured event: who moved, where from, where to, their best known email at the new company, whether that company and role fit your ICP, and when we caught it. Take it as a signed webhook, poll it on a timestamp, or let us write it back for you.

Webhook push Timestamp polling OpenAPI 3.1
The event

What every move tells you

A useful event is more than "someone moved". If your automation has to make a second call to work out who this person is or who should get the alert, the event is doing half its job. So each one arrives already matched to your record and already attributed to an owner.

person object

The contact who moved — first_name, last_name and linkedin_url — already matched to the record you track, not a fresh identity you have to reconcile.

old_company, old_title string

What they left. This is also your churn signal: when the move is out of a customer account, Champions raises a second churn_risk play against it.

new_company, new_title string

Where they landed. The title is what decides whether this is the same buyer you already sold to or a new seat at a bigger table.

email, email_status string

The best known email for the person at the new company, with its verification state, so outreach does not bounce on a guessed address pattern.

account_fit, contact_fit boolean

The new company and the new role scored against your own ICP rules before the event is sent. Both true is a direct play; account only is a referral.

type, segment enum

What to do and who this was to you: direct, referral, watch, promotion or churn_risk, against customer, churned, closed_lost or open_pipeline.

owner, owner_id, crm_record_id string

The rep who held the relationship and the CRM ids behind them, so the alert routes itself to the right person without a second lookup.

detected_at timestamp

When Champions detected the move. The first days are where the warmth is, so this is the field your SLAs and reporting should key on.

Examples you can build against

Base URL https://api.getchampions.io/v1. The full reference lives at docs.getchampions.io, and the OpenAPI 3.1 document is published if you would rather generate a client than read one.

Event payload play.created
{
  "id": "4812",
  "event": "play.created",
  "event_key": "play:8f14e45f-ea2b-4c1d-9b7a-2c3d4e5f6071",
  "created_at": "2026-10-02T08:14:22Z",
  "workspace_id": "0b9c1f2e-7a3b-4551-8e6d-7f0a1b2c3d4e",
  "data": {
    "id": "8f14e45f-ea2b-4c1d-9b7a-2c3d4e5f6071",
    "type": "direct",
    "status": "open",
    "person": {
      "first_name": "Sarah",
      "last_name": "Jenkins",
      "linkedin_url": "https://www.linkedin.com/in/example"
    },
    "old_company": "Acme Analytics",
    "old_title": "VP Revenue Operations",
    "new_company": "NewCo",
    "new_title": "Chief Revenue Officer",
    "segment": "customer",
    "account_fit": true,
    "contact_fit": true,
    "email": "[email protected]",
    "email_status": "verified",
    "owner": "Dana Reyes",
    "owner_id": "62104418",
    "crm_record_id": "118402931",
    "detected_at": "2026-10-02T08:14:22Z"
  }
}
Create a webhook endpoint POST /v1/webhooks
curl -X POST https://api.getchampions.io/v1/webhooks \
  -H "Authorization: Bearer $CHAMPIONS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://hooks.yourcompany.com/champions",
    "events": ["play.created", "contact.changed"],
    "description": "Production receiver"
  }'
Poll instead GET /v1/changes
curl -G https://api.getchampions.io/v1/changes \
  -H "Authorization: Bearer $CHAMPIONS_API_KEY" \
  --data-urlencode "since=2026-10-01T00:00:00Z" \
  --data-urlencode "page_size=100"
Delivery

Push, pull, or both

Polling tells you a contact moved eventually. A webhook tells you the moment it happens. Most teams run push as the live path and a nightly poll as the safety net, which is the pattern we walk through in job change webhooks.

Push

Webhooks

Register an HTTPS endpoint on /v1/webhooks and we call it as each move is detected. Right default for a signal that is sparse, individually valuable, and worth acting on the same day.

  • play.created, contact.changed, run.completed and run.paused
  • Every delivery signed, so you can reject anything else that posts
  • Dedupe on event_key — a retried delivery is safe to replay
Pull

Polling

Ask GET /v1/changes for everything detected since a timestamp you control. Right default when you are backfilling, reconciling, or cannot expose a public endpoint.

  • since takes an ISO timestamp, so a replay is a re-read not a special case
  • Page with page and page_size, up to 200 records a request
  • The webhook data object is the same shape — one parser, two transports
Auth

Scoped keys, signed deliveries

The feed runs against your own contacts, so credentials are scoped to your workspace rather than handed out at public self-signup. You call us with a bearer token over HTTPS. We call you with a signature you can verify, because a webhook endpoint is a public endpoint and nothing else should be able to post moves into your pipeline.

Verify a delivery by recomputing HMAC-SHA256 over the timestamp, a dot, and the raw body, keyed with the endpoint secret we return once at creation. Compare in constant time and reject anything over five minutes old.

  • Bearer token or X-API-Key over HTTPS, created in your workspace settings
  • read scope for every GET, write for anything that changes state
  • OAuth 2.1 with PKCE for agents, plus a hosted MCP server
  • 600 read and 120 write requests a minute — see the FAQ
Request shape
You → Champions
Authorization: Bearer chp_live_…
Champions → You
X-Champions-Signature: t=<unix>,v1=<hmac>
HTTPS only Per-workspace scope OAuth 2.1 for agents
HubSpot Contact write-back, create new or update existing Live
Slack Channel alerts and a DM to the record owner Live
Your endpoint play.created webhook, signed per delivery Live
Your warehouse GET /v1/changes and /v1/plays on your schedule Live
Write-back

Skip the receiver entirely

You do not have to consume the feed to use it. Champions writes each play back to HubSpot and routes alerts into Slack — a channel for the team, a DM to the rep who owns the record — so the warm lead is waiting before anyone opens a laptop. You choose whether a push creates a new contact or updates the existing one, and which play types are worth pushing at all.

That is the managed path, and it is how most customers run: no app to install, no logins for reps. The platform page covers it, and how it works walks the monitoring end to end.

  • HubSpot write-back: create a new contact or update the existing one
  • Filter by play type, so only what you act on reaches the CRM
  • Departures raise a churn_risk play for CS the day they are detected

The endpoint is never the hard part

You can assemble this on top of a raw people-data source, and some teams do. The webhook plumbing is a weekend. These three are where home-built pipelines leak:

01
Match rate

Re-identifying someone who changed their email, their company, and sometimes their name.

02
Freshness

Catching the move while the window is open, ideally on intent signals before it is public.

03
Coverage

Breadth past a single Salesforce-first integration, when your team runs on HubSpot or Zoho.

Compare providers on those three axes rather than on whether they can send a webhook, because most can. The job change tracking landscape sets ours against UserGems, Champify and the rest. Because the leads convert, the signal is backed by a contractual ROI guarantee: if champion-sourced revenue does not reach 2x your annual service fee, you are covered under our terms. What you build in-house, you insure yourself.

Developer questions

Where is the full API reference?

The reference is at docs.getchampions.io and the machine-readable contract is the OpenAPI 3.1 document at api.getchampions.io/openapi.json, which you can point a client generator straight at. The base URL is https://api.getchampions.io/v1, and the same API is served at https://app.getchampions.io/api/v1. The samples on this page are generated from that spec, not sketched by hand.

How do I get API access?

Workspaces are invite-only rather than public self-signup, because the feed runs against your own contacts and credentials are scoped to that data. Once you have a workspace you create a key yourself under Settings, API and integrations, and send it as a bearer token. Book a demo or email [email protected] to get started.

Should I use webhooks or polling?

Use webhooks as the default, because a job change is most valuable in its first days and push removes the lag. Use GET /v1/changes with a since timestamp for backfills and as a reconciliation job alongside webhooks, so a delivery you missed during a deploy still lands. The webhook data object carries the same shape the REST endpoints return.

What are the rate limits?

Six hundred read and 120 write requests a minute, per workspace. Over the limit you get a 429 with code rate_limited. That is generous against the real volume: 2 to 3% of B2B contacts change jobs each month, so a 10,000-contact CRM produces roughly 200 to 300 events a month, not a firehose.

How do I verify a webhook delivery?

Every delivery carries X-Champions-Signature in the form t=<unix seconds>,v1=<signature>, where the signature is an HMAC-SHA256 of the timestamp, a dot, and the raw request body, keyed with the endpoint secret returned once when you create the endpoint. Recompute it over the raw body before parsing, compare in constant time, and reject anything older than five minutes.

Do I need the API if Champions already writes to my CRM?

No, and most teams never touch it. Champions can write each play back to HubSpot and route alerts into Slack channels and owner DMs with no app to install and no logins for reps. Reach for the API when you want the signal somewhere that is not the CRM: a data warehouse, a lead router, a scoring model, an internal app, or an AI agent through the hosted MCP server.

Can I send Champions my own contact list instead of connecting a CRM?

Yes. POST /v1/contacts takes up to 1,000 people a call and deduplicates on CRM id, then LinkedIn URL, then email, then name and company. Anyone with a LinkedIn URL starts tracking immediately. Teams usually start with closed-won contacts, power users and promoters rather than every name in the table, because those are the relationships that convert when they move.

Get a workspace and a key.

Book a demo and we will run detection against your own contacts, then walk through the delivery mode that fits your stack and issue a key you can start building against the same day.