- Capture is fail-open. Recording a decision must never take your agent down. If the platform is unreachable, the record is retried and then dropped with a warning, and your agent keeps running.
- Enforcement is fail-closed. Asking permission to act is different. If Sakshi cannot return an enforcement decision, the SDK raises rather than let an ungoverned action through.
Fail-open is the default for capture. Pass
fail_open=False to
SakshiClient for jobs where losing a record is worse than stopping, such as
batch pipelines. Capture then delivers synchronously and raises on failure.The SDK exception model
The SDK raises three exceptions, all fromenforce(). Import them from the top
level:
enforce() returns an EnforcementDecision whose outcome is one of auto,
async_review, sync_review, or block. By default (raise_on_hold=True) a
block raises SakshiBlocked and a sync_review raises
SakshiSyncReviewRequired, so the allowed path is the one that returns.
Handling enforcement
The common shape is atry/except around the intended action:
async_review allows the action to run now with review after the fact, so it
returns normally. If you would rather branch on the outcome yourself instead of
catching exceptions, pass raise_on_hold=False and read the decision:
SakshiEnforcementUnavailable is still raised even with raise_on_hold=False,
because an unreachable platform is not an outcome you can branch on. It is the
fail-closed guarantee.
Handling capture
Capture through awitness session does not raise in the default fail-open
mode. The record is queued and delivered by a background worker; if delivery
fails after retries, the record is dropped and a warning is logged to the
sakshi logger. Your with client.witness(...) block always completes.
An exception inside the witness block is recorded as the decision’s outcome and
re-raised, because failures are evidence too:
fail_open=False, capture delivers synchronously and raises the underlying
transport error instead of dropping the record. Use it only where a lost record
is worse than a stopped job.
HTTP status codes
If you call the API directly, or want to reason about what the SDK is reacting to, these are the statuses Sakshi returns.
Validation errors (
422) follow the standard schema for this API and list each
invalid field, so you can map the failure back to a specific parameter.
Retries and idempotency
The SDK’s capture layer retries transient failures with jittered exponential backoff up tomax_retries=3 (configurable on the client). A fleet
of agents backing off in lockstep would hammer a recovering platform, so the
backoff is jittered. After the last attempt it either drops the record
(fail-open) or raises (fail_open=False).
Not every call is safe to retry, so not every call is retried:
Pass a stable
client_ref to witness so a retried capture reconciles to the
same record rather than writing a second one.
Next steps
Bound autonomy
How envelopes route an action to auto, review, or block, and how the kill
switch halts an agent.
Python SDK
The client, the witness session, and the full exception reference.
Authentication
Ingest keys versus OIDC JWTs, and why a
403 usually means the wrong token
type.API reference
Every endpoint and its response schema, generated from the OpenAPI spec.

