Skip to content
Keel
Dashboard

Audit and integrations API

Endpoints for the project audit log, the security overview, and the Vercel integration, with parameters and response shapes.

Last updated

These endpoints require a browser session and are available to owners and admins.

Audit log#

GET /api/projects/:projectId/audit#

Permission: audit:read. Events are returned newest first with cursor pagination.

Query parameterTypeNotes
limitinteger1 to 100, default 50
categorystringsecrets, access, project or integrations
actionstringAn exact action such as secret.updated. Takes precedence over category.
actorstringA user id
from, todateYYYY-MM-DD or a full ISO timestamp. A date-only to includes the whole day.
cursorstringThe nextCursor of the previous page
Shell
curl -s "$KEEL_URL/api/projects/$PROJECT_ID/audit?category=secrets&limit=20" \
  -H "Cookie: $SESSION_COOKIE"

Response 200:

JSON
{
  "events": [
    {
      "id": "0a5f...",
      "at": "2026-10-11T09:42:10.512Z",
      "action": "secret.revealed",
      "targetType": "secret",
      "targetId": "7c1e...",
      "metadata": { "key": "API_KEY", "environment": "development", "via": "cli" },
      "actor": { "userId": "user_...", "email": "alice@example.com", "isYou": false, "isMember": true }
    }
  ],
  "nextCursor": "MjAyNi0xMC0xMVQwOTo0MjoxMC41MTJafDBhNWY..."
}

nextCursor is null on the last page. Errors: 400 Invalid cursor., 400 Invalid date filter., 400 Invalid category filter., 400 Invalid actor filter., 400 Invalid action filter.

Event metadata never contains secret values or tokens. The recorded actions are listed in Audit logs.

Security overview#

GET /api/projects/:projectId/security#

Permission: audit:read. A read-only summary derived from the real state of the project: encryption facts, role counts, pending invitations, per-environment grants and integration status. It never returns key material, values or tokens.

JSON
{
  "encryption": {
    "algorithm": "AES-256-GCM",
    "secretsEncryptedAtRest": true,
    "keyConfigured": true,
    "currentKeyVersion": 1,
    "records": { "total": 11, "onCurrentKeyVersion": 11, "other": 0 },
    "keyRotation": false,
    "externalKms": false
  },
  "authentication": { "provider": "Clerk", "cliAccessTokenMinutes": 60, "cliRefreshTokenDays": 30 },
  "access": { "members": { "owner": 1, "admin": 1, "member": 3, "viewer": 1 }, "pendingInvitations": 0 },
  "integrations": { "vercel": null },
  "auditLog": { "events": 128 }
}

The response also includes free-text notes and a per-environment grant summary, omitted above.

Vercel integration#

All Vercel endpoints require integrations:manage (admin or owner). The access token and deploy hook are never returned. See the Vercel guide for behavior.

GET /api/projects/:projectId/integrations/vercel#

Returns { "connected": false } or the connection state:

JSON
{
  "connected": true,
  "status": "active",
  "vercelProjectId": "prj_...",
  "vercelProjectName": "web-app",
  "teamId": null,
  "mappings": { "development": "development", "production": "production" },
  "autoRedeploy": false,
  "hookConfigured": false,
  "conflictPolicy": "skip",
  "lastSyncAt": "2026-10-11T09:30:00.000Z",
  "lastSyncStatus": "success",
  "lastError": null,
  "syncing": false,
  "pending": 0,
  "environments": ["development", "production"]
}

status is connected (token saved, no project linked), active, or attention (reconnect needed). The response also contains lastRun and lastDeployment details.

POST /api/projects/:projectId/integrations/vercel#

Connects or reconnects with a token. Rate limited to 10 per minute.

FieldTypeNotes
tokenstring, requiredA Vercel access token
teamIdstring, optionalLooks like team_xxxxxxxx

Response 201: the connection state. Errors: 400 Enter a Vercel access token., 400 Team ID should look like team_xxxxxxxx., 400 or 502 with a fixed message when Vercel rejects the token.

GET /api/projects/:projectId/integrations/vercel/options#

Lists the Vercel teams and projects the stored token can see. Query: teamId (optional). Rate limited to 20 per minute. Only ids and names are returned.

Response 200: { "teams": [ ... ], "projects": [ ... ] }

PUT /api/projects/:projectId/integrations/vercel#

Links a Vercel project and sets the options.

FieldTypeNotes
vercelProjectIdstring, required
teamIdstring, optional
mappingsobject, requiredKeel environment slug to development, preview or production. At least one, no two on the same target.
conflictPolicystring, optionalskip (default) or overwrite
autoRedeployboolean, optionalRequires a deploy hook
deployHookUrlstring or null, optionalA URL on api.vercel.com. null removes it.
JSON
{
  "vercelProjectId": "prj_placeholder",
  "mappings": { "development": "development", "staging": "preview", "production": "production" },
  "conflictPolicy": "skip",
  "autoRedeploy": false
}

Response 200: the connection state.

POST /api/projects/:projectId/integrations/vercel/sync#

Runs a sync now. Optional body { "redeploy": true } also triggers the deploy hook. Rate limited to 6 per minute.

Response 200: { "summary": { ... } }. Errors: 400 Link a Vercel project first., 400 Reconnect Vercel before syncing., 409 A sync is already running. It will pick up your changes.

DELETE /api/projects/:projectId/integrations/vercel#

Disconnects. Variables already in Vercel stay. Response 200: { "ok": true }.

Next steps#

Review Error codes.