> For the complete documentation index, see [llms.txt](https://docs.fuul.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.fuul.xyz/developer-guide/tracking-referrals-in-your-app.md).

# Tracking Referrals

For Fuul to attribute conversions to the right referrer, your app must send two tracking events: **`pageview`** and **`connect_wallet`** (via `identifyUser`).

{% hint style="info" %}
These events require the `send:tracking_event` API key. See [API Key Management](/developer-guide/api-key-management.md).
{% endhint %}

## How attribution works

```
User clicks referral link → pageview event → user connects wallet → identifyUser event
                                   ↓                                        ↓
                         Captures referrer info                Links wallet to tracking session
                                   ↓                                        ↓
                            Later, user converts → Fuul attributes conversion to the referrer
```

## 1. Send pageview

Call `sendPageview` on every page load. The SDK automatically captures the referrer's affiliate code from the URL (`?af=code`, or the legacy `?referrer=code`) and stores a tracking ID in localStorage.

```typescript
import { Fuul } from '@fuul/sdk';

await Fuul.sendPageview();
```

{% hint style="info" %}
The SDK reads referral parameters from the URL automatically. You don't need to persist them when navigating between pages.
{% endhint %}

## 2. Identify the user

Call `identifyUser` every time a user connects a wallet or logs in — including when they switch wallets during a session. This sends a `connect_wallet` event that links their identity to the tracking session.

```typescript
import { Fuul } from '@fuul/sdk';

await Fuul.identifyUser({
  identifier: '0x1234...',
  identifierType: 'evm_address', // evm_address | solana_address | xrpl_address | sui_address | stellar_address | email
  signature: '0xabc...',
  message: 'Sign to verify your identity',
});
```

### Smart contract wallets

For smart contract wallets (EIP-1271), add the `accountChainId` parameter:

```typescript
await Fuul.identifyUser({
  identifier: '0x1234...',
  identifierType: 'evm_address',
  signature: '0xabc...',
  message: 'Sign to verify your identity',
  accountChainId: 1, // Chain ID where the smart contract wallet is deployed
});
```

Supported chains for smart contract wallets:

| Network    | Chain ID |
| ---------- | -------- |
| Ethereum   | 1        |
| Arbitrum   | 42161    |
| Optimism   | 10       |
| Base       | 8453     |
| Polygon    | 137      |
| zkSync Era | 324      |
| BNB Chain  | 56       |
| Avalanche  | 43114    |
| Mode       | 34443    |
| Abstract   | 2741     |
| Bob        | 60808    |
| Berachain  | 80094    |

### XRPL signatures

For XRPL wallets, include the `signaturePublicKey`:

```typescript
await Fuul.identifyUser({
  identifier: 'rAddress...',
  identifierType: 'xrpl_address',
  signature: '0xabc...',
  signaturePublicKey: '0x1234...',
  message: 'Sign to verify your identity',
});
```

## Sending identifyUser from the backend

If you identify users server-side, send the `connect_wallet` event via the REST API. You'll need the `tracking_id` from the browser's `localStorage` (key: `fuul.tracking_id`) — it must match the one sent in the pageview event.

```python
import requests

url = "https://api.fuul.xyz/api/v1/events"

payload = {
    "metadata": {
        "tracking_id": "trackingId123"
    },
    "name": "connect_wallet",
    "user": {
        "identifier": "0x1234...",
        "identifier_type": "evm_address"  # evm_address | solana_address | xrpl_address | sui_address | stellar_address | email | uuid
    },
    "signature": "0xabc...",
    "signature_message": "Sign to verify your identity",
    "account_chain_id": 1  # Only for smart contract wallets
}

headers = {
    "content-type": "application/json",
    "authorization": "Bearer your-api-key"
}

response = requests.post(url, json=payload, headers=headers)
```

**cURL:**

```bash
curl -X POST https://api.fuul.xyz/api/v1/events \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your-api-key" \
  -d '{
    "metadata": { "tracking_id": "trackingId123" },
    "name": "connect_wallet",
    "user": {
      "identifier": "0x1234...",
      "identifier_type": "evm_address"
    },
    "signature": "0xabc...",
    "signature_message": "Sign to verify your identity"
  }'
```

{% hint style="info" %}
Valid `identifier_type` values: `evm_address` | `solana_address` | `xrpl_address` | `sui_address` | `stellar_address` | `email` | `uuid`
{% endhint %}

## Signature types

Fuul supports two signature verification methods:

| Type                 | Verification                                                                 | Use case                |
| -------------------- | ---------------------------------------------------------------------------- | ----------------------- |
| Regular message      | [Viem verifyMessage](https://viem.sh/docs/actions/public/verifyMessage.html) | Standard EOA wallets    |
| Typed data (EIP-712) | [Viem verifyTypedData](https://viem.sh/docs/actions/public/verifyTypedData)  | Structured data signing |

For typed data signatures, stringify the typed data object:

```typescript
import { Fuul } from '@fuul/sdk';

const typedData = { address, domain, types, primaryType, message };

await Fuul.identifyUser({
  identifier: '0x1234...',
  identifierType: 'evm_address',
  message: JSON.stringify(typedData),
  signature: '0xabc...',
});
```

{% hint style="info" %}
Requiring users to sign a message ensures event validity and prevents attribution fraud. Mandatory for wallet-based identifiers; optional for `uuid`.
{% endhint %}
