Audit logs

A verifiable, immutable record of who did what in your account, and from where.

Audit logs give your team a tamper-proof record of the sensitive actions taken in your account — keys created and revoked, domains and inboxes added or deleted, webhooks changed. Each event captures what happened, who did it, and where it came from, so you can answer a compliance or incident question after the fact with a real answer.

Read the trail

Fetch events from GET /v1/audit-logs, newest first. The call needs the account:read scope. The trail spans every inbox of the organization, so a key restricted to specific inboxes gets a 403. Events come back in the standard list envelope under data, paginated with a cursor.

curl https://api.dairo.app/v1/audit-logs \
  -H "Authorization: Bearer $DAIRO_API_KEY"

A page is the standard list envelope — data holds the events and pagination carries the cursor for the next page:

{
  "object": "list",
  "data": [
    {
      "object": "audit_log",
      "id": "9f1c2b7e-7e3a-4c0b-9a8e-2f6d1c0b4a11",
      "action": "api_key.created",
      "resourceType": "api_key",
      "resourceId": "key_8f2a",
      "metadata": {},
      "actor": "key_a1b2",
      "ip": "203.0.113.4",
      "createdAt": "2026-06-10T14:22:03Z"
    }
  ],
  "pagination": { "nextCursor": "eyJvIjoxMjN9", "hasMore": true }
}

Event fields

FieldMeaning
actionThe audited action, e.g. api_key.created, domain.deleted, webhook.created.
resourceTypeThe kind of resource the action targeted, e.g. api_key, domain, inbox, webhook. May be null.
resourceIdIdentifier of the affected resource, when one applies.
metadataAction-specific context recorded with the event. May be null.
actorWho performed the action — an API key (by id), dashboard for console actions, or a system actor. May be null.
ipSource IP the request came from, when captured.
createdAtWhen the event was recorded, in UTC.

metadata holds action-specific context — some actions record a field or two and others record none. An inbox.created event records the new inbox's address, for example, while an api_key.created event records an empty object. Treat it as free-form context, not a fixed schema.

Actions you'll see

The trail records security-relevant actions. The most common, grouped by what they target:

resourceTypeActions
api_keyapi_key.created, api_key.revoked
domaindomain.created, domain.deleted
inboxinbox.created, inbox.deleted
webhookwebhook.created, webhook.deleted
audienceaudience.created, audience.deleted, audience.sent

The set of audited actions grows over time. Don't hard-code an exhaustive list: match on the prefix (api_key., domain.) or read resourceType, and tolerate actions you don't recognize yet.

Page through history

Pass limit (1–100, default 25) and follow pagination.nextCursor the usual way — see pagination. The trail is account-wide and newest-first. Add action to return only one exact action, such as ?action=api_key.created (dotted lowercase, up to 64 characters; anything else is a 400); filter by resourceType on each page in your own code.

Audit events are append-only — there's no endpoint to edit or delete them, and that immutability is the point. For long-term retention under your own control, schedule a signed export to your own archive.

API-key IP allowlisting

For high-trust automation, you can lock an API key to a set of trusted IP addresses, so a leaked secret is useless from anywhere else. It's a governance control that sits on top of scopes: scopes limit what a key can do; the allowlist limits where it can do it from.

Set an allowlist

Send allowedIps when you create a key. Each entry is an IPv4 or IPv6 address, or a CIDR range — up to 50 of them.

curl -X POST https://api.dairo.app/v1/api-keys \
  -H "Authorization: Bearer $DAIRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "name": "ci-runner",
        "scopes": ["messages:send"],
        "allowedIps": ["203.0.113.0/24", "198.51.100.7"]
      }'

The created key — and the key returned by the list endpoint — echoes its allowedIps. A key with no allowlist returns an empty allowedIps array ([]) and authenticates from any IP, which is the default.

See where a key is locked to

The allowlist shows up wherever a key's details appear, so you can audit it without re-creating the key. List your keys to see every key's allowedIps and confirm each one is pinned to the IPs you expect.

# Every key's allowlist
curl https://api.dairo.app/v1/api-keys \
  -H "Authorization: Bearer $DAIRO_API_KEY"

How enforcement works

On every authenticated request, Dairo resolves the caller's source IP and checks it against the key's allowlist before the request does any work:

Resolve the source IP

The trusted client IP is the real connecting address, never a header the caller supplies — so it can't be spoofed.

Match against the allowlist

The IP must fall inside at least one configured address or CIDR range.

Allow or deny (deny-by-default)

A match proceeds. Anything else — an IP that can't be matched, or can't be resolved at all — is rejected with 403, even when the bearer secret and scopes are otherwise valid. A correct secret from an untrusted network gets you nothing.

Operating notes

Next steps

On this page