Use Kernall
Check an action
Call the gate before an operation, handle every decision, and keep retries tied to the same proposal.
Send a check
Send JSON to POST /api/check with an active project key in Authorization: Bearer <key>. X-API-Key is also accepted; prefer the Bearer header for new integrations.
curl --fail-with-body "$KERNALL_BASE_URL/api/check" \
-H "Authorization: Bearer $KERNALL_API_KEY" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: quickstart-submit-1' \
-d '{
"action_type": "submit",
"data": "Sample form content",
"destination": "https://example.com/form",
"task_context": "Testing a human review"
}'| Field | Required | Limit / behavior |
|---|---|---|
| action_type | Yes | Non-empty string, up to 64 characters; trimmed and lowercased. |
| data | No | String, up to 20,000 characters; evaluated in memory. |
| destination | No | String, up to 2,048 characters; URL or hostname. Needed when an allowlist is configured. |
| task_context | No | String, up to 1,000 characters; stored as context, not evaluated as a rule. |
Optional fields may be omitted or null. Strings are trimmed; empty optional strings are absent. All policy verdicts return HTTP 200, including denials. Inspect the JSON decision.
Handle the result
Execute only on explicit approval. Stop on denial. On requires_human, retain the proposal and wait for its confirmation. Treat errors, timeouts, and unknown decisions as blocked actions.
// Server-side JavaScript; keep the project key out of client bundles.
async function checkAction(action, stepId) {
const response = await fetch(
process.env.KERNALL_BASE_URL + "/api/check",
{
method: "POST",
headers: {
Authorization: "Bearer " + process.env.KERNALL_API_KEY,
"Content-Type": "application/json",
"Idempotency-Key": stepId,
},
body: JSON.stringify(action),
signal: AbortSignal.timeout(10000),
}
);
if (!response.ok) throw new Error("Check failed: HTTP " + response.status);
const verdict = await response.json();
if (!["approved", "denied", "requires_human"].includes(verdict.decision)) {
throw new Error("Unexpected Kernall decision");
}
return verdict;
}
// A thrown error must leave the action blocked.
const verdict = await checkAction(
{ action_type: "navigate", destination: "https://example.com" },
"agent-run-42-navigation-1"
);
if (verdict.decision === "approved") {
// Execute the same proposed action here.
} else if (verdict.decision === "requires_human") {
// Pause and poll the confirmation status. See Human approvals.
} else {
// Stop the action.
}Keep the action’s content and destination fixed while waiting. If either changes, make a new check. Kernall does not cryptographically bind the submitted data to your later tool call.
Retry without duplicate checks
Set Idempotency-Key to a stable identifier for one logical action, such as a run ID plus step ID. Keys are project-scoped and limited to 200 characters.
- Reuse the key after a timeout or uncertain response.
- Use a new key for another action or a fresh evaluation after a policy change.
- A replay returns the original check with
idempotent_replay: true. - The server does not compare bodies on replay. Reusing a key with different data still returns the old verdict.
- A replay keeps the initial human-review verdict. Poll the confirmation for its final decision.
Rate limits and failures
The limit is 120 new evaluations per project per fixed minute window. Fresh check responses include X-RateLimit-Remaining. A 429 includes Retry-After in seconds.
Wait that interval before retrying. Use bounded backoff for transient failures and keep the action blocked. Recognized replays return before consuming an evaluation slot; concurrent retries can still touch the rate counter.
See API errors and troubleshooting.