Read API

Read-only access, with an organisation key, to your account’s AI governance records.

Available on the M2 and M3 plans and during the trial. The organisation Owner creates and revokes the key from Settings → API.

Authentication

Every request carries the organisation key in the Authorization header, as a Bearer token.

Authorization: Bearer alx_live_…

The key starts with alx_live_. Its first 17 characters are the prefix shown in Settings → API. The full key is shown only once, when it is created: Alethexis stores only its SHA-256 fingerprint and cannot show it to you again.

A revoked key stops working on the next request.

A missing, unknown, wrong or revoked key gets the same 401 response: the API does not tell these cases apart.

Quota

Each key allows 120 requests per minute and 10,000 per day.

Every authenticated response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. When the quota runs out, the response is 429 with Retry-After, in seconds.

In addition, each IP address allows 60 requests every 10 seconds. Above that, requests are blocked for 10 seconds.

Pagination

List responses return an object with data and next_cursor.

limit ranges from 1 to 200; the default is 50.

For the next page, repeat the request with cursor set to the next_cursor you received. On the last page, next_cursor is null.

The cursor is opaque: do not build or modify it.

Errors

All errors share one shape: an error object with code and message. The message is technical English; the code is stable.

StatusCodeMeaning
400invalid_cursorThe cursor is not valid for that resource.
400invalid_parameterA parameter does not exist, is repeated or has a value that is not accepted.
401invalid_api_keyMissing, unknown, wrong or revoked key.
403module_requiredThe organisation has neither M2 nor M3 active.
404not_foundThe resource does not exist.
429rate_limitedThe key’s quota is exhausted.
500internalInternal error.

Resources

All of them accept limit, cursor and since (an ISO 8601 date with time zone).

GET /api/v1/systems

The organisation’s inventory of AI systems.

Filters: status

Fields: id, name, provider_name, status, is_agentic, created_at, updated_at

GET /api/v1/classifications

Risk classifications for each system, with their history.

Filters: system_id, current

Fields: id, system_id, level, is_current, classified_at

GET /api/v1/controls

Assigned controls and their status, per system or for the whole organisation.

Filters: system_id, status

Fields: control_id, code, title, module, kind, system_id, status, updated_at

GET /api/v1/evidence

Evidence index: category, size and date. The file itself is not included.

Filters: system_id

Fields: id, section_id, category, size_bytes, uploaded_at

GET /api/v1/sections

Status of each section of the platform.

Filters: status

Fields: section_id, module, status, updated_at

GET /api/v1/approvals

Submission, review, approval, rejection, replacement and declaration acts, with their fingerprint.

Filters: subject_type

Fields: id, subject_type, subject_id, subject_version, action, decided_at, content_hash, statement_version

GET /api/v1/audit-log

The organisation’s audit log, with the chained fingerprint of each row.

Filters: action

Fields: id, action, family, table_name, record_id, occurred_at, source, row_hash

GET /api/v1/requests

AI use requests that have been submitted. Drafts are not included.

Filters: status

Fields: id, status, tool_name, provider_name, urgency, involves_personal_data, submitted_at, decided_at, system_id

What the API does not return

The API returns no data about people and no hand-written text. Per resource, it leaves out:

  • systems: who created or edited the system, the person named in the prohibited-practices check, the purpose and the notes.
  • classifications: who classified it and the written justification.
  • controls: the assigned owner and approver, the notes and the links to evidence files.
  • evidence: the file, its storage path, the title, the description and who uploaded it.
  • sections: the review reason and its details.
  • approvals: who recorded the act, their roles and the comment.
  • audit-log: who acted, the IP address, the browser and the values before and after the change.
  • requests: who raised, reviewed or decided it, the purpose, the intended users, the notes on personal data, the decision comment and the comments.

Webhooks

A webhook notifies your system as soon as something happens, without polling the API. The Owner registers them in Settings → Webhooks: an https URL, the events you want and a secret shown only once.

Every delivery is a POST with this body:

{
  "id": "0f3a…",                 // Alethexis-Event-Id
  "type": "section.closed",
  "created_at": "2026-09-18T09:12:03.412Z",
  "api_version": "v1",
  "data": { "account_id": "…", "section_id": "GOB-2", "status": "complete", "closed_at": "…" }
}

Headers: Alethexis-Signature, Alethexis-Event-Id, Alethexis-Event-Type and User-Agent: Alethexis-Webhooks/1.

Signature

The v1 value is the HMAC-SHA256 of the timestamp, a dot and the exact body you receive, using the endpoint secret. Compare in constant time and reject anything more than five minutes old.

Verification:

// Node.js
import { createHmac, timingSafeEqual } from 'node:crypto';

export function verify(rawBody, header, secret, toleranceSeconds = 300) {
  const [t, v1] = header.split(',').map((part) => part.split('=')[1]);
  if (Math.abs(Date.now() / 1000 - Number(t)) > toleranceSeconds) return false;

  const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest();
  const received = Buffer.from(v1, 'hex');
  return expected.length === received.length && timingSafeEqual(expected, received);
}
# Python
import hashlib, hmac, time

def verify(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    if abs(time.time() - int(parts["t"])) > tolerance:
        return False

    expected = hmac.new(
        secret.encode(), f'{parts["t"]}.'.encode() + raw_body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, parts["v1"])

Retries

A 2xx response is a good delivery; anything else, or a 10-second timeout, is a failure. Retries happen after one, two, four, eight and sixteen hours; after the fifth attempt the delivery is exhausted and you can replay it by hand from Settings → Webhooks. Exhausted deliveries appear in Alethexis’s internal daily report.

Idempotency

Alethexis-Event-Id identifies the event, not the attempt: a retry repeats the same identifier. Keep the ones you have processed and discard repeats. The same event arrives once per subscribed endpoint.

v1 catalogue

v1 has eight events and the list is closed: a new event ships with a new catalogue version.

EventWhen it is emitteddata fields
section.closedWhen a section is closed.section_id, status, closed_at
section.reopenedWhen a section is reopened or moves to review.section_id, status, reason_key, reopened_at
system.createdWhen an AI system is added.system_id, system_name, created_at
system.reclassifiedWhen a new classification is recorded for a system.system_id, system_name, classification_from, classification_to, version, classified_at
risk.high_registeredWhen a high inherent risk (15 or above) is recorded, or a risk crosses that threshold.risk_id, system_id, system_name, risk_category, inherent_score, risk_level, registered_at
approval.registeredWhen an approval is recorded.approval_id, subject_type, subject_id, subject_version, content_hash, decided_at
evidence.uploadedWhen evidence is uploaded or a PDF is registered as evidence.evidence_id, section_id, system_id, category, size_bytes, uploaded_at
request.decidedWhen an AI use request is decided.request_id, status, tool_name, system_id, decided_at

What a webhook never carries

Payloads carry identifiers, states, dates and the name of the system or tool. They never carry who performed the action, their roles, comments, hand-written reasons or email addresses. If your system needs the detail, read it afterwards with the API.

Receiver requirements

The URL must be https and resolve to a public address: webhooks are not delivered to private or internal addresses. Respond within 10 seconds; if you need longer, accept the delivery and process it afterwards.

Versions

v1 is a contract: its responses do not change in incompatible ways. An incompatible change ships as a new version, v2.

OpenAPI specification

The OpenAPI 3.1 specification is generated from the same schemas that validate every response: /api/v1/openapi.json

Example

curl -H "Authorization: Bearer $ALETHEXIS_API_KEY" \
  "https://www.alethexis.com/api/v1/systems?limit=50"