Audit events
Every read and mutation — through the app or the API — writes an audit event: who did it, what changed, and when. Even retrieving a passport records a read event, so the log lets you reconstruct exactly how your data reached its current state. Reading the log is a privileged operation: only team admins and owners may query it.
The audit event model
- Name
object- Type
- string
- Description
Always
audit_event.
- Name
created- Type
- string
- Description
ISO 8601 timestamp of the event, like every other timestamp in the API. It is also the log's sort key, so its resolution is a whole second and not a tie-breaker between two events recorded in the same one. On input the
created[gte]/created[lte]filters are more forgiving than the output format: either an ISO string or unix seconds is accepted.
- Name
action- Type
- string
- Description
The action performed, e.g.
create,modify,delete,read,lab_transform,lab_rollback, orimport_rows_retried. Open-ended — new action types are added over time, so treat unknown values gracefully (see Versioning). Nullable.
- Name
user- Type
- object
- Description
The acting user, as
{ id, name }.
- Name
passport- Type
- object
- Description
The affected passport, as
{ id, friendly_id }, ornullfor team-level actions that aren't scoped to a single passport.
- Name
context- Type
- object
- Description
Action-specific payload. A
modifyevent carriesproperty_id,old_value, andvalue; other actions vary and the object may be empty.
Retention per action type is a per-team setting the API can both read and change. Lab actions such as lab_transform and lab_rollback are written here too — see Lab.
The audit log and the change feed are not interchangeable.
Audit answers who did what, reads included, with team-configured retention
and no per-entry id. Events answers what changed, with deduplicable evt_
ids, resumable cursors and fixed 30-day retention — and never records a read.
Neither is a superset of the other.
List audit events
Lists the team's audit log, newest first. This is a role-gated endpoint: it requires the audit:read ability and the key's owner must be a team admin or owner at request time. Keys owned by other roles receive 403, even with audit:read.
Unlike other paginated lists, this endpoint pages at a fixed size of 25 events and ignores limit; walk the log with cursor. All filters combine with AND. The user, passport, and action filters each accept a single value or an array (repeat the key with [], e.g. action[]=create&action[]=modify).
Optional attributes
- Name
created[gte]- Type
- string
- Description
Only events at or after this time. Unix timestamp or ISO date.
- Name
created[lte]- Type
- string
- Description
Only events at or before this time. Unix timestamp or ISO date.
- Name
user- Type
- string
- Description
Filter by acting user id. A single value or an array.
- Name
passport- Type
- string
- Description
Filter by affected passport id. A single value or an array.
- Name
action- Type
- string
- Description
Filter by action. A single value or an array, e.g.
action[]=create&action[]=modify.
- Name
order- Type
- string
- Description
ascordesc(defaultdesc).
- Name
cursor- Type
- string
- Description
A pagination cursor from a previous response's
next_cursor.
Request
curl -G https://api.synexcloud.com/v1/audit_events \
-H "Authorization: Bearer $SYNEX_API_KEY" \
-d "created[gte]=1751328000" \
-d "action[]=create" \
-d "action[]=modify" \
-d "order=desc"
Response
{
"object": "list",
"data": [
{
"object": "audit_event",
"created": "2026-07-12T09:31:00Z",
"action": "modify",
"user": { "id": 42, "name": "Jane Ops" },
"passport": { "id": "pass_8f2k7d9a1b2c3d4e", "friendly_id": "PACK-001" },
"context": {
"property_id": "property_9f2k7d",
"old_value": "4400",
"value": "4500"
}
},
{
"object": "audit_event",
"created": "2026-07-12T09:29:00Z",
"action": "read",
"user": { "id": 42, "name": "Jane Ops" },
"passport": { "id": "pass_8f2k7d9a1b2c3d4e", "friendly_id": "PACK-001" },
"context": {}
},
{
"object": "audit_event",
"created": "2026-07-12T09:20:00Z",
"action": "lab_transform",
"user": { "id": 7, "name": "Sam Admin" },
"passport": null,
"context": { "run_id": 3187, "matched": 128 }
}
],
"has_more": true,
"next_cursor": "ae_01J8Z9QK7M4RXN",
"url": "/v1/audit_events"
}
Retention settings
How long the log keeps an entry is a per-team setting, and the API can both read and change it. The object is four integers — one per audited action, in days, where 0 means keep forever.
Both operations carry the same gate as reading the log itself: the key's owner must be a team admin or owner, and a key whose owner is neither gets 403 audit_role_required however its abilities are configured. audit:write is additionally not in a key's default ability set — you have to grant it deliberately, which is the intended amount of friction for a setting that governs how long your own evidence survives.
A key holding audit:read does not gain audit:write. Abilities are
fixed when a key is minted and never granted retroactively, so no key issued
before audit:write existed can change retention. Edit the key in API
Keys to add it — no secret is rotated by doing so.
A change is not retroactive
This is the paragraph to read twice: changing retention does nothing whatever to entries already written. Retention is enforced by a TTL attribute stamped on each audit row at write time, so already-written rows keep the window they were written with.
What that means in practice:
- If you shorten retention to satisfy a compliance requirement, the log does not shrink — every entry already in it lives out the window it was born with, on the old policy.
- If you lengthen retention after a gap, the gap stays — nothing refills the period that already expired.
Retention is a decision about the future only. Make it before the data you need is gone, rather than at the point you notice it missing.
Which window to tune
read is by far the highest-volume of the four: every passport read writes an entry, and most integrations read far more than they write. It is consequently the shortest window most teams set, and the first one to look at if the log is larger or noisier than you want.
Retrieve retention settings
Reads the team's retention windows. Requires audit:read and an admin/owner key owner.
Request
curl https://api.synexcloud.com/v1/audit_settings \
-H "Authorization: Bearer $SYNEX_API_KEY"
Response
{
"object": "audit_settings",
"create": 365,
"modify": 365,
"delete": 730,
"read": 30
}
Update retention settings
Changes one or more retention windows. Requires audit:write and an admin/owner key owner.
PATCH is partial: only the fields you send move, and an absent field means "leave it alone".
An explicit null is rejected rather than treated as absent — a null read as zero would quietly mean "keep forever", which is the one value you least want to set by accident. An empty body is 400 parameter_invalid rather than a no-op, on the same reasoning: a request that changes nothing is far more likely a bug in your caller than something you meant to send.
The ceiling is 3650 days — ten years. That is an API-only limit; the form in the app has no upper bound. The reason is mechanical rather than editorial: the value becomes an expiry timestamp stamped on each stored entry, so an unbounded integer produces a timestamp the store will not honour rather than the "keep it longer" you asked for. 0 remains the way to say keep forever.
A successful PATCH emits audit_settings.updated on the change feed, carrying a payload byte-identical to the response. The irony is worth naming: a change to how long the audit record is kept is announced on the other log. If reconstructing your retention policy over time matters to you, subscribe to that event.
Optional attributes
- Name
create- Type
- integer
- Description
Days to keep
createentries, 0–3650.0means forever.
- Name
modify- Type
- integer
- Description
Days to keep
modifyentries, 0–3650.
- Name
delete- Type
- integer
- Description
Days to keep
deleteentries, 0–3650.
- Name
read- Type
- integer
- Description
Days to keep
readentries, 0–3650. The highest-volume of the four.
Request
curl -X PATCH https://api.synexcloud.com/v1/audit_settings \
-H "Authorization: Bearer $SYNEX_API_KEY" \
-H "Content-Type: application/json" \
-d '{"read": 30}'
Response
{
"object": "audit_settings",
"create": 365,
"modify": 365,
"delete": 730,
"read": 30
}