Skip to content

SENTINEL REST API

All endpoints live under /api/sentinel. JWT-based auth via Authorization: Bearer <token>.

  • Public routes require verifyJwt only.
  • Admin routes require verifyJwt + requireOwner — wallet must appear in AUTHORIZED_WALLETS env var.

All SENTINEL routes that emit errors return a structured envelope:

{ "error": { "code": "<CODE>", "message": "<human-readable>" } }
CodeHTTP statusMeaning
VALIDATION_FAILED400Required fields missing or malformed
NOT_FOUND404Resource ID does not exist
FORBIDDEN403Reserved (auth middleware emits its own legacy shape today)
UNAVAILABLE503Server up but a dependency is unconfigured
INTERNAL500Unexpected server-side failure

Note on auth errors: 401/403 responses from verifyJwt and requireOwner middleware still use the legacy {error: "string"} shape. Normalizing those is tracked separately as a future API-wide cleanup.


Auth: verifyJwt

Description: Run a one-shot risk assessment for a proposed action. Used by external integrators or internal callers that want a verdict without committing to execution. The SENTINEL assessor (a Pi-SDK LLM agent) evaluates the action context and returns a structured RiskReport.

Request body:

{
"action": "string (required)",
"wallet": "string (required)",
"recipient": "string (optional)",
"amount": "number (optional)",
"token": "string (optional)",
"metadata": "object (optional)"
}

Response 200RiskReport:

{
"risk": "high",
"score": 100,
"reasons": [
"SENTINEL output failed schema validation"
],
"recommendation": "block",
"blockers": [
"schema-violation"
],
"decisionId": "01KQP24PG0KZCJVWJDQM8H3JAY",
"durationMs": 440
}

Response 400:

{ "error": { "code": "VALIDATION_FAILED", "message": "action and wallet are required strings" } }

Returned when action or wallet is missing or not a string.

Response 500:

{ "error": { "code": "INTERNAL", "message": "<error message from assessor>" } }

Returned when the assessor throws an unexpected error.

Response 503:

{ "error": { "code": "UNAVAILABLE", "message": "SENTINEL assessor not configured" } }

Only returned when no assessor is registered at startup. Production agents always register one via setSentinelAssessor, so this path is rarely hit in deployed environments.

Example:

Terminal window
curl -X POST http://localhost:5006/api/sentinel/assess \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{
"action": "vault_refund",
"wallet": "C1phrE76Wrkmt1GP6Aa9RjCeLDKHZ7p4MPVRuPa8x85N",
"amount": 1.5
}'

Auth: verifyJwt

Description: List blacklisted addresses. Soft-removed entries (where removed_at is set) are filtered out by default.

Query params:

ParamTypeDefaultDescription
limitnumber50Maximum entries to return

Response 200:

{
"entries": []
}

Example:

Terminal window
curl -H "Authorization: Bearer $JWT" \
"http://localhost:5006/api/sentinel/blacklist?limit=50"

Auth: verifyJwt

Description: List queued circuit-breaker pending actions (SQLite-backed). Optionally filter by wallet or status.

Query params:

ParamTypeDefaultDescription
walletstringFilter by originating wallet address
statusstringFilter by action status — lifecycle is pendingexecutingexecuted | cancelled

Response 200:

{
"actions": []
}

Example:

Terminal window
curl -H "Authorization: Bearer $JWT" \
"http://localhost:5006/api/sentinel/pending?wallet=C1phrE76Wrkmt1GP6Aa9RjCeLDKHZ7p4MPVRuPa8x85N&status=pending"

Auth: verifyJwt

Description: SENTINEL runtime status snapshot — current mode, preflight scope, configured model, daily LLM cost accrued, and daily budget ceiling.

Response 200:

{
"mode": "advisory",
"preflightScope": "fund-actions",
"model": "openrouter:anthropic/claude-sonnet-4.6",
"dailyBudgetUsd": 10,
"dailyCostUsd": 0,
"blockOnError": false
}
FieldTypeDescription
mode"yolo" | "advisory" | "off"Current operating mode
preflightScopestringWhich tool categories trigger the preflight gate
modelstringLLM model identifier used for assessments
dailyBudgetUsdnumberMaximum daily spend allowed for LLM calls
dailyCostUsdnumberActual spend accumulated today (UTC)
blockOnErrorbooleanWhether assessment errors cause the action to be blocked

Example:

Terminal window
curl -H "Authorization: Bearer $JWT" \
http://localhost:5006/api/sentinel/status

Admin routes require both verifyJwt AND requireOwner middleware. The calling wallet must appear in the AUTHORIZED_WALLETS environment variable (comma-separated list).

Auth: verifyJwt + requireOwner

Description: Insert a new blacklist entry. The added_by field is set to admin:<wallet> using the verified wallet from the JWT, or admin if no wallet is present.

Request body:

{
"address": "string (required)",
"reason": "string (required)",
"severity": "string (required)",
"expiresAt": "string (optional, ISO 8601 timestamp)",
"sourceEventId": "string (optional)"
}

Response 200:

{
"success": true,
"entryId": "01KQP25BM8RVZJ92CTANFDNTMG"
}

entryId is a ULID generated by the SQLite insert.

Response 400:

{ "error": { "code": "VALIDATION_FAILED", "message": "address, reason, severity required" } }

Example:

Terminal window
curl -X POST http://localhost:5006/api/sentinel/blacklist \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{
"address": "BadActor11111111111111111111111111111111111",
"reason": "Flagged by SENTINEL risk assessment",
"severity": "high"
}'

Auth: verifyJwt + requireOwner

Description: Soft-remove a blacklist entry. Sets removed_at, removed_by, and removed_reason columns. The row is not deleted from SQLite — it is retained for audit purposes and filtered from GET /blacklist responses.

Path params:

ParamTypeDescription
idstringULID of the blacklist entry to remove

Request body (optional):

{ "reason": "string (default: 'manual removal')" }

Response 200:

{
"success": true
}

Example:

Terminal window
curl -X DELETE http://localhost:5006/api/sentinel/blacklist/01KQP25BM8RVZJ92CTANFDNTMG \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{ "reason": "false positive, cleared by review" }'

POST /api/sentinel/circuit-breaker/:id/cancel

Section titled “POST /api/sentinel/circuit-breaker/:id/cancel”

Auth: verifyJwt + requireOwner

Description: Cancel a circuit-breaker pending action (SQLite-backed). Calls cancelCircuitBreakerAction in packages/agent/src/sentinel/circuit-breaker.ts. The cancelled_by field is set to user:<wallet> from the JWT, or admin if unavailable.

Path params:

ParamTypeDescription
idstringULID of the circuit-breaker pending action

Request body (optional):

{ "reason": "string (default: 'manual cancel')" }

Response 200:

{ "success": true }

Returned when the action ID exists and was cancelled.

Response 404:

{ "error": { "code": "NOT_FOUND", "message": "pending action not found or already resolved" } }

Returned when the action ID does not exist or is already settled. A missing or already-resolved action returns this 404 envelope (not 200 + {success: false}).

Example:

Terminal window
curl -X POST http://localhost:5006/api/sentinel/circuit-breaker/01KQP25BM8RVZJ92CTANFDNTMG/cancel \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{ "reason": "admin override, action no longer needed" }'

Auth: verifyJwt + requireOwner

Description: List recent SENTINEL decisions — the assessment outcomes recorded each time SENTINEL evaluated an action. Optionally filter by source.

Query params:

ParamTypeDefaultDescription
limitnumber50Maximum decisions to return
sourcestringFilter by source label (e.g., agent, scanner, assess)

Response 200:

{
"decisions": []
}

Example:

Terminal window
curl -H "Authorization: Bearer $JWT" \
"http://localhost:5006/api/sentinel/decisions?limit=20&source=agent"

Auth: verifyJwt + requireOwner

Description: Return the current SENTINEL configuration snapshot — operating mode, preflight scope/thresholds, blacklistAutonomy, rate limits, scan intervals, autoRefundThreshold, model, dailyBudgetUsd, accrued dailyCostUsd, blockOnError, and the fundMovingTools list. See Configuration for what each field controls.

Response 200:

{
"mode": "advisory",
"preflightScope": "fund-moving",
"blacklistAutonomy": true,
"model": "openrouter:anthropic/claude-sonnet-4.6",
"dailyBudgetUsd": 10,
"dailyCostUsd": 0,
"blockOnError": false,
"fundMovingTools": ["executeRefund", "addToBlacklist"]
}

Example:

Terminal window
curl -H "Authorization: Bearer $JWT" \
http://localhost:5006/api/sentinel/config

POST /api/sentinel/promise-gate/:flagId/resolve

Section titled “POST /api/sentinel/promise-gate/:flagId/resolve”

Auth: verifyJwt + requireOwner

Description: Resolve a pending in-memory promise-gate flag — the admin approval path when SENTINEL is in advisory mode and has paused an action for human review. Calls resolvePending(flagId) in packages/agent/src/sentinel/pending.ts.

Path params:

ParamTypeDescription
flagIdstringID of the in-memory pending promise-gate flag

Response 204: No body — flag was found and resolved. The paused agent tool call proceeds.

Response 404:

{ "error": { "code": "NOT_FOUND", "message": "flag not found or expired" } }

Returned when no in-memory flag exists for the given flagId. Flags expire when the waiting agent times out.

Example:

Terminal window
curl -X POST http://localhost:5006/api/sentinel/promise-gate/flag_01KQP24PG0KZCJVWJDQM8H3JAY/resolve \
-H "Authorization: Bearer $JWT"

POST /api/sentinel/promise-gate/:flagId/reject

Section titled “POST /api/sentinel/promise-gate/:flagId/reject”

Auth: verifyJwt + requireOwner

Description: Reject a pending in-memory promise-gate flag — the admin denial path when SENTINEL is in advisory mode and has paused an action. Calls rejectPending(flagId, 'cancelled_by_user') in packages/agent/src/sentinel/pending.ts. The paused tool call receives a rejection and the agent aborts the action.

Path params:

ParamTypeDescription
flagIdstringID of the in-memory pending promise-gate flag

Response 204: No body — flag was found and rejected. The paused agent tool call receives a rejection error.

Response 404:

{ "error": { "code": "NOT_FOUND", "message": "flag not found or expired" } }

Returned when no in-memory flag exists for the given flagId.

Example:

Terminal window
curl -X POST http://localhost:5006/api/sentinel/promise-gate/flag_01KQP24PG0KZCJVWJDQM8H3JAY/reject \
-H "Authorization: Bearer $JWT"