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

# Security checklist

> What to lock down before real users and real documents flow through your integration.

A verification system handles the most sensitive data your users will ever give you, and it is a natural target. Most of what follows is standard practice; it is listed here because every item has been missed by someone.

## Credentials

* [ ] The OAuth client id and secret live in a secret manager, not in source, config files or environment files that get committed.
* [ ] They exist **only** on your backend. No mobile build, browser bundle or third-party service holds them.
* [ ] You know how to rotate them and who to contact when you need to.

<Warning>
  If a client secret ever appears in a repository, a log, a ticket or a chat, treat it as compromised and rotate it — even if the repository is private. Deleting the commit does not un-leak it.
</Warning>

## The token boundary

* [ ] Your app receives the SDK token from an endpoint **you** own, over **your** authentication. It never calls chmod to get one.
* [ ] That endpoint returns only the SDK token. The transaction id stays on your server, so a client cannot query someone else's transaction.
* [ ] That endpoint is rate-limited per user. Minting tokens costs you verification volume, and an unauthenticated or unlimited endpoint is an invitation to burn it.
* [ ] The time limit on each transaction is as short as your flow allows. A token for a flow the user starts immediately does not need 24 hours.

## Configuration comes from the server

* [ ] The transaction configuration is built on your backend from your own records. Nothing in it is copied from a value the app sent.
* [ ] The app cannot choose the verification type, the thresholds, or the accepted document list.

A modified client that can lower the liveness threshold or widen the accepted documents is weakening its own verification. The [SDK configuration](/sdk/configuration/overview) is safe to build in the app because it is cosmetic; the transaction configuration is not.

## Webhooks

* [ ] Your endpoint verifies the signature against the published keys **before** doing anything else.
* [ ] It rejects calls whose timestamp is older than a few minutes.
* [ ] It treats the webhook as a notification and fetches the transaction to read the decision, rather than trusting a status in the body.
* [ ] It is idempotent — the same notification twice does not apply twice.
* [ ] It is HTTPS and publicly reachable, but nothing else on that host trusts requests just because they arrived on it.

See [Webhooks](/results/webhooks) for the verification code.

## Personal data

The full result contains extracted document fields, references to document and face images, and device signals. It is personal data under every privacy regime you operate in.

* [ ] You store the minimum you act on: decision, finding codes, transaction id, timestamp. You fetch the full result by id when you need it rather than persisting it.
* [ ] Whatever you do persist is encrypted at rest and access-controlled to the people who need it.
* [ ] The full result never reaches your application logs. Log the transaction id and the decision, not the payload.
* [ ] You have a retention policy for whatever you keep, and it is enforced.
* [ ] Your privacy notice covers identity verification, and the SDK's in-flow privacy notices are reviewed by whoever owns that for you.

## What users are told

* [ ] Detailed error output is disabled in production builds of the SDK.
* [ ] Finding messages are never shown verbatim. Codes are mapped to your own copy, which tells a genuine user what to do and tells a fraudster nothing. See [Decisions and issues](/concepts/decisions-and-issues#what-to-show-the-person).
* [ ] Retries are capped, and fraud-signal findings go to manual review instead of offering another attempt.

## Device rules for real users

* [ ] Compromised-device and emulator detection are set to reject. If you relaxed them while testing on emulators, the configuration your real users get does not inherit that.
* [ ] Any rule you set to ignore was a deliberate decision, not a leftover.

## Operational

* [ ] Someone is alerted when the rate of undetermined decisions rises. It usually means a configuration change broke something.
* [ ] Webhook delivery failures and server errors from the API are monitored.
* [ ] A reconciliation job periodically fetches transactions that never reached a terminal status, so a lost webhook cannot leave a user stuck.


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