Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
355 changes: 355 additions & 0 deletions docs/learn/poh-tutorial.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,355 @@
---
title: Proof of Humanity tutorial
description: Learn how to verify users with Proof of Humanity V2 on Linea.
image: /img/socialCards/verify-users-with-proof-of-humanity.jpg
---

Proof of Humanity (PoH) lets you verify whether a wallet belongs to a real human on Linea. It
provides a simple and reliable way for projects to gate access to rewards or features exclusively for
verified users.

PoH V2 is powered by a single attestation issued by Sumsub through the Verax Attestation Registry.
This version replaces the (too) complex multi-provider setup that was used for PoH V1, simplifying
verification to a single, trusted data source.

## The Sybil problem

In Web3, identity is fluid, meaning users can create as many wallets as they want. This openness
fuels innovation but also creates opportunities for abuse. A Sybil attack occurs when one actor
generates multiple fake identities to exploit a system designed for fair participation.

Common examples include:

- Airdrop farming, where a single individual controls thousands of wallets to claim rewards.
- Fake voting in DAOs or community governance, undermining collective decisions.
- Spam campaigns that flood onchain communities or social apps.
- Automated account creation to game loyalty programs or incentive systems.

Recent data shows how widespread this problem has become. Nansen and other web3 analytics firms
have revealed that a significant share of wallet activity in certain programs comes from non-human
or clustered entities. For instance, several major projects recently had to filter tens of millions
of wallets to preserve fairness in their distributions (e.g. Linea had to filter more than half a
million wallets to preserve our TGE from farmers).

The takeaway is clear: without human verification, decentralized systems remain vulnerable. Linea's
Proof of Humanity V2 addresses this by providing developers a reliable way to ensure "one human,
one action."

## Why verifying users matters

Verifying users isn't about restricting access - it's about building integrity into digital
communities and economies.

For developers, it means:

- Preventing fraud and abuse by excluding automated or duplicate accounts. Bots inflate all your
metrics, giving you misleading data about your app growth and you end up optimizing for noise, not
real adoption.
- Protecting incentive systems like governance tokens, loyalty points, or rewards programs. Bots can
quickly drain incentives systems without adding value, PoH helps you target your spending to real
users only.
- Reducing moderation costs by ensuring real humans drive community engagement.

For users, it builds:

- Trust and fairness, knowing that everyone participates on equal footing.
- Safety, by reducing exposure to spam, scams, or bots.
- Reputation portability, since a verified identity can be reused across multiple dapps within the
Linea ecosystem.

Verification also enables new categories of applications — from proof-of-personhood voting to
verified social reputation, identity-based access control, or even credit scoring.

## Unlocking growth through verification

Human verification isn't just a security measure — it's a growth accelerator.

When users trust that interactions are genuine, engagement and retention improve. Developers can
focus on building features instead of moderating spam or chasing fraudulent claims. Communities
become healthier, and token incentives circulate among real participants.

With PoH V2, Linea dapps can:

- Simplify onboarding with a single verification flow across the ecosystem.
- Enhance brand credibility by signaling transparency and integrity.
- Foster sustainable ecosystems, where every wallet represents a verified human being. This will
fuel every human-to-human interaction, knowing bots won't have a seat at the table.

Trust, in this context, becomes a competitive advantage, and a key differentiator for web3
applications aiming to scale responsibly.

## Verify: how users complete their Proof of Humanity

You can integrate Proof of Humanity in your app to restrict access, rewards or actions to real users
only. Use the **Sumsub Proof of Personhood flow** to allow users to verify their humanity directly
from your app. This flow handles user verification through **Sumsub's Proof of Personhood** flow and
issues a **Verax attestation** onchain.

:::tip Before you start
You'll need:

- A frontend app connected to the Linea network
- A reliable Linea Mainnet RPC provider such as [Infura](https://developer.metamask.io)
- A way to read the user's wallet address (e.g. [Wagmi](https://wagmi.sh/react/api/hooks/useAccount))
:::

### Typical flow

Here's how the complete user verification process works step-by-step:

1. The user connects their Ethereum wallet.
2. Your app queries the Linea PoH API to check whether the user is already verified. Endpoint:
`https://poh-api.linea.build/poh/v2/{address}` Documentation
[here](https://poh-api.linea.build/#/PoH/PohController_getOnePOHv2).

The endpoint returns `true` if the address is verified, or `false` otherwise. Use `false` to trigger
the Sumsub flow (next step).

3. Your app asks them to sign a message, containing 2 variables:

- Their wallet address
- A timestamp (ISO date)

The signature is used by Sumsub to confirm wallet ownership and link the verification to the correct
address onchain. A recommended way to sign the message is to use Wagmi's
[`useSignMessage` hook](https://wagmi.sh/react/api/hooks/useSignMessage):

```tsx
import {useAccount, useSignMessage} from "wagmi";

function App() {
const { address } = useAccount();
const { signMessage } = useSignMessage();

const date = new Date().toISOString();

return (
<button onClick={() => signMessage({
message: `in.sumsub.com wants you to sign in with your Ethereum account:\n${address}\n\nI confirm that I am the owner of this wallet and consent to performing a risk assessment and issuing a Verax attestation to this address.\n\nURI: https://in.sumsub.com\nVersion: 1\nChain ID: 59144\nIssued At: ${date}`,
})}>
Sign message
</button>
);
}
```

Note: this message contains the Linea Mainnet chain ID (59144).

4. Generate a link including this signed payload and redirect to it, or open it in an iframe. You
need to encode the JSON payload in base64 to safely include it in the Sumsub verification URL.

Payload to encode:

```json
{
"signInMessage": "in.sumsub.com wants you to sign in with your Ethereum account:\n<WALLET_ADDRESS>\n\nI confirm that I am the owner of this wallet and consent to performing a risk assessment and issuing a Verax attestation to this address.\n\nURI: https://in.sumsub.com\nVersion: 1\nChain ID: 59144\nIssued At: <ISO_DATE>",
"signature": "<SIGNATURE>"
}
```

You then need to generate a base64-encoded JSON from this payload, to be used as the `msg`
parameter to append to the URL below:

```typescript
const msg = btoa(JSON.stringify(payload));
const url = new URL("https://in.sumsub.com/websdk/p/uni_BKWTkQpZ2EqnGoY7");
url.search = new URLSearchParams({ msg }).toString();
```

5. The Sumsub verification process happens on their side.
6. Once verified, the user is redirected back to your app, or your frontend receives a message event
from the iframe wrapper. Listening for the event:

```typescript
const iframe = document.getElementById("sumsub-frame") as HTMLIFrameElement;
const ac = new AbortController();

window.addEventListener(
"message",
(e: MessageEvent) => {
if (e.source !== iframe.contentWindow) return;
if (e.origin !== "https://in.sumsub.com") return;

if (e.data.status === "completed") { /* proceed */ }

ac.abort();
},
{ signal: ac.signal },
);
```
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Bug

The Sumsub iframe integration example has two issues: the AbortController on line 175 removes the event listener prematurely, which can cause the app to miss the "completed" status. Also, the tutorial references an iframe by ID ('sumsub-frame') without explaining how to create it.

Fix in Cursor Fix in Web


Alternatively, you can redirect users back to your app via a callback URL instead of using an
iframe.

7. You can then query the Linea PoH API to confirm that the attestation has been issued onchain.
Endpoint: `https://poh-api.linea.build/poh/v2/{address}` Documentation
[here](https://poh-api.linea.build/#/PoH/PohController_getOnePOHv2).

### Notes

- The attestation is issued only if none exists for this wallet address.
- It may take a few seconds before the attestation is visible onchain after verification.
- Once verification is complete, you can programmatically confirm a user's PoH status using either
an API or an onchain contract (see below).

## Check: how to confirm verification status

Once users have completed verification, you can confirm their Proof of Humanity status through an
API call or onchain verification.

The APIs presented below are free to use, without any key or authentication.

### Offchain verification

The API base URL for the service is: `https://poh-api.linea.build/`

**Usage (GET):**

Call the endpoint with the format: `https://poh-api.linea.build/poh/v2/{address}`

**Example:**

```bash
curl https://poh-api.linea.build/poh/v2/0xc5fd29cC1a1b76ba52873fF943FEDFDD36cF46C6
# Content-Type: text/plain;
# Body: "true" | "false"
```

**Response:**

The response is raw text, not JSON.

- `false` = address does not have PoH status.
- `true` = address has PoH status.

**Example:**

```typescript
const res = await fetch(`.../poh/v2/${address}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const text = (await res.text()).trim(); // "true" | "false"
const isHuman = text === "true";
```

**Reference:**

You can explore all available endpoints and test them directly from the [Swagger
UI](https://poh-api.linea.build/#/PoH/PohController_getOnePOH).

### Signed onchain verification V2

If you need fully onchain verification (e.g. from a smart contract), you can use the signed PoH
status, and the `PohVerifier` contract. The
[`PohVerifier.sol` contract](https://lineascan.build/address/0xBf14cFAFD7B83f6de881ae6dc10796ddD7220831)
can be used together with a trusted source of PoH status data.

#### 1. Get signed PoH status

The API base URL for the service is: `https://poh-signer-api.linea.build/`

**Usage (GET):**

Call the endpoint with the format: `https://poh-signer-api.linea.build/poh/v2/{address}`

**Example:**

```bash
curl https://poh-signer-api.linea.build/poh/v2/0xc5fd29cC1a1b76ba52873fF943FEDFDD36cF46C6
# Content-Type: text/plain; charset=utf-8
# 0xa11a6c92fa0027d9de2a0c8ab363b1af083497da57f871c93aeb9efcd32ffaeb677fafb2c005e8165181713220b8a1da2f70ed31d7820b8fa086a8e7361dbf121c
```

This returns a signed message that contains the PoH status of the provided address. Response
format: plain text (not JSON).

Example response:

```
0xa11a6c92fa0027d9de2a0c8ab363b1af083497da57f871c93aeb9efcd32ffaeb677fafb2c005e8165181713220b8a1da2f70ed31d7820b8fa086a8e7361dbf121c
```

Example:

```typescript
const sig = await (await fetch(`https://poh-signer-api.linea.build/poh/v2/${address}`)).text();
await pohVerifier.verify(sig, address); // See step 2
```

#### 2. Call `PohVerifier.sol`

Call the `verify()` function with the signed message and the address of the account being queried.
The contract confirms that the signed message was issued by the trusted signer and returns a
boolean.

```solidity
function verify(
bytes memory signature,
address human
) external view virtual returns (bool){ ... }
```

Parameters:

- `signature`: The signed message from the previous step.
- `address`: The address of the account being queried.

It returns a boolean:

- `true`: The account has PoH status.
- `false`: The account does not have PoH status.

Example:

```solidity
import { IPohVerifier } from "./interfaces/IPohVerifier.sol";

error PohVerificationFailed(address sender);

/* ... */

if (!pohVerifier.verify(signature, msg.sender)) {
revert PohVerificationFailed(msg.sender);
}

/* ... */
```

## Ensuring security and reliability

Proof of Humanity V2 is built with robust security, privacy, and transparency in mind.

- Trusted verification: All attestations are issued by Sumsub, a regulated KYC provider with
industry-standard compliance.
- Onchain transparency: Every verification is recorded through the Verax Attestation Registry,
ensuring public verifiability.
- User privacy: Only the attestation (not personal data) is stored onchain — maintaining a balance
between identity assurance and privacy.
- System reliability: The PoH API handles rate limits, temporary downtimes, and blockchain
propagation gracefully, ensuring developers always receive accurate verification states.

Developers can confidently integrate PoH knowing it provides a secure, composable, and future-proof
foundation for identity-driven experiences.

### A note on privacy

When users complete the verification flow, Sumsub processes a face-video and biometric images to
check for liveness and identity-document match, but they only stores a hash, they do not store the
full image.

Sumsub adheres to the General Data Protection Regulation (GDPR) principles (lawful, transparent,
purpose-limited, minimal, secure), and implements ISO/IEC 27001/27017/27018, SOC 2 Type 2 and
PCI-DSS security frameworks, giving users the best standards for data protection and privacy.

For our most privacy savvy users, we're working on a privacy-preserving verification alternative that
will give you further control while still ensuring "one-human-one-wallet" trust.

## Conclusion

Proof of Humanity V2 is a true enable of growth for all builders and dapps on the Linea ecosystem.
By giving dapps the ability to verify real humans, easily, securely, and onchain, it helps transform
the way web3 applications grow and interact with their communities.

If you're building on Linea, PoH V2 is now live and available through our developer documentation.

Start verifying your users today, and build experiences meant for real humans.