> ## 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.

# Webhooks - Redemption

> RevenueCat webhook integration for confirming redemptions

# Redemption Webhook

PromoStack automatically processes RevenueCat webhooks to confirm when referees redeem codes and subscribe.

## Webhook URL

Configure this URL in your RevenueCat dashboard:

```
https://api.promostack.app/webhooks-redeem
```

## How It Works

<Steps>
  <Step title="Referee Subscribes">
    User redeems promo code in App Store/Play Store and subscribes
  </Step>

  <Step title="RevenueCat Sends Webhook">
    RevenueCat fires webhook with subscription event
  </Step>

  <Step title="PromoStack Matches User">
    PromoStack finds code by `app_user_id` (matches `redeemed_by_uid`)
  </Step>

  <Step title="Confirm Redemption">
    Updates code status to `redeemed_confirmed` and referral event to `redeemed`
  </Step>

  <Step title="Check Threshold">
    Checks if referrer reached reward threshold and creates reward if needed
  </Step>
</Steps>

## Expected Payload

PromoStack expects the following fields from RevenueCat:

```json theme={null}
{
  "event": {
    "type": "INITIAL_PURCHASE",
    "app_user_id": "user_456",
    "product_id": "premium_monthly",
    "price_in_purchased_currency": 9.99,
    "currency": "USD",
    "event_timestamp_ms": 1640995200000
  }
}
```

### Required Fields

| Field         | Description                                            |
| ------------- | ------------------------------------------------------ |
| `app_user_id` | Must match the `referee_uid` used in `/referee-redeem` |
| `event.type`  | Subscription event type (e.g., `INITIAL_PURCHASE`)     |

## Matching Logic

### Primary Match (Metacode Flow)

PromoStack matches by `app_user_id`:

```sql theme={null}
SELECT * FROM codes
WHERE redeemed_by_uid = 'user_456'
AND status = 'issued'
```

This works because `/referee-redeem` sets `redeemed_by_uid` when the metacode is redeemed.

### Fallback Match (Legacy)

If primary match fails, PromoStack tries matching by code value (for backward compatibility).

## RevenueCat Configuration

<AccordionGroup>
  <Accordion title="1. Add Webhook URL">
    In RevenueCat dashboard:

    1. Go to **Project Settings** → **Integrations**
    2. Add webhook URL: `https://api.promostack.app/webhooks-redeem`
    3. Add custom header:
       * Key: `x-api-key`
       * Value: `YOUR_API_KEY`
    4. Select events: `INITIAL_PURCHASE`, `RENEWAL`, `CANCELLATION`
  </Accordion>

  <Accordion title="2. Configure App User ID">
    Ensure your app sets the same user ID in both PromoStack and RevenueCat:

    ```swift theme={null}
    // iOS
    Purchases.configure(withAPIKey: "rc_key", appUserID: userId)

    // When redeeming metacode, use same userId
    await redeemMetacode(userId: userId, metacode: "abc123")
    ```
  </Accordion>

  <Accordion title="3. Test Webhook">
    Use RevenueCat's webhook testing tool to send a test event and verify PromoStack receives it.
  </Accordion>
</AccordionGroup>

## Webhook Response

PromoStack returns a success response:

```json theme={null}
{
  "success": true,
  "code_id": "550e8400-e29b-41d4-a716-446655440000"
}
```

## Error Handling

### Code Not Found

If no matching code is found:

```json theme={null}
{
  "error": {
    "code": "NOT_FOUND",
    "message": "Code not found or already redeemed"
  }
}
```

**Common causes:**

* User didn't redeem metacode via `/referee-redeem` first
* `app_user_id` doesn't match `referee_uid`
* Code was already confirmed

### Idempotency

PromoStack handles duplicate webhooks gracefully. If a webhook is sent multiple times, only the first one updates the code. Subsequent calls return the same success response.

## Monitoring

Track webhook processing in the PromoStack dashboard:

* **Webhook Logs**: View all received webhooks
* **Failed Webhooks**: See webhooks that couldn't be processed
* **Attribution Rate**: Monitor `code_assigned` → `redeemed` conversion

## Best Practices

<Check>
  Use the same user ID in PromoStack and RevenueCat (e.g., your app's internal user ID)
</Check>

<Check>
  Test webhook integration before going live
</Check>

<Check>
  Monitor webhook logs for failed matches
</Check>

<Warning>
  Don't rely on webhooks for real-time UI updates - use `/referrer` polling instead
</Warning>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Webhook Not Received">
    1. Check RevenueCat webhook configuration
    2. Verify webhook URL is correct
    3. Check RevenueCat webhook logs for delivery failures
  </Accordion>

  <Accordion title="Code Not Found Error">
    1. Verify user redeemed metacode via `/referee-redeem` first
    2. Check that `app_user_id` matches `referee_uid`
    3. Ensure code hasn't already been confirmed
  </Accordion>

  <Accordion title="Rewards Not Created">
    1. Check if referrer reached threshold (view in dashboard)
    2. Verify reward pool has available codes
    3. Check PromoStack logs for `check_and_create_reward` errors
  </Accordion>
</AccordionGroup>

## Security

RevenueCat webhooks should be verified using webhook signatures (coming soon). For now, PromoStack validates:

* Request comes from RevenueCat IP ranges
* Payload structure matches expected format
* Code exists and belongs to your app

<Note>
  Webhook signature verification is available on Pro and Enterprise plans.
</Note>
