v1API Reference · base URL https://api.intentiq.dev/v1 · 99.97% uptime over 90d
Developers · API v1

One endpoint.
Any company. Explicit coverage.

POST a domain — get back a coverage-aware 0–100 score, four dated intent triggers, account context, an action, and an AI summary. Scores below 60% reliable trigger coverage return 422 with a null score.

P50 latency
412ms
P99 latency
2.84s
Uptime · 90d
99.97%
Cache hit
71%

Quickstart

Get a real score back in under a minute. You'll need an API key (Settings → Developers) and a domain you want to score. Everything else is a single POST.

1. Get an API key

Open Settings → Developers and click Create key. Keys are shown once on creation, then stored as SHA‑256 hashes on our side — copy it into your secret manager. Test‑mode and live keys are separate: test keys are prefixed iiq_test_ and don't deduct credits.

2. Score your first account

Pick a domain. We'll fetch four intent triggers (funding, hiring, non-funding news, and dated technology changes), collect Web and GitHub context, compute coverage, and write back an AI summary. A personalized six-hour cache makes repeat scores free.

Use the apex domain. Send stripe.com. Schemes, paths, and a leading www. are normalized, but arbitrary subdomains are not guessed back to an apex.

3. Hook it up

For real‑time pipelines, subscribe to the score.computed webhook and let VesperWise push deltas to you. For batch enrichment, queue a bulk job and poll its status.

Authentication

All requests are authenticated with a bearer token in the Authorization header. Keys are tied to a workspace, not a user — rotate them when seat holders leave.

Header format

Pass the key as Authorization: Bearer <key>. Keys never appear in URL parameters; never log them. Workspace ID is inferred from the key.

Test vs live mode

Test keys return synthetic but plausible scores against a fixed set of well‑known domains. They never call upstream vendors, never deduct credits, and never fire webhooks. Test responses include "mode": "test" at the top level.

Rotation & revocation

Create the new key, deploy it, then revoke the old one — zero downtime. Revoked keys 401 within ~5 seconds. We also auto‑revoke a key if we detect leakage on GitHub or a public paste.

Errors

Standard HTTP status codes. The body is always JSON with a type, a stable code, and a human message. We never leak credentials, request bodies, or stack traces.

400invalid_request
The request was malformed — usually a missing field or a bad domain. The field property tells you which.
401unauthorized
Key missing, malformed, or revoked. Don't retry — fix the key first.
402insufficient_credits
Out of credits for the cycle. The resets_at field tells you when the next cycle begins.
403restricted_use
Returned when scoring inputs trigger our AUP filter (special‑category data, prohibited verticals).
404not_found
No object with that ID exists in your workspace. IDs are namespaced (scr_, job_, wl_).
409idempotency_conflict
You reused an Idempotency-Key with a different request body, or the same score run is still in progress.
422unscorable_domain
Domain resolves but has no usable signal surface — parked, defunct, or no public presence. Returns score null.
429rate_limited
Back off and retry. The Retry-After header gives you the seconds to wait.

Retry policy

Retry idempotent requests with exponential backoff: 1s, 2s, 4s, 8s — five attempts max. 5xx and 429 should retry; 4xx (except 429) should not.

Rate limits

Limits are per workspace, applied at the edge. If you're hitting them, you almost certainly want the bulk endpoint instead of a tight loop.

Scoring · singlePOST /v1/score
60 req/min on Starter, 300 req/min on Team, 1,200 req/min on Scale. Per‑workspace, sliding window.
Scoring · bulkPOST /v1/score/bulk
10 concurrent jobs per workspace; up to 1,000 domains per job. Jobs over 100 domains are eligible for our overnight cache window (50% credit discount).
ReadsGET *
600 req/min on all plans. Cache‑backed; cheap.
Webhooks · deliveryoutgoing
Up to 10,000 events/hour outbound to your endpoint. We retry failed deliveries 8 times over 24 hours with exponential backoff.

Every response includes X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers.

Pagination

Cursor‑based on all list endpoints. Pass limit (max 100, default 20) and cursor (from the previous response's next_cursor).

dataarray
The objects on this page, ordered by created_at descending.
has_moreboolean
True if more results are available — fetch the next page with next_cursor.
next_cursorstring · nullable
null when there are no more results.

Idempotency

POST /v1/score accepts an Idempotency-Key header. The key is atomically bound to its request and completed response, so retries cannot double-charge.

Use any unique string up to 255 chars (UUIDv4 is fine). Reusing a key with a different request returns 409 idempotency_conflict. Completed replays return the original response with an Idempotency-Replayed: true header.

POST/v1/scoreScore an account

Compute or retrieve a personalized score for a domain. Results are cached for six hours by user, canonical domain, profile fingerprint, and scoring version; cache hits are free.

200 ok422 unscorable_domain402 insufficient_credits429 rate_limited

Body parameters

domainrequiredstring
Apex domain to score. Schemes, paths, and a leading www. are normalized.
Example: stripe.com
companyoptionalstring
A display name for the response and reasoning. Provider evidence is always resolved and cached by canonical domain, not this caller-supplied label.

Optional header

Send Idempotency-Key: <unique value> to make retries exact-once. The endpoint accepts keys up to 255 characters.

Returns

A Score object. The HTTP response also carries X-IIQ-Cache: hit|miss for billing attribution. Unscorable domains return 422, intent_score: null, and are not charged or written to score history.

Request
curl -X POST https://api.intentiq.dev/v1/score  \
  -H "Authorization: Bearer $IIQ_KEY"  \
  -H "Content-Type: application/json"  \
  -d '{ "domain": "stripe.com" }'
200 OK · POST /v1/score1.42s · cache miss
{
  "score_id": "scr_01HZ9X3F7QMHN4T",
  "domain": "stripe.com",
  "intent_score": 84,
  "score_band": "HOT",
  "score_status": "complete",
  "data_coverage": 1,
  "scoring_version": "v2-linear-2026-07",
  "icp_fit_score": 100,
  "signals": {
    "funding": { "score": 24, "max": 25, "status": "ok" },
    "hiring": { "score": 17, "max": 20, "status": "ok" },
    "news": { "score": 15, "max": 20, "status": "ok" },
    "technology": { "score": 16, "max": 20, "status": "ok" }
  },
  "recommended_action": "Reference Series H. Anchor on RevOps.",
  "ai_summary": "Stripe is showing fresh capital and aggressive RevOps hiring...",
  "cached": false,
  "charged": true
}

GET/v1/score?domain=…Compatibility score request

Legacy GET wrapper over the same scoring service as POST. It uses the same cache, exact-once charging, coverage rules, and response shape. New integrations should use POST.

200 ok422 unscorable_domain

Query parameters

domainoptionalstring
The canonical company domain. Recommended for reliable matching.
companyoptionalstring
Compatibility fallback when no domain is supplied. We derive a best-effort .com domain, so explicit domain is safer.

POST/v1/score/bulkBulk score job

Submit up to 1,000 domains per job. Returns a job_ ID you can poll, or supply a webhook_url and we'll POST score.bulk.completed when the run finishes.

202 accepted400 invalid_request402 insufficient_credits

Body parameters

domainsrequiredarray<string>
1–1,000 apex domains. Duplicates are collapsed before billing.
deferredoptionalboolean
If true, runs in our overnight cache window for a 50% credit discount. Results return within 8 hours.
Default: false
webhook_urloptionalstring
POST target for the score.bulk.completed event. We sign the payload — see verify signatures.
tagoptionalstring
Free‑form label echoed back in webhook payloads — useful for correlating with your queue.

POST/v1/people/scoreScore a person

Score the human behind the logo. Provide an email or a LinkedIn URL — we enrich via Apollo with PDL fallback, then layer the underlying account's intent on top.

200 ok422 unresolvable_person403 restricted_use

Body parameters

emailone ofstring
Work email. Free‑mail providers (Gmail, etc) return 422 unresolvable_person.
linkedin_urlone ofstring
Canonical LinkedIn profile URL — must include the /in/ path segment.
role_hintoptionalstring
Disambiguator when a name resolves to multiple people. Examples: "RevOps", "Founder".

GET/v1/watchlistsList watchlists

Paginated list of watchlists in your workspace, ordered by most recently updated. Each entry includes a count of accounts and the current band distribution.

200 ok

Query parameters

limitoptionalinteger · 1–100
Page size.
Default: 20
cursoroptionalstring
Pagination cursor from the previous response.

POST/v1/watchlistsCreate a watchlist

Pin up to 250 accounts (Starter), 1,000 (Team), or 10,000 (Scale). The moment any account crosses your band threshold, we fire account.band_changed.

Body parameters

namerequiredstring
Display name. Max 80 chars.
accountsoptionalarray<string>
Initial set of domains. Same as calling PUT /accounts after create.
alert_thresholdoptionalenum
When to fire account.band_changed.
  • "hot_entry" (default)
  • "any_band_change"
  • "score_delta_10"

PUT/v1/watchlists/{id}/accountsAdd accounts to a watchlist

Idempotent — adding a domain that's already on the list is a no‑op, not an error. Returns the full updated account list.

DELETE/v1/watchlists/{id}/accountsRemove accounts from a watchlist

Removes the domains in accounts from the watchlist. Does not delete the underlying score history.

Webhooks

We POST events to your endpoint as JSON. Deliveries are signed (HMAC‑SHA256), at‑least‑once, and retried on non‑2xx with exponential backoff for 24 hours. Subscribe in Settings → Webhooks.

Delivery contract

Respond 2xx within 5 seconds — do the work asynchronously. We send User-Agent: VesperWise-Webhook/1.0 and a X-IIQ-Signature header you should verify. Events carry a delivery_attempt integer so you can dedupe.

Local development. Point a webhook at the VesperWise CLI (iiq webhooks listen) — it tunnels deliveries to http://localhost:3000/webhooks without ngrok.

Event types

score.computed
Fires every time a score is computed — both fresh cold‑cache misses and forced refreshes. The most common event in the system.
~6/sec p50
score.bulk.completed
Fires when a bulk job finishes. Payload includes the full result array and any unscorable domains separated out.
on demand
account.band_changed
A watchlist account crossed a band threshold (e.g. WARM → HOT). The flagship "now is the time" event for sales workflows.
~40/day p50
signal.spike
A single signal (e.g. funding) for a watchlist account jumped >25 points week‑over‑week. Often precedes a band change by 24–48h.
~12/day p50
person.scored
A previously unknown person was successfully resolved and scored. Pair with your CRM enrichment flow.
on demand
credits.low
Your workspace dropped below 10% of the cycle's credit allocation. Fired once per cycle.
≤1/month
Webhook
{
  "id":       "evt_01HZ9X3FK8M2P",
  "type":     "score.computed",
  "created":  "2026-05-28T14:02:11Z",
  "data": {
    "score": { /* Score object */ },
    "trigger": "watchlist_refresh"
  },
  "delivery_attempt": 1
}
POST → your.app/iiqX-IIQ-Signature: t=…,v1=…

Verify webhook signatures

Every delivery includes X-IIQ-Signature: t=<timestamp>, v1=<hmac>. Reject any request where the timestamp is older than 5 minutes (replay protection) and the HMAC does not validate against your signing secret.

The signing secret is shown once when you create the webhook endpoint, then stored hashed on our side — rotate it from the Webhooks settings page. The signed payload is the raw request body, not the parsed JSON.

The Score object

The canonical scoring-v2 response shape returned by POST /v1/score and its GET compatibility wrapper.

score_idstring
Stable persisted score ID on scorable results.
domainstring
Apex domain, lower‑cased.
intent_scoreinteger · 0–100, nullable
null when eligible trigger coverage is below 60%.
score_bandenum
One of "HOT" (≥75), "WARM" (50–74), "COLD" (<50), or null.
score_statusenum
complete, partial, or unscorable.
data_coveragenumber · 0–1
Eligible trigger weight divided by total trigger weight. Stale last-known-good evidence contributes half weight.
scoring_versionstring
v2-linear-2026-07 for the new linear model.
icp_fit_scoreinteger · 0–100, nullable
Separate fit score from verified industry (60%) and employee-range alignment (40%).
signalsobject
Four scored triggers plus Web and GitHub context. See Signal for shape.
contributionsarray
Auditable base/effective weight, normalized value, freshness, and contribution for each intent trigger.
recommended_actionstring
Schema-validated recommended action, with a deterministic fallback if the AI call fails.
ai_summarystring
Bounded score reasoning, with deterministic fallback.
cachedboolean
true if served from cache; false if computed fresh.
chargedboolean
true only when this request atomically consumed a credit.

The Signal object

Funding, Hiring, News, Technology, Web, and GitHub are returned inside signals. Only the first four can contribute to intent_score; Web and GitHub are context only.

scorenumber
Raw provider score, normalized against max before freshness and weighting.
statusenum
ok, no_signal, not_found, stale, or unavailable.
observed_attimestamp · nullable
Actual event date used for freshness. Positive v2 evidence without a valid date cannot contribute.
evidencearray
Structured observations backing the signal, including source metadata.
sourcestring
Upstream vendor that provided the data.
fetched_attimestamp · ISO 8601
When this signal was last fetched from its source.
source_urlURL · optional
Source page for an individual evidence item, when available.

The Person object

Returned by POST /v1/people/score and in person.scored webhook payloads.

idstring
Stable person ID. Prefixed prs_.
namestring
Full name as resolved from the enrichment provider.
emailstring
Work email. May differ from the input if resolved via LinkedIn.
titlestring
Current job title at the resolved company.
companystring
Company domain. Use this to retrieve the underlying Score object.
intent_scoreinteger · 0–100
The underlying company intent score at resolution time.
seniorityenum
One of "ic", "manager", "director", "vp", "c_level".
resolved_attimestamp · ISO 8601
When this person was last enriched.

Official SDKs

Thin wrappers around the REST API with retry/backoff, typed responses, and idiomatic ergonomics. Source on GitHub; bug reports welcome.

Node / TypeScript@vesperwise/node
Node 18+, fully typed. npm i @vesperwise/node.
Pythonintentiq
Python 3.10+, sync and async clients. pip install intentiq.
Gogithub.com/intentiq/go-sdk
Go 1.21+, context‑aware, zero deps outside stdlib + x/sync.
CommunityRuby · PHP · Elixir
Maintained by customers. Listed on our community SDKs page; we don't guarantee parity.

API changelog

We version the API by URL prefix (currently /v1). Breaking changes ship under a new version with at least 12 months of overlap. Additive changes ship anytime.

2026‑05‑12additive
Added include=people expansion on POST /v1/score. Added signal.spike webhook event.
2026‑03‑04additive
deferred option on bulk jobs (50% credit discount, 8h SLA). New credits.low webhook.
2026‑01‑22behavior
Default cache freshness moved from 14d to 7d across all plans. X-IIQ-Cache response header added.
2025‑11‑08v1 stable
API marked stable; SLAs in effect. Frozen surface area for the next 12 months.
Questions? developers@intentiq.dev