# TapResearch Publisher Documentation — Eligibility API # Source: https://supply-docs.tapresearch.com · generated at build time, do not edit by hand. # Each section below is one documentation page. # Eligibility API Source: https://supply-docs.tapresearch.com/docs/3.x/eligibility-api/overview # Eligibility API The **Eligibility API** is a pre-send eligibility check. You make a single server-side call before showing a survey call-to-action, and TapResearch tells you whether the user currently has an offer available. In short: **one call in, `has_offer` true/false out.** Gate your call-to-action on that answer. :::tip Creating your app In the [Supplier Dashboard](https://www.tapresearch.com/supplier_dashboard/overview), create a new app and select **Eligibility API**. See the [Setup checklist](../setup) for the full flow. ::: :::info The Eligibility API returns a **yes/no and a link** — it does **not** return a list of surveys. If you need to render survey tiles or run a profile-first impanel flow, use the **Survey Feed API** instead. ::: ## How it works 1. Before rendering your "Earn rewards" button (or any survey entry point), your backend calls the [has-offer check](has-offer). 2. TapResearch responds with `has_offer`: * **`true`** — the user has at least one offer waiting. Show the call-to-action and link the user to the returned `offer_url`. * **`false`** — the user has nothing available right now. Hide or disable the call-to-action so the user never taps into an empty wall. 3. When the user finishes an offer, TapResearch notifies your server through a [callback](callbacks). ## When to use it Use the Eligibility API when you want to: * Show a survey entry point **only** when there is something for the user to do. * Keep all logic on your server — no SDK, no client-side survey rendering. * Control the exact placement, copy, and styling of the call-to-action yourself. ## What you need * An **API token** for your app (from the Publisher Dashboard). * A stable **`user_identifier`** for each user. * An **`offer_identifier`** for the placement you are checking. ## Next steps * [Eligibility check](has-offer) — the request and response in full. * [Callbacks](callbacks) — how rewards are reported back to your server. --- # Integrate with AI Source: https://supply-docs.tapresearch.com/docs/3.x/eligibility-api/integrate-with-ai # Integrate the Eligibility 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 Eligibility API reference, so it works from the real documentation instead of guessing. Your agent can implement the server-side eligibility check and the hand-off link for 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 eligibility request. The Integration tab marks this once it sees the request. 2. **Eligibility handled** — Confirm you receive the yes/no plus the hand-off link and branch on it correctly. 3. **Reward callback configured** — Set your server-to-server postback URL. 4. **Survey complete & reward redeemed** — Send a test user through the hand-off link, complete a 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). --- # Eligibility check Source: https://supply-docs.tapresearch.com/docs/3.x/eligibility-api/has-offer # Eligibility check Use this endpoint to check whether a user currently has a survey offer available **before** you show them a call-to-action. It returns a simple `has_offer` true/false plus the links you need to send the user into (or away from) the offer. ``` POST https://www.tapresearch.com/supply_api/v4/surveys/offer ``` All requests must be made over **HTTPS**. --- ## Request Send the parameters as form-encoded body fields. | Parameter | Type | Required | Description | | ------------------- | ------ | -------- | -------------------------------------------------------------------------------------------- | | `user_identifier` | String | ✅ | Your persistent, unique identifier for this user. | | `api_token` | String | ✅ | The API token for your app, found in the Publisher Dashboard. Must belong to an app created with the **Eligibility API** type. | | `client_ip` | String | ✅ | The end user's IP address. | ### Example Request ```bash curl -X POST https://www.tapresearch.com/supply_api/v4/surveys/offer \ -d "user_identifier=user-12345" \ -d "api_token=your-api-token" \ -d "client_ip=203.0.113.42" ``` --- ## Response The response is a JSON object. `has_offer` is the field to branch on; the URLs and `message_hash` are only meaningful when `has_offer` is `true`. | Field | Type | Description | | -------------- | ------- | ------------------------------------------------------------------------------------------------ | | `has_offer` | Boolean | `true` if the user has an offer available, `false` otherwise. | | `offer_reason` | Integer | A code explaining the result. **Always present.** See [Reason codes](#reason-codes). | | `reason_comment` | String | Human-readable explanation of `offer_reason`. **Always present.** | | `offer_url` | String | The URL to send the user to in order to start the offer. Present when `has_offer` is `true`. | | `abandon_url` | String | The URL to send the user to if they decline or abandon the offer. Present when `has_offer` is `true`. | | `message_hash` | Object | Details about the reward for completing the offer. See [message_hash](#message_hash) below. | ### `message_hash` A preview of the reward **range** currently available to this user, given current survey inventory. | Field | Type | Description | | ---------- | ------ | -------------------------------------------- | | `min` | String | Minimum reward amount currently available. | | `max` | String | Maximum reward amount currently available. | | `currency` | String | The name of the currency the user earns. | ### Example Response — offer available ```json { "has_offer": true, "offer_url": "https://www.tapresearch.com/supply_api/v3/surveys/start?token=...", "abandon_url": "https://www.tapresearch.com/supply_api/v3/surveys/abandon?token=...", "message_hash": { "min": "50", "max": "200", "currency": "gold" } } ``` ### Example Response — no offer ```json { "has_offer": false, "offer_reason": 19, "reason_comment": "No content available" } ``` --- ## Using the response * **`has_offer: true`** — Show your call-to-action and link the user to `offer_url`. Use `message_hash` to display the reward range (for example, "Earn 50–200 gold"). If the user backs out, send them to `abandon_url`. * **`has_offer: false`** — Do not show the call-to-action. There is nothing for the user to do right now. When the user completes the offer, TapResearch reports the reward to your server through a [callback](callbacks). --- ## Reason codes Every response includes `offer_reason` (an integer) and a matching `reason_comment`. Branch on the code, not the comment text. Common values: | Code | Meaning | | ---- | ------- | | 0 | Qualified — an offer is available (`has_offer: true`) | | 1 | Not qualified — no surveys available for this user right now | | 2 | App is in test mode — only registered test devices are eligible | | 4 | Not a supported country | | 5 | User is under the supported age | | 6 | Missing user identifier | | 8 | User is banned | | 19 | No active offers available | | 20 | User identifier is an anonymous device advertising ID, not a real user ID | | 22 | User is blacklisted | This is the common subset; other codes may appear. ## Errors Errors return `{ "error": "" }` with an HTTP status: | Status | When | | ------ | ---- | | `401 Unauthorized` | Missing or invalid `api_token` — body `{"error":"Unauthorized."}` | | `422 Unprocessable Entity` | `user_identifier` missing (`{"error":"user_identifier must be specified"}`), or the token isn't an Eligibility API app (`{"error":"This endpoint requires a v4 Eligibility API app."}`) | TapResearch may also return `503` during maintenance. --- # Callbacks Source: https://supply-docs.tapresearch.com/docs/3.x/eligibility-api/callbacks # Callbacks When a user completes an offer surfaced through the Eligibility API, TapResearch notifies your server with a server-to-server postback. Validate every callback before you reward the user. ## 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. ---