Skip to content

Start typing to search the documentation.

Basic Integration

The @selfxyz/core library provides the essential building blocks to integrate Self’s verification flows into your backend. It contains utilities, types, and helpers for working with scopes, configs, and verification data.

Within this library, the SelfBackendVerifier class is the main tool you’ll use on the server. It helps you verify that the proofs produced by the frontend/mobile flow are valid against the on‑chain hub and your configured rules.

Creating a verifier instance

You typically instantiate a SelfBackendVerifier once with your chosen scope, endpoint, and other settings.

import {
    SelfBackendVerifier,
    DefaultConfigStore,
    AllIds
} from '@selfxyz/core'

const selfBackendVerifier = new SelfBackendVerifier(
    'docs', // scope string
    'https://docs.self.xyz/api/verify', // endpoint (your backend verification API)
    true, // mockPassport → true = testnet, realPassport → false = mainnet
    AllIds, // allowed attestation IDs map
    new DefaultConfigStore({ // config store (see separate docs)
        minimumAge: 18,
        excludedCountries: ['USA'],
        ofac: false,
    }),
    'hex' // user identifier type
);

Parameters

  • scope: identifier for your application. Must match the frontend scope.
  • endpoint: the URL where your proofs will be verified (including the route). Must match the frontend endpoint.
  • mockPassport: toggle to use testnet/staging vs mainnet hub.
  • allowedIds: map of attestation IDs you want to accept.
  • configStorage: implementation of IConfigStorage (for now, use DefaultConfigStore or InMemoryConfigStore see configstore.md).
  • userIdentifierType: either 'uuid' or 'hex' (depending on how your frontend built the user identifier).

Verifying a proof

Call .verify() with the attestation ID, proof, public signals, and user context data. These will be passed to your endpoint from Self’s relayers. The verifier will check validity against on‑chain contracts and your config store.

const result = await selfBackendVerifier.verify(
    attestationId, // e.g. AttestationId.PASSPORT / BIOMETRIC_ID_CARD / AADHAAR / KYC
    proof, // zkSNARK proof object
    pubSignals, // array of public signals from prover
    userContextData //user context data
);

console.log(result)

Example output

{
    "attestationId": 1,
    "isValidDetails": {
        "isValid": true,
        "isMinimumAgeValid": true,
        "isOfacValid": false
    },
    "forbiddenCountriesList": ["USA"],
    "discloseOutput": {
        "minimumAge": "18",
        "nationality": "IND",
        "gender": "M"
    ...
    },
    "userData": {
        "userIdentifier": "8e4e6f24-...",
        "userDefinedData": "..." //what you pass in the qrcode
    }
}

Using in an API endpoint

You’ll usually expose this via a backend route that your Self’s relayers call after creating your proof.

import express from "express"
import bodyParser from "body-parser"
import { SelfBackendVerifier } from "@selfxyz/core"
import { AllIds } from "./utils/constants.js"
import { DefaultConfigStore } from "./store/DefaultConfigStore.js"

const app = express()
app.use(bodyParser.json())

const selfBackendVerifier = new SelfBackendVerifier(
  process.env.SELF_SCOPE_SEED,
  process.env.SELF_ENDPOINT,
  true,
  AllIds,
  new DefaultConfigStore({
    minimumAge: 18,
    excludedCountries: ["USA"],
    ofac: false,
  }),
  "hex"
)

app.post("/api/verify", async (req, res) => {
  try {
    const { attestationId, proof, publicSignals, userContextData } = req.body
    if (!proof || !publicSignals || !attestationId || !userContextData) {
      return res.status(200).json({
        status: "error",
        result: false,
        reason: "Proof, publicSignals, attestationId and userContextData are required",
      })
    }

    const result = await selfBackendVerifier.verify(
      attestationId,
      proof,
      publicSignals,
      userContextData
    );

    const { isValid, isMinimumAgeValid } = result.isValidDetails;
    if (!isValid || !isMinimumAgeValid) {
      let reason = "Verification failed"
      if (!isMinimumAgeValid) reason = "Minimum age verification failed"
      return res.status(200).json({
        status: "error",
        result: false,
        reason,
      })
    }

    return res.status(200).json({
      status: "success",
      result: true,
    })
  } catch (error) {
    return res.status(200).json({
      status: "error",
      result: false,
      reason: error instanceof Error ? error.message : "Unknown error",
    });
  }
})

app.listen(3000, () => {
  console.log("Server listening on http://localhost:3000");
})

Endpoint API Reference

POST /api/verify

Verifies a Self Identity proof.

Headers

NameValue
Content-Typeapplication/json

Body

NameTypeDescription
attestationIdnumberThe id of the document being verified.
proof
{
    a: [string, string],
    b: [[string, string],
        [string, string]],
    c: [string, string]
}
The self identity proof.
publicSignalsstring[]The public signals for the proof
userContextDatastringContains information about the user id and app defined user data.

Response

{
    status: "success",
    result: true,
}

Accepting only certain type of documents

If you want to only accept certain types of documents (for example if you don’t want to use Aadhaar or KYC) then you can create a map with only the attestation IDs you allow.

import { ATTESTATION_ID, AttestationId } from "@selfxyz/core"

const allowedIds = new Map<AttestationId, boolean>([
    [ATTESTATION_ID.PASSPORT, true],
    [ATTESTATION_ID.BIOMETRIC_ID_CARD, true],
    // [ATTESTATION_ID.AADHAAR, true],
    // [ATTESTATION_ID.KYC, true], // KYC
]);