# Test referrals

A referral test follows the same path a friend takes: tap the link, install from a store, open the app, sign up, and claim. This page shows the steps for iPhone and Android, how to test your webhook without a phone, where to read the result, and what a failure usually means. Start with the setup checks, then run the platform test that fits your app.

## Before you test

1. Update the app to SDK 1.2.0 or later. Builds on 1.1.x cannot read an invitation.
2. Open the app in the dashboard, go to **Referral handoff**, and choose **Setup**. Fix anything the checks list. The check ids are `ios_recovery_store_url`, `ios_recovery_sdk` and `android_recovery_fingerprint`. `GET /v1/apps/{id}` returns the same answer as `referral_readiness`.
3. For iPhone, turn on **Offer referral handoff on iOS**.
4. Create a referral link, or turn on **Referral link** for an existing one. A tap from before the link became a referral link is not a referral.
5. Add a webhook endpoint under **Referral handoff**, then **Webhook**.

## Test the webhook without a phone

**Send test** on the webhook tab sends one signed `webhook.test` event to the saved address and shows what your server answered. The event has the same shape and headers as a real one, with an id that starts `evt_test_`. We send it once and never retry it. It appears in the delivery log.

```bash
curl -X POST https://api.warplink.app/v1/webhook-endpoints/ENDPOINT_ID/test \
  -H "Authorization: Bearer wl_live_YOUR_API_KEY"
```

The MCP tool `send_test_webhook` does the same. A passing test proves your address, your signature check and your reply. It does not prove the link or the SDK, so run a phone test next. [Receive referral webhooks](https://warplink.app/docs/guides/webhooks) covers the signature steps.

## Test on an iPhone

1. Delete the app from the phone, so the tap counts as a first install.
2. Open the referral link on the phone. You see the invitation page, then the App Store or a TestFlight build.
3. Install and open the app. Tap the paste button on your first screen.
4. Check that `redeemInvitation` returned a link. A `nil` result means nothing was confirmed.
5. Sign up as a new test user with a new installer id, and call `claimReferral`.

An invitation pasted on an iPhone is an exact match: `matchMethod` is `install_token` and `isExact` is true. If your app pastes into a field of your own, as in React Native and Flutter, paste the text from the invitation page into that field.

Test the paste step from a build that went through the store path, such as TestFlight. The system paste button pastes in one tap with no prompt, so the person sees only your screen.

## Test the Android install referrer

The Play install referrer exists only when Google Play installs the app. A debug build installed from a cable does not go through the store, so it does not test the real path. To test it:

1. Upload a build to a Google Play internal testing track and install it from the store listing on a test phone.
2. Delete the app from the phone before each run.
3. Open the referral link on the phone. It goes to Google Play with the invitation inside the install referrer.
4. Install, then open the app. The SDK reads the referrer at first launch, and the match arrives in `onLink` with `isDeferred` true.
5. Sign up as a new test user with a new installer id, and call `claimReferral`.

The match is exact: `matchMethod` is `android_referrer`. Two details explain most Android surprises:

- **The check runs once per install.** It runs on the first launch and does not run again until a reinstall. To repeat a test, delete the app and install again.
- **Sideloaded installs have no referrer.** An install by `adb install` or a direct APK, a device without Google Play Services, and referrer data that has expired all lack the install referrer. Such an install gets no referral credit.

Register the signing certificate SHA256 fingerprint on the app before you test. For a build from a Play track, use the fingerprint of the key that signed that build. [Find your app identifiers](https://warplink.app/docs/guides/find-app-identifiers) shows where to find it.

## Read the result

**In the app.** `claimReferral` returns a result with a `status`. A `claimed` status means the install is credited. See the quickstart for your SDK: [iOS](https://warplink.app/docs/guides/referrals/ios), [Android](https://warplink.app/docs/guides/referrals/android), [React Native](https://warplink.app/docs/guides/referrals/react-native), [Flutter](https://warplink.app/docs/guides/referrals/flutter).

**In the Referrals report.** Open **Referral handoff**, then **Referrals**, or `/referrals` in the dashboard. Each row shows the link, the installer id, the match, and the webhook result. Open a row to see the claims, the events we sent with their payloads, and every delivery attempt.

**Over the API.** `GET /v1/referrals` returns the same list, newest first. Filter it to your test user:

```bash
curl "https://api.warplink.app/v1/referrals?installer_id=TEST_INSTALLER_ID&status=delivered" \
  -H "Authorization: Bearer wl_live_YOUR_API_KEY"
```

The filters are `installer_id`, `link_id`, `status`, `match`, `platform`, `app_id`, `from` and `to`. The key needs `referrals:read`. The MCP tools `list_referrals` and `get_referral` return the same data. See the [Referrals API](https://warplink.app/docs/api/referrals).

The report `status` tells you how far the referral got:

| Status | Meaning |
|--------|---------|
| `claimed` | Recorded. No webhook delivery has finished yet |
| `delivered` | Your endpoint answered `2xx` |
| `retrying` | A delivery failed and another attempt is scheduled |
| `failed` | Every attempt failed. Resend it after you fix the endpoint |
| `flagged` | Claimed after the credit window |
| `duplicate` | A later claim for an installer that already held a credit |

## Run the test again

The first valid claim per app and installer id wins, so use a fresh installer id for each run. To start over with the same id, delete the test referral with `DELETE /v1/referrals/{id}` or the MCP tool `delete_referral`. Then delete the app from the phone and repeat the steps.

## Common failures

**The claim answers `none` and you expected a credit.** Check the link first: it must be a referral link, and the tap must be after `referral_since`. Then check the credit window you chose: 24 hours, 7 days or 30 days from the tap. Last, check whether the phone already had the app. That counts only when **Count people who already have the app** is on, and it is off by default.

**The claim answers `flagged`.** The claim came after the credit window closed, and the referral carries the flag `outside_window`. Tap the link again, or choose a longer window.

**The claim answers `ignored`.** This installer id already holds a credit. The result names the first referral in `duplicateOf`. Use a fresh installer id.

**`redeemInvitation` returns no link on iPhone.** The text was not an invitation, or it matched nothing. Copy the invitation again from the invitation page. Confirm that the app's `ios_recovery_sdk` check passes and that the iOS handoff is on.

**An error says the invitation expired or is invalid.** `RECOVERY_TOKEN_EXPIRED` means the invitation ran out, or its link is paused or ended. `RECOVERY_TOKEN_INVALID` means the text was changed, is incomplete, or belongs to another app. Open the link again. See the [errors](https://warplink.app/docs/errors) page.

**`409 LINK_NOT_REFERRAL`.** The `link_id` names an ordinary link. Turn on `is_referral` for it.

**`403 SDK_KEY_ROUTE_FORBIDDEN`.** An SDK key reached a route other than the SDK routes. Use an API key with `referrals:read` or `referrals:write` for the report and webhook routes.

**No webhook arrives.** Open the referral in the report and read its delivery attempts. A `5xx` answer, a timeout or a failed connection triggers a retry, and the event is tried up to 5 times over 20 minutes. A `4xx` or `3xx` answer ends the event as failed. Fix the endpoint, then choose **Resend**. A signature that does not match usually means a JSON parser touched the body before you verified it. Verify the raw body.

**The iOS switch will not turn on.** Setup lists what is missing. Add the App Store URL to the app. A build on SDK 1.2.0 or later that was opened once clears the SDK warning.

## Next

- [Referrals guide](https://warplink.app/docs/guides/referrals) for the full path from link to report
- [Reward recipes](https://warplink.app/docs/guides/referrals/rewards) for what your backend does with the event
- [Receive referral webhooks](https://warplink.app/docs/guides/webhooks) for signatures, retries and resends
