The Short Answer
To prove who changed a configuration, you need four things recorded at write time and queryable afterwards: the actor's identity, a value-level before-and-after of the fields that moved, an ISO8601 timestamp, and a caller-supplied reason. Mixpeek records all four on every audited write and exposes them at
GET /v1/organizations/audit/logs, filterable by resource, actor, action and time range, with an NDJSON export at /export for handing evidence to a reviewer. The part most systems miss is the WHY: it has to be captured from the caller at the moment of the change, because nothing downstream can reconstruct intent.Why "check the logs" usually fails a governance review
Most platforms can tell you that a configuration changed. A review asks harder questions, and each one breaks a different common implementation.
Who, specifically. A log line saying an API key made the change is not an identity. A reviewer wants a person, and an audit record has to carry the resolved name and email alongside the key that was used, or you are handing over a token and an alibi.
What the value was before. "Retriever updated" is not a change record. Without the previous value you cannot show that a threshold moved from 0.7 to 0.4, which is usually the entire question.
Why. This is the one that cannot be reconstructed later. Timestamps and diffs are recoverable from the system; intent only exists in the head of whoever made the change, and it has to be captured at that moment or lost.
In a form they can keep. A reviewer wants an artifact, not a dashboard session. Paginated JSON in a UI is not evidence handoff.
Steps to reconstruct a change
1. Narrow to the resource. Call
GET /v1/organizations/audit/logs with resource_type and resource_id to get every recorded event against that object, newest first.
2. Bound the window. Add start and end as ISO8601 timestamps to cover the period under review, which keeps the result set to the events a reviewer actually asked about.
3. Identify the actor. Each event carries actor_id, actor_type, actor_name, actor_email and actor_key_name, so you can name a person and the credential they used rather than one or the other.
4. Read the value-level diff. The changes field records what moved, field by field, which is what turns "it was updated" into "this threshold went from one value to another".
5. Read the reason. The reason field holds the caller-supplied WHY, captured from the X-Change-Reason request header when it was present on the write.
6. Export the evidence. Call GET /v1/organizations/audit/logs/export with the same filters to receive application/x-ndjson, one event per line, which a reviewer can archive and diff independently of your UI.# Every change to one retriever in the review window GET /v1/organizations/audit/logs?resource_type=retriever&resource_id=ret_1a7be6&start=2026-08-01T00:00:00Z&end=2026-09-01T00:00:00Z # The same slice, as NDJSON for the reviewer to keep GET /v1/organizations/audit/logs/export?resource_type=retriever&resource_id=ret_1a7be6&start=2026-08-01T00:00:00Z&end=2026-09-01T00:00:00Z
What one audit record contains
| Field | What it answers |
audit_id, timestamp | Which event, and when, as ISO8601 |
resource_type, resource_id | Which object was touched |
action, status | What was attempted, and whether it succeeded |
actor_id, actor_type | Who, as the system sees them |
actor_name, actor_email, actor_key_name | Who, as a reviewer needs them: a person and the key used |
changes | The value-level before and after |
reason | The caller-supplied WHY |
ip_address, user_agent | Where the request came from |
actor_email with actor_key_name. A shared service key with no resolved identity fails a review, and a name with no key cannot be tied to the request that carried it.Capturing the WHY at write time
Send
X-Change-Reason on the mutating request and it lands on the audit record as reason. It is worth wiring into whatever already makes changes on your behalf, because that is the only moment the intent exists:curl -X POST https://api.mixpeek.com/v1/retrievers \
-H "Authorization: Bearer $MIXPEEK_API_KEY" \
-H "X-Change-Reason: lowering recall threshold for the Q3 relevance test (TICKET-4192)" \
-H "Content-Type: application/json" \
-d '{ ... }'changes and the ticket is what a reviewer follows next.Frequently Asked Questions
What is the difference between an audit log and an application log?
An application log records what the system did; an audit log records what a person or credential caused it to do, in a form meant to be read by someone who was not there. The practical difference is the fields. Application logs rarely carry a resolved human identity, almost never carry a before value, and never carry intent. An audit record is built so a reviewer can reconstruct a decision without asking anyone what happened.
Which configuration surfaces are covered?
Audit events are keyed by
resource_type and resource_id, so filtering by resource is how you scope a review to a retriever, a collection, a bucket, a namespace, or a taxonomy. Query GET /v1/organizations/audit/logs without a resource filter to see the full set your organization has generated.How do I hand evidence to an auditor?
Use
GET /v1/organizations/audit/logs/export with the same filters as the list endpoint. It returns application/x-ndjson, one JSON object per line, which is append-friendly and diffable, and takes a max_events cap so a wide export cannot run away. NDJSON matters here because a reviewer can hold the file and re-check it later without access to your account.What happens if nobody sends a reason?
The
reason field is null and the rest of the record is unaffected. That is the honest failure mode, and it is why the header is worth wiring into automation rather than leaving to whoever remembers. A change with a full diff and no reason still proves what happened and cannot prove why.Does this replace SOC 2 or HIPAA certification?
No. An audit trail is a control, not an attestation. It is the kind of evidence a reviewer asks for while assessing a control, and it is useful whether or not a certification exists. Mixpeek's current certification status is published at mixpeek.com/trust.