Security Events API
STACK automatically monitors your organization for security anomalies and generates events when suspicious activity is detected. The Security Events API lets you list, inspect, and resolve these events. All endpoints require authentication via the Authorization: Bearer sk_live_... header.
Security events require operator-level access. Team member API keys cannot access these endpoints and will receive 403 Forbidden. Only the operator's master API key or a valid session JWT can be used.
List Security Events
Retrieve unresolved security events for your organization, with optional filtering by agent and pagination.
# List unresolved events (default)
curl "https://api.getstack.run/v1/security-events?page=1&limit=50" \
-H "Authorization: Bearer sk_live_your_key"
# Filter by agent
curl "https://api.getstack.run/v1/security-events?agent_id=agt_xyz&limit=20" \
-H "Authorization: Bearer sk_live_your_key"Query Parameters
- agent_id (string, optional) - filter events by a specific agent ID
- page (integer, optional) - page number, default 1 (1-indexed)
- limit (integer, optional) - events per page, default 50, max 100
Response
{
"events": [
{
"id": "sev_abc123",
"operator_id": "op_xyz",
"agent_id": "agt_xyz",
"passport_jti": "jti_abc",
"signal_type": "credential_burst",
"severity": "warning",
"message": "Agent retrieved 15 credentials within 30 seconds",
"metadata": {
"credential_count": 15,
"time_window_seconds": 30
},
"resolved": false,
"resolved_at": null,
"created_at": "2026-04-15T10:05:00Z"
},
{
"id": "sev_def456",
"operator_id": "op_xyz",
"agent_id": "agt_abc",
"passport_jti": "jti_def",
"signal_type": "credential_outside_scope",
"severity": "critical",
"message": "Proxy request for notion not in passport scope",
"metadata": {
"intent_services": ["slack", "github"],
"granted_providers": ["slack", "github"]
},
"resolved": false,
"resolved_at": null,
"created_at": "2026-04-15T09:30:00Z"
}
],
"unresolved_count": 7,
"page": 1,
"limit": 50
}The unresolved_count field reflects the total number of unresolved events across your entire organization, regardless of pagination or agent filter. Use this to power badge counts in your UI.
Get a Security Event
Retrieve full details for a specific security event by ID.
curl https://api.getstack.run/v1/security-events/sev_abc123 \
-H "Authorization: Bearer sk_live_your_key"Returns 404 if the event does not exist or belongs to a different operator.
Signal Types
STACK detects and categorizes security events into the following signal types, each with an associated severity level.
credential_outside_scope
An agent attempted to access a credential or proxy a request for a service that is not listed in the passport's scope. In enforced mode this blocks the request; in logged mode the request proceeds but the event is recorded.
- Severity: critical (enforced mode) or warning (logged mode)
- Metadata: intent_services, granted_providers
credential_after_checkout
An agent attempted to access a credential or use the proxy after the passport has been checked out. This is always blocked regardless of accountability mode.
- Severity: critical
- Metadata: service name, passport JTI
credential_burst
An agent retrieved an unusually high number of credentials in a short time window, which may indicate credential exfiltration.
- Severity: warning or critical depending on volume
- Metadata: credential_count, time_window_seconds
delegation_without_intent
A passport delegation was created without a matching intent declaration.
- Severity: warning
delegation_downgrade
A delegated passport was issued with broader scope than the parent passport. Delegation should only narrow scope.
- Severity: critical
checkpoint_silence
An agent failed to report a checkpoint within the expected interval.
- Severity: warning
expired_no_checkout
A passport expired without ever being checked out, which may indicate an agent that abandoned a task without proper cleanup.
- Severity: info
scope_escalation_pattern
An agent is systematically requesting passports with increasingly broader scopes, potentially probing for access.
- Severity: warning or critical
credential_unreported
An agent accessed credentials during a passport session but did not report them in the checkout. This signal is generated by comparing Redis-tracked credential access against the checkout report.
- Severity: warning
- Metadata: accessed services, reported services
Severity Levels
Every security event has one of three severity levels:
- info - informational, no immediate action required (e.g., expired_no_checkout)
- warning - suspicious pattern detected, should be reviewed (e.g., credential_burst, credential_unreported)
- critical - active security threat, requires immediate investigation (e.g., credential_outside_scope in enforced mode, credential_after_checkout)
Automatic Agent Blocking
When a critical-severity event is recorded, STACK checks the agent's on_critical configuration. If set to block, the agent is automatically blocked from receiving new passports. The agent's passport_blocked flag is set to true with a reason message.
Eval Traffic
Agents with eval_traffic: true (set viaPATCH /v1/agents/:id) run benchmark or eval workloads. Detection stays fully on and every event still records and appears in this API, but events at severity warning or below insert already resolved (resolved_by: "auto:eval_traffic") and skip notification dispatch. Critical events stay completely live - recorded unresolved, notified, and honored by automatic agent blocking. The expensive LLM classification layer (L3) of the prompt-injection chain is skipped for these agents; the regex and normalization layers still produce verdicts. Distinct fromis_synthetic, which hides events from this API entirely. Both flags are operator-set only: agent contexts cannot PATCH them and partner grants are explicitly denied both.
Resolve a Security Event
Mark a security event as resolved after investigation. The resolve endpoint takes no request body.
curl -X POST https://api.getstack.run/v1/security-events/sev_abc123/resolve \
-H "Authorization: Bearer sk_live_your_key"{
"resolved": true,
"id": "sev_abc123"
}Returns 404 if the event does not exist or belongs to a different operator.
Once resolved, an event cannot be re-opened. If the same anomaly recurs, STACK generates a new event automatically.
Governance Approvals (Human Step-Up)
Four scope-expanding actions require a human-approved governance approval when called with a Bearer credential (operator key, member key, or OAuth token - including agentic MCP sessions): a detector config write whose delta loosens detection, detector config reset, a review decision of approved, and agent unblock. Resolving a security event is deliberately NOT gated, nor is any read or any tightening change. Dashboard sessions never step up - the human in the dashboard is the approval.
A gated call without the header does not execute. It creates a pending approval and returns 403 GOVERNANCE_APPROVAL_REQUIRED whose message carries the approval id (gvr_...) and the dashboard URL. A notification is pushed to the operator. After a human approves at/governance/approvals, retry the SAME call with the approval id in the X-Governance-Approval header.
# 1. Gated call from a Bearer credential -> 403 with an approval id
curl -X POST https://api.getstack.run/v1/agents/agt_xyz/unblock \
-H "Authorization: Bearer sk_live_your_key"
# -> 403 { "error": { "code": "GOVERNANCE_APPROVAL_REQUIRED",
# "message": "... approve request gvr_abc123 at https://getstack.run/governance/approvals,
# then retry the same call with header X-Governance-Approval: gvr_abc123" } }
# 2. Human approves in the dashboard, then retry the SAME call:
curl -X POST https://api.getstack.run/v1/agents/agt_xyz/unblock \
-H "Authorization: Bearer sk_live_your_key" \
-H "X-Governance-Approval: gvr_abc123"The approval binds tightly: same operator, same action and resource, and an exact hash of the request payload - a retried body that differs from what the human saw is refused. Approvals are single-use and expire after 15 minutes.
GET /v1/governance/approvals
List this operator's governance approvals, pending first. Statuses:pending, approved,rejected, consumed,expired. Agent-authenticated contexts are denied.
curl https://api.getstack.run/v1/governance/approvals \
-H "Authorization: Bearer sk_live_your_key"{
"approvals": [
{
"id": "gvr_abc123",
"action": "agent_unblock",
"resource_id": "agt_xyz",
"summary": "Unblock agent agt_xyz for passport issuance",
"requested_via": "oauth",
"status": "pending",
"expires_at": "2026-07-19T10:45:00.000Z",
"created_at": "2026-07-19T10:30:00.000Z",
"decided_at": null
}
]
}POST /v1/governance/approvals/:id/decide
Approve or reject a pending approval. Body:{ "decision": "approve" } or{ "decision": "reject" }. This endpoint accepts ONLY a signed-in dashboard session - a Bearer credential can never approve its own elevation. In practice you decide from the dashboard approvals page, not curl.
Automated Response
Every detector fire publishes a notification event whose type issecurity.<signal_type> - for examplesecurity.credential_outside_scope orsecurity.intent_deviation. Subscribe at per-detector granularity and gate by minimum severity. Both filters apply - the channel receives the notification only if it is subscribed to the event AND the fire severity meets the channel's min_severity floor.
# Page oncall on hard scope violations and delegation downgrades only
curl -X POST https://api.getstack.run/v1/notifications/channels \
-H "Authorization: Bearer sk_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"events": [
"security.credential_outside_scope",
"security.delegation_downgrade"
],
"min_severity": "critical",
"destination_ids": ["ndst_pagerduty", "ndst_oncall_email"]
}'The notification payload carries signal_type,agent_id, passport_jti,event_id, and the message, so webhook handlers can switch on signal_type - e.g. auto- revoke all passports for an agent when credential_outside_scopefires, just log intent_deviation.
Best Practices
- Subscribe to at least security.credential_outside_scope and security.delegation_downgrade at severity=critical before going to production
- Use webhook delivery for automated remediation; email/SMS for human-in-the-loop operators
- Review and resolve open events regularly - check unresolved_count from the dashboard
- Configure on_critical: "block" on agents that handle sensitive data for belt-and-suspenders mitigation
- Use enforced accountability mode on passports so auto-revoke can fire on critical signals