> ## Documentation Index
> Fetch the complete documentation index at: https://docs.atlasent.io/llms.txt
> Use this file to discover all available pages before exploring further.

# 3. Verify a permit explicitly

> The lower-level verify call — used when a permit crossed a boundary and the receiving side wants to re-confirm before acting.

<Note>
  If you used [`protect()` / `with_permit()` / `withPermit()`](/quickstart/first-evaluation) on the previous page, the SDK already issued this verify call for you and bound it into the audit chain. **Read this page when you hold a raw permit token** — for example, a permit minted in one service and forwarded to another, or when you want to confirm a permit is still current immediately before a state-changing call.
</Note>

A permit token is not proof of authorization on its own — it's proof of authorization **if and only if** AtlaSent confirms it's current. Verification:

* Rejects tokens that were revoked (for example, policy changed between `/v1-evaluate` and execution).
* Rejects expired tokens (default TTL: 1 hour).
* Verifies the Ed25519 signature.
* Binds this permit to its execution in the audit chain.

## The request

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST "$ATLASENT_API_URL/v1/permits/$PERMIT_ID/verify" \
    -H "Authorization: Bearer $ATLASENT_API_KEY"
  ```

  ```typescript TypeScript theme={null}
  const verified = await client.verifyPermitById(result.permitId);
  if (verified.valid) {
    await executeTheAction();
  }
  ```

  ```python Python theme={null}
  verified = client.verify_permit_by_id(result.permit_token)
  if verified.valid:
      execute_the_action()
  ```
</CodeGroup>

## The response

The canonical verification envelope:

<ResponseField name="valid" type="boolean" required>
  `true` if the permit is current, signature-valid, and not revoked.
</ResponseField>

<ResponseField name="verification_type" type="string" required>
  Always `"permit"` on this surface.
</ResponseField>

<ResponseField name="reason" type="string">
  On `false`, an operator-readable explanation (e.g. "Permit expired",
  "Permit was explicitly revoked"). `null` on success.
</ResponseField>

<ResponseField name="verified_at" type="string" required>
  ISO 8601 server clock at verification.
</ResponseField>

<ResponseField name="evidence" type="object" required>
  Type-specific evidence: `{ permit_id, status, actor_id, action_id, expires_at, payload_hash, decision_id }`.
</ResponseField>

## When to call verify explicitly

The canonical execution-boundary surface — [`protect()` / `with_permit()` / `withPermit()`](/quickstart/first-evaluation) — already issues this verify call internally and binds it into the audit chain. Reach for the explicit verify call in these cases:

* **Permit crossed a process boundary.** Service A called `protect()` and forwarded the `permit_id` to service B; B wants to re-confirm the permit is still current before acting on it.
* **Long-running flow.** Time has elapsed between the policy decision and the state-changing call, and policy or actor state may have moved (e.g. operator revocation, TTL drift). Verify just before execute.
* **You held the permit token after `evaluate()` rather than `protect()`.** The raw `evaluate()` primitive does not verify — callers using it must verify before executing.

Permits are single-use. Once a permit has been consumed, verifying it again returns an error rather than a valid result — a spent permit cannot be replayed.

<Note>
  **Why a separate verify call?** Separating evaluate from verify lets you short-circuit on policy changes that happen between "I got permission" and "I'm doing the thing" — e.g. an operator revoking the permit, or the permit's TTL elapsing.
</Note>

## Next

Now [download the audit bundle →](/quickstart/audit-export)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.