Docs
grovs.io Start for free
Examples for

Audit log

The audit log is an append-only record of security-relevant actions: logins, membership, configuration, credentials, links and billing. Each entry is chained to the previous one with a SHA-256 hash. A SIEM pulls it with an export token and a cursor.

Availability: grovs Cloud instances with an enterprise subscription, and every self-hosted Enterprise deployment. Other instances get 403. The open-source image has no audit endpoints.

Auth: an export token (aet_…). An instance admin creates it in Settings › Audit Logs › Export tokens › Create token. The token is shown once.

The audit log overview lists what is recorded. The SIEM integration guide covers the poll loop and how to verify the hash chain.

Find the instance ID

The log covers a whole grovs project, production and test together. Paths use the numeric {instance_id}, not an environment's project ID.

The dashboard does not show the instance ID yet. To find it:

  1. Open Settings › Audit Logs.
  2. Open your browser's network panel.
  3. Click Export tokens.
  4. Read the number in the request path /api/v1/instances/INSTANCE_ID/audit_export_tokens.

A token used with another instance's ID gets 403.

Text
/api/v1/instances/1395/audit_events
                  ^^^^
                  instance ID (integer)

The audit event object

Attributes
idinteger
Row ID.
sequenceinteger
Gap-free counter per instance. The only cursor you need.
occurred_atstring
UTC, ISO 8601 with six fractional digits.
actionstring
What happened, such as instance.member_added or user.login. See the action families.
outcomeenum
success, failure or pending.
actorobject
Who did it: type, id, email, and via, the credential channel (for example dashboard, password, sso:google_oauth2, api_key).
targetobject
What it was done to, such as { "type": "instance_role", "id": 9, "email": "…" }.
changesobject
A before / after diff where one applies. Secrets appear as "[FILTERED]".
ipstring | null
Client IP.
user_agentstring | null
Client user agent.
request_idstring | null
Request ID. Echoes a client-supplied X-Request-Id, so use it for correlation, not as evidence.
prev_hashstring | null
hash of the previous entry. null on the first one.
hashstring
SHA-256 of this entry. See verifying the chain.
JSON
The audit event object
JSON
{
  "id": 42,
  "sequence": 12,
  "action": "instance.member_added",
  "outcome": "success",
  "actor": { "type": "user", "id": 3, "email": "[email protected]", "via": "dashboard" },
  "target": { "type": "instance_role", "id": 9, "email": "[email protected]" },
  "ip": "203.0.113.9",
  "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_6) …",
  "request_id": "b1e5c0d2-7a41-4f6e-9a0b-3c2d1e0f9a8b",
  "prev_hash": "9f2c41e0…",
  "occurred_at": "2026-10-22T09:12:04.123456Z",
  "changes": { "after": { "role": "member" } },
  "hash": "4a7d93b1…"
}

List audit events

GET/api/v1/instances/{instance_id}/audit_events

Returns a page of audit events ordered by sequence. A SIEM starts at after=0 and sets after to next_after until events comes back empty.

AuthScoped keyAuthentication ›

Send an export token as Authorization: Bearer aet_….

Path
instance_idintegerRequired
The numeric instance ID. See Find the instance ID.
Query
afterintegerOptional
Only entries with a sequence greater than this.
beforeintegerOptional
Only entries with a sequence less than this. Use with order=desc to page backwards.
limitintegerOptional
1 to 1000. Default 100.
orderenumOptional
asc (default, oldest first) or desc.
event_actionstringOptional
Only this action. A SIEM should page on after alone.
actor_emailstringOptional
Only this actor.
fromstringOptional
ISO 8601. Only entries at or after this time.
tostringOptional
ISO 8601. Only entries at or before this time.

Returns schema_version, events (an array of audit event objects), next_after (the highest sequence on the page) and next_before (the lowest). Both are null on an empty page.

In production, each export token can make 60 requests a minute. Over that, the API answers 503.

Errors

400`order` is not `asc` or `desc`, or a cursor or date is not valid.
401Missing, invalid or revoked token.
403The token belongs to another instance, or the audit log is not enabled for this instance.
503More than 60 requests a minute for this token. Back off and retry.
Request · cURL
curl -X GET "https://api.sqd.link/api/v1/instances/{instance_id}/audit_events?after=0&limit=1000" \
  -H "Authorization: Bearer $GROVS_AUDIT_KEY"
JSON
{
  "schema_version": 1,
  "events": [
    {
      "id": 42,
      "sequence": 12,
      "action": "instance.member_added",
      "outcome": "success",
      "actor": { "type": "user", "id": 3, "email": "[email protected]", "via": "dashboard" },
      "target": { "type": "instance_role", "id": 9, "email": "[email protected]" },
      "ip": "203.0.113.9",
      "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_6) …",
      "request_id": "b1e5c0d2-7a41-4f6e-9a0b-3c2d1e0f9a8b",
      "prev_hash": "9f2c41e0…",
      "occurred_at": "2026-10-22T09:12:04.123456Z",
      "changes": { "after": { "role": "member" } },
      "hash": "4a7d93b1…"
    }
  ],
  "next_after": 12,
  "next_before": 12
}

Get the chain head

GET/api/v1/instances/{instance_id}/audit_events/head

Returns the latest sequence and hash without fetching entries. Compare it with your stored cursor to see how far behind a poller is, or with the last entry you ingested to detect a gap.

AuthScoped keyAuthentication ›
Path
instance_idintegerRequired
The numeric instance ID.

Returns schema_version, sequence (0 when the log is empty) and hash (null when empty). Shares the 60 requests a minute per token limit.

Errors

401Missing, invalid or revoked token.
403The token belongs to another instance, or the audit log is not enabled for this instance.
Request · cURL
curl -X GET "https://api.sqd.link/api/v1/instances/{instance_id}/audit_events/head" \
  -H "Authorization: Bearer $GROVS_AUDIT_KEY"
JSON
{ "schema_version": 1, "sequence": 4182, "hash": "4a7d93b1…" }
Was this page helpful?
Last updated 2026-10-26