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, or import_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 }, or null for team-level actions that aren't scoped to a single passport.

  • Name
    context
    Type
    object
    Description

    Action-specific payload. A modify event carries property_id, old_value, and value; 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.


GET/v1/audit_events

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

    asc or desc (default desc).

  • Name
    cursor
    Type
    string
    Description

    A pagination cursor from a previous response's next_cursor.

Request

GET
/v1/audit_events
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 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.


GET/v1/audit_settings

Retrieve retention settings

Reads the team's retention windows. Requires audit:read and an admin/owner key owner.

Request

GET
/v1/audit_settings
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
}

PATCH/v1/audit_settings

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 create entries, 0–3650. 0 means forever.

  • Name
    modify
    Type
    integer
    Description

    Days to keep modify entries, 0–3650.

  • Name
    delete
    Type
    integer
    Description

    Days to keep delete entries, 0–3650.

  • Name
    read
    Type
    integer
    Description

    Days to keep read entries, 0–3650. The highest-volume of the four.

Request

PATCH
/v1/audit_settings
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
}

Was this page helpful?