# TapResearch Publisher Documentation — Survey Feed API
# Source: https://supply-docs.tapresearch.com · generated at build time, do not edit by hand.
# Each section below is one documentation page.
# Survey Feed API
Source: https://supply-docs.tapresearch.com/docs/3.x/survey-feed-api/overview
# Survey Feed API
The **Survey Feed API** is the only TapResearch integration path where you send a
participant's profile up front to **impanel** them, then fetch the surveys they
qualify for and render those survey tiles inside your own UI. You stay in control
of profiling, the survey list, and the participant experience end to end.
:::tip Creating your app
In the [Supplier Dashboard](https://www.tapresearch.com/supplier_dashboard/overview),
create a new app and select **Survey Feed API**. See the [Setup checklist](../setup)
for the full flow.
:::
:::info Not the Eligibility API
The Survey Feed API returns **survey data** for a profiled participant. It is a
different product from the **Eligibility API**, which answers a yes/no "does this
user have an offer?" check. The two were historically both referred to as the
"Supply API" — they are not the same surface. If you only need an eligibility
check before sending a user, use the Eligibility API instead.
:::
## How it works
1. **Look up countries & qualifications.** Call `/countries` and
`/qualifications` to learn what profile data you can collect.
2. **Impanel the participant.** `POST` their profile to `/players/impanel` so
TapResearch can match surveys — or skip this and let TapResearch profile them.
3. **Fetch surveys.** Call `/campaigns` to get the surveys the participant
qualifies for.
4. **Render the tiles.** Display the surveys in your own UI and send the
participant into one.
5. **Rewards arrive via callback.** When the participant earns a reward,
TapResearch notifies your backend with a server-to-server postback.
### Two ways to profile
You pass profile data
Collect profiling data from your users and send it via the Impanel endpoint;
TapResearch uses it to match surveys. Uses multiple endpoints — countries &
qualifications, impanel, and campaigns.
Start with Impanel →
TapResearch handles profiling
Skip collecting profile data — TapResearch shows its built-in profiling
experience. The only required endpoint is GET /campaigns.
Fetch surveys →
## When to use it
Use the Survey Feed API when you want to:
* Render survey tiles **inside your own UI**, fully styled by you.
* Send participant profile data up front to target surveys (impanel) — or hand
profiling to TapResearch.
* Stay in control of the survey list and participant experience end to end.
Reach for a different path if you need:
* **A simple yes/no eligibility check** before sending a user — use the
**Eligibility API**.
* **The fastest low-code launch** — use the **Hosted Link**.
* **Native mobile placements** — use the **SDK**.
## What you need
The base URL for every endpoint in this API:
```
https://api.tapresearch.com/supply_api/v4
```
* An **API token** for your app, from the Publisher Dashboard.
* For some endpoints, a **security hash** (`sech`) generated server-side from
your **API secret**. The secret should **never be sent** in any request — use
it only to compute the `sech`. See [Impanel](./impanel) for how it's computed.
* All requests must be made over **HTTPS**.
## Next steps
| Endpoint | Method | Description |
| ----------------------------------------------------- | ------ | --------------------------------------------------------- |
| [`/countries`](./countries-qualifications) | GET | List supported countries and their IDs. |
| [`/qualifications`](./countries-qualifications) | GET | Retrieve qualification questions per country. |
| [`/players/impanel`](./impanel) | POST | Register a participant and store qualification responses. |
| [`/campaigns`](./fetch-surveys) | GET | Fetch available surveys for a qualified participant. |
After a participant completes a survey, rewards are reported to your backend —
see [Callbacks](./callbacks) for the postback payload and signature validation.
## Support
For credentials, redirect configuration, or questions about either integration
path, contact your TapResearch account manager or email
**[developers@tapresearch.com](mailto:developers@tapresearch.com)**.
---
# Integrate with AI
Source: https://supply-docs.tapresearch.com/docs/3.x/survey-feed-api/integrate-with-ai
# Integrate the Survey Feed API with AI
Copy the prompt below into your AI assistant — Claude, ChatGPT, Cursor, Codex,
or whatever you use. It points the agent at our full, always-current Survey Feed
API reference, so it works from the real documentation instead of guessing.
The Survey Feed API is the full-control path — fetching surveys, rendering them,
and handling reward callbacks yourself — so there's the most to build here. Your
agent can work through it with you. Review what it writes, and reach for the rest
of these docs whenever you want the details yourself.
## After you integrate: verify it's working
Run through these checks with help from your AI agent — it can write and run them
and debug failures, but it can't see your account. So your app's **Integration**
tab in the
[Publisher Dashboard](https://tapresearch.com/supplier_dashboard/overview) is what
confirms each step actually passed.
:::tip Use a real test session
These checks register from real activity — a test postback on its own won't
complete them. Add a test device first (**More → Test devices**).
:::
1. **API request received** — Make your first authenticated fetch-surveys request.
The Integration tab marks this once it sees the request.
2. **Surveys rendered & entered** — Render a fetched survey and open it in your
webview so a session reaches the survey.
3. **Reward callback configured** — Set your server-to-server postback URL.
4. **Survey complete & reward redeemed** — Complete a test survey and confirm your
callback endpoint receives the signed reward and redeems it.
If a step doesn't check off, it's the fastest signal of where the integration
broke. For deeper testing guidance, see [Testing & QA](/docs/3.x/testing-and-qa).
---
# Impanel / player profile
Source: https://supply-docs.tapresearch.com/docs/3.x/survey-feed-api/impanel
# POST `/players/impanel`
The `/players/impanel` endpoint ingests a participant's demographic and qualification data.
This information is used by TapResearch's targeting system to determine which surveys the user is eligible for.
Call this endpoint **before** requesting surveys so that [`/campaigns`](./fetch-surveys) can return only the campaigns the user qualifies for.
---
## Request
### Headers
| Name | Required | Description |
| --------------------- | -------- | --------------------------------------------------------- |
| `X-Api-Token` | ✅ | Your API token. |
| `X-User-Identifier` | ✅ | Unique user identifier. |
| `X-Device-Identifier` | ❌ | Optional hardware/device ID for multi-device distinction. |
---
### Body Parameters (JSON)
| Name | Type | Required | Description |
| ----------------- | ------- | -------- | --------------------------------------------------------------------------------- |
| `country_id` | Integer | ✅ | Country ID from [`/countries`](./countries-qualifications). |
| `profile_data` | Array | ✅ | Array of qualification responses from [`/qualifications`](./countries-qualifications). |
| `ts` | Integer | ✅ | Current Unix timestamp (in seconds). |
| `sech` | String | ✅ | SHA-256 hash of `user_identifier:timestamp`, signed with your API secret. |
| `attributes_hash` | Object | ❌ | Key-value map of custom publisher-defined attributes used for segmentation. |
---
### Profile Data Format
Each object in `profile_data` represents a qualification and its responses.
| Field | Type | Description |
| ------------- | ------- | ---------------------------------------------------------------------------- |
| `question_id` | Integer | ID of the qualification question. |
| `values` | Array | Selected `pre_code` integers or text values, depending on the question type. |
---
### Segmentation
The optional `attributes_hash` field allows publishers to attach custom attributes to a user profile at impanel time.
These attributes can be used for **segmentation and targeting** within TapResearch's system.
* Keys are defined by the publisher
* Values can be strings, numbers, or booleans
* Attributes are persisted and may influence downstream campaign eligibility
#### Example
```json
"attributes_hash": {
"is_new_user": true,
"subscription_tier": "premium",
"days_since_signup": 3
}
```
---
### Example Request
```json
{
"country_id": 1,
"profile_data": [
{
"question_id": 42,
"values": [25]
},
{
"question_id": 43,
"values": [1, 2]
},
{
"question_id": 44,
"values": ["94568"]
}
],
"attributes_hash": {
"is_new_user": true,
"subscription_tier": "premium"
},
"ts": 1713456789,
"sech": "a3f9b6c40b31..."
}
```
---
### Response
A successful impanelment returns:
```json
{ "success": true }
```
If there's an issue, an error object is returned with details:
```json
{ "error": "Invalid security hash" }
```
---
## Notes
* The **API secret must never be included** in the request — only use it server-side to generate the `sech` hash.
* Use the `pre_code` values from [`/qualifications`](./countries-qualifications) for Single select and Multi select data points.
* The `sech` is computed as a SHA-256 digest of `user_identifier:ts` using your API secret.
* Both `ts` and `sech` must be provided as JSON fields in the request body.
* Call this endpoint **before** retrieving campaigns from [`/campaigns`](./fetch-surveys).
* A `200 OK` response with `"success": true` confirms the participant has been impaneled successfully.
* `attributes_hash` is optional — if omitted, no custom segmentation attributes are applied.
---
**Next step:** [Fetch surveys →](./fetch-surveys) — retrieve the campaigns the participant qualifies for.
---
# Fetch surveys
Source: https://supply-docs.tapresearch.com/docs/3.x/survey-feed-api/fetch-surveys
# GET `/campaigns`
Retrieve available campaigns (surveys) for a participant profile.
This endpoint should be called after impaneling a participant to discover live survey opportunities.
:::warning Configure a post-survey redirect URL first
`/campaigns` requires your app to have a post-survey redirect URL configured (and
an active placement) before it will return surveys. Without one, the call returns
`422` with `A valid post-survey redirect URL is required.` — before any surveys
are evaluated. See [Redirects](./redirects).
:::
---
## Request
### Headers
| Name | Required | Description |
| --------------------- | -------- | --------------------------------------------------------- |
| `X-Api-Token` | ✅ | Your API token. |
| `X-User-Identifier` | ✅ | Participant UUID. |
| `X-Device-Identifier` | ❌ | Optional hardware/device ID for multi-device distinction. |
### Query Parameters
| Name | Required | Description |
| ------------- | -------- | -------------------------------------------------------- |
| `user_ip` | ✅ | Participant IP address (e.g., `107.193.140.76`). |
| `device_type` | ✅ | Device type — `1` = mobile, `2` = desktop, `3` = tablet. |
---
## Response
The response returns a JSON object containing:
| Field | Type | Description |
| -------------------- | ------ | ---------------------------------------------- |
| `surveys` | Array | List of available surveys for the participant. |
| `player_eligibility` | Object | Participant eligibility status. |
---
### Surveys Object
Each item in the `surveys` array contains:
| Field | Type | Description |
| ------------------- | ---------------- | ------------------------------------------------------------------------------------------------------- |
| `identifier` | String | Unique campaign identifier. |
| `length_in_minutes` | Integer | Estimated survey duration. |
| `conversion_rate` | Integer | Completion rate percentage. |
| `reward_amount` | Integer | Reward amount for the participant on a full complete. |
| `epc` | Float | Earnings per click **to you**, factoring in completes, DQs, and OQs. |
| `currency_name` | String | Name of the in-app currency. |
| `offer_entry_url` | String | URL to start the survey (redirect the participant here). |
| `is_hot_tile` | Boolean/Integer | Indicates whether this survey's performance exceeds our internal threshold. |
| `support_url` | String | TapResearch-hosted support contact URL for the player's current session. Present this as a "Get Help" link alongside the campaign in your UI. |
---
### Player Eligibility Object
| Field | Type | Description |
| ------------- | ------- | --------------------------------------------------------- |
| `is_eligible` | Boolean | Indicates if the participant is eligible to take surveys. |
| `reason` | Integer | Eligibility reason code. See [Reason codes](#reason-codes). |
| `comment` | String | Human-readable eligibility comment. |
#### Reason codes
When `is_eligible` is `false`, `reason` tells you why. An empty `surveys` array
almost always traces back to one of these. Common values:
| Code | Meaning |
| ---- | ------- |
| 0 | Eligible |
| 2 | App is in test mode — only registered test devices are eligible |
| 4 | Not a supported country |
| 5 | Under the supported age |
| 6 | Missing user identifier |
| 8 | Banned |
| 12 | Missing device/user identifier |
| 20 | User identifier is an anonymous device advertising ID, not a real user ID |
| 22 | Blacklisted |
This is the common subset; other codes may appear.
---
### Example Response
```json
{
"surveys": [
{
"identifier": "4da2192594d2420648e5996e8bc1cb59",
"length_in_minutes": 6,
"conversion_rate": 66,
"reward_amount": 68,
"epc": 1.5,
"currency_name": "points",
"offer_entry_url": "https://www.tapresearch.com/router/offers/28e0f041675fb74b7ef254482ad1d502/pre_entry...",
"is_hot_tile": 1
},
{
"identifier": "7e41c40fa02ed36035b5955b58e52388",
"length_in_minutes": 15,
"conversion_rate": 8,
"reward_amount": 232,
"epc": 2.0,
"currency_name": "points",
"offer_entry_url": "https://www.tapresearch.com/router/offers/28e0f041675fb74b7ef254482ad1d502/pre_entry...",
"is_hot_tile": 1
}
],
"player_eligibility": {
"is_eligible": true,
"reason": 0,
"comment": "Qualified"
}
}
```
---
## Errors
Errors return `{ "message": "", "error_code": }`:
| `error_code` | Status | When |
| ------------ | ------ | ---- |
| 1000 | `401` | Invalid or missing `X-Api-Token` |
| 1001 | `422` | Missing `X-User-Identifier` |
| 1003 | `422` | A required header is missing |
| 1002 | `503` | Temporarily unavailable (maintenance) |
Other validation failures return `422` with a descriptive `message` and
`error_code: null` — for example, `A valid post-survey redirect URL is required.`
(see the prerequisite at the top of this page).
---
## Notes
- Call this endpoint after successfully impaneling a participant. See [Impanel](./impanel).
- Cache responses for up to **2 minutes** to reduce load.
- Use the `offer_entry_url` to redirect participants into a live survey.
- The `player_eligibility` block determines whether the user can proceed. If the participant is **not qualified**, the `surveys` array will be empty.
- Surveys are already **ordered by our internal yield management algorithm**, so we recommend using them in the order returned.
- **Reward amounts** represent what the participant earns **on a full survey complete**. The actual value to you may be lower since TapResearch also pays out for DQs/OQs based on time spent in the survey.
- **EPC (earnings per click)** reflects **all payout types**—not just completes—giving you a holistic view of expected earnings. Use this field if you plan to **manage yield on your end** instead of relying solely on reward amount.
---
**Next step:** [Callbacks →](./callbacks) — receive reward notifications on your backend when a participant completes a survey.
---
# Countries & qualifications
Source: https://supply-docs.tapresearch.com/docs/3.x/survey-feed-api/countries-qualifications
# Countries & qualifications
Before you impanel a participant, fetch the supported countries and the
qualification questions for that country. The values returned here are what you
send as `country_id` and `profile_data` to the [Impanel](./impanel) endpoint.
---
## GET `/countries`
List all countries where campaigns are available.
### Parameters
| Type | Name | Required | Description |
| --- | --- | --- | --- |
| Header | `X-Api-Token` | Yes | Your API token. |
### Response
Returns an array of countries.
| Field | Description |
| --- | --- |
| `id` | Internal numeric country ID. |
| `name` | Localized country name. |
Use the `id` value when calling [Qualifications](#get-qualifications) and [Impanel](./impanel).
---
## GET `/qualifications`
Retrieve all qualification questions available for a given country.
These questions are used to collect profiling data before a participant is impaneled.
### Request
#### Headers
| Name | Required | Description |
| --- | --- | --- |
| `X-Api-Token` | ✅ | Your API token. |
#### Query Parameters
| Name | Required | Description |
| --- | --- | --- |
| `country_id` | ✅ | Country ID returned from [`/countries`](#get-countries). |
### Response
An array of qualification objects.
| Field | Type | Description |
| --- | --- | --- |
| `question_id` | Integer | Unique question identifier. |
| `question_text` | String | Text of the qualification question. |
| `answer_type` | String | One of `single select`, `multi select`, or `text`. |
| `qualification_answers` | Array | For select types: array of `{ option_text, pre_code }`. |
| `required` | Boolean | Whether the answer is mandatory. |
#### Example Response
```json
[
{
"question_id": 50017,
"question_text": "When were you born?",
"country_id": 1,
"name": "Birthday",
"answer_type": "Text",
"qualification_answers": [],
"required": true
},
{
"question_id": 725,
"question_text": "What are your hobbies and interests? Select all that apply.",
"country_id": 1,
"name": "Hobbies & Interests",
"answer_type": "Multi select",
"qualification_answers": [
{ "option_text": "Arts and Crafts", "pre_code": 1 },
{ "option_text": "Boating", "pre_code": 2 },
{ "option_text": "Cooking", "pre_code": 3 }
],
"required": false
}
]
```
### Notes
- Use the `pre_code` values from `qualification_answers` when sending `profile_data` in the [`/players/impanel`](./impanel) request.
- Supported answer types:
- **Single select:** user selects one option.
- **Multi select:** user can select multiple options.
- **Text:** free-form input (e.g., ZIP code, birth year).
---
**Next step:** [Impanel the participant →](./impanel) — register them with their qualification responses so TapResearch can match surveys.
---
# Callbacks
Source: https://supply-docs.tapresearch.com/docs/3.x/survey-feed-api/callbacks
# Callbacks
TapResearch reports every reward to your backend through a server-to-server
postback. Because the Survey Feed API has no in-app SDK surface, only the
server-to-server postback applies here.
## In-App Callbacks
In-app callbacks report rewards to your app through the SDK, as a single `RewardCollection` callback.
**Example:** If a user completes three surveys, the SDK fires one callback containing all three rewards.
**Key features:**
* TapResearch notifies users of their rewards
* Default callback method (fires unless server-to-server is configured)
**When callbacks fire:**
* When the user closes TapResearch
* After successful SDK initialization
:::info
In-app callbacks are the default. To receive rewards on your backend instead, configure the server-to-server postback described below.
:::
## Server-to-Server Postback
The server-to-server postback is a `GET` request that TapResearch sends to your callback URL immediately after a user earns a reward. The reward details are passed as query parameters, and an HMAC-MD5 signature (`sig`) lets you verify the request originated from TapResearch.
**Important:** With server-to-server callbacks your app is responsible for notifying users of the rewards they receive — TapResearch does not notify users with this method.
### Update Callback URL
Set or update your callback URL in the **"Edit App"** section of the [Publisher Dashboard](https://tapresearch.com/supplier_dashboard/overview).
**URL requirements:**
* No spaces or special characters
* Must return a 2xx response code to be considered valid
### Sample Request
```
https://your-callback-url.example.com/postback?uid=developers%40tapresearch.com&tid=777ca23551a4a9173920c22e1ed7f4f3&cpid=tap_37939e4ede350f3a8d5149d2fcaa025e&payout_amount=191&payout_currency=gold&revenue=0.5&payout_type=3&sig=42cbd66af5b670bed293d9b01c06d3c4
```
### Query Parameters
| Parameter | Type | Description |
|-----------|------|-------------|
| uid | String | Persistent unique identifier for this user. |
| tid | String | Transaction ID or click ID. This value will not be unique if the user completed multiple surveys in a single session. Use `cpid` for deduping purposes. |
| cpid | String | Unique survey complete identifier. We recommend that you store and check against this value to ensure you reward the user only once per survey complete. |
| payout_amount | Integer | Amount of currency earned by this user. |
| payout_currency | String | The type of currency the user earned. |
| revenue | Decimal | Amount you will be paid for this survey complete, in USD. |
| payout_type | Integer | The action that the user was rewarded for. 0 - Profile Complete, 3 - Survey Rewarded, 9 - Quick Question Completed. |
| sig | String | The URL signature: an HMAC-MD5 generated from the query string minus the `sig` parameter. See [Signature Validation](#signature-validation) below. |
:::note The parameters above aren't the complete set
The callback may include additional parameters beyond those listed here, and more
may be added over time. This is expected — the signature always covers the
**entire** query string, so validate against whatever you actually receive rather
than a fixed list of fields (see [Signature Validation](#signature-validation)).
:::
### Callback Testing
Use the **Test Callback** button in the dashboard to generate a test completion that fires a callback to your specified URL.
**Best practice:** Test callbacks before deploying to production.
:::note
Test callbacks use fixed currency values. Commercial survey values will vary.
:::
### Signature Validation
For security, every callback request includes an HMAC-MD5 signature (`sig`) generated from the query string. Validate it on every request before you reward a user.
**Sign the entire query string exactly as you receive it, minus `sig` — don't rebuild it from a fixed list of parameters.** The signature covers every parameter present, so hashing whatever arrives (as the steps and code below do) stays correct even as parameters are added.
Grab your API secret from your main dashboard. Example: `26dcc0fc7b6208fdfeffaf19f627cb4a`
1. Start with the incoming callback request, for example:
`https://your-callback-url.example.com/postback?uid=developers%40tapresearch.com&tid=777ca23551a4a9173920c22e1ed7f4f3&cpid=tap_37939e4ede350f3a8d5149d2fcaa025e&payout_amount=191&payout_currency=gold&revenue=0.5&payout_type=3&sig=42cbd66af5b670bed293d9b01c06d3c4`
2. Isolate the query string, leaving out the question mark.
3. Strip out the `sig` parameter, including the leading ampersand (`&`).
4. URL-decode the remaining query string.
5. Run your API secret and the decoded query string through an HMAC-MD5 generator.
6. Compare your generated HMAC against the `sig` parameter value. If they match, record the complete and reward the user.
```ruby
# Sample request URL
url = "https://your-callback-url.example.com/postback?uid=developers%40tapresearch.com&tid=777ca23551a4a9173920c22e1ed7f4f3&cpid=tap_37939e4ede350f3a8d5149d2fcaa025e&payout_amount=191&payout_currency=gold&revenue=0.5&payout_type=3&sig=42cbd66af5b670bed293d9b01c06d3c4"
# Isolate query string
query_string = url.gsub(/^(.*?)\?/, "")
# Strip out the sig parameter
stripped_query_string = query_string.gsub(/&sig.*/, "")
# Decode URL
decoded = URI.decode_www_form_component(stripped_query_string)
# Generate HMAC-MD5
api_secret = "26dcc0fc7b6208fdfeffaf19f627cb4a"
digest = OpenSSL::Digest.new("md5")
md5 = OpenSSL::HMAC.hexdigest(digest, api_secret, decoded)
puts md5 # 42cbd66af5b670bed293d9b01c06d3c4
```
```php
```
```java
private static final String API_SECRET = "26dcc0fc7b6208fdfeffaf19f627cb4a";
private static final String ALGO = "HmacMD5";
try {
// URL
String urlString = "https://your-callback-url.example.com/postback?uid=developers%40tapresearch.com&tid=777ca23551a4a9173920c22e1ed7f4f3&cpid=tap_37939e4ede350f3a8d5149d2fcaa025e&payout_amount=191&payout_currency=gold&revenue=0.5&payout_type=3&sig=42cbd66af5b670bed293d9b01c06d3c4";
// Generate HMAC-MD5
Mac mac = Mac.getInstance(ALGO);
SecretKeySpec key = new SecretKeySpec(API_SECRET.getBytes(StandardCharsets.UTF_8), ALGO);
mac.init(key);
// Isolate query string and remove sig parameter
String queryString = urlString.substring(urlString.indexOf("?") + 1);
queryString = queryString.substring(0, queryString.indexOf("&sig=")) + queryString.substring(queryString.indexOf("&sig=") + 37);
// Decode URL
String decode = URLDecoder.decode(queryString, StandardCharsets.UTF_8);
byte[] bytes = mac.doFinal(decode.getBytes(StandardCharsets.UTF_8));
String md5 = DatatypeConverter.printHexBinary(bytes).toLowerCase();
System.out.println(md5); // 42cbd66af5b670bed293d9b01c06d3c4
} catch (Exception e) {
e.printStackTrace();
}
```
### Whitelisted IP Addresses
To further harden your endpoint, restrict incoming callback requests to the following TapResearch source IP addresses:
| IP Address |
|------------|
| 34.198.225.203 |
| 3.88.121.21 |
| 3.89.214.250 |
| 3.92.129.242 |
### Retries
If a callback fails, our system continues retrying to send the reward for 48 hours OR until a `2xx` response code is returned.
## Survey outcome parameter
For every callback generated via the Survey Feed API, the `survey_outcome`
parameter is included — an integer representing the participant's final status:
| Value | Status | Description |
| ----- | ------------ | ------------------------------------------------------------------- |
| **1** | Complete | The participant successfully completed the survey. |
| **2** | Disqualified | The participant was screened out based on targeting requirements. |
| **3** | Over Quota | The survey reached its quota while the participant was in progress. |
**Callbacks only fire for revenue-generating matches.** If a participant is
disqualified or the survey reaches its quota, a callback is sent only when that
outcome results in a partial reward (as configured in your App Settings). If no
revenue is generated for the event, no postback is triggered.
---
# Authentication & request signing
Source: https://supply-docs.tapresearch.com/docs/3.x/survey-feed-api/authentication
# Authentication and Request Signing
TapResearch’s Partner API uses two mechanisms to control access and verify request integrity:
1. **API Token** — identifies your partner account. Only required on endpoints that need authentication.
2. **API Secret** — used to generate `sech` signatures for specific flows. Never sent directly in a request.
All Partner API endpoints require your API token for authentication, so this page focuses on when credentials are needed and how request signing works at a high level.
---
## API Token
Your **API token** is required on **all** Partner API endpoints.
Send it via header:
| Header | Description |
| ------------- | -------------------------------- |
| `X-Api-Token` | Identifies your partner account. |
This header is always required.
---
## User Identifiers
For endpoints involving participant context, include the relevant identifiers:
| Header | Required | Description |
| --------------------- | -------- | ------------------------------------------------ |
| `X-User-Identifier` | Yes | Unique user or device identifier (UUIDv4). |
| `X-Device-Identifier` | Optional | Hardware/device ID for multi-device distinction. |
---
## Using the API Secret
Your **API secret** is never transmitted. It is only used to generate signatures (`sech`) for two flows:
1. **Impanel Security Hash** (`/players/impanel`)
2. **Redirect Signing** (verifying redirect callbacks)
For complete signing instructions, see:
* **[Security Hash](#security-hash)** — below
* **[Redirect Signing](./redirects.md)**
---
## Security Hash
The security hash verifies authenticity for `/players/impanel`.
### How to Generate
1. Concatenate `user_identifier` and Unix timestamp with a colon.
Example: `123e4567-e89b-12d3-a456-426614174000:1713456789`
2. Compute the SHA-256 HMAC using your **API secret**.
3. Send the following **as request params** (not headers):
* `sech`: the hex-encoded HMAC digest
* `ts`: the same Unix timestamp used in the payload
4. Hashes expire after **5 minutes**.
#### Ruby Example
```ruby
payload = "#{user_identifier}:#{timestamp}"
digest = OpenSSL::HMAC.hexdigest('SHA256', api_secret, payload)
```
---
# Segmentation
Source: https://supply-docs.tapresearch.com/docs/3.x/survey-feed-api/segmentation
# Segmentation
Segmentation allows publishers to control which users are eligible for specific survey experiences by attaching **custom attributes** to a participant’s profile.
These attributes are sent during impanelment and can be used to **target or restrict access to certain offers**.
---
## How Segmentation Works
Segmentation is powered by the `attributes_hash` field on the [`/players/impanel`](./impanel.md) endpoint.
When a user is impaneled:
1. You send custom attributes via `attributes_hash`
2. TapResearch stores these attributes on the user profile
3. These attributes are evaluated when determining survey eligibility
4. Only users matching an offer’s targeting rules will qualify
`
`---
## Sending Segmentation Data
Segmentation attributes are passed as a JSON object:
```json
{
"attributes_hash": {
"is_premium_user": true,
"subscription_tier": "gold",
"days_since_signup": 7
}
}
```
### Supported Value Types
| Type | Example |
| ------- | --------------------- |
| Boolean | `true`, `false` |
| Number | `1`, `42`, `3.14` |
| String | `"gold"`, `"us_west"` |
---
## Example Use Cases
Segmentation can be used to target surveys based on internal business logic or user state.
### Examples
* Target only new users:
```json
{ "is_new_user": true }
```
* Segment by subscription tier:
```json
{ "subscription_tier": "premium" }
```
* Limit surveys to highly engaged users:
```json
{ "days_since_signup": 30 }
```
* Exclude users who have already completed onboarding:
```json
{ "completed_onboarding": false }
```
---
## How Targeting is Applied
Each offer can be configured with segmentation rules.
When a user requests surveys:
* Their stored attributes are evaluated against available offers
* Only offers with matching criteria are considered eligible
* If no segmentation rules are defined, the offer is available to all users
---
## When to Send Attributes
* Attributes should be sent during [/players/impanel](./impanel.md).
* You may update attributes over time by calling the endpoint again
* Updates take effect immediately for future survey requests
---
## Best Practices
* Use clear, consistent naming for keys (e.g. `is_premium_user`, not `premiumFlag`)
* Keep attribute values simple and predictable
* Prefer booleans and enums over free-form strings
* Avoid sending sensitive or personally identifiable information (PII)
* Only send attributes that are relevant for targeting decisions
---
## Notes
* `attributes_hash` is optional — if not provided, no segmentation is applied
* Attributes are stored per user and persist across sessions
* Segmentation is evaluated automatically — no additional API calls are required
* Attributes may influence which surveys are returned from [`/campaigns`](./fetch-surveys.md)
---
# Pass-through values
Source: https://supply-docs.tapresearch.com/docs/3.x/survey-feed-api/pass-through
# Pass-Through Values
Pass-through values allow publishers to send custom key-value data when launching a survey. These values are stored and then returned on the **callback** when a reward is issued.
This is useful for passing internal identifiers such as:
* Session IDs
* Placement IDs
* Campaign IDs
* User segmentation flags
* Any custom reconciliation metadata
---
## Where to Append Pass-Through Values
The **offer entry URL** is returned as one of the attributes in the `campaigns#index` API response.
To use pass-through values:
1. Retrieve the offer entry URL from the `campaigns#index` response.
2. Append the `pass_through_values` parameter to that URL before redirecting the user.
---
## Parameter Format
Append the following parameter to the end of the offer entry URL:
```
pass_through_values=
```
### Encoding Requirements
* The value must be a JSON object (key-value pairs).
* The JSON string must be Base64 encoded using **strict encoding**.
* The encoded value must be URL-safe.
---
## Example
### Original JSON
```json
{
"session_id": "abc123",
"placement_id": "wall_top",
"campaign": "spring_promo"
}
```
### Base64 Encoded
```
eyJzZXNzaW9uX2lkIjoiYWJjMTIzIiwicGxhY2VtZW50X2lkIjoid2FsbF90b3AiLCJjYW1wYWlnbiI6InNwcmluZ19wcm9tbyJ9
```
### Final Offer Entry URL
```
https://www.tapresearch.com/offers/identifier/pre_survey_entry?...&pass_through_values=eyJzZXNzaW9uX2lkIjoiYWJjMTIzIiwicGxhY2VtZW50X2lkIjoid2FsbF90b3AiLCJjYW1wYWlnbiI6InNwcmluZ19wcm9tbyJ9
```
---
## Callback Behavior
When a reward callback fires:
* The stored pass-through JSON is parsed.
* Each key/value pair is merged into the callback payload.
* Each value is returned as a **separate top-level parameter**.
* Values are **not nested** under `pass_through_values`.
### Example Callback Payload
```json
{
"uid": "developers",
"cpid": "tap_37939e4ede350f3a8d5149d2fcaa025e",
"payout_amount": 191,
"revenue": 0.5,
"session_id": "abc123",
"placement_id": "wall_top",
"campaign": "spring_promo",
"sig": "b438afd7426c743e777c387d7e2712d4"
}
```
Each pass-through value appears as its own attribute in the callback.
---
## Error Handling
If:
* `pass_through_values` is not valid Base64, or
* The decoded value is not valid JSON
The values will be ignored and no custom attributes will be stored.
---
## Best Practices
* Keep the JSON payload small.
* Use stable, predictable keys.
* Avoid sending sensitive information.
* Ensure values are unique per click when needed for reconciliation.
* Avoid using keys that conflict with existing callback parameters.
---
---
# Redirect URLs
Source: https://supply-docs.tapresearch.com/docs/3.x/survey-feed-api/redirects
# Redirect URLs
After survey completion, participants are redirected to your supplied URL with signed parameters.
The redirect URL can be handled in **two ways**:
1. **Placeholder substitution** (recommended for full control)
2. **Query parameter appending** (automatic fallback)
In both cases, a SHA-256 HMAC signature (`sech`) is added to verify integrity.
---
## Parameters
| Name | Description |
|-----|-------------|
| `status` | Click key state (see Redirect Statuses). |
| `revenue` | Partner revenue amount (float). |
| `reward` | Participant reward amount (float). |
| `tid` | Publisher-provided session or match identifier. This should be unique per click and is returned exactly as received. |
| `click_id` | TapResearch internal identifier for the click. |
| `pass_through` | Base64-encoded JSON payload containing pass-through values originally provided during the survey request. |
| `sech` | SHA-256 HMAC signature verifying the signed parameters. |
---
## Redirect Modes
### Placeholder Substitution
If your redirect URL contains placeholders using curly braces (e.g., `{STATUS}`), those placeholders will be replaced before redirecting.
Supported placeholders:
* `{STATUS}`
* `{REVENUE}`
* `{REWARD}`
* `{TID}`
* `{CLICK_ID}`
* `{PASS_THROUGH}`
Example:
```text
https://example.com/callback?status={STATUS}&tid={TID}&pt={PASS_THROUGH}
```
After substitution:
```text
https://example.com/callback?status=1&tid=session_123&pt=eyJrZXkiOiJ2YWx1ZSJ9&sech=
```
**Important behavior**
* Only placeholders **present in the URL** are included in the signature.
* After substitution, the system:
* Parses the resulting query parameters
* Computes `sech`
* Appends `sech` to the query string.
---
### Query Parameter Appending (No Placeholders)
If your redirect URL does **not** contain placeholders, the system automatically appends all parameters:
* `status`
* `revenue`
* `reward`
* `tid`
* `click_id`
* `pass_through`
Example base URL:
```text
https://example.com/callback
```
Result:
```text
https://example.com/callback?status=1&revenue=0.45&reward=50&tid=session_123&click_id=abc123&pass_through=eyJrZXkiOiJ2YWx1ZSJ9&sech=
```
In this mode, **all parameters are always signed**.
---
## Pass-Through Values
The `pass_through` parameter contains **Base64-encoded JSON** representing metadata provided earlier in the survey request flow.
Example decoded value:
```json
{
"sub_id": "user_456",
"campaign": "spring_sale"
}
```
Publishers may decode this value to recover their original tracking metadata.
---
## How Signing Works
The system computes a SHA-256 HMAC using your API secret.
### Signing Order
Canonical signing order:
```text
status,revenue,reward,tid,click_id,pass_through
```
### Data String Construction
The signature is created by:
1. Taking only the relevant keys (depending on redirect mode).
2. Ordering them according to the canonical signing order above.
3. Joining their values with commas.
Example:
```text
1,0.45,50,session_123,abc123,eyJrZXkiOiJ2YWx1ZSJ9
```
If `tid` is blank, it is included as an empty value:
```text
1,0.45,50,,abc123,eyJrZXkiOiJ2YWx1ZSJ9
```
### Signature Generation
```text
HMAC-SHA256(data_string, api_secret)
```
The resulting lowercase hex digest becomes the `sech` parameter.
---
## How to Validate the Redirect
When you receive the redirect:
1. Extract the relevant parameters.
2. Reconstruct the comma-separated string in the correct order.
3. Compute the SHA-256 HMAC using your API secret.
4. Compare your result to the received `sech`.
If they do not match:
* The parameters were modified, or
* The wrong API secret was used.
---
# Redirect statuses
Source: https://supply-docs.tapresearch.com/docs/3.x/survey-feed-api/redirect-statuses
# API Redirect Statuses
When TapResearch redirects a participant back to your platform, we include a `status` value that explains **why** the participant returned. Redirects fall into three stages: session checks, pre-match checks, and post-match outcomes.
For **session checks** and **pre-match checks**, all redirects return with a **blank `tid`** (match ID) because no reward is issued. **Post-match outcomes always include a `tid`** since most result in a reward and the value supports de-duplication.
---
## 1. Session Checks (Immediate Returns)
These checks run **as soon as the participant loads the entry URL**. If anything looks suspicious — unusual activity patterns, mismatched regions, unsupported devices, or risky behavior — the participant is returned immediately.
| Code | Status | Description |
| ------ |---------------------------------|---------------------------------------------------------------------------|
| **19** | ZIP / IP Mismatch | The participant’s ZIP code doesn’t match their detected IP region. |
| **20** | Device/IP Activity Limit | This device or IP has exceeded safe activity levels in the past 24 hours. |
| **21** | IP Blocked | The participant’s IP is blocked due to risk or abuse. |
| **29** | IP Activity Limit (Completed) | This IP has too many recent completions across surveys. |
| **30** | IP Activity Limit (Participant) | This participant’s IP has had too many sessions. |
| **31** | Unsupported Device | The participant opened a mobile-only integration on desktop. |
| **32** | Modified User Agent | The participant changed their browser/user agent mid-session. |
| **33** | Underage | The participant does not meet the minimum age requirement. |
| **34** | Quality Blocked | Past behavior indicates quality risks; session blocked. |
| **38** | Fraud Blocked | Fraud-like signals detected. |
| **52** | Location Validation Block | Participant failed location checks (used to detect VPNs/proxies). |
---
## 2. Pre-Match Checks (Before the Survey)
These checks happen **after session checks pass** but **before** matching the participant to a live survey.
| Code | Status | Description |
| ------ | ----------------- | ---------------------------------------------------------------------------------------------------------- |
| **5** | Unqualified | The participant did not qualify before entering the survey. |
| **6** | Over Quota | The quota closed before the participant entered the survey. |
| **8** | Traffic Throttled | System predicted the participant would hit traffic limits. |
| **27** | Entry Term | The participant failed partner-level eligibility checks. *(Only pre-match code that may include a `tid`.)* |
---
## 3. Post-Match Outcomes (After Matching or Survey Interaction)
These statuses reflect what happened **after** a match attempt or after the participant interacted with the survey.
| Code | Status | Description |
| ------ | --------------------- | ------------------------------------------------------------------- |
| **1** | Complete | The participant completed the survey. |
| **2** | Disqualified | The participant was screened out based on targeting requirements. |
| **3** | Over Quota | The survey reached its quota while the participant was in progress. |
| **4** | Survey Quality Term | Participant failed survey-level quality or consistency checks. |
| **10** | System Quality Term | Internal system-level quality checks triggered a termination. |
| **41** | Invalid Security Hash | The participant returned with an invalid or tampered security hash. |
---
## 4. Profile Completion
For partners who rely on TapResearch to collect participant profile data instead of calling `/players/impanel`.
| Code | Status | Description |
| ------ | ---------------- | --------------------------------------------------------------------------------------- |
| **61** | Profile Complete | The participant finished the TapResearch profile flow and is ready for survey matching. |
---
# Support center
Source: https://supply-docs.tapresearch.com/docs/3.x/survey-feed-api/support-center
# Support Center
The `support_url` field returned on each campaign in the `/campaigns` response provides a TapResearch-hosted support contact form pre-loaded with session context for that participant. Use it to give players a direct path to TapResearch support without requiring you to handle survey-related issues yourself.
---
## When to show it
Display a **"Get Help"** or **"Contact Support"** link alongside each campaign in your offerwall UI. The link should point to the `support_url` value for that specific campaign.
Show it wherever the player can see the survey — on the survey list, on a detail card, or on a post-redirect screen. The goal is that if a player has a problem with a survey (e.g., completed but didn't receive credit), they have a clear path to report it.
---
## End-user flow
1. Player clicks your "Get Help" link.
2. TapResearch's support form opens, pre-loaded with the player's session context (campaign, publisher, session identifiers).
3. Player describes their issue and submits.
4. TapResearch support handles the inquiry directly.
Publishers are not in the loop for individual support submissions — TapResearch owns resolution.
---
## Caveats
### No match or expired session
The `support_url` encodes session state at the time the `/campaigns` response was generated. If TapResearch cannot match the session when the player opens the URL (e.g., the session has expired or the URL was cached), the support form will show an error directing the player back to the publisher's support team.
**Publishers must provide a fallback contact channel.** Players who hit this error have no other path forward from TapResearch's side.
### Don't cache the URL
`support_url` is session-specific. Fetch it fresh from the `/campaigns` response on each wall load — do not store and reuse it across sessions.
### Don't construct it manually
Always use the value from the API response. The URL format is internal and may change without notice.
---
# Best practices
Source: https://supply-docs.tapresearch.com/docs/3.x/survey-feed-api/best-practices
# Best Practices
- **Refresh surveys every 2 minutes and whenever a user returns from a survey.**
Survey availability changes frequently due to targeting, quotas, and real-time supply/demand. Regularly refreshing ensures users always see fresh, available surveys. Additionally, refresh immediately when a user returns from a survey — completing or exiting a survey can change which surveys they qualify for, so a fresh call ensures the list reflects their current eligibility.
- **Keep participant profiles up to date.**
Accurate and current profile data directly improves targeting and increases the likelihood of receiving surveys the user qualifies for. Each player's profile data is cleared after 90 days to ensure current data is used.
- **Map qualifications carefully by country.**
Each country has its own qualification set and `pre_code` values. Ensuring correct mapping improves match rates, reduces disqualification rates, and provides a better user experience.
- **Use the `epc` field if you plan to manage your own yield.**
Surveys are already ordered using TapResearch’s internal yield management algorithm. However, if you choose to override this ordering, rely on the `epc` field rather than `reward_amount`. Note that `reward_amount` reflects **only what the user is paid on a full complete** — TapResearch also pays out for DQs and OQs based on time spent in the survey.
`epc` incorporates *all* payout types, giving you a more accurate signal of expected earnings per click if you're doing your own yield management.
- **Remove surveys from the UI once a user clicks them.**
Most surveys do **not** allow re-entry — if a user taps the same survey again, they’ll be immediately terminated. For surveys that *do* allow multiple entries, they will naturally reappear on the next refresh cycle.
---