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

# External screeners

> Run your own second screener on your platform and post the results back to Respondent

Some partners run their own screening step — proprietary questions, an internal qualification model, or compliance checks that have to happen on their own platform. **External screeners** let you do that without losing Respondent's recruitment funnel: participants qualify on the Respondent screener first, then complete *your* screener on *your* site, and only then does their application present to the researcher as submitted and invitable.

This page covers:

* How the flow works end to end, and the two response statuses it adds
* Setting up a project: the screener link and the declared question catalog
* Receiving participants (the `respondent_*` correlation parameters)
* Posting answers back, corrections, and how the qualification verdict is derived
* The return contract — the two rules your redirect must follow
* Webhooks and status handling

<Note>
  External screeners are enabled per organization. Contact Respondent to have the flow turned on for your organization — until then the fields below are accepted and stored, but every project runs the classic single-screener flow.
</Note>

## How the flow works

1. A participant applies to your project and completes the **Respondent screener** as usual.
2. If they qualify, their screener response moves to **`SECOND_SCREENER_IN_PROGRESS`** and Respondent shows them a handoff page explaining they'll finish the screener on your platform. When they continue, they arrive at your `externalScreenerLink` with correlation parameters appended.
3. The participant completes your screener on your site.
4. You **post their answers back** with [Post external screener answers](/screener-responses/post-external-screener-answers), then redirect them to the Respondent page they came from. You must ensure all answers are posted before the user can be redirected back.
5. Once the answers cover your declared question catalog (or you signal early completion), the response moves to **`SECOND_SCREENER_SUBMITTED`**. From there it behaves exactly like a classic submitted application: it's visible to the researcher, counts toward the project's applications, and can be invited — manually or by auto-invite.

Two statuses carry the flow, and each behaves like a status you already know:

| Status                        | Behaves like | Meaning                                                                                                                                                                                                                                          |
| ----------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `SECOND_SCREENER_IN_PROGRESS` | `DRAFT`      | Qualified on the Respondent screener, now in yours. Not visible to the researcher, not invitable, holds no recruitment slot. Participants may sit here indefinitely — there is no expiry and Respondent sends no reminder emails for this state. |
| `SECOND_SCREENER_SUBMITTED`   | `PENDING`    | Your answers are in and the application is settled. Everything keyed on `PENDING` — researcher visibility, webhooks, auto-invite, invitability — applies here too.                                                                               |

<Note>
  `SECOND_SCREENER_SUBMITTED` does not mean the participant *passed* your screener. Like a classic submission, the pass/fail verdict lives in the `qualified` flag — a participant who fails your screener still settles on `SECOND_SCREENER_SUBMITTED`, with `qualified: false`.
</Note>

## Setting up a project

An external-screener project declares two things:

* **`externalScreenerLink`** — the URL of your screener. Participants are sent here with correlation parameters appended, so the URL may include its own query string.
* **`externalQuestions`** — the declared catalog of your screener's questions. Answer rows you post back are validated against these `questionId`s, and the catalog defines when a response counts as complete.

You can set both on [Create a project](/projects/create-a-project), or create the project with just the link and declare the catalog later with [Replace external screener questions](/projects/replace-external-screener-questions):

```json theme={null}
PUT /v1/projects/{projectId}/external-screener-questions/bulk

[
  {
    "questionId": "team-size",
    "questionType": "RADIO",
    "text": "How many people do you manage?",
    "options": ["None", "1-5", "6-20", "More than 20"]
  },
  {
    "questionId": "tooling",
    "questionType": "TEXT",
    "text": "Which tools does your team use day to day?"
  }
]
```

* The endpoint **replaces the whole catalog** (like the [screener questions bulk endpoint](/screener-questions/create-multiple-screener-questions)). Send the full list every time.
* `questionId` is **your** stable identifier — answer rows reference it, and completion is counted against it. Duplicate `questionId`s are rejected.
* `questionType` is one of `RADIO`, `CHECKBOX`, `TEXT`, `NUMBER`, or `OTHER`. Use `OTHER` for anything not modelled — the `text` keeps the answers legible to researchers either way.
* Don't change `questionId`s while a project is live: answers already posted are keyed by them, and completion counting depends on the declared set.

**Publishing requires the pair.** A draft may hold just the link (or just the catalog) while you're setting up, but [Publish a project](/projects/publish-a-project) rejects a half-configured pair — a link needs at least one declared question, and declared questions need a link.

## Receiving participants

When a qualified participant continues to your screener, Respondent appends these query parameters to your `externalScreenerLink`:

| Parameter                         | What it is                                                                                                 |
| --------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `respondent_screener_response_id` | The screener response this participant's answers belong to. **This is the id you post answers back with.** |
| `respondent_project_id`           | The project the participant applied to. Used in the answers URL, and to build the return URL.              |
| `respondent_user_id`              | The participant's user id.                                                                                 |
| `respondent_profile_id`           | The participant's profile id.                                                                              |

Persist at least `respondent_screener_response_id` and `respondent_project_id` for the session — you need both to post answers and to send the participant back.

<Warning>
  Ask participants **only your screener questions**. Never ask for their Respondent password, a sign-in code, or contact details such as email or phone — Respondent tells participants to close the tab and report any site that does.
</Warning>

## Posting answers back

Post the participant's answers — and your per-question verdicts — with [Post external screener answers](/screener-responses/post-external-screener-answers):

```json theme={null}
POST /v1/projects/{respondent_project_id}/screener-responses/{respondent_screener_response_id}/external-screener-answers

{
  "answers": [
    {
      "questionId": "team-size",
      "answerValues": ["6-20"],
      "qualifies": true
    },
    {
      "questionId": "tooling",
      "answerText": "Jira, Figma, and an internal planning tool",
      "qualifies": true
    }
  ]
}
```

How it behaves:

* **Rows upsert idempotently per `questionId`.** Re-sending a row replaces the earlier one, so a correction is just another POST — including flipping an earlier `qualifies` verdict. Respondent re-derives the response's state from the stored rows on every write.
* Each row must reference a declared `questionId` (undeclared ids are rejected) and must carry content: at least one of `answerValues` (a non-empty array — multi-select friendly) or `answerText` (a non-empty string).
* **Completion**: the response moves to `SECOND_SCREENER_SUBMITTED` once the stored rows cover **every** declared question. If participants may legitimately skip questions on your side, send `"complete": true` alongside the answers to settle the response early — you can post in as many batches as you like before that.
* **The verdict is derived**: the response ends up `qualified` only if *every* stored row has `qualifies: true`. A failed verdict settles the response on `SECOND_SCREENER_SUBMITTED` with `qualified: false` — visible to the researcher, but excluded from qualified counts and auto-invite.
* You can keep posting corrections while the response is in `SECOND_SCREENER_IN_PROGRESS`, `SECOND_SCREENER_SUBMITTED`, or `PENDING`. Once the participant has been invited, the response is closed to answer writes.
* For an overall disqualification decision that isn't tied to answer rows, use [Qualify participant](/screener-responses/qualify-participant) with `qualifyStatus: false` — same as on classic projects. Note that a later answers re-post re-derives the verdict from the rows, which can undo a manual flip.

## The return contract

Two rules, both load-bearing:

<Steps>
  <Step title="Post the answers before you redirect">
    Send the answers request and wait for the response **before** sending the participant back. If the participant lands back on Respondent before the answers arrive, they see a waiting state instead of a submitted application — the page recovers on its own once your POST lands, but posting first makes the handoff seamless.
  </Step>

  <Step title="Redirect to the apply page — never the completion URL">
    Send the participant back to the project's apply page:

    ```text theme={null}
    https://app.respondent.io/participants/projects/{respondent_project_id}/apply
    ```

    Do **not** redirect through the project's [`completionUrl`](/docs/Screener-responses/completion-url). The completion URL marks the *study task* complete — on an unmoderated study or survey it starts the review-then-autopay clock. A screener is not the study; routing screener traffic through it would pay participants for applying.
  </Step>
</Steps>

Respondent never auto-forwards the participant in either direction: the handoff to your screener and the return are both explicit pages, so a participant who navigates back early can always resume from where they left off.

## Webhooks and statuses

If you use [webhooks](/docs/Webhooks/webhooks), the two new statuses arrive as ordinary `SCREENER_RESPONSES.UPDATED` events — `updatedFields` carries the raw status strings:

* the handoff: `status` `DRAFT` → `SECOND_SCREENER_IN_PROGRESS`
* the submission: `status` `SECOND_SCREENER_IN_PROGRESS` → `SECOND_SCREENER_SUBMITTED`

Both are available from the webhook **simulate** endpoint (`screenerResponseUpdatedType` values `STATUS.SECOND_SCREENER_IN_PROGRESS` and `STATUS.SECOND_SCREENER_SUBMITTED`), so you can integrate before your organization's flow is enabled.

A few status behaviors worth knowing:

* **Plain `PENDING` on an external-screener project means "held"** — the response is in an identity-verification or review hold. It settles back to `SECOND_SCREENER_SUBMITTED` when released. Your answer posts are still accepted during the hold.
* **[Cancel invite](/screener-responses/cancel-invite) reverts to `SECOND_SCREENER_SUBMITTED`**, not `PENDING` — the participant already completed your screener, so withdrawing an invitation doesn't send them through it again.
* [**List screener responses**](/screener-responses/list-screener-responses) accepts both new values in its `statuses` filter.

## Quick reference

| I want to…                       | Do this                                                                                                                                |
| -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| Turn the flow on                 | Contact Respondent to enable it for your organization                                                                                  |
| Configure a project              | Set `externalScreenerLink` + `externalQuestions` on create, or link first and catalog via the bulk endpoint — both required by publish |
| Identify an arriving participant | Read `respondent_screener_response_id` + `respondent_project_id` from the query string                                                 |
| Report answers                   | `POST …/external-screener-answers` — before redirecting back                                                                           |
| Let participants skip questions  | Send `"complete": true` with the final batch                                                                                           |
| Fix a wrong answer or verdict    | Re-post the corrected row — rows upsert per `questionId`                                                                               |
| Fail a participant               | Per-question: `qualifies: false` rows. Overall: the qualify endpoint                                                                   |
| Send the participant back        | The project's apply page — never `completionUrl`                                                                                       |
