These examples use
ehr.write, which a new organization does not have until you provision that action and a policy for it. Without them every call here returns deny. On a new account, start with the Developer / API quickstart: it uses the database.execute_sql demo action the console seeds for you.allow happens.
The Permit is the artifact you act on; the call site picks the form
that fits.
Both resolve to the same wire contract (
/v1-evaluate +
/v1-verify-permit) and produce the same audit-chain entry.
You can mix them freely in a codebase.
protect() — acquire a verified Permit
protect() is the primitive. It returns a verified
Permit on success and raises on anything else.
There is no permitted=False return path. If protect() returns,
the action is authorized end-to-end.
Permit is a first-class artifact:
permit_id next to your action record. That is the link that
makes the audit chain two-way navigable and that satisfies the
electronic-signature record requirement under 21 CFR Part 11 §11.50
(details).
with_permit() / withPermit() — the lexically-scoped form
When the action body is a single scope, bind it to the permit’s
lifetime directly. The body runs only on a verified permit; on
deny, hold, escalate, verification failure, or transport error it is
never invoked.
protect(). Same audit-chain entry. The
difference is purely lexical: the call site reads as one statement
that says “execute this body under a permit,” and the verified Permit
is visible inside the body if you need it.
What just happened
In a single call, the SDK:- Posted the request to
/v1-evaluate. The policy engine returnedallowand issued a signed Permit token (pt.v4.…) with a default 1-hour TTL. - Posted the permit to
/v1-verify-permit. The signature was checked, the TTL was checked, and the verification was bound into the audit chain. - Returned the verified Permit to you (or, with
with_permit()/withPermit(), invoked your body with it).
/v1/permits/{id} or the Console.
Other decisions
protect() and with_permit() raise AtlaSentDeniedError on any
non-allow outcome. If you need to inspect the four-value decision
(allow / deny / hold / escalate) at the call site — for
example, to route hold to an approval queue — drop down to the raw
evaluate() primitive:
evaluate() does not verify the permit for you. Callers using
the raw primitive must call verify_permit_by_id() / verifyPermitById()
before executing — see the next page.
Next
protect() already verified the permit for you. The next page covers
the explicit verify call — needed only when you hold a permit token
that crossed a boundary (e.g. between two services) and the receiving
side wants to re-confirm before acting.
Verify a permit explicitly →