diff --git a/docs/base-account/guides/verify-social-accounts.mdx b/docs/base-account/guides/verify-social-accounts.mdx
new file mode 100644
index 000000000..f435ed670
--- /dev/null
+++ b/docs/base-account/guides/verify-social-accounts.mdx
@@ -0,0 +1,888 @@
+---
+title: "Verify Social Accounts"
+description: "Use Base Verify to let users prove ownership of verified accounts (X, Coinbase, Instagram, TikTok) without sharing credentials, enabling Sybil-resistant airdrops, gated content, and identity-based rewards."
+---
+
+## What is Base Verify?
+
+Base Verify allows users to prove ownership of verified accounts on [X](https://x.com/), [Coinbase](https://www.coinbase.com/), [Instagram](https://www.instagram.com/), and [TikTok](https://www.tiktok.com/) without sharing credentials. Your app receives a deterministic token for Sybil resistance.
+
+Even if a wallet has few transactions, Base Verify reveals whether the user is high-value through their verified social accounts on X, Instagram, and TikTok or a [Coinbase One](https://www.coinbase.com/one) subscription. This lets you identify quality users regardless of onchain activity.
+
+**Example use cases:**
+
+- Token-gated airdrops or daily rewards
+- Exclusive content access (e.g., creator coins)
+- Identity-based rewards and loyalty programs
+
+If you still need wallet connection or message signing in your app, start with [Authenticate users](/base-account/guides/authenticate-users), [Sign and verify typed data](/base-account/guides/sign-and-verify-typed-data), or the [web React quickstart](/base-account/quickstart/web-react).
+
+---
+
+## Core concepts
+
+### Provider
+
+An identity platform that Base Verify integrates with. Currently supports **[X](https://x.com/)**, **[Coinbase](https://www.coinbase.com/)**, **[Instagram](https://www.instagram.com/)**, and **[TikTok](https://www.tiktok.com/)**.
+
+### Verification
+
+Cryptographic proof that a wallet owns an account with a specific provider.
+
+### Trait
+
+A specific attribute of the provider account that can be verified.
+
+**Examples:**
+
+- `verified: true` — X account has blue checkmark
+- `coinbase_one_active: true` — active Coinbase One subscription
+- `followers: gt:1000` — X account has over 1,000 followers
+- `followers_count: gte:5000` — Instagram account with 5,000+ followers
+- `video_count: gte:50` — TikTok account with 50+ videos
+
+### Action
+
+A developer-defined string that identifies what the user is doing with their verification. Actions let you issue different tokens for different use cases within the same app.
+
+**Examples:**
+
+- `claim_daily_reward` — claiming a daily reward
+- `join_allowlist` — joining an exclusive allowlist
+- `unlock_premium_content` — accessing gated content
+- `participate_in_raffle` — entering a raffle
+
+#### How actions work
+
+Actions are specified in the [Sign-In with Ethereum (SIWE)](https://eips.ethereum.org/EIPS/eip-4361) message resources:
+
+```typescript SIWE resources with action
+resources: [
+ 'urn:verify:provider:x',
+ 'urn:verify:action:claim_daily_reward'
+]
+```
+
+The action is returned in the API response:
+
+```json Verification response with action
+{
+ "token": "abc123...",
+ "action": "claim_daily_reward",
+ "wallet": "0x1234..."
+}
+```
+
+#### Why actions matter
+
+**Different actions produce different tokens.** This enables multiple independent claims from the same verified account:
+
+- User verifies X account with action `claim_airdrop` → Token: `abc123`
+- Same X account with action `join_allowlist` → Token: `def456` (different)
+- Same X account with action `claim_airdrop` again → Token: `abc123` (same as first)
+
+**Use cases:**
+
+- **Multiple campaigns** — run separate airdrops without interference
+- **Feature gating** — different tokens for different premium features
+- **Time-based events** — new action per event (e.g., `raffle_jan_2025`, `raffle_feb_2025`)
+
+#### Choosing action names
+
+Use descriptive, lowercase names with underscores:
+
+| Good | Bad |
+| :--- | :--- |
+| `claim_genesis_airdrop` | `airdrop` (too generic) |
+| `unlock_pro_features` | `action1` (meaningless) |
+| `enter_weekly_raffle` | `base_verify_token` (reserved/confusing) |
+
+
+Once you launch with an action name, don't change it. Changing the action generates different tokens for the same users, breaking your Sybil resistance.
+
+
+### Token — Sybil resistance
+
+A deterministic identifier tied to the provider account, not the wallet. **This is the key anti-Sybil mechanism.**
+
+#### How it works
+
+1. Wallet A verifies an X account → Base Verify returns `Token: abc123` → you have never seen it, so grant the airdrop.
+2. The same X account tries again with Wallet B → Base Verify returns `Token: abc123` → you have seen it, so block the duplicate claim.
+
+Without Base Verify, users could claim multiple times with different wallets. With Base Verify, one verified account = one token = one claim.
+
+#### Token properties
+
+- **Deterministic** — the same provider account always produces the same token
+- **Unique per provider** — a user's X token is different from their Instagram token
+- **Unique per app** — your app receives different tokens than other apps (privacy)
+- **Action-specific** — tokens vary based on the action in your SIWE message
+- **Persistent** — tokens don't expire or rotate (unless the user deletes their verification)
+- **Trait-independent** — tokens stay the same even if traits change (e.g., follower count increases)
+
+#### How to store tokens
+
+```json Verification token storage record
+{
+ token: "abc123...",
+ walletAddress: "0x1234...",
+ provider: "x",
+ claimedAt: "2024-01-15",
+}
+```
+
+#### Prevent double claims
+
+```typescript Prevent duplicate claims by token expandable lines
+async function claimAirdrop(verificationToken: string, walletAddress: string) {
+ const existingClaim = await db.findClaimByToken(verificationToken);
+
+ if (existingClaim) {
+ return { error: "This X account already claimed" };
+ }
+
+ await db.createClaim({
+ token: verificationToken,
+ wallet: walletAddress,
+ claimedAt: new Date()
+ });
+
+ return { success: true };
+}
+```
+
+---
+
+## Architecture and flow
+
+```text Base Verify architecture and verification flow
+ ┌─────────────┐
+ │ │ 1. User connects wallet
+ │ Your │
+ │ Mini App │
+ │ (Frontend) │
+ └──────┬──────┘
+ │
+ │ 2. App generates SIWE message (frontend)
+ │ • Includes wallet address
+ │ • Includes provider (x, coinbase, instagram, tiktok)
+ │ • Includes traits (verified:true, followers:gt:1000)
+ │ • Includes action (e.g. claim_airdrop)
+ │
+ │ 3. User signs SIWE message with wallet
+ │
+ │ 4. Send signature + message to YOUR backend
+ │
+ ▼
+ ┌──────────────┐
+ │ Mini App │ • Validates trait requirements
+ │ Backend │ • Verifies signature with Base Verify API
+ │ (Your API) │
+ └──────┬───────┘
+ │
+ ▼
+
+ 200 OK ←───────┌──────────────────┐───────→ 400
+ Verified! │ │ User has account
+ (DONE) │ Base Verify API │ but traits not met
+ │ verify.base.dev │ (DONE)
+ └────────┬─────────┘
+ │
+ │ 404 Not Found
+ ▼
+
+ 5. Redirect to Base Verify Mini App
+ │
+ ▼
+ ┌──────────────────────┐
+ │ Base Verify │ 6. User completes OAuth
+ │ Mini App │ (X, Coinbase, Instagram, TikTok)
+ │ verify.base.dev │ 7. Base Verify stores verification
+ └──────────┬───────────┘
+ │
+ │ 8. Redirects back to your app
+ ▼
+ ┌─────────────┐
+ │ Your │ 9. Check again (step 4)
+ │ Mini App │ → Now returns 200 or 400
+ └─────────────┘
+```
+
+### Your app's responsibilities
+
+- Generate SIWE messages with trait requirements
+- Handle user wallet connection
+- Redirect to the Base Verify Mini App when verification is not found
+- Store the returned verification token to prevent reuse
+- Keep your secret key secure on the backend
+
+### Base Verify's responsibilities
+
+- Validate SIWE signatures
+- Store provider verifications (X, Coinbase, Instagram, TikTok)
+- Check if verification meets trait requirements
+- Facilitate OAuth flow with providers
+- Return deterministic tokens for Sybil resistance
+
+### Response codes
+
+| Code | Meaning | Action |
+| :--- | :--- | :--- |
+| **200 OK** | Wallet has verified the provider account AND meets all trait requirements. Returns a unique token. | Grant access, store the token. |
+| **404 Not Found** | Wallet has never verified this provider. | Redirect user to the Base Verify Mini App. |
+| **400 Bad Request** (`verification_traits_not_satisfied`) | Wallet has verified the provider, but doesn't meet the trait requirements. | Show user they don't meet requirements. Do **not** redirect. |
+
+---
+
+## Getting started
+
+### Prerequisites
+
+1. **API key** — fill out the [interest form](https://forms.gle/6L4hWAHkojYcefz27) to get access
+2. **Wallet integration** — users must be able to connect and sign messages. See [Authenticate users](/base-account/guides/authenticate-users) or the [web React quickstart](/base-account/quickstart/web-react)
+3. **Backend server** — to securely call the Base Verify API and keep your secret key private. For a similar frontend-to-backend signing pattern, see [Sign and verify typed data](/base-account/guides/sign-and-verify-typed-data)
+
+### Register your app
+
+Provide the Base Verify team:
+
+1. Your **Mini App domain**
+2. Your **redirect URI** — where users return after verification (e.g., `https://yourapp.com`)
+
+
+Your secret key must **never** be exposed in frontend code. All Base Verify API calls must go through your backend.
+
+
+---
+
+## Implementation
+
+
+
+
+Create a configuration file for your Base Verify integration:
+
+```typescript lib/config.ts highlight={3}
+export const config = {
+ appUrl: 'https://your-app.com',
+ baseVerifySecretKey: process.env.BASE_VERIFY_SECRET_KEY,
+ baseVerifyApiUrl: 'https://verify.base.dev/v1',
+ baseVerifyMiniAppUrl: 'https://verify.base.dev',
+}
+```
+
+Add your secret key to `.env.local`:
+
+```shell .env.local
+BASE_VERIFY_SECRET_KEY=your_secret_key_here
+```
+
+
+
+
+
+Build a [SIWE](https://eips.ethereum.org/EIPS/eip-4361) message that includes the provider, trait requirements, and action:
+
+```typescript lib/signature-generator.ts expandable lines highlight={7-12,15-24}
+import { SiweMessage, generateNonce } from 'siwe'
+import { config } from './config'
+
+export async function generateSignature(
+ signMessageFunction: (message: string) => Promise,
+ address: string
+) {
+ const resources = [
+ 'urn:verify:provider:x',
+ 'urn:verify:provider:x:verified:eq:true',
+ 'urn:verify:provider:x:followers:gte:100',
+ 'urn:verify:action:claim_airdrop'
+ ]
+
+ const siweMessage = new SiweMessage({
+ domain: new URL(config.appUrl).hostname,
+ address,
+ statement: 'Verify your X account',
+ uri: config.appUrl,
+ version: '1',
+ chainId: 8453,
+ nonce: generateNonce(),
+ issuedAt: new Date().toISOString(),
+ expirationTime: new Date(Date.now() + 6 * 60 * 60 * 1000).toISOString(),
+ resources,
+ })
+
+ const message = siweMessage.prepareMessage()
+ const signature = await signMessageFunction(message)
+
+ return { message, signature, address }
+}
+```
+
+
+
+
+
+The frontend generates the signature and sends it to **your** backend, which calls the Base Verify API.
+
+**Frontend:**
+
+```typescript Frontend verification check expandable lines highlight={2-8,11-19}
+async function checkVerification(address: string) {
+ const signature = await generateSignature(
+ async (msg) => {
+ return new Promise((resolve, reject) => {
+ signMessage(
+ { message: msg },
+ { onSuccess: resolve, onError: reject }
+ )
+ })
+ },
+ address
+ )
+
+ const response = await fetch('/api/check-verification', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ signature: signature.signature,
+ message: signature.message,
+ address: address
+ })
+ })
+
+ const data = await response.json();
+ return data;
+}
+```
+
+**Backend (your API endpoint):**
+
+```typescript pages/api/check-verification.ts expandable lines highlight={5-8,15,19-24}
+import { validateTraits } from '../../lib/trait-validator';
+
+export default async function handler(req, res) {
+ const { signature, message, address } = req.body;
+
+ const expectedTraits = {
+ 'verified': 'true',
+ 'followers': 'gte:100'
+ };
+
+ const validation = validateTraits(message, 'x', expectedTraits);
+
+ if (!validation.valid) {
+ return res.status(400).json({
+ error: 'Invalid trait requirements in message',
+ details: validation.error
+ });
+ }
+
+ const response = await fetch('https://verify.base.dev/v1/base_verify_token', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${process.env.BASE_VERIFY_SECRET_KEY}`,
+ },
+ body: JSON.stringify({
+ signature: signature,
+ message: message,
+ })
+ });
+
+ if (response.ok) {
+ const data = await response.json();
+ return res.status(200).json({ verified: true, token: data.token });
+ } else if (response.status === 404) {
+ return res.status(404).json({ verified: false, needsVerification: true });
+ } else if (response.status === 400) {
+ const data = await response.json();
+ if (data.message === 'verification_traits_not_satisfied') {
+ return res.status(400).json({ verified: false, traitsNotMet: true });
+ }
+ }
+
+ return res.status(500).json({ error: 'Verification check failed' });
+}
+```
+
+
+Your backend **must** validate that the trait requirements in the SIWE message match what your backend expects. This prevents users from modifying trait requirements on the frontend to bypass your access controls. See [security best practices](#validate-trait-requirements) for details.
+
+
+
+
+
+
+If you receive a 404 response, redirect the user to the Base Verify Mini App to complete OAuth:
+
+```typescript Open the Base Verify Mini App highlight={3-6,9}
+function redirectToVerifyMiniApp(provider: string) {
+ const params = new URLSearchParams({
+ redirect_uri: config.appUrl,
+ providers: provider,
+ })
+
+ const miniAppUrl = `${config.baseVerifyMiniAppUrl}?${params.toString()}`
+
+ const deepLink = `cbwallet://miniapp?url=${encodeURIComponent(miniAppUrl)}`
+ window.open(deepLink, '_blank')
+}
+```
+
+After verification, the user returns to your `redirect_uri` with `?success=true`. Run the check again (step 3) and it now returns 200 with a token. If you're building for the Base app, see the [Mini apps overview](/mini-apps/introduction/overview) for broader app structure and lifecycle guidance.
+
+
+
+
+### Error handling
+
+| Response | What to do |
+| :--- | :--- |
+| **404** | User hasn't verified. Redirect to the Base Verify Mini App. |
+| **400** (`verification_traits_not_satisfied`) | User has account but doesn't meet requirements. Show a message — don't redirect and don't retry. |
+| **200** | Store the token and grant access. |
+
+
+Do **not** retry 404 responses — the user simply hasn't verified yet. Do **not** retry 400 responses with `verification_traits_not_satisfied` — retrying won't help unless the user's account metrics change (e.g., they gain more followers).
+
+
+---
+
+## API reference
+
+### Authentication
+
+All API requests require your secret key in the `Authorization` header:
+
+```http Authorization header
+Authorization: Bearer YOUR_SECRET_KEY
+```
+
+### POST /v1/base_verify_token
+
+Check if a wallet has a specific verification and retrieve the verification token.
+
+#### Request
+
+```json POST /v1/base_verify_token request body
+{
+ signature: string, // SIWE signature from wallet
+ message: string // SIWE message (includes provider/traits in resources)
+}
+```
+
+#### Example request
+
+```bash POST /v1/base_verify_token cURL example wrap
+curl -X POST https://verify.base.dev/v1/base_verify_token \
+ -H "Content-Type: application/json" \
+ -H "Authorization: Bearer YOUR_SECRET_KEY" \
+ -d '{
+ "signature": "0x1234...",
+ "message": "verify.base.dev wants you to sign in..."
+ }'
+```
+
+#### Responses
+
+**200 OK — verified:**
+
+```json 200 OK response
+{
+ "token": "abc123...",
+ "action": "claim_airdrop",
+ "wallet": "0x1234..."
+}
+```
+
+| Field | Type | Description |
+| :--- | :--- | :--- |
+| `token` | string | Deterministic verification token for Sybil resistance. Same provider account + same action = same token. |
+| `action` | string | The custom action specified in the SIWE message. Different actions produce different tokens. |
+| `wallet` | string | User's wallet address. |
+
+**404 Not Found — verification not found:**
+
+```json 404 not found response
+{
+ "error": "verification_not_found"
+}
+```
+
+Redirect the user to the Base Verify Mini App to complete verification.
+
+**400 Bad Request — traits not satisfied:**
+
+```json 400 traits not satisfied response
+{
+ "code": 9,
+ "message": "verification_traits_not_satisfied",
+ "details": []
+}
+```
+
+The user has the provider account but doesn't meet trait requirements. Do not redirect.
+
+**401 Unauthorized — invalid key:**
+
+```json 401 unauthorized response
+{
+ "error": "unauthorized"
+}
+```
+
+Check that your secret key is correct and included in the Authorization header.
+
+### Mini App redirect
+
+To redirect users to Base Verify for verification:
+
+```text Base Verify redirect URL format
+https://verify.base.dev?redirect_uri={your_app_url}&providers={provider}
+```
+
+| Parameter | Required | Description | Example |
+| :--- | :--- | :--- | :--- |
+| `redirect_uri` | Yes | Where to send the user after verification | `https://yourapp.com` |
+| `providers` | Yes | Provider to verify | `x`, `coinbase`, `instagram`, `tiktok` |
+
+---
+
+## Trait catalog
+
+Traits are specific attributes of a provider account that you can verify. They are specified in [SIWE](https://eips.ethereum.org/EIPS/eip-4361) message resources using this format:
+
+```text Trait resource format
+urn:verify:provider:{provider}:{trait_name}:{operation}:{value}
+```
+
+### Operations
+
+| Operation | Symbol | Applies to | Description | Example |
+| :--- | :--- | :--- | :--- | :--- |
+| Equals | `eq` | All types | Exact match | `verified:eq:true` |
+| Greater than | `gt` | Integers | Strictly greater | `followers:gt:1000` |
+| Greater/equal | `gte` | Integers | Greater or equal | `followers:gte:1000` |
+| Less than | `lt` | Integers | Strictly less | `followers:lt:5000` |
+| Less/equal | `lte` | Integers | Less or equal | `followers:lte:5000` |
+| In (list) | `in` | Strings | Value in comma-separated list | `verified_type:in:blue,government` |
+
+### Type system
+
+**Boolean traits:**
+
+- Values: `"true"` or `"false"` (as strings)
+- Only supports `eq` operation
+- Example: `verified:eq:true`
+
+**Integer traits:**
+
+- Values: numbers as strings
+- Supports: `eq`, `gt`, `gte`, `lt`, `lte`
+- Example: `followers:gte:1000`
+
+**String traits:**
+
+- Values: text strings
+- Supports: `eq`, `in`
+- Example: `verified_type:eq:blue` or `verified_type:in:blue,government`
+
+### Combining traits
+
+When you specify multiple traits for the same provider, **all** must be satisfied (AND logic):
+
+```typescript Multiple traits for one provider
+resources: [
+ 'urn:verify:provider:x',
+ 'urn:verify:provider:x:verified:eq:true',
+ 'urn:verify:provider:x:followers:gte:10000'
+]
+```
+
+
+You can only check one provider per request. To check multiple providers, make separate API calls.
+
+
+### Common patterns
+
+**Tiered access:**
+
+```typescript Tiered access trait rules
+// Bronze tier: any verified account
+traits: { 'verified': 'true' }
+
+// Silver tier: 1k+ followers
+traits: { 'followers': 'gte:1000' }
+
+// Gold tier: 10k+ followers
+traits: { 'followers': 'gte:10000' }
+```
+
+---
+
+### Coinbase
+
+**Provider:** `coinbase` ([Coinbase](https://www.coinbase.com/))
+
+| Trait | Type | Operations | Description | Example values |
+| :--- | :--- | :--- | :--- | :--- |
+| `coinbase_one_active` | Boolean | `eq` | Active [Coinbase One](https://www.coinbase.com/one) subscription | `"true"`, `"false"` |
+| `coinbase_one_billed` | Boolean | `eq` | User has been billed for [Coinbase One](https://www.coinbase.com/one) | `"true"`, `"false"` |
+
+```typescript Coinbase trait examples
+// Check for Coinbase One subscribers
+{
+ provider: 'coinbase',
+ traits: { 'coinbase_one_active': 'true' }
+}
+
+// Check for billed Coinbase One subscribers
+{
+ provider: 'coinbase',
+ traits: { 'coinbase_one_billed': 'true' }
+}
+```
+
+### X
+
+**Provider:** `x` ([X](https://x.com/))
+
+| Trait | Type | Operations | Description | Example values |
+| :--- | :--- | :--- | :--- | :--- |
+| `verified` | Boolean | `eq` | Has any type of verification | `"true"`, `"false"` |
+| `verified_type` | String | `eq` | Type of verification | `"blue"`, `"government"`, `"business"`, `"none"` |
+| `followers` | Integer | `eq`, `gt`, `gte`, `lt`, `lte` | Number of followers | `"1000"`, `"50000"` |
+
+```typescript X trait examples expandable lines
+// Check for any verified account
+{
+ provider: 'x',
+ traits: { 'verified': 'true' }
+}
+
+// Check for specific verification type
+{
+ provider: 'x',
+ traits: { 'verified_type': 'blue' }
+}
+
+// Check for follower count (greater than or equal to)
+{
+ provider: 'x',
+ traits: { 'followers': 'gte:1000' }
+}
+
+// Combine multiple traits
+{
+ provider: 'x',
+ traits: {
+ 'verified': 'true',
+ 'followers': 'gte:10000'
+ }
+}
+```
+
+### Instagram
+
+**Provider:** `instagram` ([Instagram](https://www.instagram.com/))
+
+| Trait | Type | Operations | Description | Example values |
+| :--- | :--- | :--- | :--- | :--- |
+| `username` | String | `eq` | Instagram username | `"john_doe"` |
+| `followers_count` | Integer | `eq`, `gt`, `gte`, `lt`, `lte` | Number of followers | `"1000"`, `"50000"` |
+| `instagram_id` | String | `eq` | Unique Instagram user ID | `"1234567890"` |
+
+```typescript Instagram trait examples expandable lines
+// Check for follower count (greater than)
+{
+ provider: 'instagram',
+ traits: { 'followers_count': 'gt:1000' }
+}
+
+// Check for follower count (greater than or equal to)
+{
+ provider: 'instagram',
+ traits: { 'followers_count': 'gte:5000' }
+}
+
+// Combine multiple traits
+{
+ provider: 'instagram',
+ traits: {
+ 'username': 'john_doe',
+ 'followers_count': 'gte:10000'
+ }
+}
+```
+
+### TikTok
+
+**Provider:** `tiktok` ([TikTok](https://www.tiktok.com/))
+
+| Trait | Type | Operations | Description | Example values |
+| :--- | :--- | :--- | :--- | :--- |
+| `open_id` | String | `eq` | TikTok Open ID (unique per app) | `"abc123..."` |
+| `union_id` | String | `eq` | TikTok Union ID (unique across apps) | `"def456..."` |
+| `display_name` | String | `eq` | TikTok display name | `"John Doe"` |
+| `follower_count` | Integer | `eq`, `gt`, `gte`, `lt`, `lte` | Number of followers | `"1000"`, `"50000"` |
+| `following_count` | Integer | `eq`, `gt`, `gte`, `lt`, `lte` | Number of accounts following | `"500"`, `"2000"` |
+| `likes_count` | Integer | `eq`, `gt`, `gte`, `lt`, `lte` | Total likes received | `"10000"`, `"100000"` |
+| `video_count` | Integer | `eq`, `gt`, `gte`, `lt`, `lte` | Number of videos posted | `"50"`, `"200"` |
+
+```typescript TikTok trait examples expandable lines
+// Check for follower count
+{
+ provider: 'tiktok',
+ traits: { 'follower_count': 'gt:1000' }
+}
+
+// Check for likes count
+{
+ provider: 'tiktok',
+ traits: { 'likes_count': 'gte:10000' }
+}
+
+// Combine multiple traits (e.g., active creator)
+{
+ provider: 'tiktok',
+ traits: {
+ 'follower_count': 'gte:5000',
+ 'likes_count': 'gte:100000',
+ 'video_count': 'gte:100'
+ }
+}
+```
+
+---
+
+## Security and privacy
+
+### SIWE signature requirement
+
+Every API call requires a valid [SIWE](https://eips.ethereum.org/EIPS/eip-4361) signature from the wallet owner. This prevents:
+
+- Arbitrary lookup of verification status
+- Third parties checking if a wallet is verified
+- Enumeration attacks
+
+The user signs a structured message proving they control the wallet and agree to check specific traits:
+
+```json SIWE payload example
+{
+ domain: "your-app.com",
+ address: "0x1234...",
+ chainId: 8453,
+ resources: [
+ "urn:verify:provider:x",
+ "urn:verify:provider:x:verified:eq:true",
+ "urn:verify:action:claim_airdrop"
+ ]
+}
+```
+
+### Validate trait requirements
+
+
+When your backend receives a SIWE message from the frontend, you **must** validate that the trait requirements in the message match what your backend expects. This prevents users from modifying trait requirements on the frontend to bypass your access controls.
+
+
+**Example attack without validation:**
+
+1. Your app requires users to have 100 followers
+2. User modifies the frontend to request only 10 followers
+3. User signs the modified message
+4. Without validation, your backend forwards the request to Base Verify
+5. User gains access with fewer than 100 followers
+
+**Implementation:**
+
+```typescript Validate traits on the backend lines highlight={2-4,7}
+import { validateTraits } from './lib/trait-validator';
+
+const expectedTraits = {
+ 'followers': 'gte:100'
+};
+
+const validation = validateTraits(message, 'x', expectedTraits);
+
+if (!validation.valid) {
+ return res.status(400).json({
+ error: 'Invalid trait requirements in message',
+ details: validation.error
+ });
+}
+```
+
+### Protect your secret key
+
+**Never:**
+
+- Include the secret key in frontend code
+- Use `NEXT_PUBLIC_*` or similar environment variables that expose to the browser
+- Commit secret keys to version control
+- Share secret keys in chat, email, or documentation
+
+**Always:**
+
+- Store the secret key in backend environment variables only
+- Use `.env` files that are gitignored
+- Rotate keys immediately if accidentally exposed
+- Call the Base Verify API only from your backend
+
+### OAuth security model
+
+Base Verify validates provider accounts through [OAuth](https://oauth.net/2/):
+
+1. User initiates OAuth in the Base Verify Mini App
+2. Provider (X, Instagram, etc.) authenticates the user
+3. Provider returns an OAuth token to Base Verify
+4. Base Verify fetches account data using the OAuth token
+5. Base Verify stores the verification linked to the user's wallet
+6. OAuth token is encrypted and stored securely
+
+Your app never handles OAuth tokens or redirects — this is all handled within the Base Verify Mini App.
+
+### Data storage
+
+**What Base Verify stores:**
+
+- Wallet addresses associated with verified provider accounts
+- Provider account metadata (username, follower counts, verification status)
+- OAuth tokens (encrypted, never shared with apps)
+- Verification timestamps
+
+**What Base Verify does not store:**
+
+- Users' private keys
+- Provider account passwords
+- User activity or browsing history
+- Any data beyond what's needed for verification
+
+**What your app receives:**
+
+When you call `/v1/base_verify_token`, you receive only `token`, `action`, and `wallet`. No PII is returned.
+
+### User control
+
+Users can delete their verifications at any time:
+
+- Removes all stored provider data
+- Invalidates future token generation
+- Your app's stored tokens become meaningless (user can't re-verify with the same account)
+
+### Caching
+
+Cache verification results to reduce API calls:
+
+- Cache for the user's session (not permanently)
+- Clear cache when the user disconnects their wallet
+- Don't check verification on every page load
+
+---
+
+## Support
+
+**Want to integrate Base Verify?** Fill out the [interest form](https://forms.gle/6L4hWAHkojYcefz27) and the team will reach out with API access.
diff --git a/docs/base-account/llms-full.txt b/docs/base-account/llms-full.txt
index 5618fa5be..945264e43 100644
--- a/docs/base-account/llms-full.txt
+++ b/docs/base-account/llms-full.txt
@@ -2,11 +2,12 @@
## Base Account — Deep Guide for LLMs
-> Base Account is a passkey‑secured, ERC‑4337 smart wallet with universal sign‑in, USDC payments, sponsored gas, batch transactions, spend permissions, and sub‑accounts.
+> Base Account is a passkey‑secured, ERC‑4337 smart wallet with universal sign‑in, USDC payments, social verification, sponsored gas, batch transactions, spend permissions, and sub‑accounts.
### What you can do here
- Add Base Account to web and mobile apps
- Authenticate users with SIWE + ERC‑6492
+- Verify social and Coinbase One accounts for Sybil-resistant rewards and gated access
- Accept USDC with Base Pay and sponsor gas with a paymaster
- Use batch transactions and ERC‑20 gas payments
- Grant revocable spend permissions and create app‑scoped sub‑accounts
@@ -33,10 +34,12 @@ const { status } = await getPaymentStatus({ id })
### Guides
- [Authenticate Users](https://docs.base.org/base-account/guides/authenticate-users.md) — SIWE + ERC‑6492
- [Accept Payments](https://docs.base.org/base-account/guides/accept-payments.md) — Base Pay
+- [Accept Recurring Payments](https://docs.base.org/base-account/guides/accept-recurring-payments.md) — Subscriptions
- [Batch Transactions](https://docs.base.org/base-account/improve-ux/batch-transactions.md) — Multi‑call
- [Paymasters](https://docs.base.org/base-account/improve-ux/sponsor-gas/paymasters.md) — Sponsor gas
- [ERC‑20 Paymasters](https://docs.base.org/base-account/improve-ux/sponsor-gas/erc20-paymasters.md) — ERC‑20 gas
- [Spend Permissions](https://docs.base.org/base-account/improve-ux/spend-permissions.md) — Spending
+- [Verify Social Accounts](https://docs.base.org/base-account/guides/verify-social-accounts.md) — Base Verify
- [Sub‑Accounts](https://docs.base.org/base-account/improve-ux/sub-accounts.md) — App‑scoped accounts
- [MagicSpend](https://docs.base.org/base-account/improve-ux/magic-spend.md) — Coinbase balance spend
- [Sign & Verify Data](https://docs.base.org/base-account/guides/sign-and-verify-typed-data.md) — Signatures
@@ -219,6 +222,25 @@ await provider.request({
})
```
+Example: Verify a social account and store the deterministic token
+
+Source: `https://docs.base.org/base-account/guides/verify-social-accounts.md`
+
+```ts
+const response = await fetch('/api/base-verify/check', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ signature, message })
+})
+
+if (response.status === 404) {
+ redirectToVerifyMiniApp('x')
+} else if (response.ok) {
+ const { token, action } = await response.json()
+ await db.saveVerificationToken({ token, action, walletAddress })
+}
+```
+
Example: Create and use a sub‑account for scoped spend
Source: `https://docs.base.org/base-account/improve-ux/sub-accounts.md`
diff --git a/docs/base-account/llms.txt b/docs/base-account/llms.txt
index a8646e527..3ec15fc8a 100644
--- a/docs/base-account/llms.txt
+++ b/docs/base-account/llms.txt
@@ -2,19 +2,22 @@
## Base Account Documentation
-> Base Account is a passkey-secured, ERC-4337 smart wallet with one‑tap payments, spend permissions, and sub‑accounts—built for seamless multi‑chain apps.
+> Base Account is a passkey-secured, ERC-4337 smart wallet with one‑tap payments, social verification, spend permissions, and sub‑accounts—built for seamless multi‑chain apps.
## Introduction
- [What is Base Account?](https://docs.base.org/base-account/overview/what-is-base-account.md) — Core concepts and benefits
## Quickstart
- [Web (Next.js)](https://docs.base.org/base-account/quickstart/web.md) — Add Base Account to a web app
+- [Web (React)](https://docs.base.org/base-account/quickstart/web-react.md) — React quickstart for wallet connection and signing
- [React Native Integration](https://docs.base.org/base-account/quickstart/mobile-integration.md) — Mobile setup and flows
## Guides
- [Authenticate Users](https://docs.base.org/base-account/guides/authenticate-users.md) — SIWE with ERC‑6492 signatures
- [Accept Payments](https://docs.base.org/base-account/guides/accept-payments.md) — One‑tap USDC payments with Base Pay
- [Accept Recurring Payments](https://docs.base.org/base-account/guides/accept-recurring-payments.md) — Subscription payments with Base Pay
+- [Verify Social Accounts](https://docs.base.org/base-account/guides/verify-social-accounts.md) — Base Verify for Sybil-resistant rewards and gated access
+- [Sign & Verify Data](https://docs.base.org/base-account/guides/sign-and-verify-typed-data.md) — Typed data signing and backend verification
## Framework Integrations
- [Wagmi: Setup](https://docs.base.org/base-account/framework-integrations/wagmi/setup.md) — Configure connectors and chains
diff --git a/docs/docs.json b/docs/docs.json
index 446f38a5c..a6415806c 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -234,6 +234,7 @@
"base-account/improve-ux/sponsor-gas/paymasters",
"base-account/improve-ux/sub-accounts",
"base-account/improve-ux/spend-permissions",
+ "base-account/guides/verify-social-accounts",
"base-account/improve-ux/magic-spend",
"base-account/guides/sign-and-verify-typed-data",
"base-account/improve-ux/sponsor-gas/erc20-paymasters"
diff --git a/docs/llms-full.txt b/docs/llms-full.txt
index 0ab198984..0b82a1de1 100644
--- a/docs/llms-full.txt
+++ b/docs/llms-full.txt
@@ -21,11 +21,18 @@ Sources:
### Authentication and API usage
Sources:
- `https://docs.base.org/base-account/guides/authenticate-users.md`
+- `https://docs.base.org/base-account/guides/verify-social-accounts.md`
- `https://docs.base.org/mini-apps/features/Authentication.md`
- `https://docs.base.org/onchainkit/config/onchainkit-provider.md`
> Auth patterns vary by product. Use SIWE (ERC‑6492 compatible) for user sign-in where smart wallets may not yet exist. In Mini Apps, defer auth until needed; prefer scoped, revocable permissions and transaction trays for intent. For SDK/server APIs (e.g., OnchainKit), supply API keys via environment variables, rotate regularly, and verify server-side signatures or webhooks. Never store user passkeys or raw private keys server-side.
+### Identity verification and Sybil resistance
+Sources:
+- `https://docs.base.org/base-account/guides/verify-social-accounts.md`
+
+> Base Verify lets users prove ownership of verified accounts on X, Coinbase, Instagram, and TikTok without sharing credentials. Your backend sends a SIWE signature and message to the Base Verify API, receives a deterministic token tied to the provider account and action, and stores that token to prevent duplicate claims across wallets. Treat `404` as "verification not found" and redirect the user into the verification flow; treat `400 verification_traits_not_satisfied` as a real eligibility failure and do not redirect. Keep the secret key server-side only.
+
### Errors and retries
Sources:
- `https://docs.base.org/onchainkit/api/get-swap-quote.md`
diff --git a/docs/llms.txt b/docs/llms.txt
index ffeace6e3..8f97498e7 100644
--- a/docs/llms.txt
+++ b/docs/llms.txt
@@ -6,7 +6,7 @@
- [Get Started](./get-started/llms.txt) — Orientation, products, use cases, and quickstarts
- [Base Chain](./base-chain/llms.txt) — Deploy/connect, network info, tools, node ops, security
-- [Base Account](./base-account/llms.txt) — Passkey smart wallet, payments, sponsored gas, sub‑accounts
+- [Base Account](./base-account/llms.txt) — Passkey smart wallet, payments, social verification, sponsored gas, sub‑accounts
- [AI Agents](./ai-agents/llms.txt) — Build onchain AI agents: wallets, payments, identity, frameworks
- [Mini Apps](./mini-apps/llms.txt) — MiniKit, manifests, features, growth, troubleshooting
- [OnchainKit](./onchainkit/llms.txt) — React SDK: provider, components, APIs, utilities, templates
diff --git a/docs/mini-apps/core-concepts/base-account.mdx b/docs/mini-apps/core-concepts/base-account.mdx
index 95b57c4ef..000d659dd 100644
--- a/docs/mini-apps/core-concepts/base-account.mdx
+++ b/docs/mini-apps/core-concepts/base-account.mdx
@@ -150,6 +150,36 @@ The next section lists the methods and capabilities that are not supported in Mi
---
+## Verify social accounts with Base Verify
+
+[Base Verify](/base-account/guides/verify-social-accounts) lets your Mini App users prove ownership of verified accounts (X, Coinbase, Instagram, TikTok) without sharing credentials. You receive a deterministic token for Sybil resistance — one verified account produces the same token regardless of which wallet the user connects with.
+
+**How it works:**
+
+1. Check if the wallet has a verification → API returns yes/no
+2. If no → redirect to the Base Verify Mini App for OAuth
+3. User verifies → returns to your app
+4. Check again → now verified, you receive a token
+
+**Quick example — check if a user has a verified X account:**
+
+```typescript Title "Base Verify check" highlight={1-4,7-8}
+const resources = [
+ 'urn:verify:provider:x',
+ 'urn:verify:provider:x:verified:eq:true',
+ 'urn:verify:action:claim_airdrop'
+]
+
+// Generate SIWE message with these resources, sign it,
+// then send to your backend to call the Base Verify API
+```
+
+
+ Complete walkthrough covering all providers, traits, API reference, security, and implementation details.
+
+
+---
+
## Unsupported Methods and Capabilities
The following methods and capabilities are not yet supported in Mini Apps but will be added soon:
@@ -158,10 +188,7 @@ The following methods and capabilities are not yet supported in Mini Apps but wi
|---------|------------------|
| Sign in with Base | `wallet_connect` |
| Sub accounts | `wallet_getSubAccounts`, `wallet_addSubAccount` |
-| Spend permissions | `coinbase_fetchPermissions`, `coinbase_fetchPermission` |
| Profiles | `datacallback` |
-| Signing typed data | `signTypedData` |
-| Signing messages | `wallet_sign` |
All other standard Ethereum and Base Account RPC methods work as expected.