Skip to content

Status-change history for a licence

GET
/v1/licenses/{license_id}/history

Time-ordered status changes for one licence — active → suspended → revoked, etc. Most recent first. Each row cites the scraper run that detected the change and the regulator URL at the time. Aggregate _meta.freshness_range surfaces the oldest + newest change in the returned window so agents can answer “was there activity in the last 90 days?” in one request.

license_id
required
string format: uuid

History entries, most-recent first.

object
license_id
required
string format: uuid
license_number
required
string
history
required
Array<object>
object
id
string format: uuid
changed_at
string format: date-time
previous_status
One of:

Canonical licence status. Only active means “licensed right now”. Every other value is “not a pass” — but three of them are not enforcement decisions, and a rule that lumps them in with revoked publishes a claim about a real business that no regulator made.

not_in_register — the regulator’s public register no longer lists this licence, since not_listed_since. An observation about the register, not a decision by the regulator: licences drop off because they expired, were surrendered, were renumbered, or the page had a bad day. Nobody published a reason and we do not invent one. It is not a revocation. (Until 2026-09-17 we reported this as revoked; that was wrong, every affected record has been corrected, and its history says so.)

surrendered — the operator gave the licence up (UKGC publishes “Surrendered”; Kahnawake “voluntary termination … in good standing”). Also not an enforcement decision: nobody took it away. 627 UKGC licences carried revoked for this until 2026-09-17.

unknown — the register lists this licence and published a status we could not classify: a new phrase, a reworded sentence, a typo. We do not guess: two of our scrapers used to default an unreadable status to active, and that is precisely the bug this value exists to make impossible. Read upstream_status for the regulator’s own words.

None of these is a synonym for any other:

  • None of them means “not licensed” in the enforcement sense. Do not present them as such.
  • None of them means “probably fine”. Do not treat them as a pass.

A rule like if (status === "revoked") block() will silently let surrendered, not_in_register and unknown through. If your flow makes an automated allow/deny decision, approve only on active and escalate everything else to a human.

revoked and suspended are only ever set from a regulator publication we actually read — an enforcement register, a revoked-licences page, an explicit status column — never from a licence disappearing.

string
Allowed values: active suspended revoked expired pending unknown surrendered not_in_register
new_status

Canonical licence status. Only active means “licensed right now”. Every other value is “not a pass” — but three of them are not enforcement decisions, and a rule that lumps them in with revoked publishes a claim about a real business that no regulator made.

not_in_register — the regulator’s public register no longer lists this licence, since not_listed_since. An observation about the register, not a decision by the regulator: licences drop off because they expired, were surrendered, were renumbered, or the page had a bad day. Nobody published a reason and we do not invent one. It is not a revocation. (Until 2026-09-17 we reported this as revoked; that was wrong, every affected record has been corrected, and its history says so.)

surrendered — the operator gave the licence up (UKGC publishes “Surrendered”; Kahnawake “voluntary termination … in good standing”). Also not an enforcement decision: nobody took it away. 627 UKGC licences carried revoked for this until 2026-09-17.

unknown — the register lists this licence and published a status we could not classify: a new phrase, a reworded sentence, a typo. We do not guess: two of our scrapers used to default an unreadable status to active, and that is precisely the bug this value exists to make impossible. Read upstream_status for the regulator’s own words.

None of these is a synonym for any other:

  • None of them means “not licensed” in the enforcement sense. Do not present them as such.
  • None of them means “probably fine”. Do not treat them as a pass.

A rule like if (status === "revoked") block() will silently let surrendered, not_in_register and unknown through. If your flow makes an automated allow/deny decision, approve only on active and escalate everything else to a human.

revoked and suspended are only ever set from a regulator publication we actually read — an enforcement register, a revoked-licences page, an explicit status column — never from a licence disappearing.

string
Allowed values: active suspended revoked expired pending unknown surrendered not_in_register
change_type
string
Allowed values: created status_change renewed revoked data_update
source_url
string
detected_by
string
note

What we observed, in words — e.g. “No longer listed in the public register since 2026-08-04.”

string | null
snapshot_sha256

SHA-256 of the exact payload this change was read from, stored on our side. null for events recorded before 2026-09-17 and for runs whose snapshot could not be filed. Ask support for that payload by hash if you need to audit a claim — a hash you can check against your own copy of the regulator’s page is the point.

string | null
snapshot_fetched_at

When that payload was fetched from the regulator.

string | null format: date-time
corrected_at

Set when a later review found this event asserted something we had not read from the regulator. The event is kept for the audit trail; treat it as withdrawn and read correction_note.

string | null format: date-time
correction_note
string | null
_meta
required

Aggregate provenance envelope for list responses. Describes the freshness window of the returned rows.

object
freshness_range
required
object
oldest
required
string | null format: date-time
newest
required
string | null format: date-time
total_sources
required

Distinct upstream sources represented by this page of rows (roughly: distinct jurisdictions).

integer
{
"freshness_range": {
"oldest": "2026-04-18T03:00:00Z",
"newest": "2026-04-19T03:45:00Z"
},
"total_sources": 1
}

Missing / malformed / revoked API key.

object
error
required

Human-readable error summary.

string
code
required

HTTP-status-level class. Stable enum; branch on details.reason for finer control. Current values: invalid_query, invalid_slug, invalid_license_id, invalid_jurisdiction_code, invalid_pagination, not_found, auth_required, auth_invalid, auth_revoked, payment_required, quota_exceeded, rate_limited, server_error.

string
details
required
object
reason
required

Machine-readable refinement of the top-level code. Stable vocabulary; branch on this in clients. Examples: invalid_input, missing_required_parameter, conflicting_parameters, operator_not_found, license_not_found, jurisdiction_not_found, route_not_found, api_key_missing, malformed_header, api_key_invalid, api_key_revoked, quota_exceeded, internal_error.

string
field

Present only when the error maps to a specific request input field (query param, path param, body key). Omitted for errors that aren’t field-scoped (e.g. rate_limited, auth_revoked).

string
suggestion

Optional human-readable / agent-actionable hint describing how to resolve the error.

string
{
"reason": "api_key_revoked",
"suggestion": "Generate a new API key at https://app.igregulator.io/settings. Revoked keys cannot be restored."
}
{
"error": "API key has been revoked",
"code": "auth_revoked",
"details": {
"reason": "api_key_revoked",
"suggestion": "Generate a new API key at https://app.igregulator.io/settings. Revoked keys cannot be restored."
}
}

No row matched.

object
error
required

Human-readable error summary.

string
code
required

HTTP-status-level class. Stable enum; branch on details.reason for finer control. Current values: invalid_query, invalid_slug, invalid_license_id, invalid_jurisdiction_code, invalid_pagination, not_found, auth_required, auth_invalid, auth_revoked, payment_required, quota_exceeded, rate_limited, server_error.

string
details
required
object
reason
required

Machine-readable refinement of the top-level code. Stable vocabulary; branch on this in clients. Examples: invalid_input, missing_required_parameter, conflicting_parameters, operator_not_found, license_not_found, jurisdiction_not_found, route_not_found, api_key_missing, malformed_header, api_key_invalid, api_key_revoked, quota_exceeded, internal_error.

string
field

Present only when the error maps to a specific request input field (query param, path param, body key). Omitted for errors that aren’t field-scoped (e.g. rate_limited, auth_revoked).

string
suggestion

Optional human-readable / agent-actionable hint describing how to resolve the error.

string
{
"reason": "api_key_revoked",
"suggestion": "Generate a new API key at https://app.igregulator.io/settings. Revoked keys cannot be restored."
}
{
"error": "API key has been revoked",
"code": "auth_revoked",
"details": {
"reason": "api_key_revoked",
"suggestion": "Generate a new API key at https://app.igregulator.io/settings. Revoked keys cannot be restored."
}
}

Unexpected server error.

object
error
required

Human-readable error summary.

string
code
required

HTTP-status-level class. Stable enum; branch on details.reason for finer control. Current values: invalid_query, invalid_slug, invalid_license_id, invalid_jurisdiction_code, invalid_pagination, not_found, auth_required, auth_invalid, auth_revoked, payment_required, quota_exceeded, rate_limited, server_error.

string
details
required
object
reason
required

Machine-readable refinement of the top-level code. Stable vocabulary; branch on this in clients. Examples: invalid_input, missing_required_parameter, conflicting_parameters, operator_not_found, license_not_found, jurisdiction_not_found, route_not_found, api_key_missing, malformed_header, api_key_invalid, api_key_revoked, quota_exceeded, internal_error.

string
field

Present only when the error maps to a specific request input field (query param, path param, body key). Omitted for errors that aren’t field-scoped (e.g. rate_limited, auth_revoked).

string
suggestion

Optional human-readable / agent-actionable hint describing how to resolve the error.

string
{
"reason": "api_key_revoked",
"suggestion": "Generate a new API key at https://app.igregulator.io/settings. Revoked keys cannot be restored."
}
{
"error": "API key has been revoked",
"code": "auth_revoked",
"details": {
"reason": "api_key_revoked",
"suggestion": "Generate a new API key at https://app.igregulator.io/settings. Revoked keys cannot be restored."
}
}