> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cabalspy.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Verify a wallet

> POST /v1/platform-users/wallets/verify: payment verification for a platform user's wallet, with status, QR code and cancel.

Proves that a platform user owns a wallet, with the same on-chain payment verification as the CabalSpy ID website: the user sends a tiny, unique amount from the wallet, directly and not through an exchange or a contract. Once the payment is final, the wallet is **verified** on that chain.

Verification is optional. Use it as a trust badge, or because your instance only counts verified wallets (`wallet_trust: verified_only`). Whoever verifies an address owns it: unverified copies of it on other accounts stop counting.

> [!WARNING]
>
> The amount is a non-refundable verification fee. Show the user the `instructions` and make clear that the exact amount must be sent from that wallet before `expires_at`.

## Flow

**1. Start**

`POST /v1/platform-users/wallets/verify` with the user, the chain and the address. If the address is not yet one of the user's wallets, it is added.

**2. Show the payment**

Show `amount`, `currency`, `receiver` and `expires_at`, the `payment_uri` as a link for wallet apps, or the QR code from `GET /v1/platform-users/wallets/verification/qr`.

**3. Wait for the payment**

Poll `GET /v1/platform-users/wallets/verification` until `status` is `verified`. If the window passes without payment, the status turns `expired` and you receive the `verification.expired` [webhook](https://docs.cabalspy.xyz/cabalspy-id/webhooks.md#white-label-events).

## Start a verification

`POST /v1/platform-users/wallets/verify`

**external\_id** `string` *required*

Your id of the user. Alternatively pass csid\_id.

**chain** `string` *required*

The chain to verify on: solana, bnb, base, eth or rh.

**address** `string` *required*

The wallet. It must match the chain: a Solana address on solana, an EVM address on an EVM chain.

```bash cURL theme={null}
curl -X POST "https://api-id.cabalspy.xyz/v1/platform-users/wallets/verify" \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"external_id": "user_8412", "chain": "solana", "address": "7j7AA3HZR2zEjwAQEKPFh2qucLY4fqZpB9iodf39w8xW"}'
```

```python Python theme={null}
import time, requests

H = {"Authorization": "Bearer YOUR_KEY"}
v = requests.post("https://api-id.cabalspy.xyz/v1/platform-users/wallets/verify", json={
    "external_id": "user_8412", "chain": "solana",
    "address": "7j7AA3HZR2zEjwAQEKPFh2qucLY4fqZpB9iodf39w8xW",
}, headers=H).json()["data"]
print(v["instructions"])

while v["status"] == "pending":
    time.sleep(10)
    v = requests.get("https://api-id.cabalspy.xyz/v1/platform-users/wallets/verification",
                     params={"id": v["verification_id"]}, headers=H).json()["data"]
print(v["status"], v["explorer_url"])
```

```javascript JavaScript theme={null}
const res = await fetch("https://api-id.cabalspy.xyz/v1/platform-users/wallets/verify", {
  method: "POST",
  headers: { Authorization: "Bearer YOUR_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({
    external_id: "user_8412",
    chain: "base",
    address: "0x4b1e9c0d2a7f3e5b8c6d1a9f0e2b7c4d5a6e8f01",
  }),
});
const { data } = await res.json();
console.log(data.payment_uri, data.expires_at);
```

Answers **201** with the verification object and `external_id`.

## Response

The verification object, returned by every endpoint on this page.

**verification\_id** `string`

The verification's id.

**status** `string`

`pending`, `verified`, `expired` or `cancelled`.

**chain** `string`

Chain id, for example solana.

**chain\_name** `string`

Chain name, for example Solana.

**address** `string`

The wallet that must send the payment.

**amount** `string`

The exact amount to send, in the chain's currency.

**amount\_base** `string`

The same amount in base units (lamports, wei).

**currency** `string`

SOL, BNB or ETH.

**decimals** `integer`

Decimals of the currency.

**receiver** `string`

The address to send the payment to.

**expires\_at** `string`

End of the payment window.

**seconds\_left** `integer`

Seconds until expires\_at while pending, otherwise 0.

**payment\_uri** `string`

`solana:<receiver>?amount=...` or `ethereum:<receiver>@<chain id>?value=...`, for wallet apps and QR codes.

**evm\_chain\_id** `integer`

EVM chain id (56, 8453, 1 or 4663), null on Solana.

**instructions** `string`

A ready-made sentence to show the user.

**tx\_hash** `string`

The payment transaction once seen, otherwise null.

**explorer\_url** `string`

Explorer link of the payment, or null.

**created\_at** `string`

When the verification was started.

**external\_id** `string`

Your id of the user.

```json Response theme={null}
{
  "success": true,
  "data": {
    "verification_id": "c4d5e6f7-8a9b-4c0d-9e1f-2a3b4c5d6e7f",
    "status": "pending",
    "chain": "solana",
    "chain_name": "Solana",
    "address": "7j7AA3HZR2zEjwAQEKPFh2qucLY4fqZpB9iodf39w8xW",
    "amount": "0.001042",
    "amount_base": "1042000",
    "currency": "SOL",
    "decimals": 9,
    "receiver": "Hx7kQ...9vRp",
    "expires_at": "2026-10-26T08:34:11Z",
    "seconds_left": 1800,
    "payment_uri": "solana:Hx7kQ...9vRp?amount=0.001042",
    "evm_chain_id": null,
    "instructions": "Send exactly 0.001042 SOL on Solana from 7j7AA3HZR2zEjwAQEKPFh2qucLY4fqZpB9iodf39w8xW to Hx7kQ...9vRp before 2026-10-26T08:34:11Z. Send it directly from that wallet (a normal transfer, not through an exchange or a contract). The amount is a non-refundable verification fee.",
    "tx_hash": null,
    "explorer_url": null,
    "created_at": "2026-10-26T08:04:11Z",
    "external_id": "user_8412"
  },
  "meta": {
    "request_id": "req_5162c7cf7f87",
    "cached": false,
    "cache_age_seconds": 0,
    "version": "v1",
    "timestamp": "2026-10-26T08:04:11Z"
  }
}
```

## Check the status

`GET /v1/platform-users/wallets/verification?id=<verification_id>`

Returns the verification object with `external_id`. Open verifications are also listed in the user's `pending_verifications`.

```bash theme={null}
curl "https://api-id.cabalspy.xyz/v1/platform-users/wallets/verification?id=c4d5e6f7-8a9b-4c0d-9e1f-2a3b4c5d6e7f" \
  -H "Authorization: Bearer YOUR_KEY"
```

## QR code

`GET /v1/platform-users/wallets/verification/qr?id=<verification_id>`

Returns the `payment_uri` as a QR code, an SVG image (`image/svg+xml`), not the JSON envelope. Fetch it from your backend and pass it on to your page; your API key must not reach the browser.

```bash theme={null}
curl "https://api-id.cabalspy.xyz/v1/platform-users/wallets/verification/qr?id=c4d5e6f7-8a9b-4c0d-9e1f-2a3b4c5d6e7f" \
  -H "Authorization: Bearer YOUR_KEY" -o verify.svg
```

## Cancel

`POST /v1/platform-users/wallets/verification/cancel`

Cancels an open verification, for example when the user closes the dialog. Returns the verification object with status `cancelled` and `external_id`.

**id** `string` *required*

The verification\_id.

```bash theme={null}
curl -X POST "https://api-id.cabalspy.xyz/v1/platform-users/wallets/verification/cancel" \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"id": "c4d5e6f7-8a9b-4c0d-9e1f-2a3b4c5d6e7f"}'
```

## Errors

| Status | Code | Meaning |
| - | - | - |
| 400 | missing\_parameter | external\_id, chain, address or id missing. |
| 400 | invalid\_parameter | Unknown or disabled chain, or id is not a verification id. |
| 400 | invalid\_address | Not a valid address, or the address does not match the chain. |
| 401 | missing\_api\_key | API key missing. |
| 403 | invalid\_api\_key | API key invalid or not enabled for CabalSpy ID. |
| 403 | plan\_user\_limit | The user is beyond your plan's user limit. |
| 404 | platform\_user\_not\_found | No user with this id in your instance. |
| 404 | verification\_not\_found | No verification with this id for your users. |
| 409 | account\_claimed | The person claimed the account and manages it themselves. |
| 409 | wallet\_already\_verified | Another account already verified this wallet. |
| 409 | already\_verified | The wallet is already verified on this chain. |
| 409 | too\_many\_wallets | The user would have more than 5 Solana or 5 EVM wallets. |
| 429 | too\_many\_open\_verifications | Too many verifications are open at once. Wait or cancel one. |
| 429 | rate\_limited | Verifications started too fast. |
| 503 | service\_unavailable | Verification is temporarily unavailable. |
| 429 | rate\_limit\_exceeded | Rate limit exceeded. |

See [Errors](https://docs.cabalspy.xyz/cabalspy-id/errors.md) for the full list.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.