/Docs

Public API: Audit & Observability

Every state-changing request authenticated by a service token leaves a trail you can audit, query, and stream. This page covers four signals: the in-app audit log, structured server-side logs, the rate-limit headers on every response, and webhooks for state-change subscriptions.

Audit log entries for API actions

Every write that authenticates with a service token produces an audit entry alongside the resource change. The entry records:

  • actorType: API_TOKEN — distinguishes API actions from dashboard (USER) and system actions.
  • source: API — the channel that triggered the action. Other values: DASHBOARD, CLI, SYSTEM.
  • metadata.tokenId + metadata.tokenName — the specific token row that authenticated the request. The token's display name is preserved even if you later rotate the secret.
  • ip + userAgent— the calling client's observable identity.
  • before / after — JSON diff of the resource (where applicable).
Info
Service-token audit entries appear identically to dashboard entries in the UI, but with a token-shaped icon and a footer reading via API · {token name}. No fields are redacted — the full diff is visible to anyone with the audit:read scope or org admin access.

Viewing audit entries in-app

The dashboard ships a dedicated audit-log view at /audit-log. Open it from the organisation sidebar, or link directly:

text
1https://app.avsb.cloud/audit-log

Filters available in the UI:

  • Actor type — User, API token, System.
  • Source — Dashboard, API, CLI, System.
  • Specific token — pick one of your service tokens to see only its actions.
  • Resource type — Project, Experiment, Flag, Audience, etc.
  • Time range — last hour through last 90 days.

Querying audit entries via the API

You can read the audit log itself through the API with the audit:read scope. Filter by token to track exactly what each service identity has done:

bash
1curl 'https://app.avsb.cloud/api/orgs/<orgId>/audit-log?actorType=API_TOKEN&tokenId=tok_abc&limit=100' \
2 -H "Authorization: Bearer avsb_svc_..."

Sample response row:

json
1{
2 "id": "audit_01HX...",
3 "createdAt": "2026-05-17T13:24:00Z",
4 "actorType": "API_TOKEN",
5 "actorId": "tok_abc",
6 "actorName": "Terraform CI",
7 "source": "API",
8 "action": "experiment.update",
9 "resourceType": "EXPERIMENT",
10 "resourceId": "exp_def",
11 "ip": "203.0.113.42",
12 "userAgent": "terraform-provider-avsb/0.4.1",
13 "metadata": { "tokenId": "tok_abc" },
14 "before": { "trafficPct": 50 },
15 "after": { "trafficPct": 80 }
16}

Structured server-side logging

Every public-API request is logged server-side via pino. Each log line is a single JSON object suitable for ingestion into your log pipeline (Datadog, Sumo, ELK, BetterStack, Axiom, etc.). Guaranteed fields:

  • requestId — opaque id, also echoed in the X-Request-Id response header so you can correlate.
  • method + path + status + latencyMs.
  • orgId — from the URL.
  • tokenId — the authenticating service token (omitted for unauthenticated requests).
  • scope — the specific scope evaluated for the request.
  • idempotencyKey — when supplied.
  • idempotencyReplayedtrue when the response was served from the idempotency cache.
Warning
Logs never include request bodies, response bodies, or token secrets. If you need the diff of a write, query the audit log; the structured log is for traffic shape, latency, and error rate.

Rate-limit headers as observability signal

Every response carries the standard rate-limit headers — graph them in your observability stack to spot capacity issues before they turn into 429s:

  • X-RateLimit-Limit — current quota (requests/minute) for this token + scope family.
  • X-RateLimit-Remaining — alarm on remaining/limit < 0.1.
  • X-RateLimit-Reset — unix timestamp; use to drive token-bucket back-off.
  • Retry-After — present only on 429 responses; respect it before retrying.
javascript
1// Forward rate-limit headers into your metrics pipeline
2function recordRateLimit(res) {
3 metrics.gauge('avsb_api.rate_limit.remaining', Number(res.headers.get('X-RateLimit-Remaining')))
4 metrics.gauge('avsb_api.rate_limit.limit', Number(res.headers.get('X-RateLimit-Limit')))
5}

Webhooks for state changes

For push-based observability — i.e. you want to be told when a resource changes rather than polling the audit log — subscribe to webhooks. Webhooks are configured per-project (snippet / experiment events) and per-org (token, flag, member events).

Event types relevant to API-token activity:

  • api_token.created / .rotated / .revoked
  • experiment.published / .paused / .completed
  • flag.rule.published / .archived
  • audit.entry.created — every audit entry, in real time.
bash
1curl https://app.avsb.cloud/api/orgs/<orgId>/webhooks \
2 -X POST \
3 -H "Authorization: Bearer avsb_svc_..." \
4 -H "Content-Type: application/json" \
5 -d '{
6 "url": "https://hooks.your-host/avsb",
7 "events": ["audit.entry.created"],
8 "secret": "whsec_..."
9 }'

Every webhook delivery is signed with an HMAC-SHA256 of the body keyed by your secret, passed as the X-AvsB-Signature header. Reject deliveries with an invalid signature.

Info
Webhook delivery is at-least-once with exponential back-off, so always de-duplicate by the delivery id header. See the Webhooks guide for the full signing flow, retry schedule, and replay UI.