> For the complete documentation index, see [llms.txt](https://docs.lumia.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.lumia.org/build-on-lumia/identity/integrate-on-chain-kyc-dev-guide.md).

# Integrate on chain KYC dev guide

You want your app to gate an action behind proof of a KYC credential, verified without your app ever seeing the underlying personal data.

**Who this is for:** developers adding a KYC-gated flow (an allowlist, a mint, a transfer) to a Lumia app. **Prerequisites:** [Networks & Endpoints](https://app.gitbook.com/s/chmhPiVNAafGw48c4oU2/reference/networks-and-endpoints); [KYC (concept)](https://app.gitbook.com/s/chmhPiVNAafGw48c4oU2/ecosystem/lumia/compliance/kyc-know-your-customer) if you haven't already read what KYC means in Lumia's compliance model.

### How it works

The verifier flow is a two-call REST handshake, documented in full at [`/reference/rest-apis/kyc-verifier-api`](https://app.gitbook.com/s/chmhPiVNAafGw48c4oU2/reference/rest-apis). This page walks through using it; that page is the endpoint table.

1. Your backend calls `POST /sign-in` on the verifier, scoped to the network you're checking against.
2. The response includes a `qrCode` and a `sessionID`. Your frontend renders the QR code; the user scans it with their credential wallet.
3. Your backend polls `GET /status?sessionID=...` until it returns a terminal status (`success`/`approved`, or `failed`).
4. Only once status is successful does your app grant the gated action.

### Steps

{% stepper %}
{% step %}
**Pick the network you're scoping the check to**

Use its confirmed chain ID from [`/reference/networks`](https://app.gitbook.com/s/chmhPiVNAafGw48c4oU2/reference/networks-and-endpoints).

{% hint style="warning" %}
Do not use `1952959480`: that's a retired network some older examples label "Lumia Testnet," and it is not Beam.
{% endhint %}
{% endstep %}

{% step %}
**Start a session**

```js
const res = await fetch("https://verifier-backend.privado.id/sign-in", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    chainID: "2030232745", // Beam, see /reference/networks; do not use 1952959480
    scope: [
      {
        circuitId: "<your circuit ID>", // from your verifier/circuit configuration
        id: 1,
        query: {
          allowedIssuers: ["*"], // restrict this for a production check
          context: "<your query context>",
          type: "<your credential type>",
          credentialSubject: {},
        },
      },
    ],
    skipClaimRevocationCheck: false,
  }),
});
const { qrCode, sessionID } = await res.json();
```

{% endstep %}

{% step %}
**Render the QR code**

Render `qrCode` for the user to scan with their credential wallet.
{% endstep %}

{% step %}
**Poll for status**

```js
async function pollStatus(sessionID) {
  const res = await fetch(`https://verifier-backend.privado.id/status?sessionID=${sessionID}`);
  const { status } = await res.json();
  return status; // "success" / "approved" / "failed" / a pending state; see /reference/rest-apis/kyc-verifier-api
}
```

{% endstep %}

{% step %}
**Grant the gated action**

On a successful status, grant the gated action in your app/contract logic.
{% endstep %}
{% endstepper %}

### Verify it worked

Run the flow end to end with a test credential wallet: confirm `/sign-in` returns a scannable QR and a `sessionID`, that scanning it changes `/status` from pending to `success`/`approved`, and that your app's gated action becomes available only after that.

### Troubleshooting

<details>

<summary><code>/status</code> never leaves pending</summary>

Confirm the `chainID` you sent matches the network your credential wallet is actually scoped to; a mismatch here is easy to introduce if you copy an old example using `1952959480`.

</details>

<details>

<summary><code>circuitId</code>/<code>query</code> fields rejected</summary>

Get the exact expected values from your own verifier/circuit configuration rather than guessing from another chain's Privado ID integration.

</details>

<details>

<summary>No error codes to branch on</summary>

See [`/reference/rest-apis/kyc-verifier-api`](https://app.gitbook.com/s/chmhPiVNAafGw48c4oU2/reference/rest-apis) for the endpoint's response shape; handle non-200 responses generically if it doesn't cover what you're seeing.

</details>
