Compliance API Documentation
Welcome to the Compliance API documentation. These endpoints connect a ChatGPT Enterprise workspace to Enkrypt, then keep reading its compliance logs and scanning every message through one of your saved guardrails.Purpose
Your workspace already records what people send to and receive from the assistant. A compliance scan turns that record into something you can audit: it polls the provider’s compliance export on a fixed interval, runs every message through the guardrail you name, and files the result — so you can answer “was anything sensitive pasted into ChatGPT last month” without asking anyone to forward a screenshot. A scan is a long-lived object, not a one-off job. You create it once, point it at a workspace and a start time, and it keeps up from there.cursor_end_time is where the provider’s own clock got to, and it moves forward on its own as the scan reads.
Message text is never stored. The findings, the counts and the coverage record are; the text of a message is fetched live from the provider when you deliberately ask for it with Get Compliance Message, and only while the provider still holds it.
Who can call these APIs
Every endpoint on this page — reads as well as writes — requires the governance officer role.- The role is granted per project, inside an organization, by an organization administrator.
- The organization owner is not exempt. An owner who has not been granted the role gets
403from every endpoint here, exactly like any other member. Compliance is the one part of the platform where owning the account does not by itself let you read it. - Individual accounts cannot hold the role, because there is no organization to grant it in. An individual account gets
403with a message saying so. Compliance is therefore an organization-only feature today.
403, ask an administrator of your organization to grant you the governance officer role on the project your API key belongs to — the role is the only thing that changes the answer.
Projects
A scan lives in a project, and the project comes from your API key, not from the request: a project key files its scans in that project, any other key usesdefault. Scan names are unique per project, so the same name may be used once in each.
Offered APIs
- test-connection: Probe a compliance key and a workspace before any scan exists. Says whether the key works, whether the workspace was found, which event types that workspace can actually serve, and — if you propose a start time — how much history a backfill from there would pull. Stores nothing.
-
add-scan: Create a scan. Starts polling immediately unless you create it with
status: "paused". -
get-scan: One scan’s record, plus
lag_s— how far behind the provider’s clock the scan currently is. - modify-scan: Change a scan’s name, guardrail, event types, cursor, cadence, status or stored key.
- delete-scan: Stop the scan and delete its record, along with its log-file history.
- list-scans: Every scan in your project, paginated, optionally filtered by status.
- start-scan / pause-scan: Resume or suspend polling without editing anything else.
- list-log-files: The coverage record — which provider export files this scan opened and closed, how many messages each held and how many were flagged.
- get-message: One message’s text, fetched live from the provider.
Connecting a workspace
Two values identify the source, and one of them is easy to get wrong.compliance_api_keyis the compliance API key issued by the provider for that workspace. Enkrypt stores it encrypted, and no endpoint ever returns it — not in plaintext and not masked. Replace it by sending a new one to Modify Compliance Scan.workspace_idis the ChatGPT workspace UUID. It is not an OpenAI organization id. An organization id has the right shape, so it is accepted, and then every poll comes back empty forever with nothing to explain why. If a scan is finding nothing at all, check this value first.
200 with key_valid: false. Read the fields, not the status code.
It also answers the two questions that are expensive to get wrong later:
- Which event types this workspace serves. Each entry in
event_typescomes back withsupported: trueorfalse. Selecting a service the workspace will never serve produces a scan that stays empty with no explanation. - How big the backfill is.
proposed_cursormakes the response carry abackfillestimate — files and bytes since that time. Volume varies enormously between workspaces, so the same 30-day start that seeds one workspace in minutes pulls millions of messages in another. Quote the estimate before committing to a start time.
Creating a scan
guardrails_nameis one of your saved guardrails — the ones Add Guardrail creates. Every message the scan reads is evaluated against it, so it decides what “flagged” means for this scan. The name is not checked against your saved guardrails when the scan is created, so a typo shows up later as a scan that runs and flags nothing.cursor_end_timeis where reading starts. It is the most consequential field on the request: it sets how much history gets pulled.event_typesmust be non-empty, and every value must be one the workspace serves.providerandworkspace_idare immutable. Sending either to Modify Compliance Scan is a400— a scan whose workspace changed would carry a cursor and a history belonging to something else. Delete it and create a new one instead.
status is active, list_interval_s is 60 seconds and pending_cap is 5000 queued events before the poller backs off.
Addressing a scan
Every endpoint except Add Compliance Scan, List Compliance Scans and Test Compliance Connection addresses one scan by name, in theX-Enkrypt-Scan header:
400; a name that is not yours is a 404. The internal scan_id is returned on the record, but it is never something you have to send.
Names may contain letters, numbers, spaces and _ & - . /, must start and end with a letter or a number, and are at most 64 characters. Renaming is allowed — send scan_name to Modify Compliance Scan — and the new name is what X-Enkrypt-Scan must carry afterwards.
Status, and the one value you cannot set
needs_reconnect is not a value you may send — Add and Modify accept only the first three. It is how a scan tells you its credential stopped working. Recover by sending a fresh compliance_api_key to Modify Compliance Scan, or by calling Start Compliance Scan once the key is replaced; either one re-resolves the key.
You can list exactly the scans in that state:
Is it keeping up?
Get Compliance Scan answers this with three fields:cursor_end_time— how far through the provider’s timeline the scan has read.lag_s— seconds between that cursor and now. A backfill starts with a very large value and works it down; a caught-up scan sits near its polling interval.last_polled_at— set on every successful poll, whether or not anything new came back. This is what separates a quiet workspace (polling, nothing to read) from a stopped scan (not polling at all). Alag_sthat keeps growing whilelast_polled_atkeeps moving means the workspace is busier than the scan can drain; alast_polled_atthat has stopped moving means something else.
messages_count and flagged_count once the file is closed. A file marked skipped aged out of the provider’s 30-day retention window before it could be downloaded.
Reading a message
410 is not a failure. It means the file has aged out of the provider’s 30-day retention window, which is the normal steady state for anything older than a month. The record’s content_expires_at says when that will happen.
Deleting a scan
Delete Compliance Scan stops the scan first and deletes the record only if the stop succeeded — if the scanner cannot be reached, the call fails and the scan is left intact rather than being orphaned into a workspace it keeps polling. The scan’s log-file history is deleted with it, so export the coverage record first if you need to keep it.Errors you will actually see
A write that succeeded in the database but could not reach the scanning service comes back as a normal success carrying a
warning field — the record is saved and the scanner picks it up on its next reconcile. Check for warning rather than assuming a 200 means the timer reloaded.
Python SDK
The Python SDK gains aComplianceClient in version 1.0.42, with one method per endpoint on this page: test_connection, add_scan, get_scan, modify_scan, delete_scan, list_scans, start_scan, pause_scan, list_log_files and get_message.
