Changelog
API-level changes only. Internal refactors, scraper updates, and infra changes don’t appear here unless they surface in a response shape or error behaviour.
Versioning policy
Section titled “Versioning policy”All /v1/* endpoints are maintained indefinitely. We do not silently
retire versioned routes. Future major versions will be introduced under a
new path (/v2/*), with v1 and v2 running in parallel for a minimum of
12 months after v2 launch. Migration guidance lands in this changelog at
v2 launch.
Breaking-change policy
Section titled “Breaking-change policy”- Breaking changes always land behind a new URL path (
/v2/...) — we never break a stable endpoint’s response shape in place. - Additive changes (new fields, new endpoints, loosened validation) ship at any time without a version bump.
- Deprecation flow for individual fields inside a stable version:
Deprecation: trueheader on affected responses the day the field is marked.Sunsetheader (RFC 9745) with the removal date, minimum 90 days out.- Changelog entry below with the migration guidance.
- For agents:
Link: …; rel="deprecation"surfaced alongside the header.
1.12.0 — 2026-09-20
Section titled “1.12.0 — 2026-09-20”Additive on the wire. One of these changes what as_of answers for a small set of
licences — read the first bullet if you use point-in-time.
as_ofno longer serves back events we withdrew. Newknowledgevalue:corrected, withstatus_as_of: nulland acorrectionobject (changed_at,withdrawn_status,corrected_at,note). The 93 revocations we had inferred from a licence dropping off a register were corrected on 2026-09-17 (see 1.10.0), but history is never deleted here, andas_ofread the latest event without checking whether it had been withdrawn — so a date inside one of those windows still answeredrevoked/observed. It now answerscorrected. We do not fall back to the previous status either: that would reportactivefor a period in which the register did not list the licence. If you branch onknowledge === 'observed', nothing changes for you —correctedlands in your “unknown” path, which is where it belongs. See point-in-time./v1/check—_meta.source_urlnow cites the page that published the matched licence’s status, as/v1/licenses/{id}has since 1.11.0. For most licences that is still the register. Where it is not — a Curaçao revocation from the CGA Enforcement Register, an MGA status from the company’s verification page, a certificate-backed Curaçao licence — you now get that page instead of a register that never said so.- Fixed:
?as_of=on/v1/checkwas silently ignored. The parameter has been documented since point-in-time shipped, but on/v1/check— and therefore on the MCPcheck_domaintool — it never reached the handler: the response carried noas_ofobject andmatch.statuswas simply today’s./v1/operators/{slug}and/v1/licenses/{id}were not affected. It works now, and a malformed or futureas_ofanswers400 invalid_queryinstead of200. If you relied on/v1/check?as_of=for a historical verdict, you were reading the current status — check for theas_ofobject in the response and re-run those lookups. - MCP:
get_operatorreturnslicense_idon each licence. It is the UUIDget_licenseandget_license_historytake, and no tool returned it, so those two were unreachable from an agent. Their descriptions now cover the provenance fields (status_source_url,status_observed_at,not_listed_since,last_listed_at) and corrected history events.
1.11.0 — 2026-09-18
Section titled “1.11.0 — 2026-09-18”Additive. Every status now says where it came from, and one billing bug is fixed.
- Per-status provenance on licences —
status_source_url,status_observed_atandlast_listed_atonGET /v1/licenses/{id}and on the licences insideGET /v1/operators/{slug}. A licence’s register (source_url) is often not where its status was published: a Curaçao revocation comes from the CGA Enforcement Register, an Anjouan suspension from the Authority’s suspended-licences page, a Kahnawake termination from an advisory notice.status_source_urlis that page;last_listed_atis when we last saw the licence in its register. _meta.source_urlfor a licence now cites the status’s source (falling back to the register when they are the same page). If you show “source” next to a status, this is the URL that actually published it.- Evidence on history events —
GET /v1/licenses/{id}/historygainssnapshot_sha256andsnapshot_fetched_at: the hash of the bytes we read and when we first read them. It is the hash, not a file — checkable against your own copy of the regulator’s page.nullwhere we hold no stored copy — we started keeping the bytes on 2026-09-17. - Enforcement statuses are read from the regulator’s own publication on every
run, in every jurisdiction that has one: AGA’s revoked / suspended lists, the
CGA Enforcement Register, KGC’s advisory notices, the MGA enforcement register.
Tobique publishes none, so a Tobique licence can only ever be
activeornot_in_register. A “Notice of Cancellation” from the MGA is a notice, not a cancellation, and changes no status. - MGA statuses are now read, not assumed. Every MGA licence used to be
activebecause the register “only lists live licensees”. We now read the per-licence word the MGA publishes:Licensed/Approved→active,Surrendered→surrendered,Expired→expired,Suspended/Voluntary Suspended→suspended(verbatim wording inupstream_status). A handful of MGA licences changed status as a result. A word we have never seen maps tounknown, never to a default. GET /v1/operators/search—_meta.scraped_atis now when we last read a register about that operator (its freshest licence check). It used to be the operator row’s own timestamp, which only moves on a rename — so it claimed April for records verified three days ago._meta.freshness_rangefollows.- Fixed: authenticated calls to
/v1/operators/{slug},/v1/licenses,/v1/watchlistand/v1/webhookswere metered twice (fixed 2026-09-16). The per-second limit behaved as half your plan’s, and monthly quota was consumed 2× on those routes. If you saw unexplained 429s on operator lookups, that was us. September usage counters for affected keys may still read high until the 1 October reset.
1.10.0 — 2026-09-17
Section titled “1.10.0 — 2026-09-17”Additive on the wire, but two status values changed on existing records. If you
store our status verbatim, re-read the licences you hold.
- Two new
license_statusvalues:not_in_registerandsurrendered. Neither is a revocation, and neither isactive.not_in_register— the regulator’s public register no longer lists the licence, sincenot_listed_since. Registers drop rows for many reasons — expiry, surrender, renumbering, a bad day on their side — and none of them is published, so we do not publish one either.surrendered— the operator gave the licence up. UKGC publishes it verbatim (“Surrendered”); Kahnawake calls it a voluntary termination.
- What changed on existing records. Licences that had dropped off a register were
reported as
revoked. That asserted an enforcement decision no regulator had made, and it was wrong: 80 licences across six jurisdictions carried it. They are nownot_in_register, except the seven where a regulator does publish an explicit decision (those arerevokedorsuspended, sourced to that publication). Separately, 627 UKGC licences published asSurrenderedwere mapped torevokedand are nowsurrendered. Every corrected record’s history says what happened and when, and the superseded event is kept and marked corrected rather than deleted. - If you branch on status,
if (status === 'revoked') block()now misses these values — by design, because they are not revocations. Approve only onactiveand route the rest to a human.GET /v1/licenses/{id}/historygainsnote,corrected_atandcorrection_note; licences gainnot_listed_since. upstream_statusisnullwhen a licence isnot_in_register: there is no current regulator wording for a licence the register does not list, and repeating the last one we read produced pages that said “revoked · upstream: valid”.
1.9.0 — 2026-07-16
Section titled “1.9.0 — 2026-07-16”All additive — no breaking changes.
verification_urlon/v1/check. Where the regulator publishes a per-domain verification page — a Curaçao certificate (cert.cga.cw) or a Tobique validation seal (validate.thetgc.ca) —match.verification_urllinks straight to it, so you can confirm any verdict against the primary source in one click.nullwhere the regulator has no such page (UKGC, MGA, KH, AN). We re-read every stored verification page nightly and movedomain_statusonly on a clean read of the regulator’s own words — in both directions, never on a guess.- Multi-jurisdiction domains:
jurisdictions[]on/v1/check. The data model is now many-to-many — a brand can be licensed by two legal entities in two jurisdictions at once (spinsup.com: Anjouan + Tobique; me88.com: Anjouan active + Curaçao). When a matched domain has more than one (operator, jurisdiction) pair, the response carries a best-firstjurisdictions[]array with per-linkstatus,domain_status,domain_association, andverification_url. Per-link status means “delisted in Curaçao” can no longer clobber “active in Anjouan” — both are true at once, and now both are visible. Absent for ordinary single-licence domains. - Curaçao domain coverage 0% → 91%. Curaçao publishes no bulk domain list, so we
built the harvest around the regulator’s own verification portal: the CGA
/tokenregistry (which lists each licence’s complete approved-domain cluster — and is more complete than the published OGL PDF) plus archived certificate URLs. ~3,500 CW domains loaded, every one carrying its regulatorverification_url, kept fresh by the nightly re-verification pass and a weekly/tokendiscovery sweep. - Tobique domain coverage 0% → live. Same authoritative-seal approach against
validate.thetgc.ca: one seal page lists the licence’s whole registered-website cluster. Weekly headless-render discovery finds new seals (they’re JS-injected in casino footers, invisible to plain fetches); the nightly shared verifier keeps every found seal current. Coverage is growing run by run — see coverage methodology. - MCP
check_domainoutput now includesverification_urland (when present) the compactjurisdictions[]array, so agents cite the regulator’s own page and never phrase a single-register verdict about a dual-licensed brand. - OpenAPI:
CheckMatch.verification_url, theDomainJurisdictionschema andCheckResult.jurisdictionsare documented in the spec.
1.8.0 — 2026-05-29
Section titled “1.8.0 — 2026-05-29”All additive — no breaking changes.
- Batch domain check. New
POST /v1/check/batch(authenticated) resolves up to 100 domains in one request; each result mirrors the single-check shape,checked_jurisdictionsis returned once at the top, and a malformed hostname comes back as a per-rowerrorwithout failing the batch. See Batch domain check. match_absence_reason+checked_jurisdictionson/v1/check. Whenmatchisnull, the response now says why —generic_term(ambiguous label) vsno_record_found(checked, not present) — and lists the exact registers checked, so a “not licensed” verdict is scoped to coverage, never absolute. Present only on a miss. See Confidence scoring.- Point-in-time lookups (
?as_of=)./v1/check,/v1/licenses/{id}, and/v1/operators/{slug}accept?as_of=to reconstruct a licence’s status as of a past date from transition history — strictly within our observation window (knowledge: observed | before_tracking | no_such_license; never extrapolated beforetracking_since; future dates400). See Point-in-time lookups. domains[].associationon operator detail.GET /v1/operators/{slug}now returnsdirect/white_labelper domain (the column was always populated; the field was missing from the response)./v1/checkdomain matching now collapses www / apex / subdomain variants via the Public Suffix List (registrable-domain fallback after exact-host), sovirginbet.comandwww.virginbet.comresolve to the same operator. Single-owner guard prevents over-matching shared white-label platform domains.- Rate-limit headers on authenticated responses.
X-RateLimit-Limit/-Remaining/-Reset+X-Upgrade-URLnow accompany authenticated/v1/*responses, not just the public path. - OpenAPI completeness.
GET /v1/operators/{slug}/regulatory-actionsandGET /v1/health/coverageare now in the spec;ApiError.codedocumentspayment_required+quota_exceeded. - MCP server caught up to the API. New tools
check_domain_batch,get_operator_regulatory_actions,check_coverage;check_domain+get_operatoracceptas_of. Tool output is now lean (a compact verdict, not the full REST payload) — andmatch_absence_reason+checked_jurisdictionsare preserved on a miss, so agents never collapse “not found” into “unlicensed”. See MCP server.
1.7.0 — 2026-04-29
Section titled “1.7.0 — 2026-04-29”- Tobique Gaming Commission (TGC) re-enabled — 6th jurisdiction live. The Cloudflare Worker proxy at
igregulator-scraper-proxy.scvgr-agent.workers.devnow bridges btc → thetgc.ca, bypassing the IP-reputation block that paused us in 1.6.1. Same scraper code, same daily 04:15 UTC slot — only the transport changed (HMAC-signed POST to the Worker, the Worker fetches the upstream CF→CF). Scraper opts in viaUSE_PROXY=true; other scrapers unaffected. - New
@igregulator/scraper-utilspackage carrying the reusableproxyFetchhelper. Future jurisdictions whose upstream blocks our IP can opt in by setting two env vars (USE_PROXY=true,PROXY_HMAC_SECRET=…) and adding their hostname to the Worker’sALLOWED_DOMAINSlist — no code change in the scraper itself. - Migration 0018 re-inserts the TGC jurisdiction row removed in 0017.
1.6.1 — 2026-04-28
Section titled “1.6.1 — 2026-04-28”- Tobique Gaming Commission (TGC) ingestion deferred. PR #119 shipped the scraper, but the upstream
thetgc.cablocks the production origin IP at the Cloudflare edge (HTTP 403 across all UAs and header shapes). Other hosts return 200; this is an IP-reputation block on the Hetzner range. We’ve surgically rolled the public surfaces back to 5 jurisdictions while the scraper code stays merged. Re-enables cleanly once we land a Cloudflare Worker proxy onscvgr-agent.workers.dev. Investigation + path-forward documented in docs/scrapers/tobique-investigation.md.
1.6.0 — 2026-04-28
Section titled “1.6.0 — 2026-04-28”- Tobique Gaming Commission (TGC) added — 6th jurisdiction. (Reverted on the public surface — see 1.6.1. Scraper code stays merged for re-enable when the proxy lands.) ~160 licences ingested daily from thetgc.ca/license-holders/. License-type vocabulary
B2C/B2B. License numbers are synthesised asTGC/<TYPE>/<slug>(TGC doesn’t publish IDs, same convention as KH). Cron 04:15 UTC; regulatory tier shifted +15 min. The fuzzy + domain-exact match flow on/v1/checkincludes TGC automatically. - Domain coverage 0% out of the box for TGC. Same upstream-doesn’t-publish-websites bucket as Curaçao. Documented at /docs/coverage-methodology. Phase 4 WHOIS / Tranco enrichment will close this for both regulators in one pass.
1.5.0 — 2026-04-28
Section titled “1.5.0 — 2026-04-28”- Trust-signals + positioning sweep. New pages: /about, /terms, /privacy. Footer reorganised — Company column now lists About / Changelog / Terms / Privacy. Stale “MCP server (soon)” replaced with a live link.
- Legal disclaimer surfaced. Now rendered as a callout at the top of /docs and embedded in the OpenAPI spec’s top-level
info.descriptionso agents reading the spec see it. Same wording: results are informational, customers responsible for their own compliance decisions. - Hero copy iteration — pain-driven. “iGaming licensing intelligence API” → “Verify gambling operator licenses before they cost you.” Sub-copy mentions the buyer profiles (compliance teams, payment providers, affiliate networks) explicitly. Meta description, og:description, llms.txt opening line aligned. No data or endpoint changes.
1.4.0 — 2026-04-28
Section titled “1.4.0 — 2026-04-28”- MGA domain enrichment. The MGA scraper now fans out from each B2C license to the legacy
authorisation.mga.org.mt/verification.aspxpage and pulls the Website URL(s) field. Runs daily in the same 03:15 UTC slot as the primary register pass, ~30 s wall time at p-limit 5, no separate cron entry. Recovers ~110 B2C operator domains from a previous baseline of zero. B2B / CRP licenses are skipped — the upstream verification page omits the Website URL section for non-consumer-facing license classes. - Coverage methodology update.
/v1/health/coveragenow exposesdomain_coverage.{operators_with_domain, normative_operators, coverage_pct}per jurisdiction. The denominator is operators where domain disclosure is normative for their license type — excludes B2B-only types (CSPA, B2B, Non-Remote, Ancillary, supplier permits, etc.) that don’t have consumer-facing domains by design. Methodology change only; no data changes. Affected metrics under the new denominator:- AN — 98% of B2C operators have ≥1 domain
- CW — 0% (upstream ceiling, no scraper-side work possible — see scope doc)
- KH — 69% of Interactive Gaming Permit holders (gap is upstream non-disclosure)
- MGA — 0% pre-1.4.0 enrichment, ~90% post
- UKGC — 40% of
Remote-license operators (upstream meaningful ceiling, the rest don’t operate consumer sites)
- UKGC parser audit closed without code changes. Time-boxed re-audit confirmed the ~17.3% raw figure is 96% of the 482-operator meaningful ceiling (Active + White Label distinct accounts in
domain-names.csv). Inactive-domain rows aren’t ingested deliberately to avoid stale/v1/checkmatches. Findings documented in docs/scrapers/ukgc-domain-gap.md.
1.3.1 — 2026-04-28
Section titled “1.3.1 — 2026-04-28”- MCP server verified live in production. End-to-end smoke against
https://mcp.igregulator.io/mcp:tools/listreturns all 7 tools,check_domainresolves UKGC + AN matches with correct confidence hints,api_request_logrows taggedsource='mcp'. DNS via Cloudflare proxy, TLS via the existing Origin CA cert (extended to cover the new subdomain). Pricing page lists MCP support on Starter onwards. Claude Code path documented at /docs/mcp viaclaude mcp add --transport http.
1.3.0 — 2026-04-28
Section titled “1.3.0 — 2026-04-28”- MCP server live at
mcp.igregulator.io. Streamable-HTTP transport (current MCP spec, SSE for streaming). Seven tools exposed:check_domain,search_operators,get_operator,list_jurisdictions,get_jurisdiction,get_license,get_license_history. Bearer-token auth — same API keys as the direct HTTP API; tool calls forward to api.igregulator.io and count against your existing per-key quota. Setup walkthrough at /docs/mcp. Discovery manifests at/.well-known/mcp.jsonon all three iGregulator surfaces. api_request_log.sourcecolumn added. New requests are taggedhttpormcpso admin analytics can split MCP usage from direct-HTTP usage. No client-visible change.
1.2.0 — 2026-04-28
Section titled “1.2.0 — 2026-04-28”- Anjouan jurisdiction live. 5th regulator: Anjouan Gaming Authority (AGA), ~1,275 licences ingested daily from anjouangaming.com/license-register/. License-type vocabulary
B2C/B2B/White Labeling. The fuzzy + domain-exact match flow on/v1/checkincludes AN automatically — no client-side change needed. Coverage table at /docs/ refreshed. - Marketing copy correction. Hero / meta / llms.txt now say “Daily-refreshed … updated within 24 hours” instead of “Real-time.” The data was never real-time; the new wording matches what scrapers actually deliver (cron 03:00–04:00 UTC, depending on jurisdiction).
1.1.0 — 2026-04-28
Section titled “1.1.0 — 2026-04-28”- Flat field removal on
/v1/check. Top-level legacy keys (licensed,jurisdiction,license_number,operator,status,expires_at) removed along with theDeprecation/Sunset/Linkheaders. Readmatch.*instead. Removed ahead of the announced 2026-05-19 sunset because no customers are integrated against the flat shape yet. - Pre-launch surface gating.
trialkeys are now restricted to/v1/checkonly (1,000/day per-key cap). Other authenticated endpoints return402 payment_requiredwithdetails.reason=endpoint_requires_paid_plan. Behaviour lifts automatically oncePRELAUNCH_DAILY_CAP=0(post-Stripe). - Webhook retention 7 → 30 days.
webhook_deliverieshistory now matches thewebhook_eventsreplay window. Existing rows live longer immediately; nothing to migrate. - Generic-label blocklist on
/v1/checkwidened. Now substring-match instead of exact-match —bestcasino.com,casino-bonus.comreturnconfidence: lowlikecasino.comalready did. Licensed brands containing a generic keyword (bet365.com,pokerstars.com) still resolve via the domain hit before the gate runs. - OpenAPI spec documents the per-jurisdiction
license_typesvocabulary inline so client-side enums can be coded against the audited values (UKGCRemote/Non-Remote/Ancillary Remote, MGAType 1-4/B2B/B2C, CWB2C/B2B, KHInteractive Gaming Permit/CSPA).
1.0.0 — 2026-04-19
Section titled “1.0.0 — 2026-04-19”Initial public docs release.
- /v1/check response shape:
{ query, match, alternatives, confidence }.match.confidence∈high | medium | low,match_type∈domain_exact | trading_name_fuzzy | name_similarity,domain_association∈direct | white_label | null. - Public endpoints:
/v1/check,/v1/jurisdictions,/v1/operators/search. Rate-limited 10 req / IP / hour. - White Label ingestion live for UKGC — domains with
Status = 'White Label'in the UKGC register now load withassociation = 'white_label'on the domain row. - OpenAPI 3.1 spec available at api.igregulator.io/openapi.json (canonical source; Scalar + starlight-openapi both consume it). The old Swagger UI on
api.igregulator.io/docsnow 301-redirects to /docs/api/ — unified docs at /docs supersede it.
0.3.0 — 2026-04-17
Section titled “0.3.0 — 2026-04-17”- Regulatory actions surfaced on the operator detail page. Cross-jurisdiction feed: UKGC Public Register, MGA Decisions, CGA Warnings.
- New endpoint:
/v1/operators/:slug/regulatory-actions(authenticated).
0.2.0 — 2026-04-08
Section titled “0.2.0 — 2026-04-08”- Kahnawake Gaming Commission jurisdiction added.
- Curaçao scraper migrated to the post-LOK OGL PDF source.
- Licence category harmonisation:
remote | non-remote | ancillary | permit | other.
0.1.0 — 2026-03-28
Section titled “0.1.0 — 2026-03-28”- First operational release. UKGC-only coverage.
- Core schema:
operators,licenses,domains,jurisdictions. - Dashboard lives at
app.igregulator.io.