> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.clossir.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.clossir.com/_mcp/server.

# Attest a User

GUIDES · ATTEST A USER

This guide walks you through the end-to-end attestation flow — initiating KYC verification,
creating an on-chain attestation, and verifying it.

## Prerequisites

* An organization with chains enabled ([Onboard an Organization](/guides/onboard-org))
* A user with a linked wallet
* An API key with `attestations:read` and `attestations:write` scopes

## 1. Install and authenticate

```ts title="client.ts"
import { SignumClient } from "@signum-tech/sdk";

const signum = new SignumClient({
  apiKey: process.env.SIGNUM_API_KEY,
});
```

## 2. Initiate KYC verification

Start the identity verification flow by selecting a verification level. The four levels are:
`basic`, `standard`, `enhanced`, and `institutional`.

```ts title="initiate-kyc.ts"
const kyc = await signum.kyc.initiate({
  level: "standard",
  userInfo: {
    email: "investor@example.com",
    firstName: "Jane",
    lastName: "Smith",
    country: "US",
  },
});

// kyc contains the provider URL/token for the verification flow
console.log(kyc);
```

The API returns `200` with the provider URL or token. Redirect the user to complete the
verification flow with the KYC provider.

## 3. Check KYC status

Poll the KYC status endpoint until verification completes:

```ts title="check-kyc-status.ts"
async function waitForKyc(): Promise<void> {
  const maxAttempts = 30;
  const intervalMs = 3000;

  for (let i = 0; i < maxAttempts; i++) {
    const status = await signum.kyc.getStatus();

    if (status.status === "approved") {
      console.log("KYC verification complete");
      return;
    }

    if (status.status === "rejected") {
      throw new Error("KYC verification rejected");
    }

    await new Promise((r) => setTimeout(r, intervalMs));
  }

  throw new Error("KYC verification timed out");
}
```

## 4. Create attestation (self-service path)

Once the user is verified, create an on-chain attestation. This is the self-service path where
the authenticated user attests their own wallet.

```ts title="create-attestation.ts"
const result = await signum.attestations.create({
  walletAddress: "0xabc...def",
  chainEid: 30101,
});

console.log(result.accepted);       // true
console.log(result.attestationId);  // "att_xyz789"
console.log(result.status);         // "pending"
console.log(result.message);        // Guidance on polling for completion
```

The API returns `202 Accepted` — the attestation is queued for on-chain settlement. The
`kycLevel` defaults to the user's verified level. You can also pass optional fields:

| Field            | Type      | Description                                          |
| ---------------- | --------- | ---------------------------------------------------- |
| `walletAddress`  | `string`  | Wallet address to attest (required)                  |
| `chainEid`       | `number`  | LayerZero Endpoint ID of the target chain (required) |
| `kycLevel`       | `number`  | KYC level 0-4 (defaults to user's verified level)    |
| `countryCode`    | `string`  | ISO 3166-1 alpha-2 country code                      |
| `expiresAt`      | `string`  | Attestation expiry (ISO 8601)                        |
| `sanctionsClear` | `boolean` | Sanctions clearance status                           |
| `isAccredited`   | `boolean` | Accredited investor flag                             |
| `riskScore`      | `number`  | Risk score (0-100)                                   |

## 5. Create attestation (org-initiated path)

Organizations can attest members on their behalf. This is useful for institutional onboarding
where the org has completed KYC outside of Clossir.

```ts title="org-attest.ts"
const result = await signum.orgs.members.attest(org.id, userId, {
  kycLevel: 2,
  countryCode: "US",
  reason: "Bank onboarding KYC complete",
});

console.log(result.accepted);      // true
console.log(result.attestations);   // Array of created attestations
console.log(result.message);
```

The API returns `202 Accepted`. Omitting `walletAddresses` attests all wallets linked to
the user. Omitting `chainEid` attests on all chains where the user has wallets.

The self-service path (step 4) requires the user to have completed KYC at the requested
level. The org-initiated path lets you attest members whose identity was verified outside
of Clossir — for example, through your existing institutional KYC process.

## 6. Verify an attestation

Verify the cryptographic integrity of an attestation by checking its hash:

```ts title="verify.ts"
const verification = await signum.attestations.verify(attestationId);

console.log(verification); // Hash verification result
```

## 7. Listen for events

Three attestation event channels notify you of lifecycle changes:

| Channel                             | Fires when                                        |
| ----------------------------------- | ------------------------------------------------- |
| `signum.attestation.created`        | A new attestation is issued                       |
| `signum.attestation.revoked`        | An attestation is revoked                         |
| `signum.attestation.sync_completed` | A cross-chain sync completes (success or failure) |

See [Subscribe to Webhooks](/guides/subscribe-webhooks) for how to receive these events, or
browse the full payload schemas in the [API Reference](/api/overview).

## Reference

* [API Reference](/api/overview) — operations and webhook payload schemas
* [Identity — API](/products/identity/api) — REST operations for identity

## Next steps

* [Define a Compliance Policy](/guides/define-policy) — set transfer rules for your assets
* [Async Operations](/getting-started/sdk/async-operations) — polling and webhook correlation patterns