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

# Attribution

> How PromoStack tracks and confirms referrals

# Attribution System

PromoStack uses a two-phase attribution system: **early mapping** (metacode redemption) and **confirmation** (RevenueCat webhook).

## Attribution Phases

### Phase 1: Early Mapping (Metacode Redemption)

When a referee redeems a metacode in your app:

```bash theme={null}
POST /referee-redeem
{
  "referee_uid": "user_123",
  "metacode": "abc123",
  "platform": "ios"
}
```

PromoStack immediately:

1. Maps `codes.redeemed_by_uid = "user_123"`
2. Links code to referrer via `codes.referrer_id`
3. Creates referral event with status `code_assigned`

**At this point, attribution is established** - we know who referred whom.

### Phase 2: Confirmation (RevenueCat Webhook)

When the referee subscribes, RevenueCat sends webhook:

```json theme={null}
{
  "app_user_id": "user_123",
  "event_type": "initial_purchase"
}
```

PromoStack:

1. Finds code by `codes.redeemed_by_uid = "user_123"`
2. Updates `codes.status = 'redeemed_confirmed'`
3. Updates `referral_events.status = 'redeemed'`
4. Checks if referrer reached threshold → creates reward

## Why Two Phases?

<CardGroup cols={2}>
  <Card title="Early Attribution" icon="bolt">
    Know who referred whom immediately, before payment
  </Card>

  <Card title="Payment Confirmation" icon="check">
    Only count confirmed subscriptions toward rewards
  </Card>

  <Card title="Anti-Fraud" icon="shield">
    Prevent duplicate redemptions at metacode phase
  </Card>

  <Card title="Flexibility" icon="arrows-split">
    Works with any payment provider, not just RevenueCat
  </Card>
</CardGroup>

## Referral Event Lifecycle

```mermaid theme={null}
graph LR
    A[clicked] --> B[code_assigned]
    B --> C[redeemed]
    C --> D[qualified]
```

| Status          | Trigger            | Meaning                          |
| --------------- | ------------------ | -------------------------------- |
| `clicked`       | Landing page visit | User clicked referral link       |
| `code_assigned` | `/referee-redeem`  | Metacode redeemed, code issued   |
| `redeemed`      | RevenueCat webhook | Subscription confirmed           |
| `qualified`     | Threshold check    | Counted toward referrer's reward |

## Anti-Fraud Measures

### Duplicate Prevention

The `/referee-redeem` endpoint checks if `referee_uid` already redeemed a code for this campaign:

```sql theme={null}
SELECT id FROM codes
WHERE campaign_id = ? AND redeemed_by_uid = ?
```

If found, returns `409 ALREADY_REDEEMED` error.

### One Code Per Campaign

Each referee can only redeem **one code per campaign**, preventing:

* Multiple redemptions by same user
* Gaming the system with multiple accounts (requires unique app\_user\_id)

## Matching Logic

### Primary Match (Metacode Flow)

```sql theme={null}
-- Find code by referee UID
SELECT * FROM codes
WHERE redeemed_by_uid = 'user_123'
AND status = 'issued'
```

### Fallback Match (Legacy)

If primary match fails, try matching by code value:

```sql theme={null}
-- Fallback to code value
SELECT * FROM codes
WHERE code_value = 'PROMO-ABC-123'
AND status = 'issued'
```

This ensures backward compatibility with pre-metacode implementations.

## Best Practices

<AccordionGroup>
  <Accordion title="Use Unique User IDs">
    Ensure `referee_uid` is unique per user. Use your app's internal user ID or RevenueCat's `app_user_id`.
  </Accordion>

  <Accordion title="Handle Webhook Retries">
    RevenueCat may send duplicate webhooks. PromoStack handles this with idempotent updates (only updates if `status = 'issued'`).
  </Accordion>

  <Accordion title="Monitor Attribution Rate">
    Track ratio of `code_assigned` → `redeemed` events to identify drop-off points.
  </Accordion>
</AccordionGroup>
