# Screening Questions (https://terac.com/docs/developers/guides/screening-questions) Define screening questions to qualify, disqualify, or collect freeform responses from participants during application. Screening questions are asked to participants during application. Most are multi-option questions that qualify or disqualify based on the chosen answer (design these so the qualifying answer is not obvious). You can also ask open-ended freeform questions (`pick: "text"`) when you want a written response to review rather than an automatic qualify/reject. ## Example [#example] ```json [ { "key": "role_type", "text": "Which best describes your current role?", "pick": "one", "answers": [ { "text": "Individual contributor", "qualify_logic": "may" }, { "text": "People manager", "qualify_logic": "may" }, { "text": "Executive / C-suite", "qualify_logic": "may" }, { "text": "Not currently employed", "qualify_logic": "reject" } ] }, { "key": "facility_size", "text": "What is the approximate total square footage of facilities you currently oversee?", "pick": "one", "answers": [ { "text": "I don't manage any facilities", "qualify_logic": "reject" }, { "text": "Under 10,000 sqft", "qualify_logic": "reject" }, { "text": "10,000 - 50,000 sqft", "qualify_logic": "may" }, { "text": "50,001 - 100,000 sqft", "qualify_logic": "may" }, { "text": "100,001 - 500,000 sqft", "qualify_logic": "must_one_of" }, { "text": "Over 500,000 sqft", "qualify_logic": "must_one_of" } ] }, { "key": "team_size", "text": "How many direct reports do you have?", "pick": "one", "answers": [ { "text": "1-5", "qualify_logic": "may" }, { "text": "6-15", "qualify_logic": "may" }, { "text": "16-50", "qualify_logic": "may" }, { "text": "Over 50", "qualify_logic": "may" } ] } ] ``` The `key` field is a client-defined identifier for the question. It is required when the question is referenced by a quota. Whether or not you send one, every question reads back with a `key`, and that same value is returned as `screening_answers[].key` on `GET /submissions/{submissionId}`. That is how you join an answer to the question that produced it. Treat the value as opaque: when you omit `key` we assign one, and the format is not part of the contract. ## Question Fields [#question-fields] | Field | Type | Required | Description | | --------------------------- | ------- | ----------- | ---------------------------------------------------------------------------------------------------------------- | | `key` | string | no | Client-defined identifier. Required when referenced by quotas or display conditions. | | `text` | string | yes | The question text shown to participants (plaintext). | | `question_rich_text` | string | no | Optional Markdown (GFM) display copy. See Rich Text Prompts. | | `pick` | string | yes | Question type: `one`, `any`, `boolean`, `text`, or `grid`. | | `answers` | array | conditional | Two or more answer options. Required for choice questions (`one`, `any`, `boolean`); omit for `text` and `grid`. | | `answers[].text` | string | yes | The answer text shown to participants. | | `answers[].qualify_logic` | string | yes | Qualification behavior: `may`, `must`, `must_one_of`, `reject`, or `review`. | | `answers[].allow_free_text` | boolean | no | Reveals an "Other, please specify" write-in box when this answer is selected. Applies to `pick` `one` / `any`. | | `min_qualifying` | integer | no | Multi-select (`any`) only. Require at least this many `must_one_of` answers (see Qualify Logic). | | `grid` | object | conditional | Required for `pick: "grid"`. Rows, columns, and per-cell qualify logic (see Grid Questions). | | `display_condition` | object | no | Show this question only when earlier answers match (see Ask Only If). | | `conditional_rules` | array | no | Cross-question consistency / anti-falsification checks that reject or flag for review (see Consistency Checks). | | `skip_rules` | array | no | Branching: jump to a later question (or end the screener) when this question's answers match (see Branching). | | `allow_paste` | boolean | no | Open-ended (`text`) only. Whether participants may paste into the field. Pasting is blocked by default. | ## Question Types [#question-types] | `pick` | Description | | --------- | ---------------------------------------------------------------- | | `one` | Single select -- participant picks exactly one | | `any` | Multi select -- participant picks one or more | | `boolean` | Yes / No | | `text` | Open-ended -- participant types a freeform response (no options) | | `grid` | Matrix -- one shared set of columns chosen per row | ## Open-Ended Questions [#open-ended-questions] Set `pick: "text"` for a freeform response. Open-ended questions have no `answers` and no qualifying logic, so they do not auto-screen participants. Submissions are routed to manual review for you to read the response and approve or reject. Quotas cannot target open-ended questions. ```json [ { "key": "biggest_challenge", "text": "What is the biggest challenge you face managing your facilities today?", "pick": "text" } ] ``` ## Qualify Logic [#qualify-logic] | Value | Meaning | | ------------- | --------------------------------------------------------------------------------------------------------------------------- | | `may` | Does not affect qualification | | `must` | Participant must select this exact answer (valid with `one` or `any`) | | `must_one_of` | At least one answer in this any-of group must be selected | | `reject` | Selecting this disqualifies the participant | | `review` | Selecting this routes an otherwise-qualified applicant to manual review instead of auto-inviting (auto-invite studies only) | > **Note:** On multi-select (`pick: "any"`), `must` means *this exact answer is required*, while `must_one_of` means *at least one of a group*. Use `must` for each individually-required option (e.g. "must have run Google Ads") and `must_one_of` for an any-of group (e.g. "Meta OR TikTok"). > **Single-select collapse:** on `pick: "one"` and `pick: "boolean"` only one answer can be picked, so `may` / `must` / `must_one_of` all persist as the same qualifying disposition (and read back as `must`). Only `reject` and `review` change the outcome on a single-select — use `pick: "any"` when you need a distinct disposition per answer. On multi-select, `min_qualifying` raises the "at least one of" bar for the `must_one_of` group: with three `must_one_of` answers and `min_qualifying: 2`, a participant must select at least two of the three to qualify. Omitted or `1` keeps the default "at least one of" behavior. It is ignored for other `pick` types. ## Grid Questions [#grid-questions] Set `pick: "grid"` for a matrix question that asks the same set of choices about several items. Omit `answers` and provide `grid` instead. Per-cell `qualify_logic` uses the same values as answers. Use `grid.pick` to control how many columns a participant may choose per row: `"one"` (default) is a single column per row, and `"any"` lets them select any number of columns per row (a "select all that apply per row" matrix). | Field | Type | Required | Description | | ------------------------------ | ------ | -------- | ------------------------------------------------------------------- | | `grid.pick` | string | no | `one` (default, single column per row) or `any` (multiple per row). | | `grid.rows[].text` | string | yes | Row label (e.g. a platform name). | | `grid.rows[].key` | string | no | Stable row key; defaults to `r0`, `r1`, ... Used by `cell_actions`. | | `grid.columns[].text` | string | yes | Column label shared across every row. | | `grid.columns[].value` | string | no | Stable column value; defaults to `c0`, `c1`, ... | | `grid.cell_actions[]` | array | no | Per-cell qualify logic. Omit a cell for neutral (`may`). | | `cell_actions[].row` | string | yes | A row key from `grid.rows`. | | `cell_actions[].column` | string | yes | A column value from `grid.columns`. | | `cell_actions[].qualify_logic` | string | yes | `may`, `must`, `must_one_of`, or `reject`. | ```json [ { "key": "platforms", "text": "How active are you on each platform?", "pick": "grid", "grid": { "rows": [ { "key": "youtube", "text": "YouTube" }, { "key": "tiktok", "text": "TikTok" } ], "columns": [ { "value": "active", "text": "Active" }, { "value": "never", "text": "Never" } ], "cell_actions": [ { "row": "youtube", "column": "active", "qualify_logic": "must_one_of" }, { "row": "tiktok", "column": "active", "qualify_logic": "must_one_of" } ] } } ] ``` ## Ask Only If (Display Conditions) [#ask-only-if-display-conditions] Use `display_condition` to show a question only when earlier answers match -- for example, ask a Meta-specific follow-up only if the participant said they use Meta. Each condition references an **earlier** question by its `key` and one of its answers (for a grid source, use `"Row: Column"`). Conditions are combined with `join` (`and` / `or`). A condition that references a later question returns a `BAD_REQUEST` error. | Field | Type | Required | Description | | --------------------------------- | ------ | -------- | ------------------------------------------------------------------ | | `display_condition.conditions[]` | array | yes | One or more conditions. | | `conditions[].screening_question` | string | yes | The `key` of an earlier question. | | `conditions[].answer` | string | yes | An answer of that question. For a grid source use `"Row: Column"`. | | `conditions[].operator` | string | no | `eq` (answered that value, default) or `ne` (did not). | | `display_condition.join` | string | no | `and` (default) or `or`. | ```json [ { "key": "platforms", "text": "Which platforms do you use?", "pick": "any", "answers": [ { "text": "Meta", "qualify_logic": "may" }, { "text": "TikTok", "qualify_logic": "may" } ] }, { "key": "meta_spend", "text": "What is your monthly Meta ad spend?", "pick": "one", "answers": [ { "text": "$10k+", "qualify_logic": "must" }, { "text": "Under $10k", "qualify_logic": "reject" } ], "display_condition": { "conditions": [ { "screening_question": "platforms", "answer": "Meta", "operator": "eq" } ], "join": "and" } } ] ``` ## Rich Text Prompts [#rich-text-prompts] Set `question_rich_text` to render the prompt with Markdown (GitHub-flavored: bold, bullet lists, links). `text` stays required and remains the plaintext source of truth used by the voice agent and search, so keep the two in sync — `question_rich_text` only changes how the prompt is *displayed* to participants. ```json [ { "key": "compliance", "text": "Which compliance frameworks has your team completed in the last 12 months?", "question_rich_text": "Which compliance frameworks has your team **completed** in the last 12 months?\n\n- Count only audits that closed\n- Exclude in-progress engagements", "pick": "any", "answers": [ { "text": "SOC 2", "qualify_logic": "must_one_of" }, { "text": "ISO 27001", "qualify_logic": "must_one_of" }, { "text": "None of these", "qualify_logic": "reject" } ] } ] ``` ## Consistency Checks (Conditional Rules) [#consistency-checks-conditional-rules] `conditional_rules` catch inconsistent or low-quality answers that per-answer qualify logic can't express on its own — for example, a participant who claims a senior role but selects none of the tasks that role would actually do. When a rule's conditions match, it either **rejects** the participant or **flags the submission for human review**. Unlike a `display_condition` (which references an *earlier* question to gate visibility), a consistency-check condition may reference any question by `key`, or omit `screening_question` to test an answer on the question the rule lives on (a self-check). Write one all-`and` rule per pattern you want to catch. | Field | Type | Required | Description | | ---------------------------------- | ------ | -------- | ------------------------------------------------------------------------------------------------ | | `conditional_rules[].conditions[]` | array | yes | One or more conditions (up to 26). | | `conditions[].screening_question` | string | no | The `key` of the question to read. Omit to test an answer on THIS question (a self-check). | | `conditions[].answer` | string | yes | An answer by its text. For a grid source use `"Row: Column"`. | | `conditions[].operator` | string | no | `eq` (selected that answer, default) or `ne` (did not). | | `conditional_rules[].join` | string | no | `and` (default) or `or`. | | `conditional_rules[].outcome` | string | yes | `reject` screens the participant out; `review` flags the submission for a human when it matches. | ```json [ { "key": "role", "text": "What is your role?", "pick": "one", "answers": [ { "text": "Store Manager", "qualify_logic": "may" }, { "text": "Associate", "qualify_logic": "may" } ] }, { "key": "tasks", "text": "Which of these do you do day to day?", "pick": "any", "answers": [ { "text": "Approve schedules", "qualify_logic": "may" }, { "text": "Manage the P&L", "qualify_logic": "may" }, { "text": "None of these", "qualify_logic": "may" } ], "conditional_rules": [ { "conditions": [ { "screening_question": "role", "answer": "Store Manager" }, { "answer": "None of these", "operator": "eq" } ], "join": "and", "outcome": "review" } ] } ] ``` The second condition omits `screening_question`, so it tests an answer on the `tasks` question the rule lives on: a "Store Manager" who also picks "None of these" is flagged for review. ## Branching (Skip Rules) [#branching-skip-rules] `skip_rules` let a question jump the participant forward — to a later question, or straight to the end of the screener — based on how they answered it. A rule tests the answers of the question it lives on, and the target must be a *later* question (the screener only walks forward). Not available for `pick: "text"` or `"grid"`. | Field | Type | Required | Description | | --------------------------------- | -------------- | -------- | -------------------------------------------------------------------------------- | | `skip_rules[].conditions[]` | array | yes | One or more conditions (up to 26) testing THIS question's answers. | | `conditions[].screening_question` | string | yes | The `key` of the question this rule lives on. | | `conditions[].answer` | string | yes | One of that question's answers (its text; `"Yes"`/`"No"` for `pick: "boolean"`). | | `conditions[].operator` | string | yes | `eq` (answered that value) or `ne` (did not). | | `skip_rules[].join` | string | yes | `and` or `or`. | | `skip_rules[].target_question` | string \| null | yes | `key` of a later question to jump to; `null` ends the screener. | ```json [ { "key": "uses_product", "text": "Do you currently use the product?", "pick": "boolean", "answers": [ { "text": "Yes", "qualify_logic": "may" }, { "text": "No", "qualify_logic": "may" } ], "skip_rules": [ { "conditions": [ { "screening_question": "uses_product", "answer": "No", "operator": "eq" } ], "join": "and", "target_question": null } ] }, { "key": "plan_tier", "text": "Which plan are you on?", "pick": "one", "answers": [ { "text": "Free", "qualify_logic": "may" }, { "text": "Pro", "qualify_logic": "may" } ] } ] ``` A participant who answers "No" skips straight to the end; everyone else continues to the plan-tier question. ## Reading Screen-Out Rates [#reading-screen-out-rates] `GET /opportunities/{opportunityId}` returns `screening_stats` on a live opportunity: one entry per question, keyed by the same `key` you authored. ```json { "screening_question": "years_experience", "graded": 412, "rejected": 321, "rejection_rate": 0.78, "answers": [ { "text": "0-10", "selected": 91, "share": 0.22 }, { "text": "Over 10 years", "selected": 321, "share": 0.78 } ] } ``` `graded` counts the finished screeners that were **graded on this question**, which is not the same as how many people answered it. A screener with `skip_rules` or a `display_condition` stops grading at the first failure, so an answer the evaluator never reached carries no verdict and is excluded here. That distinction is what makes `rejection_rate` useful: it is the share of the population that *could* have been rejected on this question, not the share of everyone who applied. A question late in a branching screener can show a high rate on a small `graded` count, which means it is strict among the few who got that far, not that it is your main bottleneck. Sort by `rejected` when you want the question costing you the most applicants, and by `rejection_rate` when you want the strictest one. `answers[].share` is out of the same `graded` count, so a lopsided split is the signal that a question is narrower than intended. In the example above, widening the accepted range on `years_experience` is worth more than touching any other question. ## Launching Without a Screener [#launching-without-a-screener] A screener is recommended, because it decides who reaches the paid task. Some studies need none, for example when the task screens participants itself. For those, your organization can launch opportunities with no `screening_questions` once Terac has enabled it. Ask your Terac contact to turn it on. With no screener, everyone who matches your filters goes straight to the task, so the launch asks for three things: 1. Every task that delivers the work, such as an interview, activity or file upload, has `review_type: "manual_review"`. Nobody is paid until you approve their submission with `POST /submissions/{submissionId}/approve`, and a `completed` redirect moves a submission into review instead of paying it. A task that only confirms a step, such as signing a document, can stay as it is, as long as one task waits for your review. 2. `customer_screening_review` is `auto_invite`. Holding applicants for review needs screening answers to review them on. 3. The launch says so explicitly: ```json POST /opportunities/{opportunityId}/launch { "without_screener": true } ``` Without `without_screener`, a draft with no screener is refused with `412` and a message naming what to do next. The flag is ignored on an opportunity that has screening questions, and it never launches a screener that nobody can pass. If Terac runs its AI screening interview on the opportunity, applicants are still screened and reviewed before they reach the task, so only the third item applies. The dashboard offers the same launch. Its builder asks you to confirm it, sets each **Needs approval** task to `manual_review` as it launches, and keeps applicants on automatic invites while there is no screener. See [Launching Without a Screener](https://terac.com/docs/researchers/opportunities/screening#launching-without-a-screener) in the researcher docs. When your task screens participants itself, send the `screened_out` redirect for anyone it turns away (see [External Surveys & Redirect URLs](https://terac.com/docs/researchers/integrations/external-surveys)). They are rejected without pay, and their place opens for the next participant. # Webhooks (https://terac.com/docs/developers/guides/webhooks) Subscribe to submission events, verify the signature, and handle retries. Webhooks push submission events to your endpoint instead of you polling for them. The contract is a conventional one: a signed POST, an idempotency header, and exponential backoff on failure, so a receiver written for any mainstream provider works here with a different header name. One endpoint is one subscription: it has a URL, a list of the events it receives, and one signing secret. Adding a second event to the same receiver is a change to that subscription, not a second one, so a receiver never has more than one secret to verify with. ## Setting one up [#setting-one-up] Two calls: create the subscription, then confirm it. ### 1. Create a subscription [#1-create-a-subscription] ```bash curl -X POST https://terac.com/api/external/v2/hooks/subscriptions \ -H "Authorization: Bearer $TERAC_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "target_url": "https://yourapp.com/hooks/terac", "event_types": ["submission.status.change", "submission.approved"] }' ``` ```json { "id": "whs_123", "target_url": "https://yourapp.com/hooks/terac", "event_types": ["submission.status.change", "submission.approved"], "is_enabled": true, "confirmed_at": null, "disabled_at": null, "disabled_reason": null, "secret": "whsec_9f8e7d...", "created_at": "2026-08-11T21:00:00.000Z", "updated_at": "2026-08-11T21:00:00.000Z" } ``` Terac generates the secret; you cannot supply your own. It is returned here and nowhere else in a create response, but `GET /hooks/subscriptions/{id}/secret` reads it back in full, so losing it does not force a rotation. Treat it like an API key. `target_url` must be `https` and must resolve to a public host: private, loopback, link-local and internal addresses are refused, because confirming makes Terac issue a request to it. One URL can be subscribed once per organization: a second `POST` for a URL you already registered returns `409` naming the existing subscription. A new subscription comes back with `"confirmed_at": null` and receives nothing until you confirm it. ### 2. Confirm it [#2-confirm-it] ```bash curl -X POST https://terac.com/api/external/v2/hooks/subscriptions/whs_123 \ -H "Authorization: Bearer $TERAC_API_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` Terac POSTs one signed `webhook.ping` to your `target_url`. Answer `2xx` and the subscription starts receiving events; anything else returns `412` with the reason, and nothing is confirmed. > This is also how you test that Terac can reach your receiver. The ping carries > the same signature headers as a real delivery, so verifying it proves your > verification code works. Send it as often as you like. To see real submission > events arrive, [test your receiver in a > sandbox](https://terac.com/docs/developers/guides/webhooks#test-your-receiver-in-a-sandbox). Note the `-d '{}'`: every `POST` on this API expects a JSON body and a `Content-Type`, even when the endpoint takes only a path parameter. A bodyless `POST` returns `415`. ## Verifying a delivery [#verifying-a-delivery] Every request carries four headers: | Header | Meaning | | --------------------------- | ------------------------------------------------------------------------ | | `X-Terac-Request-Signature` | `base64(HMAC-SHA256(secret, timestamp + raw body))` | | `X-Terac-Request-Timestamp` | Unix seconds. Part of the signed string, so it changes on each retry. | | `X-Event-ID` | Unique per delivery and **stable across retries**. Deduplicate on this. | | `X-Timestamp` | ISO-8601, when the event happened. Stable across retries. Order by this. | Sign the concatenation of the timestamp header and the **raw request body**, with no separator. Parsing and re-serializing the JSON first will change the bytes and the signature will not match. ```ts import { createHmac, timingSafeEqual } from "node:crypto"; function verify(rawBody: string, headers: Headers, secret: string): boolean { const timestamp = headers.get("X-Terac-Request-Timestamp"); const signature = headers.get("X-Terac-Request-Signature"); if (!timestamp || !signature) return false; // Reject anything too old to be a live delivery, so a captured request // cannot be replayed indefinitely. const ageSeconds = Math.abs(Date.now() / 1000 - Number(timestamp)); if (ageSeconds > 300) return false; const expected = createHmac("sha256", secret) .update(timestamp + rawBody) .digest("base64"); const a = Buffer.from(expected, "base64"); const b = Buffer.from(signature, "base64"); // timingSafeEqual throws when the lengths differ, which a malformed // signature produces. return a.length === b.length && timingSafeEqual(a, b); } ``` One secret verifies every event type the subscription carries, so this function does not need to know which event it is looking at. ## Rotating the secret [#rotating-the-secret] ```bash curl -X POST https://terac.com/api/external/v2/hooks/subscriptions/whs_123/secret \ -H "Authorization: Bearer $TERAC_API_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` The response carries the new secret, and it takes effect immediately with no overlap window: the next attempt of every delivery, including one already queued, is signed with it. Deploy the new secret to your receiver first, or accept the signature failures in between. Rotation affects only this subscription. ## Event types [#event-types] Read `GET /hooks/event-types` rather than hardcoding a list. New event types are added without a breaking change and appear there first. To change what an existing subscription receives, `PATCH` its `event_types`. The list is replaced rather than added to, so send the full set you want: ```bash curl -X PATCH https://terac.com/api/external/v2/hooks/subscriptions/whs_123 \ -H "Authorization: Bearer $TERAC_API_KEY" \ -H "Content-Type: application/json" \ -d '{"event_types": ["submission.status.change"]}' ``` ### `submission.status.change` [#submissionstatuschange] Every status transition, so one subscription follows the whole lifecycle. ```json { "event_type": "submission.status.change", "event_id": "dlv_7h3k9", "resource_id": "sub_abc123", "occurred_at": "2026-08-11T21:00:00.000Z", "opportunity_id": "fy8auvdlx7ei5y5jdiy7w35z", "from": "screening", "to": "screened_out" } ``` `to` uses the status vocabulary of `GET /submissions/{id}`, so you can always fetch the submission the event names. `from` uses that vocabulary plus two names for the phase before the work starts, which a submission has no readable status in: | `from` | Meaning | | ----------- | ----------------------------------------------------------------------------- | | `init` | Nothing answered yet. The applicant entered and had not started the screener. | | `screening` | Mid-screener: answering it, or answered and awaiting the outcome. | So `from` tells you things `to` cannot. `"from": "init", "to": "screened_out"` is an applicant filtered out on their profile before ever seeing the screener, while `"from": "screening", "to": "screened_out"` is one who answered it and did not qualify. The same split separates a drop-out who never started from one who abandoned the screener half-way. A submission is not fetchable while it is in either of those two: a half-finished screener is not readable through the API, the way it is not openable in the dashboard. That is why they name a `from` and never a `to` — you do not receive an event for *entering* the screener, only for leaving it. To find out *why* someone was screened out, read `screening_answers` on the submission: each answer carries the per-question verdict. For the rates rather than the individuals, read `screening_stats` on the opportunity. ### `submission.approved` [#submissionapproved] The same transition as `submission.status.change` with `"to": "approved"`, on its own event type. Identical payload shape. Take this one alone if you only act on approvals: a submission produces roughly five status changes, so filtering on your side means receiving five deliveries to act on one. Taking both means an approval arrives twice, once under each event type, with a different `X-Event-ID`. ## Test your receiver in a sandbox [#test-your-receiver-in-a-sandbox] A sandbox organization is a separate test organization that Terac sets up for your integration. If you do not have one, ask your Terac contact for one. Its dashboard shows a "Demo mode" banner, and nobody is recruited, contacted or paid in it. Nothing in it is charged either: a launch never touches the balance, so a draft's `funding` always reports it `affordable`, and a feasibility request is priced automatically instead of waiting for Terac. When you launch an opportunity there, Terac fills it with example submissions whose participant has already finished: screened out, approved, or awaiting your review. None of them moves by itself, so your endpoint hears nothing unless you approve or reject one. A test submission is one that does move. You ask for it, Terac plays a fake participant through the opportunity, and every step sends the webhook a real participant's step would send, through the same delivery system: the same signature, the same retries, the same delivery log. ### Before you start [#before-you-start] The sandbox is its own organization, so it has its own API keys and its own webhook subscriptions. * **An API key from the sandbox.** Create it in the sandbox organization's settings, the way you created your first key. The examples below call it `$TERAC_SANDBOX_API_KEY`. The key of your production organization does not work here: it gets `404` for an opportunity in the sandbox, and `403` for one of its own. * **A subscription in the sandbox.** A subscription receives the events of its own organization only, so one you created with your production key hears nothing from the sandbox. Create and confirm one with the sandbox key, following [Setting one up](https://terac.com/docs/developers/guides/webhooks#setting-one-up). It gets its own signing secret. The same `target_url` can be subscribed in both organizations, but a separate URL for the sandbox keeps each receiver to one secret. ### 1. Create a test submission [#1-create-a-test-submission] The opportunity has to be active, and the API key has to belong to your sandbox organization. ```bash curl -X POST https://terac.com/api/external/v2/opportunities/fy8auvdlx7ei5y5jdiy7w35z/test-submissions \ -H "Authorization: Bearer $TERAC_SANDBOX_API_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` ```json { "id": "sub_abc123", "opportunity_id": "fy8auvdlx7ei5y5jdiy7w35z", "task_url": null, "dashboard_url": "https://terac.com/acme-sandbox/onboarding/opportunities/fy8auvdlx7ei5y5jdiy7w35z/submissions" } ``` Terac creates a participant called "Test Participant", with no email address and no phone number, and a submission for it. The participant then takes its steps a few seconds apart: it answers the screener, starts the task and finishes it. A test submission is an ordinary submission in your sandbox: * It shows in the opportunity's Submissions table in the dashboard, and in the applicant queue when it is waiting there, under the name "Test Participant". You can approve, reject, invite or decline it there as well as through the API. * It counts in the opportunity's numbers, such as `submission_stats`. A sandbox opportunity stays open when its approvals reach or pass its target, so you can keep testing on it. * It does not count toward a quota, and a full quota does not stop it. * It cannot be removed. Use an opportunity you keep for testing. ### 2. Choose what happens [#2-choose-what-happens] Both fields are optional. | Field | Value | What the test participant does | | ------------------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------- | | `screening_outcome` | `passed` (default) | Gets through the screener. | | | `failed` | Answers the screener and is screened out. | | | `filtered_out` | Is screened out on its profile before the screener. Allowed even when the opportunity has no filters. | | `task_completion` | `automatic` (default) | Terac finishes the task. | | | `redirect` | Terac starts the task and stops. You open `task_url` from the response, and your own completion redirect finishes the task. | The screening answers are real answers to your screening questions, graded the way a participant's are. What the submission shows afterwards depends on what you asked for: | You asked for | `screening_outcome` on the submission | `screening_answers` | | -------------- | ----------------------------------------------------------------------------- | ---------------------------------------------- | | `passed` | `passed`, or `review` when the only answers that get through are flagged ones | The answers the participant gave. | | `failed` | `failed` | The answers the participant gave. | | `filtered_out` | `null` | Empty: the participant never saw the screener. | An opportunity with no screener also gives `null` and no answers. A participant whose answers are flagged for review waits in your applicant queue, whatever `customer_screening_review` is set to. `GET /submissions/{submissionId}` returns `404` while the participant is still in screening, which includes waiting in your applicant queue. Read a waiting applicant from `GET /opportunities/{opportunityId}/applicants`. ### 3. What your endpoint receives [#3-what-your-endpoint-receives] When the participant passes the screener and you review the work, which is the default: | Step | `from` → `to` | | --------------------------------------------------- | ---------------------------------------------------------- | | The participant passes the screener | `screening` → `screen_passed` | | It starts the task | `screen_passed` → `in_progress` | | It finishes the task | `in_progress` → `awaiting_review` | | You call `POST /submissions/{submissionId}/approve` | `awaiting_review` → `approved`, plus `submission.approved` | | or `POST /submissions/{submissionId}/reject` | `awaiting_review` → `rejected` | Other setups and choices: | Situation | What you receive | | ------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | The task approves itself on completion: `auto_approve` with a `task_url`, or `self_report` | The first two rows above, then `in_progress` → `approved`, plus `submission.approved`. | | The opportunity has no screener | The first webhook is `init` → `in_progress`. There is no `screen_passed`. | | The opportunity has no tasks | `screening` → `screen_passed`, and nothing after it. | | You review applicants first: `customer_screening_review` is `manual_review` | Nothing until you act. Joining your applicant queue sends no webhook, so poll `GET /opportunities/{opportunityId}/applicants`. `POST /submissions/{submissionId}/invite` sends `screening` → `screen_passed`, and the steps continue as above. `POST /submissions/{submissionId}/decline` sends `screening` → `screened_out`. | | `screening_outcome` is `failed` | `screening` → `screened_out` | | `screening_outcome` is `filtered_out` | `init` → `screened_out` | | `task_completion` is `redirect` | The participant stops after `screen_passed` → `in_progress`. Your redirect to the completion callback sends the next one: `in_progress` → `awaiting_review`, or `in_progress` → `approved` when the task approves itself. | ### Timing [#timing] The participant takes its first step a few seconds after your request and the next ones about three seconds apart. Each webhook is queued when its step happens and delivered shortly afterwards, so allow about a minute for the three webhooks of the default flow. The participant stops wherever the next move is yours: inviting or declining an applicant, approving or rejecting the work, or your own completion redirect. Your call sends the next webhook, as it does on a live opportunity. After you invite an applicant, the participant still has the task to do. Terac looks for your decision every 30 seconds during the first hour after you created the test submission, and every 5 minutes after that, and the participant carries on from the next look. A test submission that is more than 24 hours old no longer moves by itself. It stays where it is, and you can create a new one. Pausing the opportunity holds a test participant where it is, as it holds a real one. It carries on after you resume, on the same schedule. Stopping the opportunity ends it: the participant takes no further steps. ### Finishing the task yourself [#finishing-the-task-yourself] With `task_completion` set to `redirect`, the response carries `task_url`: the opportunity's task link with the test submission's IDs filled in, exactly as a participant receives it. Wait for the `screen_passed` → `in_progress` webhook, then open the link. When your tool redirects to Terac's completion callback, the task is finished and the next webhook is sent. A redirect that arrives before the task has started does not finish it. Send it again once the `screen_passed` → `in_progress` webhook has arrived. After the callback your browser lands on Terac's sign-in page, because nobody is signed in as the test participant. The task is finished all the same. See [External Surveys & Redirect URLs](https://terac.com/docs/researchers/integrations/external-surveys) for the callback and its `result` values. ### When the request is refused [#when-the-request-is-refused] The `message` says which case applies. | Status | When | | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `403` | The API key does not belong to a sandbox organization. | | `409` | The opportunity is not active. The `message` names the status it is in. | | `422` | The opportunity cannot produce what you asked for: `failed` with no screener, or with screening questions that no answer fails; `passed` with screening questions that no answer passes, or with no screener and no tasks; `redirect` when the first task has no `task_url`, or has one that is not a link. | | `422` | Terac could not find screening answers that give the outcome you asked for. It tries a limited number of combinations, so on a very large screener some may exist that it did not reach. Ask for a different `screening_outcome`. | ## Retries [#retries] A delivery that does not return `2xx` is retried **12 times** after the first attempt, with the first retry a minute later and exponential backoff up to 12 hours: about two and a half days of trying in total. `5xx`, `408` and `429` are retried. Any other `4xx` is treated as a deliberate rejection and not retried, since a dozen identical requests would be refused identically. `410 Gone` is the clearest way to say "stop". Redirects are never followed. The signature is bound to your host, so a `3xx` elsewhere would hand it to whoever controls that host. Point `target_url` at the final URL. Deliveries time out after 10 seconds. Acknowledge with a `2xx` first and do your work afterwards, rather than holding the connection open. ## When a subscription stops [#when-a-subscription-stops] A subscription that fails **100% of the time for five consecutive days** is disabled automatically. Someone from Terac reaches out on day three first. A subscription disabled this way comes back with `is_enabled: false` and a `disabled_reason`, which is how you tell it apart from one you turned off yourself. Re-enable it once the endpoint is healthy: ```bash curl -X PATCH https://terac.com/api/external/v2/hooks/subscriptions/whs_123 \ -H "Authorization: Bearer $TERAC_API_KEY" \ -H "Content-Type: application/json" \ -d '{"is_enabled": true}' ``` Changing `target_url` clears the confirmation, since the new host has not accepted a ping yet. Confirm again to resume. Changing `event_types` does not: that host has already accepted one. ## The delivery log [#the-delivery-log] `GET /hooks/events` lists deliveries, newest first, with `?subscription_id=` to narrow and cursor pagination. One row per delivery, updated in place across retries, so `id` is the `X-Event-ID` your endpoint saw and `attempt_count` is how many tries it took. Confirmation pings are not logged there: they are a handshake, not a delivery. ## Handling duplicates [#handling-duplicates] Deduplicate on `X-Event-ID`. It is stable across retries, so the same value arriving twice means you already have that event and can answer `2xx` without reprocessing. Terac will not deliver a payload that has already been acknowledged, but a network failure after your endpoint committed and before the response reached us looks identical to a failure from our side, and that is the case the header exists for. # Quotas (https://terac.com/docs/developers/guides/quotas) Steer participant distribution with per-answer and cross-question targets on screening answers. Quotas let you control how many participants you recruit for specific screening answers. Without quotas, Terac fills participants on a first-come basis. With quotas, recruitment is steered so each target is honored. Quotas are expressed through **`cross_quotas`** — one entry per **cell**. A cell targets a combination of one or more screening answers: * **One condition** — a simple per-answer quota (e.g. "at least 30 senior engineers"). * **Two or more conditions** — an interlocked cross-tab, so the sample stays balanced on every dimension at once (e.g. "at least 4 who are in Brazil **and** at an agency"). Cells reference screening questions by their `key`, and cells that belong to the same cross-tab share a `dimension` token so they are summed and balanced together. > Quotas require screening questions. The API returns a `BAD_REQUEST` error if you provide `cross_quotas` without screening questions. ## Fields [#fields] | Field | Type | Description | | --------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------- | | `label` | string | Human label for the cell, e.g. `"Senior engineers"` or `"Brazil × Agency"` (1–200 characters). | | `conditions` | array | One condition per dimension (1–26). One condition = a simple per-answer quota; two or more interlock a cross-tab across distinct questions. | | `conditions[].screening_question` | string | The `key` of a screening question — one dimension of the cell. | | `conditions[].answer` | string | An answer of that question, by its text. For a grid source use `"Row: Column"`. | | `conditions[].operator` | string | `eq` (selected that answer, default) or `ne` (did not). | | `join` | string | How the conditions combine: `and` (default) or `or`. | | `target` | integer | Participant count for this cell (positive). | | `quota_type` | string | How the count is enforced: `minimum` (default), `maximum`, or `exact`. | | `dimension` | string | Optional. Cells sharing a `dimension` are summed as one interlocked cross-tab. Use one token per cross-tab. | `quota_type` controls how each cell's `target` is enforced: | `quota_type` | Meaning | | ------------ | ------------------------------------------------------------------------------------------------------ | | `minimum` | At least this many. A floor — recruitment prioritizes the cell while it is short. This is the default. | | `maximum` | At most this many. A cap — applicants who match are screened out once the cell is full. | | `exact` | Both a floor and a cap — recruit exactly this many. | The sum of cell targets does not need to equal `num_participants`. Recruitment stops when **both** conditions are true: every `minimum`/`exact` cell is met, and `num_participants` is reached. `maximum` cells never extend recruitment; they only screen out once full. ## Simple quota (one condition) [#simple-quota-one-condition] You want 60 participants, with a floor on senior reviewers and a cap on juniors — one cell per answer, each on the same question. ```json { "num_participants": 60, "screening_questions": [ { "key": "code_reviews", "text": "How many code reviews do you perform per week?", "pick": "one", "answers": [ { "text": "5 or more", "qualify_logic": "must_one_of" }, { "text": "2-4", "qualify_logic": "may" }, { "text": "0-1", "qualify_logic": "reject" } ] } ], "cross_quotas": [ { "label": "Senior reviewers", "conditions": [ { "screening_question": "code_reviews", "answer": "5 or more" } ], "target": 30, "quota_type": "minimum" }, { "label": "Occasional reviewers", "conditions": [{ "screening_question": "code_reviews", "answer": "2-4" }], "target": 20, "quota_type": "maximum" } ] } ``` Recruitment continues until you have at least 30 who answered "5 or more" and 60 total, while never accepting more than 20 who answered "2-4". ## Cross-question quota (two or more conditions) [#cross-question-quota-two-or-more-conditions] You want 40 participants, balanced across country **and** organization type. Each cell interlocks two questions; both cells share a `dimension` so they form one cross-tab. ```json { "num_participants": 40, "screening_questions": [ { "key": "country", "text": "Where are you based?", "pick": "one", "answers": [ { "text": "Brazil", "qualify_logic": "may" }, { "text": "Mexico", "qualify_logic": "may" } ] }, { "key": "org_type", "text": "What kind of organization do you work for?", "pick": "one", "answers": [ { "text": "Agency", "qualify_logic": "may" }, { "text": "In-house", "qualify_logic": "may" } ] } ], "cross_quotas": [ { "label": "Brazil × Agency", "dimension": "country_x_org", "conditions": [ { "screening_question": "country", "answer": "Brazil" }, { "screening_question": "org_type", "answer": "Agency" } ], "target": 4, "quota_type": "minimum" }, { "label": "Brazil × In-house", "dimension": "country_x_org", "conditions": [ { "screening_question": "country", "answer": "Brazil" }, { "screening_question": "org_type", "answer": "In-house" } ], "target": 8, "quota_type": "minimum" } ] } ``` Recruitment honors both cells independently: at least 4 participants who are in Brazil and at an agency, and at least 8 who are in Brazil and in-house. # Getting Started (https://terac.com/docs/developers/guides) Programmatic access to Terac's recruitment, interview, and insight delivery platform. The Terac API gives you programmatic control over participant recruitment, screening, and data collection. You can target specific audiences with filters, screen applicants with custom questions, set quotas, and manage the full opportunity lifecycle programmatically. **Version:** v2 (beta) **Base URL:** `https://terac.com/api/external/v2` > This API is in beta. Endpoints and request/response shapes may change before general availability. ## Resources [#resources] * **[Filters](https://terac.com/docs/developers/guides/filters)** — Target participants by demographics, geography, profession, and participation history using typed slug filters. * **[Screening Questions](https://terac.com/docs/developers/guides/screening-questions)** — Ask explicit multi-option questions during application to qualify or disqualify participants. * **[Quotas](https://terac.com/docs/developers/guides/quotas)** — Set per-answer floors and caps, or cross-question (interlocked) targets, on screening question answers. * **[API Reference](https://terac.com/docs/developers/reference)** — Full endpoint reference generated from the OpenAPI specification. ## Getting Started [#getting-started] 1. [Authenticate](https://terac.com/docs/developers/guides/authentication) with your API key. 2. Create a [project](https://terac.com/docs/developers/reference) to organize your opportunities. 3. Browse the [filter catalog](https://terac.com/docs/developers/guides/filters/catalog) to build your targeting criteria. 4. Create an opportunity and launch it to begin recruitment. ## Opportunity Statuses [#opportunity-statuses] An opportunity's `status` decides which calls it accepts. A call its status does not accept returns `409`, and the `message` says which statuses it needs. | `status` | Meaning | Calls it accepts | | ---------------------------------------------- | ------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | | `draft` | Not launched. An opportunity Terac is still preparing from a quote reads `draft` too. | `PATCH /opportunities/{opportunityId}`, `POST .../launch`, `DELETE /opportunities/{opportunityId}` | | `pre_recruiting` | Taking applicants ahead of a launch set up in the dashboard. | `POST .../stop` | | `active` | Recruiting. | `POST .../pause`, `POST .../stop` | | `paused` | Recruiting is on hold. | `POST .../resume`, `POST .../stop` | | `fulfilled` | The participant target is reached. It settles to `completed`. | None | | `completed`, `stopped`, `expired`, `cancelled` | Ended. | None | | `failed` | Its launch never finished. Create a new opportunity rather than retrying this one. | None | `GET /opportunities?status=` filters by any one of these values. Since the API is in beta, treat a status you do not recognize as one that accepts no calls. # Authentication (https://terac.com/docs/developers/guides/authentication) Authenticate with the Terac API using API keys and learn about rate limits. ## API Key [#api-key] All requests require an API key passed via the `Authorization` header: ``` Authorization: Bearer YOUR_API_KEY ``` API keys are scoped per organization. You can generate and manage keys from your organization settings in the Terac dashboard. ## Rate Limits [#rate-limits] Each API key can make 100 requests in a row. The count starts over only after more than 60 seconds pass with no accepted request on the key, so a key that never pauses for a full minute is refused after its 100th request, even at fewer than 100 requests a minute. Refused requests are not counted and do not extend the wait. A refused request returns `429` with a `Retry-After` header: the number of seconds to wait, counted from the key's last accepted request. ```http HTTP/1.1 429 Too Many Requests Retry-After: 42 Content-Type: application/json { "error": { "code": "RATE_LIMITED", "message": "Rate limit exceeded. Retry after 42 seconds." } } ``` Wait for the number of seconds in `Retry-After`, then retry. A `429` does not mean the key is invalid, so do not rotate or revoke it. To keep steady traffic from being refused, pause for just over 60 seconds after every 100 requests on a key. # Errors (https://terac.com/docs/developers/guides/errors) The two error body shapes the Terac API returns, how to read the error code from either one, and every error code with its HTTP status. A failed request returns an HTTP status of 400 or above and a JSON body. The body has one of two shapes, depending on where the request was refused: * **API key errors** come from the key check that runs before any operation. They return `401`, `403` or `429`, with the fields nested under `error`. * **Operation errors** come from the operation itself: validation, missing resources, state conflicts, server errors, and unknown paths. They return the fields at the top level. ## How to read an error [#how-to-read-an-error] 1. Branch on the HTTP status first. It is reliable whichever shape the body has. 2. Read `code` at the top level of the body. 3. If there is no top-level `code`, read `error.code`. Only the API key check returns this shape. 4. If `code` is `BAD_REQUEST` and `issues` is present, read `issues` to find which fields were rejected. ```ts const body = await response.json(); const code = body.code ?? body.error?.code; const message = body.message ?? body.error?.message; ``` The status alone does not always tell you the shape. A `401` or a `429` always comes from the key check and is nested, but a `403` can be nested (from the key check) or flat (from an operation). The fallback above handles both. ## API key errors [#api-key-errors] The key check runs before the request reaches any operation. It returns a body with `error.code` and `error.message`, and no other fields: ```json { "error": { "code": "UNAUTHORIZED", "message": "API key required. Include Authorization: Bearer header." } } ``` | HTTP status | `error.code` | `error.message` | Cause | | ----------- | -------------- | -------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | | 401 | `UNAUTHORIZED` | `API key required. Include Authorization: Bearer header.` | The `Authorization` header is missing or does not start with `Bearer `. | | 401 | `UNAUTHORIZED` | `Invalid or expired API key` | The key is unknown, disabled, expired, or revoked, or its owner no longer exists. | | 401 | `UNAUTHORIZED` | `API key is not scoped to an organization. Please create a new key.` | The key is not linked to an organization. Create a new key from your organization settings. | | 403 | `FORBIDDEN` | `This account is not permitted to use the API.` | The account that owns the key is banned or deleted. | | 429 | `RATE_LIMITED` | `Rate limit exceeded. Retry after 42 seconds.` | The key is rate limited. The number of seconds changes with each response. See [Rate limits](https://terac.com/docs/developers/guides/errors#rate-limits). | ### Rate limits [#rate-limits] A rate limited key returns `429` with `error.code` `RATE_LIMITED` and a `Retry-After` header that gives the number of seconds to wait. Wait that long, then retry. The key is still valid, so do not rotate or revoke it. The limit itself is described in [Authentication](https://terac.com/docs/developers/guides/authentication#rate-limits). ## Operation errors [#operation-errors] Every error raised after the key check returns the fields at the top level: ```json { "message": "Input validation failed", "code": "BAD_REQUEST", "issues": [ { "code": "invalid_type", "expected": "string", "received": "undefined", "path": ["name"], "message": "Required" } ], "data": { "code": "BAD_REQUEST", "httpStatus": 400, "path": "createProject" } } ``` | Field | Type | Present | Description | | --------- | ------ | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `code` | string | Always | An error code from the [table below](https://terac.com/docs/developers/guides/errors#error-codes). | | `message` | string | Always | What went wrong. For input validation errors it is always `Input validation failed`, and the detail is in `issues`. | | `issues` | array | Input validation errors | One entry per rejected value. Absent on every other error. | | `data` | object | Always | Repeats `code` and the HTTP status (`httpStatus`), and names the operation in `path` when the request matched one. It can carry other fields. It is not part of the OpenAPI schema, so read the top-level fields instead. | ### Validation issues [#validation-issues] When the request does not match the operation's schema (a missing field, a wrong type, a value outside the allowed range or enum), the response is `400` with `code` `BAD_REQUEST`, `message` `Input validation failed`, and an `issues` array. Each issue has: | Field | Type | Description | | --------- | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `path` | array of strings and numbers | Where the value is. Object keys are strings and array positions are numbers, so `["tasks", 0, "task_type"]` is the `task_type` of the first task. Query and path parameters appear by name, such as `["limit"]`. | | `message` | string | Why the value was rejected, such as `Required` or `Expected number, received nan`. | | `code` | string | The kind of problem, such as `invalid_type`, `too_small`, `too_big`, `invalid_enum_value`, or `custom`. | Some issue codes carry extra fields that describe the problem, such as `expected` and `received` on `invalid_type`, or `minimum` on `too_small`. A number field that is missing or not numeric reports `Expected number, received nan`, not `Required`. Some rules are checked after the schema, such as an unknown filter slug. Those are also `400 BAD_REQUEST`, but they have no `issues`: the `message` names the problem and, where it can, the valid values. ## Error codes [#error-codes] | HTTP status | `code` | Shape | Returned when | | ----------- | ------------------------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | 400 | `BAD_REQUEST` | Flat | The request is invalid: it fails the schema (see `issues`), or breaks a rule the schema cannot express, such as an unknown filter slug or an unsupported filter operator. The `message` says which. | | 400 | `PARSE_ERROR` | Flat | The body is not valid JSON. An empty body sent with `Content-Type: application/json` is also rejected this way; send `{}` instead. | | 401 | `UNAUTHORIZED` | Nested | The API key is missing, invalid, expired, or revoked. See [API key errors](https://terac.com/docs/developers/guides/errors#api-key-errors). | | 403 | `FORBIDDEN` | Nested | The account that owns the API key is banned or deleted. | | 403 | `FORBIDDEN` | Flat | The key's owner lacks the organization permission the operation needs. The `message` names the missing scope. | | 403 | `FORBIDDEN` | Flat | The operation works only in a sandbox organization, and the API key belongs to one that is not. Creating a test submission is the only such operation. | | 403 | `FORBIDDEN` | Flat | The API key belongs to a Terac SPL approving a submission they made themselves. Another reviewer has to approve it. | | 404 | `NOT_FOUND` | Flat | The resource does not exist or belongs to another organization. Also returned for a path or HTTP method the API does not have, with the message `Not found`. | | 409 | `CONFLICT` | Flat | The action is not allowed in the resource's current state, such as launching an opportunity that is not a draft or approving a submission that is not awaiting review. Also returned when a webhook URL is already subscribed. | | 412 | `PRECONDITION_FAILED` | Flat | Something you can fix must be in place first: the organization balance cannot cover a launch, the opportunity is not ready to launch, or a webhook endpoint failed the confirmation request. The `message` says what to fix. | | 415 | `UNSUPPORTED_MEDIA_TYPE` | Flat | A `POST` or `PATCH` request without `Content-Type: application/json`. Send that header and a JSON body on every `POST` and `PATCH`, including actions such as launch that take no fields (send `{}`). | | 422 | `UNPROCESSABLE_CONTENT` | Flat | A feasibility request cannot price the opportunity (not priced yet, already used, or closed), or Terac cannot create the test submission asked for on that opportunity. The `message` says which and what to do. | | 429 | `RATE_LIMITED` | Nested | The API key is rate limited. Wait the number of seconds in the `Retry-After` header, then retry. See [Rate limits](https://terac.com/docs/developers/guides/errors#rate-limits). | | 500 | `INTERNAL_SERVER_ERROR` | Flat | An unexpected error on Terac's side. The `message` is usually `Unknown error`. Retry with backoff. Before retrying a create, list the resources first: the failed attempt may have created one. | # Overview (https://terac.com/docs/developers/reference) OpenAPI reference for the Terac External API. This reference is auto-generated from the Terac External API OpenAPI specification. > This API is in beta. Endpoints and request/response shapes may change before general availability. Use the sidebar to browse endpoint references grouped by resource. ## OpenAPI specification [#openapi-specification] The specification these pages are generated from is published as JSON at [`/docs/developers/openapi.json`](https://terac.com/docs/developers/openapi.json), with the API base URL as its server. Load it into a client generator, an API client such as Postman, or an agent that reads OpenAPI. # Filters (https://terac.com/docs/developers/guides/filters) Target participants using typed slug filters with structured operators. Filters let you target participants by demographics, geography, profession, and participation history. Each filter is a single-key object where the key is a typed slug and the value contains operators. ## Typed Slug Format [#typed-slug-format] Filters use the format `{type}--{attribute}`. For example: `integer--age`, `multi_select--country`. ### Why typed slugs? [#why-typed-slugs] The `{type}--{slug}` format encodes the filter's data type in the slug itself. This is intentional: * **Self-describing payloads.** You can validate operators client-side without fetching the catalog first. An `integer--` filter takes `$gte`/`$lte`, a `multi_select--` filter takes `$in`/`$eq`. * **Future cross-platform portability.** The typed slug format is designed to enable mapping filters from other panel providers in the future. ## Operators [#operators] | Operator | Meaning | Applies to | | -------- | --------------------- | -------------------------------------------- | | `$eq` | Equals | `single_select`, `multi_select` | | `$in` | Any of | `single_select`, `multi_select`, `reference` | | `$gte` | Greater than or equal | `integer` | | `$lte` | Less than or equal | `integer` | | `$all` | All of | `reference` | There is no exclusion operator, so list the values to include. An operator that a filter does not support returns a `BAD_REQUEST` error. ## Example [#example] ```json [ { "integer--age": { "$gte": 25, "$lte": 45 } }, { "multi_select--country": { "$in": ["US", "CA"] } }, { "multi_select--job_function": { "$eq": "engineering" } } ] ``` ## Supported Filters [#supported-filters] This API is in beta. Only filters listed in the [filter catalog](https://terac.com/docs/developers/guides/filters/catalog) are supported. Unrecognized slugs will return a `BAD_REQUEST` error. Support for additional custom filters and cross-platform filter mapping is on the roadmap. # Filter Catalog (https://terac.com/docs/developers/guides/filters/catalog) Complete catalog of available filters and endpoints for browsing filter options. ## Geographic [#geographic] | Slug | Type | Description | | ----------------------- | ------------- | -------------------- | | `multi_select--country` | multi\_select | Country of residence | | `multi_select--state` | multi\_select | State or province | | `multi_select--city` | multi\_select | City | ## Demographic [#demographic] | Slug | Type | Description | | ---------------------------------- | -------------- | ---------------------------------- | | `integer--age` | integer | Age range (`$gte` / `$lte`, 18-99) | | `multi_select--gender` | multi\_select | Gender identity | | `multi_select--ethnicity` | multi\_select | Race or ethnicity | | `multi_select--language` | multi\_select | Primary language | | `multi_select--household_income` | multi\_select | Household income bracket | | `multi_select--level_of_education` | multi\_select | Highest education level | | `multi_select--marital_status` | multi\_select | Marital status | | `multi_select--children` | multi\_select | Children by age group | | `single_select--home_owner` | single\_select | Homeownership status | ## Professional [#professional] | Slug | Type | Description | | --------------------------------- | -------------- | ---------------------------- | | `multi_select--job_function` | multi\_select | Job function / role | | `multi_select--industry` | multi\_select | Industry | | `multi_select--seniority` | multi\_select | Seniority level | | `multi_select--company_size` | multi\_select | Company size by headcount | | `multi_select--work_setting` | multi\_select | Remote, hybrid, or in-office | | `multi_select--employment_status` | multi\_select | Employment status | | `single_select--collar_type` | single\_select | Job collar type | ## Participation History [#participation-history] | Slug | Type | Description | | -------------------------------- | --------- | ----------------------------------- | | `reference--has_taken_study` | reference | Has completed specific study(s) | | `reference--has_not_taken_study` | reference | Has NOT completed specific study(s) | ## Endpoints [#endpoints] ### Get Filter Catalog [#get-filter-catalog] Returns all available filters with their types, operators, and option metadata. ``` GET /filters ``` #### Response [#response] ```json { "data": [ { "slug": "integer--age", "name": "Age", "type": "integer", "category": "demographic", "operators": ["$gte", "$lte"], "min": 18, "max": 99 }, { "slug": "multi_select--country", "name": "Country of Residence", "type": "multi_select", "category": "geographic", "operators": ["$eq", "$in"], "options_url": "/api/external/v2/filters/multi_select--country/options" } ] } ``` The full catalog is returned. The example above is truncated for brevity. * `min` / `max` -- only present on `integer` type filters. Defines the valid range for `$gte` and `$lte` operators. * `options_url` -- only present on `single_select` and `multi_select` filters. Fetch to get enumerable values. ### Get Filter Options [#get-filter-options] Returns available values for a specific filter. ``` GET /filters/{filter_slug}/options ``` #### Query Parameters [#query-parameters] | Param | Type | Default | Description | | ------------ | ------- | ------- | --------------------------------------------------------------------- | | `search` | string | -- | Search options by name | | `page` | integer | 1 | Page number | | `per_page` | integer | 50 | Items per page (max 100) | | `country_id` | string | -- | ISO 3166-1 alpha-2 country code. Required for state and city filters. | | `state_id` | string | -- | State ISO code (e.g. `CA`). Narrows city results to a single state. | #### Cascading Geographic Filters [#cascading-geographic-filters] State and city filters require parent context via query parameters: * **State options** (`multi_select--state`): pass `country_id` to get states for that country. Returns empty without it. * **City options** (`multi_select--city`): pass `country_id` (required) and optionally `state_id` to narrow results. Returns empty without `country_id`. Geographic IDs encode their parent hierarchy so each value is unambiguous: * **State IDs:** `{country}-{state}` (e.g. `US-CA` for California) * **City IDs:** `{country}-{state}-{city}` (e.g. `US-CA-Los Angeles`) The `state_id` parameter accepts both bare ISO codes (`CA`) and composite state IDs (`US-CA`), so you can feed a state option ID directly into the city endpoint. ``` GET /filters/multi_select--state/options?country_id=US GET /filters/multi_select--city/options?country_id=US&state_id=US-CA ``` #### Example: State Options Response [#example-state-options-response] ```json { "data": [ { "id": "US-CA", "name": "California" }, { "id": "US-TX", "name": "Texas" } ], "meta": { "total": 51, "page": 1, "per_page": 50 } } ``` #### Example: City Options Response [#example-city-options-response] ```json { "data": [ { "id": "US-CA-Los Angeles", "name": "Los Angeles" }, { "id": "US-CA-San Francisco", "name": "San Francisco" } ], "meta": { "total": 1200, "page": 1, "per_page": 50 } } ``` #### Applying Hierarchical Filters [#applying-hierarchical-filters] Use composite IDs when setting filters on an opportunity: ```json { "filters": [ { "multi_select--country": { "$in": ["US", "BR"] } }, { "multi_select--state": { "$in": ["US-CA", "BR-SP"] } }, { "multi_select--city": { "$in": ["US-CA-Los Angeles", "BR-SP-São Paulo"] } } ] } ``` # List projects (https://terac.com/docs/developers/reference/listProjects) `GET https://terac.com/api/external/v2/projects` Lists your organization's projects, alphabetically by name. Returns up to `limit` per page; while `pagination.has_more` is true, pass `pagination.next_cursor` back as `cursor` for the next page. Deleted and internal-only projects are left out. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Query Parameters - `limit` (integer): Most items to return in one page, from 1 to 100. Defaults to 25. - `cursor` (string): `pagination.next_cursor` from the previous page, to fetch the page after it. Omit it for the first page. ## Responses ### 200: One page of projects. - `data` (array) **(required)** - `id` (string) **(required)** - `name` (string) **(required)** - `slug` (string) **(required)** - `created_at` (string) **(required)** - `dashboard_url` (string, nullable) **(required)**: The opportunities that belong to one project. Give this when the customer is working inside a single project rather than the whole organization, or when they want to pause or resume one of its opportunities - those two controls live on a list row here. Null if Terac cannot resolve the organization or project this link belongs to. - `pagination` (object) **(required)** - `next_cursor` (string, nullable) **(required)** - `has_more` (boolean) **(required)** ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # Create a project (https://terac.com/docs/developers/reference/createProject) `POST https://terac.com/api/external/v2/projects` Creates a project to file opportunities under. Every opportunity belongs to one, so pass the returned `id` as `project_id` to `POST /opportunities`. The `slug` is generated from `name` with a unique suffix, and each call creates a new project even when the name is already in use. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Request Body (required) `Content-Type: application/json` - `name` (string) **(required)**: Name of the new project ## Responses ### 200: The new project. - `id` (string) **(required)** - `name` (string) **(required)** - `slug` (string) **(required)** - `created_at` (string) **(required)** - `dashboard_url` (string, nullable) **(required)**: The opportunities that belong to one project. Give this when the customer is working inside a single project rather than the whole organization, or when they want to pause or resume one of its opportunities - those two controls live on a list row here. Null if Terac cannot resolve the organization or project this link belongs to. ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # Get project details (https://terac.com/docs/developers/reference/getProject) `GET https://terac.com/api/external/v2/projects/{projectId}` Returns one project with `opportunity_count`, the number of opportunities filed under it, drafts included and deleted ones not. List them with `GET /opportunities` and `projectId`. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Path Parameters - `projectId` (string) **(required)**: ID of the project, as returned by `GET /projects` or `POST /projects`. ## Responses ### 200: The project, with its opportunity count. - `id` (string) **(required)** - `name` (string) **(required)** - `slug` (string) **(required)** - `created_at` (string) **(required)** - `dashboard_url` (string, nullable) **(required)**: The opportunities that belong to one project. Give this when the customer is working inside a single project rather than the whole organization, or when they want to pause or resume one of its opportunities - those two controls live on a list row here. Null if Terac cannot resolve the organization or project this link belongs to. - `opportunity_count` (integer) **(required)** ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # Update a project (https://terac.com/docs/developers/reference/updateProject) `PATCH https://terac.com/api/external/v2/projects/{projectId}` Renames a project. `name` is the only field you can change, and a request without it returns 400. The `slug` and `dashboard_url` stay the same. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Path Parameters - `projectId` (string) **(required)**: ID of the project to update ## Request Body (required) `Content-Type: application/json` - `name` (string): New project name ## Responses ### 200: The renamed project. - `id` (string) **(required)** - `name` (string) **(required)** - `slug` (string) **(required)** - `created_at` (string) **(required)** - `dashboard_url` (string, nullable) **(required)**: The opportunities that belong to one project. Give this when the customer is working inside a single project rather than the whole organization, or when they want to pause or resume one of its opportunities - those two controls live on a list row here. Null if Terac cannot resolve the organization or project this link belongs to. ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # List available filters (https://terac.com/docs/developers/reference/getFilters) `GET https://terac.com/api/external/v2/filters` Lists the audience filters an opportunity's `filters` can use, with each one's slug, type and supported operators. The catalog is the same for every organization and is not paginated. A filter with an `options_url` takes values from a fixed list: read them from `GET /filters/{filter_slug}/options` and send their `id`s. `integer--age` takes a `$gte` / `$lte` range within `min` and `max`, and the two `reference--` filters take opportunity ids. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Responses ### 200: Every filter in the catalog. - `data` (array) **(required)** - `slug` (string) **(required)** - `name` (string) **(required)** - `type` (string) **(required)** - `category` (string) **(required)** - `operators` (array) **(required)** - `min` (number) - `max` (number) - `options_url` (string) ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # List options for a filter (https://terac.com/docs/developers/reference/getFilterOptions) `GET https://terac.com/api/external/v2/filters/{filter_slug}/options` Lists the allowed values for one filter; send an option's `id`, not its `name`, as the value in an opportunity's `filters`. `search` matches ids, names and alternate names, ignoring case and accents. Results are paged by number with `page` and `per_page`, and `meta.total` counts every match. State options need `country_id`, and city options need `country_id` with an optional `state_id`; without `country_id` both lists are empty. An unknown `filter_slug` returns 400. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Path Parameters - `filter_slug` (string) **(required)**: A filter's `slug` from `GET /filters`, such as `multi_select--country`. A filter with no `options_url` returns an empty list. ## Query Parameters - `search` (string): Text to match against option ids, names and alternate names, ignoring case and accents. - `page` (integer): 1-based page number. Defaults to 1. - `per_page` (integer): Options per page, from 1 to 100. Defaults to 50. - `country_id` (string): ISO 3166-1 alpha-2 country code, such as `US`. `multi_select--state` and `multi_select--city` list nothing without it; other filters ignore it. - `state_id` (string): Narrows `multi_select--city` to one state or province: the state option's `id` (`US-CA`) or its bare code (`CA`). Other filters ignore it. ## Responses ### 200: One page of the filter's allowed values, with the total match count. - `data` (array) **(required)** - `id` (string) **(required)** - `name` (string) **(required)** - `meta` (object) **(required)** - `total` (number) **(required)** - `page` (number) **(required)** - `per_page` (number) **(required)** ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # List opportunities (https://terac.com/docs/developers/reference/listOpportunities) `GET https://terac.com/api/external/v2/opportunities` Lists your organization's opportunities, newest first, with each one's status, target and pricing. Filter by `projectId` or by one `status`. Returns up to `limit` per page; while `pagination.has_more` is true, pass `pagination.next_cursor` back as `cursor` for the next page. Deleted opportunities are left out. `pricing` is what launching charged, so it is null on a draft; read a draft's estimate from `GET /opportunities/{opportunityId}`. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Query Parameters - `limit` (integer): Most items to return in one page, from 1 to 100. Defaults to 25. - `cursor` (string): `pagination.next_cursor` from the previous page, to fetch the page after it. Omit it for the first page. - `status` (string): Return only opportunities in this status: `draft`, `pre_recruiting`, `active`, `fulfilled`, `paused`, `stopped`, `completed`, `expired`, `cancelled` or `failed`. `draft` includes opportunities Terac is still preparing. - `projectId` (string): Return only opportunities filed under this project ID. ## Responses ### 200: One page of opportunity summaries. - `data` (array) **(required)** - `id` (string) **(required)** - `title` (string) **(required)** - `status` (string) **(required)**: one of: `draft`, `pre_recruiting`, `active`, `fulfilled`, `paused`, `stopped`, `completed`, `expired`, `cancelled`, `failed`: Decides which calls are allowed: - `draft`: edit, launch or delete it. An opportunity Terac is still preparing from a quote reads `draft` too. - `pre_recruiting`: taking applicants ahead of a launch set up in the dashboard. Stop only. - `active`: pause or stop. - `paused`: resume or stop. - `fulfilled`: target reached; settles to `completed`. - `completed`, `stopped`, `expired`, `cancelled`: ended. - `failed`: its launch never finished. Create a new opportunity. - `num_participants` (integer) **(required)** - `pricing` (object, nullable) **(required)** - `cost_per_participant_cents` (integer) **(required)** - `total_cost_cents` (integer) **(required)** - `currency` (string) **(required)**: one of: `usd` - `created_at` (string) **(required)** - `dashboard_url` (string, nullable) **(required)**: The study's own page. It routes itself by status: an editable draft opens the builder, anything else opens submissions. Never null. Give this when you do not know which tab the customer needs; when `draft_editor` is set, prefer that instead, since it opens the same builder the customer would review in. Null if Terac cannot resolve the organization or project this link belongs to. - `pagination` (object) **(required)** - `next_cursor` (string, nullable) **(required)** - `has_more` (boolean) **(required)** ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # Create a draft opportunity (https://terac.com/docs/developers/reference/createOpportunity) `POST https://terac.com/api/external/v2/opportunities` Creates the opportunity as a DRAFT. Nothing is charged and no recruitment starts until you call `POST /opportunities/{opportunityId}/launch`. **The incentive is derived, not sent.** There is no field for participant pay, and the amount is fixed when the draft is created. Two paths decide which number you get: - **Omit `feasibility_request_id`** and the incentive is an automatic estimate made during this call. It is priced from the whole brief, not just its size: the participant count, the task duration, the audience your `filters` and `screening_questions` describe, and the recruitment window from `expected_days_to_complete`. Changing any of those changes the price, so read the result back from `pricing` on the response; do not quote a price to anyone before you have. - **Pass a `feasibility_request_id`** and that request's confirmed CPI is honored exactly, with no re-estimate. Submit the brief to `POST /feasibility/requests` first and poll `GET /feasibility/requests/{requestId}` until it reads `RESPONDED`, which is when a price exists. This is the way to control what participants are paid. The platform fee follows the same split. Priced by feasibility, the confirmed recruitment fee becomes this opportunity's fee, as a flat per-participant amount, so the all-in CPI you agreed is the one you are charged. Priced automatically, it comes from the organization's configuration instead. Either way it is not a per-opportunity input, and neither is the currency (always USD) or the pay cadence (always `one_time`). `pricing` on the response carries the all-in cost per participant and the total, and `funding` says whether the balance covers a launch. Editing after this call is draft-only: `PATCH /opportunities/{opportunityId}` returns 409 once the opportunity is launched, and a new recruitment window moves the deadline without re-pricing. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Request Body (required) `Content-Type: application/json` - `title` (string) **(required)**: Participant-facing name of the opportunity - `internal_title` (string): Internal-only name (not shown to participants) - `description` (string): What the opportunity is about; shown to participants - `project_id` (string) **(required)**: ID of the project to file this under (from GET /projects or POST /projects) - `num_participants` (integer) **(required)**: Number of completed submissions you need (1-1000 for self-serve) - `business_type` (string) **(required)**: one of: `b2c`, `b2b`: "b2c" = consumer audience, "b2b" = professional/business audience - `customer_screening_review` (string): one of: `auto_invite`, `manual_review`: What happens to an applicant who qualifies: "auto_invite" (the default) invites them and starts their tasks immediately, "manual_review" holds them for your decision. Held applicants are read with GET /opportunities/{opportunityId}/applicants and resolved with POST /submissions/{submissionId}/invite or /decline; they carry no submission status until you decide. - `tasks` (array) **(required)**: Ordered list of tasks each participant performs (at least one) - `sequence` (integer) **(required)**: 1-based order in which the participant performs this task - `task_type` (string) **(required)**: one of: `interview`, `file_upload`, `activity`: Pick by WHERE the answer lands, not by what the work is called. Work through these in order and stop at the first that fits: 1. You have a page where they do the work and submit it -> "interview". REQUIRES task_url, that page. 2. No such page, and the deliverable is a file (document, recording, screenshot) -> "file_upload": the expert uploads files through Terac and you read them off the submission, so it needs no task_url. REQUIRES instructions and accepted_mime_types. 3. No such page and the deliverable is not a file -> ask the customer for a URL. The task is still an "interview"; you simply cannot create it until they give you that page. Do not fall through to another type while you wait. 4. You want no output at all -> "activity". It lands nowhere: Terac records the button press and whatever the expert read, decided or wrote is gone. Research, auditing a site, judging output, writing something: all real work, so all end at step 1, 2 or 3. Never reach step 4 to escape step 3, because "activity" does not collect the work, it discards it. None of these book a human-moderated call; that is not available through this API. - `review_type` (string) **(required)**: one of: `auto_approve`, `manual_review`, `self_report`: How completion is reviewed and when the expert is paid: "auto_approve" = accepted and paid automatically, but ONLY when the task has a task_url whose provider redirects to the completion callback below; a task with no URL has no way to fire it and falls back to manual review; "self_report" = the participant attests completion, which auto-approves and pays them (you cannot withhold payment based on your own quality signal); "manual_review" = the submission goes to AWAITING_REVIEW and is paid only when you call approve (reject withholds payment). Choose manual_review when you need to gate payout on your own verification. - `task_url` (string): Fully-qualified URL (https://...) of the page where the expert BOTH does the work AND submits their answer. REQUIRED on "interview". Not used by "file_upload", which collects its files in Terac. The customer hosts that page, not Terac, and it owes two things: SHOW whatever the expert has to read, and give them an INPUT to answer with. The answer arrives there. If they have no such page, that is what to ask them for. Not a reference to read: a repo, doc or homepage is not a task, because there is nothing to submit from it. A Terac task screen hosts no form and no text box, so a task without this returns only a button press and the expert's work is lost. Files are the exception: use "file_upload". Pasting the material into description does not collect it, and neither does screening_questions. Terac appends tracking params per participant (submissionId, teracSubmissionId, taskId) so you can attribute an answer; the tool description carries the callback contract. - `title` (string): Short label for this task - `description` (string): What the participant should do in this task - `duration_minutes` (integer): Estimated minutes to complete this task; drives pricing, so a duration you guess is a price you invent. Take it from whoever is commissioning the work. Required except for "file_upload", which carries no time estimate. - `instructions` (string): REQUIRED on "file_upload": what the expert should upload, in words shown above the drop zone. Ignored by other task types. - `accepted_mime_types` (array): REQUIRED on "file_upload": MIME types the expert may upload. Exact types ("application/pdf"), family wildcards ("image/*") and "*/*" for any file all work. Ignored by other task types. - `min_files` (integer): For "file_upload": fewest files the expert must upload before they can submit. Defaults to 1. - `max_files` (integer): For "file_upload": most files the expert may upload; must be >= min_files and at most 20. Defaults to 1. - `max_file_size_bytes` (integer): For "file_upload": per-file size ceiling in bytes. Defaults to 25 MB. - `filters` (array): Structured hard filters for targeting; each entry maps a filter slug to an operator object (see GET /filters). Omitting this recruits worldwide, at any age, in any language, and the draft shows that as no restriction rather than as a mistake, so a create carrying neither these nor unrestricted_audience is REJECTED. - `unrestricted_audience` (boolean): Set true to record that recruiting everyone (worldwide, any age, any language) is the sample the customer wants. It is the only way to create without filters, and it is REJECTED alongside filters. Nothing checks that you asked, so reaching for it to clear the rejection defeats the point of the field. - `screening_questions` (array): Soft qualification questions with answer options. These decide WHO is accepted; they are not where the study's work is done. Ask what makes someone the right fit (experience, tools, role, context). NEVER put the research questions here: screening runs BEFORE an applicant is accepted and is UNPAID, so it collects the study's answers for free from everyone who applies, including everyone you turn away. The work belongs at task_url. Optional here, but a study cannot LAUNCH with a question no answer can pass, and launches with no screener only when Terac has enabled that for your organization and the launch sends without_screener: true. Otherwise a draft created without these has to be updated before it can go live. - `key` (string): Stable identifier for this question, referenced by quotas[].screening_question and display conditions. Defaults to q0, q1, ... by position if omitted. - `text` (string) **(required)**: The screening question text - `question_rich_text` (string): Optional Markdown (GFM) version of the prompt, used as the display copy when set (bold, bullet lists, ...). `text` stays required and remains the plaintext source of truth for the voice agent and search, so keep the two in sync. - `pick` (string) **(required)**: one of: `one`, `any`, `boolean`, `text`, `grid`: "one" = single-select, "any" = multi-select, "boolean" = yes/no, "text" = open-ended freeform response (no answer options; responses route to manual review), "grid" = matrix (put rows/columns on `grid`, omit answers). - `answers` (array): Answer options, each with qualify_logic. Required for choice questions (pick "one" | "any" | "boolean"): at least two. Omit for pick: "text" and pick: "grid". - `text` (string) **(required)**: The answer option shown to the participant - `qualify_logic` (string) **(required)**: one of: `may`, `must`, `must_one_of`, `reject`, `review`: How this answer affects qualification: "may" = acceptable; "must" = this exact answer is required (valid with pick "one" or "any"); "must_one_of" = counts toward a required any-of group (multi-select, pick: "any"); "reject" = disqualifies; "review" = flag for manual review (routes an otherwise-qualified applicant to customer review instead of auto-inviting; only acts on auto-invite studies). IMPORTANT, single-select collapse: on pick "one" and pick "boolean" only one answer can be selected, so an answer can be neither individually required nor ignored - "may", "must" and "must_one_of" all persist as the same qualifying disposition and all read back as "must". A single-select whose answers are all "may"/"must"/"must_one_of" therefore screens nobody out. Put "reject" on the answers that must screen an applicant out (or "review" to route them to a human), or use pick "any" when you need a different disposition per answer. - `allow_free_text` (boolean): Selecting this answer reveals a write-in box ("Other, please specify") whose typed value is captured with the response. Eligibility is unchanged - the answer keeps its qualify_logic. Applies to pick "one" and "any"; ignored for pick "boolean". - `grid` (object): Required when pick is "grid"; omit otherwise. - `pick` (string): one of: `one`, `any`: How many columns per row: "one" (default) = a single column per row; "any" = any number of columns per row (select all that apply per row). - `rows` (array) **(required)** - `key` (string): Stable row key; defaults to r0, r1, ... by position. - `text` (string) **(required)**: Row label, e.g. a platform name - `columns` (array) **(required)** - `value` (string): Stable column value; defaults to c0, c1, ... by position. - `text` (string) **(required)**: Column label shared across rows - `cell_actions` (array): Per-cell qualify logic. Omit a cell for neutral (may). - `row` (string) **(required)**: A row key from rows[] - `column` (string) **(required)**: A column value from columns[] - `qualify_logic` (string) **(required)**: one of: `may`, `must`, `must_one_of`, `reject` - `min_qualifying` (integer): Multi-select (pick: "any") only. Require at least this many "must_one_of" answers to be selected to qualify, e.g. min_qualifying: 2 with three must_one_of answers means "select 2+ of these three". Omitted or 1 = the default "at least one of" behavior. Ignored for other pick types. - `display_condition` (object): Show this question only if earlier answers match ("ask only if"). Conditions reference EARLIER questions by key. - `conditions` (array) **(required)** - `screening_question` (string) **(required)**: The key of an EARLIER question this one depends on - `answer` (string) **(required)**: An answer of that earlier question. For a grid source use "Row: Column". - `operator` (string): one of: `eq`, `ne`: "eq" = answered that value, "ne" = did not. - `join` (string): one of: `and`, `or` - `conditional_rules` (array): Cross-question consistency / anti-falsification checks that reject or flag when answers match a pattern (e.g. "if role is Store Manager but none of the management tasks are selected here, flag for review"). Write one all-"and" rule per role. Reference an earlier question with screening_question; omit it to test this question's own selections. - `conditions` (array) **(required)** - `screening_question` (string): Key of the question this condition reads. Omit to test an answer on THIS question (a self-check). - `answer` (string) **(required)**: An answer by its label. For a grid source use "Row: Column". - `operator` (string): one of: `eq`, `ne`: "eq" = selected that answer, "ne" = did not. - `join` (string): one of: `and`, `or` - `outcome` (string) **(required)**: one of: `reject`, `review`: "reject" screens the participant out, "review" flags for a human, when the rule matches. - `skip_rules` (array): Branching: when a rule's conditions hold, jump straight to target_question (or end the screener when it is null), skipping everything in between. Conditions test THIS question's answers, and the target must be a later question. Not available for pick "text" or "grid". - `conditions` (array) **(required)** - `screening_question` (string) **(required)**: The key of the question this rule lives on. A rule tests its OWN answers; use display_condition to gate a question on an earlier one. - `answer` (string) **(required)**: One of that question's answers (its text; "Yes"/"No" for pick: "boolean"). - `operator` (string) **(required)**: one of: `eq`, `ne`: "eq" = answered that value, "ne" = did not. - `join` (string) **(required)**: one of: `and`, `or` - `target_question` (string, nullable) **(required)**: Key of the question to jump to when the conditions hold; null ends the screener. Must be a LATER question - the screener only walks forward. - `allow_paste` (boolean): Open-ended (pick: "text") only. Whether participants may paste into the freeform field. Pasting is blocked by default to discourage low-effort copied responses; set true for questions that ask for a pasted value (e.g. a link). Ignored for choice questions. - `quotas` (array): Per-answer target counts; each quota references a screening_questions[].key. Requires screening_questions. - `screening_question` (string) **(required)**: The key of the screening question this quota applies to (must match a screening_questions[].key) - `targets` (array) **(required)**: Per-answer target counts for this question - `value` (string) **(required)**: The answer option text (matches a screening answer's text) this quota targets - `count` (integer) **(required)**: How many approved submissions to allow/require for this answer - `type` (string): one of: `exact`, `minimum`, `maximum`: How the count is enforced: "minimum" = at least this many (a floor; recruiting prioritizes the answer while it is short); "maximum" = at most this many (a cap; further applicants who pick this answer are screened out once it is full); "exact" = both. Omit for "minimum". - `cross_quotas` (array): Cross-question (interlocked cross-tab) quotas: one cell per answer combination across two or more screening questions, cells of one cross-tab sharing a dimension. Requires screening_questions. - `label` (string) **(required)**: Human label for this cell, e.g. "Brazil x Agency". - `conditions` (array) **(required)**: The conditions, one per dimension: one for a simple per-answer quota, two+ to interlock a cross-tab across distinct questions. - `screening_question` (string) **(required)**: Key of a screening_questions[].key this condition tests (one dimension of the cross-tab). - `answer` (string) **(required)**: An answer of that question, by its label. For a grid source use "Row: Column". - `operator` (string): one of: `eq`, `ne`: "eq" = selected that answer, "ne" = did not. - `join` (string): one of: `and`, `or` - `target` (integer) **(required)**: Target participant count for this cell. - `quota_type` (string): one of: `exact`, `minimum`, `maximum`: "exact" = hit exactly, "minimum" = at least, "maximum" = no more than. - `dimension` (string): Cells sharing a dimension are summed as one interlocked cross-tab quota; use one token per cross-tab. - `device_types` (array): Allowed device types. Omitting this allows desktop, mobile_ios, mobile_android and other, but NOT either tablet, so name the tablet values explicitly when a tablet audience matters. - values: `desktop`, `mobile_ios`, `mobile_android`, `tablet_ios`, `tablet_android`, `other` - `expected_days_to_complete` (integer): Recruitment window in calendar days (min 5, default 7) - `feasibility_request_id` (string): ID of a priced (RESPONDED) feasibility request. When set, the opportunity uses that confirmed CPI instead of an autonomous estimate. The request stays RESPONDED and is only closed as won once the study launches. ## Responses ### 200: The new draft, with its price and whether the balance covers a launch. - `id` (string) **(required)** - `title` (string) **(required)** - `internal_title` (string, nullable) **(required)** - `description` (string, nullable) **(required)** - `status` (string) **(required)**: one of: `draft`, `pre_recruiting`, `active`, `fulfilled`, `paused`, `stopped`, `completed`, `expired`, `cancelled`, `failed`: Decides which calls are allowed: - `draft`: edit, launch or delete it. An opportunity Terac is still preparing from a quote reads `draft` too. - `pre_recruiting`: taking applicants ahead of a launch set up in the dashboard. Stop only. - `active`: pause or stop. - `paused`: resume or stop. - `fulfilled`: target reached; settles to `completed`. - `completed`, `stopped`, `expired`, `cancelled`: ended. - `failed`: its launch never finished. Create a new opportunity. - `num_participants` (integer) **(required)** - `estimated_duration_minutes` (integer) **(required)** - `filters` (array) **(required)** - `screening_questions` (array) **(required)** - `key` (string) **(required)**: Identifier for this question, yours or assigned. Comes back as `screening_answers[].key`, so answers join to their question. Opaque. - `text` (string) **(required)** - `question_rich_text` (string): Markdown version of the prompt, when the author wrote one. `text` stays the plaintext source of truth. - `pick` (string) **(required)**: one of: `one`, `any`, `boolean`, `text`, `grid` - `answers` (array) - `text` (string) **(required)** - `qualify_logic` (string) **(required)**: one of: `may`, `must`, `must_one_of`, `reject`, `review`: The disposition that is actually stored, which is not always the one that was sent. On a single-select (pick "one", which is also how a yes/no question reads back) every qualifying answer reads back as "must", because "may", "must" and "must_one_of" collapse to one disposition where only one answer can be picked. So a "may" answer reading back as "must" is the stored truth, not a mistranslation - rewriting it to "may" will not restore a filter. Only "reject" and "review" screen or flag on a single-select; switch the question to pick "any" if you need per-answer dispositions. - `allow_free_text` (boolean): Selecting this answer reveals a write-in box ("Other, please specify"). - `grid` (object) - `pick` (string): one of: `one`, `any` - `rows` (array) **(required)** - `key` (string) - `text` (string) **(required)** - `columns` (array) **(required)** - `value` (string) - `text` (string) **(required)** - `cell_actions` (array) - `row` (string) **(required)** - `column` (string) **(required)** - `qualify_logic` (string) **(required)**: one of: `may`, `must`, `must_one_of`, `reject`, `review` - `min_qualifying` (integer) - `display_condition` (object) - `conditions` (array) **(required)** - `screening_question` (string) **(required)**: The key of an EARLIER question this one depends on - `answer` (string) **(required)**: An answer of that earlier question. For a grid source use "Row: Column". - `operator` (string) **(required)**: one of: `eq`, `ne`: "eq" = answered that value, "ne" = did not. - `join` (string) **(required)**: one of: `and`, `or` - `conditional_rules` (array) - `conditions` (array) **(required)** - `screening_question` (string) - `answer` (string) **(required)** - `operator` (string) **(required)**: one of: `eq`, `ne` - `join` (string) **(required)**: one of: `and`, `or` - `outcome` (string) **(required)**: one of: `reject`, `review` - `skip_rules` (array): Branching authored here or in the dashboard, in the same shape create accepts, so it can be sent straight back on an update. Omitted when the question has none - an absent field means no branching, never that branching was dropped on read. - `conditions` (array) **(required)** - `screening_question` (string) **(required)**: The key of the question this rule lives on. A rule tests its OWN answers; use display_condition to gate a question on an earlier one. - `answer` (string) **(required)**: One of that question's answers (its text; "Yes"/"No" for pick: "boolean"). - `operator` (string) **(required)**: one of: `eq`, `ne`: "eq" = answered that value, "ne" = did not. - `join` (string) **(required)**: one of: `and`, `or` - `target_question` (string, nullable) **(required)**: Key of the question to jump to when the conditions hold; null ends the screener. Must be a LATER question - the screener only walks forward. - `allow_paste` (boolean) - `quotas` (array) **(required)** - `screening_question` (string) **(required)**: The key of the screening question this quota applies to (must match a screening_questions[].key) - `targets` (array) **(required)**: Per-answer target counts for this question - `value` (string) **(required)**: The answer option text (matches a screening answer's text) this quota targets - `count` (integer) **(required)**: How many approved submissions to allow/require for this answer - `type` (string): one of: `exact`, `minimum`, `maximum`: How the count is enforced: "minimum" = at least this many (a floor; recruiting prioritizes the answer while it is short); "maximum" = at most this many (a cap; further applicants who pick this answer are screened out once it is full); "exact" = both. Omit for "minimum". - `cross_quotas` (array) **(required)** - `label` (string) **(required)**: Human label for this cell, e.g. "Brazil x Agency". - `conditions` (array) **(required)**: The conditions, one per dimension: one for a simple per-answer quota, two+ to interlock a cross-tab across distinct questions. - `screening_question` (string) **(required)**: Key of a screening_questions[].key this condition tests (one dimension of the cross-tab). - `answer` (string) **(required)**: An answer of that question, by its label. For a grid source use "Row: Column". - `operator` (string) **(required)**: one of: `eq`, `ne`: "eq" = selected that answer, "ne" = did not. - `join` (string) **(required)**: one of: `and`, `or` - `target` (integer) **(required)**: Target participant count for this cell. - `quota_type` (string) **(required)**: one of: `exact`, `minimum`, `maximum`: "exact" = hit exactly, "minimum" = at least, "maximum" = no more than. - `dimension` (string): Cells sharing a dimension are summed as one interlocked cross-tab quota; use one token per cross-tab. - `tasks` (array) **(required)** - `sequence` (integer) **(required)** - `task_type` (string) **(required)**: Any stored task type, so wider than the set you can create: interview, survey, file_upload, activity, scheduled_interview, agreement, join_slack, slack_handoff, e_signature. Only interview, file_upload and activity are accepted on create. - `review_type` (string) **(required)** - `task_url` (string, nullable) **(required)**: null when the task type carries no URL. - `participant_url_template` (string): task_url with the tracking params Terac appends per participant (submissionId, teracSubmissionId, taskId), shown as {submissionId}/{taskId} macros. Omitted when there is no task_url. - `title` (string) - `description` (string) - `duration_minutes` (integer) **(required)** - `available_after_sequence` (integer): This task unlocks only after that task is approved. - `available_after_delay_minutes` (integer): Wait this long after the gating task before unlocking. - `provider` (string): Scheduled interview only: the booking provider. - `calendar_owner` (string): Scheduled interview only: whose calendar is booked (researcher or expert). - `event_type_url` (string): Scheduled interview only: the booking link. - `instructions` (string): File upload only: what to submit. - `accepted_mime_types` (array): "file_upload" only, and absent on every other task type. RESOLVED rather than echoed: a task created without this reads back with the default actually enforced, not an empty field. - `min_files` (integer): "file_upload" only, and absent on every other task type. Resolved to the enforced default when the task did not set one. - `max_files` (integer): "file_upload" only, and absent on every other task type. Resolved to the enforced default when the task did not set one. - `max_file_size_bytes` (integer): "file_upload" only, and absent on every other task type. Resolved to the enforced default when the task did not set one. - `pricing` (object, nullable) **(required)** - `cost_per_participant_cents` (integer) **(required)** - `total_cost_cents` (integer) **(required)** - `currency` (string) **(required)**: one of: `usd` - `funding` (object) - `affordable` (boolean) **(required)**: Covers the cost, overdraft included. False blocks nothing. - `draws_on_credit` (boolean) **(required)**: Affordable but goes negative, invoiced later. Say so. - `balance_cents` (integer) **(required)**: Balance now. - `shortfall_cents` (integer) **(required)**: Gap after overdraft, 0 when affordable. - `finance_url` (string, nullable) **(required)**: Where billing adds credit. - `device_types` (array) **(required)** - `business_type` (string, nullable) **(required)** - `candidate_profile` (string, nullable) **(required)**: Free-text profile of the ideal candidate, when set. - `required_accounts` (array) **(required)**: Accounts an expert must have linked to apply (e.g. linkedin). - `video_required` (string) **(required)**: Whether the expert's camera is needed: none, requested, required. - `screen_share_required` (string) **(required)**: Whether a screen share is needed: none, requested, required. - `customer_screening_review` (string) **(required)**: auto_invite = qualified applicants are invited automatically; manual_review = you pick who to invite. - `pay_frequency` (string) **(required)**: How the incentive is framed: one_time, hourly, or per_task. - `screening_option_randomization` (string) **(required)**: Whether screening answer options are shown in random order: the string "enabled" or "disabled", not a boolean. - `forwarding_email_enabled` (string) **(required)**: Whether each submission gets a Terac forwarding email address: the string "enabled" or "disabled", not a boolean. - `max_responses_per_expert` (integer, nullable) **(required)** - `feasibility_request_id` (string, nullable) **(required)** - `project_id` (string) **(required)** - `created_at` (string) **(required)** - `updated_at` (string) **(required)** - `ending_at` (string, nullable) **(required)**: Target completion date for recruitment. - `launched_at` (string, nullable) **(required)** - `links` (object) **(required)** - `self` (string) **(required)** - `launch` (string, nullable) **(required)** - `submissions` (string) **(required)** - `dashboard` (object) **(required)**: Pages a person opens in a browser. `self`, `launch` and `submissions` above are API paths for you, not for them - never give a customer one of those. - `study` (string, nullable) **(required)**: The study's own page. It routes itself by status: an editable draft opens the builder, anything else opens submissions. Never null. Give this when you do not know which tab the customer needs; when `draft_editor` is set, prefer that instead, since it opens the same builder the customer would review in. Null if Terac cannot resolve the organization or project this link belongs to. - `draft_editor` (string, nullable) **(required)**: Opens this draft in the study builder so a human can review the whole setup (targeting, screener, tasks, price) before anything launches. Set exactly when `submissions` here is null, never both. Give this to the customer after you build a draft, instead of launching. Null if Terac cannot resolve the organization or project this link belongs to. - `submissions` (string, nullable) **(required)**: Who applied, who is in progress, who is awaiting review. Set exactly when `draft_editor` here is null. Do not read that as the opportunity being live: the `status` field reports several settled states (cancelled, expired, failed) as `draft`, so read status from `status` and treat this only as a link. Give this when the customer asks about progress or has submissions to review. Null if Terac cannot resolve the organization or project this link belongs to. - `recruitment` (string, nullable) **(required)**: The screener questions and quotas every applicant is filtered against. Set exactly when `draft_editor` here is null. Give this when the customer wants to check who is being screened in or out. Null if Terac cannot resolve the organization or project this link belongs to. - `task` (string, nullable) **(required)**: What a participant is actually asked to do - task type, duration, and task link. Set exactly when `draft_editor` here is null. Give this when the customer asks what the participants see. Null if Terac cannot resolve the organization or project this link belongs to. - `settings` (string, nullable) **(required)**: The study's own settings: recruitment window, participant count, and the controls to stop or delete it. Set exactly when `draft_editor` here is null. Give this when the customer wants to change or end a running study rather than look at its results. Pause and resume are not here - they are on the opportunities list. Null if Terac cannot resolve the organization or project this link belongs to. - `submission_stats` (object) - `total` (integer) **(required)** - `in_progress` (integer) **(required)** - `awaiting_review` (integer) **(required)** - `approved` (integer) **(required)** - `rejected` (integer) **(required)** - `quota_progress` (array) - `screening_question` (string) **(required)** - `targets` (array) **(required)** - `value` (string) **(required)** - `count` (integer) **(required)** - `current` (integer) **(required)** - `type` (string) **(required)**: How the count is enforced: exact, minimum or maximum. Tells you whether "current" nearing "count" means a cap is about to close or a floor is about to be met. - `cross_quota_progress` (array): Fill progress for each cross-question quota cell (live studies). - `label` (string) **(required)**: Human label for this cell, e.g. "Brazil x Agency". - `conditions` (array) **(required)**: The conditions, one per dimension: one for a simple per-answer quota, two+ to interlock a cross-tab across distinct questions. - `screening_question` (string) **(required)**: Key of a screening_questions[].key this condition tests (one dimension of the cross-tab). - `answer` (string) **(required)**: An answer of that question, by its label. For a grid source use "Row: Column". - `operator` (string) **(required)**: one of: `eq`, `ne`: "eq" = selected that answer, "ne" = did not. - `join` (string) **(required)**: one of: `and`, `or` - `target` (integer) **(required)**: Target participant count for this cell. - `quota_type` (string) **(required)**: one of: `exact`, `minimum`, `maximum`: "exact" = hit exactly, "minimum" = at least, "maximum" = no more than. - `dimension` (string): Cells sharing a dimension are summed as one interlocked cross-tab quota; use one token per cross-tab. - `current` (integer) **(required)**: Approved participants matching this cell so far. - `screening_stats` (array): Per-question screen-out rates and answer breakdown (live studies). - `screening_question` (string) **(required)**: The `screening_questions[].key`. - `graded` (integer) **(required)**: Screeners GRADED here, not answers given: a branching screener stops at the first failure. - `rejected` (integer) **(required)**: Rejected by this question. - `rejection_rate` (number) **(required)**: `rejected` / `graded`, 0 to 1. - `answers` (array) **(required)**: How often each answer was chosen, for spotting a screener that is too narrow. - `text` (string) **(required)**: The answer as written on the question, not its stored value. - `selected` (integer) **(required)** - `share` (number) **(required)**: `selected` / `graded`, between 0 and 1. ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # Get opportunity details (https://terac.com/docs/developers/reference/getOpportunity) `GET https://terac.com/api/external/v2/opportunities/{opportunityId}` Returns one opportunity in full: its brief, audience, screener, tasks and pricing. A draft also carries `funding`, which says whether the balance covers a launch. A launched one carries `submission_stats`, `quota_progress` and `screening_stats` instead, so poll this to follow recruitment. `screening_questions` reads back in the shape `POST /opportunities` accepts, ready to send back on `PATCH`, and `links.dashboard` holds its pages in the researcher dashboard. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Path Parameters - `opportunityId` (string) **(required)**: ID of the opportunity, as returned by `POST /opportunities` or `GET /opportunities`. ## Responses ### 200: The opportunity in full. - `id` (string) **(required)** - `title` (string) **(required)** - `internal_title` (string, nullable) **(required)** - `description` (string, nullable) **(required)** - `status` (string) **(required)**: one of: `draft`, `pre_recruiting`, `active`, `fulfilled`, `paused`, `stopped`, `completed`, `expired`, `cancelled`, `failed`: Decides which calls are allowed: - `draft`: edit, launch or delete it. An opportunity Terac is still preparing from a quote reads `draft` too. - `pre_recruiting`: taking applicants ahead of a launch set up in the dashboard. Stop only. - `active`: pause or stop. - `paused`: resume or stop. - `fulfilled`: target reached; settles to `completed`. - `completed`, `stopped`, `expired`, `cancelled`: ended. - `failed`: its launch never finished. Create a new opportunity. - `num_participants` (integer) **(required)** - `estimated_duration_minutes` (integer) **(required)** - `filters` (array) **(required)** - `screening_questions` (array) **(required)** - `key` (string) **(required)**: Identifier for this question, yours or assigned. Comes back as `screening_answers[].key`, so answers join to their question. Opaque. - `text` (string) **(required)** - `question_rich_text` (string): Markdown version of the prompt, when the author wrote one. `text` stays the plaintext source of truth. - `pick` (string) **(required)**: one of: `one`, `any`, `boolean`, `text`, `grid` - `answers` (array) - `text` (string) **(required)** - `qualify_logic` (string) **(required)**: one of: `may`, `must`, `must_one_of`, `reject`, `review`: The disposition that is actually stored, which is not always the one that was sent. On a single-select (pick "one", which is also how a yes/no question reads back) every qualifying answer reads back as "must", because "may", "must" and "must_one_of" collapse to one disposition where only one answer can be picked. So a "may" answer reading back as "must" is the stored truth, not a mistranslation - rewriting it to "may" will not restore a filter. Only "reject" and "review" screen or flag on a single-select; switch the question to pick "any" if you need per-answer dispositions. - `allow_free_text` (boolean): Selecting this answer reveals a write-in box ("Other, please specify"). - `grid` (object) - `pick` (string): one of: `one`, `any` - `rows` (array) **(required)** - `key` (string) - `text` (string) **(required)** - `columns` (array) **(required)** - `value` (string) - `text` (string) **(required)** - `cell_actions` (array) - `row` (string) **(required)** - `column` (string) **(required)** - `qualify_logic` (string) **(required)**: one of: `may`, `must`, `must_one_of`, `reject`, `review` - `min_qualifying` (integer) - `display_condition` (object) - `conditions` (array) **(required)** - `screening_question` (string) **(required)**: The key of an EARLIER question this one depends on - `answer` (string) **(required)**: An answer of that earlier question. For a grid source use "Row: Column". - `operator` (string) **(required)**: one of: `eq`, `ne`: "eq" = answered that value, "ne" = did not. - `join` (string) **(required)**: one of: `and`, `or` - `conditional_rules` (array) - `conditions` (array) **(required)** - `screening_question` (string) - `answer` (string) **(required)** - `operator` (string) **(required)**: one of: `eq`, `ne` - `join` (string) **(required)**: one of: `and`, `or` - `outcome` (string) **(required)**: one of: `reject`, `review` - `skip_rules` (array): Branching authored here or in the dashboard, in the same shape create accepts, so it can be sent straight back on an update. Omitted when the question has none - an absent field means no branching, never that branching was dropped on read. - `conditions` (array) **(required)** - `screening_question` (string) **(required)**: The key of the question this rule lives on. A rule tests its OWN answers; use display_condition to gate a question on an earlier one. - `answer` (string) **(required)**: One of that question's answers (its text; "Yes"/"No" for pick: "boolean"). - `operator` (string) **(required)**: one of: `eq`, `ne`: "eq" = answered that value, "ne" = did not. - `join` (string) **(required)**: one of: `and`, `or` - `target_question` (string, nullable) **(required)**: Key of the question to jump to when the conditions hold; null ends the screener. Must be a LATER question - the screener only walks forward. - `allow_paste` (boolean) - `quotas` (array) **(required)** - `screening_question` (string) **(required)**: The key of the screening question this quota applies to (must match a screening_questions[].key) - `targets` (array) **(required)**: Per-answer target counts for this question - `value` (string) **(required)**: The answer option text (matches a screening answer's text) this quota targets - `count` (integer) **(required)**: How many approved submissions to allow/require for this answer - `type` (string): one of: `exact`, `minimum`, `maximum`: How the count is enforced: "minimum" = at least this many (a floor; recruiting prioritizes the answer while it is short); "maximum" = at most this many (a cap; further applicants who pick this answer are screened out once it is full); "exact" = both. Omit for "minimum". - `cross_quotas` (array) **(required)** - `label` (string) **(required)**: Human label for this cell, e.g. "Brazil x Agency". - `conditions` (array) **(required)**: The conditions, one per dimension: one for a simple per-answer quota, two+ to interlock a cross-tab across distinct questions. - `screening_question` (string) **(required)**: Key of a screening_questions[].key this condition tests (one dimension of the cross-tab). - `answer` (string) **(required)**: An answer of that question, by its label. For a grid source use "Row: Column". - `operator` (string) **(required)**: one of: `eq`, `ne`: "eq" = selected that answer, "ne" = did not. - `join` (string) **(required)**: one of: `and`, `or` - `target` (integer) **(required)**: Target participant count for this cell. - `quota_type` (string) **(required)**: one of: `exact`, `minimum`, `maximum`: "exact" = hit exactly, "minimum" = at least, "maximum" = no more than. - `dimension` (string): Cells sharing a dimension are summed as one interlocked cross-tab quota; use one token per cross-tab. - `tasks` (array) **(required)** - `sequence` (integer) **(required)** - `task_type` (string) **(required)**: Any stored task type, so wider than the set you can create: interview, survey, file_upload, activity, scheduled_interview, agreement, join_slack, slack_handoff, e_signature. Only interview, file_upload and activity are accepted on create. - `review_type` (string) **(required)** - `task_url` (string, nullable) **(required)**: null when the task type carries no URL. - `participant_url_template` (string): task_url with the tracking params Terac appends per participant (submissionId, teracSubmissionId, taskId), shown as {submissionId}/{taskId} macros. Omitted when there is no task_url. - `title` (string) - `description` (string) - `duration_minutes` (integer) **(required)** - `available_after_sequence` (integer): This task unlocks only after that task is approved. - `available_after_delay_minutes` (integer): Wait this long after the gating task before unlocking. - `provider` (string): Scheduled interview only: the booking provider. - `calendar_owner` (string): Scheduled interview only: whose calendar is booked (researcher or expert). - `event_type_url` (string): Scheduled interview only: the booking link. - `instructions` (string): File upload only: what to submit. - `accepted_mime_types` (array): "file_upload" only, and absent on every other task type. RESOLVED rather than echoed: a task created without this reads back with the default actually enforced, not an empty field. - `min_files` (integer): "file_upload" only, and absent on every other task type. Resolved to the enforced default when the task did not set one. - `max_files` (integer): "file_upload" only, and absent on every other task type. Resolved to the enforced default when the task did not set one. - `max_file_size_bytes` (integer): "file_upload" only, and absent on every other task type. Resolved to the enforced default when the task did not set one. - `pricing` (object, nullable) **(required)** - `cost_per_participant_cents` (integer) **(required)** - `total_cost_cents` (integer) **(required)** - `currency` (string) **(required)**: one of: `usd` - `funding` (object) - `affordable` (boolean) **(required)**: Covers the cost, overdraft included. False blocks nothing. - `draws_on_credit` (boolean) **(required)**: Affordable but goes negative, invoiced later. Say so. - `balance_cents` (integer) **(required)**: Balance now. - `shortfall_cents` (integer) **(required)**: Gap after overdraft, 0 when affordable. - `finance_url` (string, nullable) **(required)**: Where billing adds credit. - `device_types` (array) **(required)** - `business_type` (string, nullable) **(required)** - `candidate_profile` (string, nullable) **(required)**: Free-text profile of the ideal candidate, when set. - `required_accounts` (array) **(required)**: Accounts an expert must have linked to apply (e.g. linkedin). - `video_required` (string) **(required)**: Whether the expert's camera is needed: none, requested, required. - `screen_share_required` (string) **(required)**: Whether a screen share is needed: none, requested, required. - `customer_screening_review` (string) **(required)**: auto_invite = qualified applicants are invited automatically; manual_review = you pick who to invite. - `pay_frequency` (string) **(required)**: How the incentive is framed: one_time, hourly, or per_task. - `screening_option_randomization` (string) **(required)**: Whether screening answer options are shown in random order: the string "enabled" or "disabled", not a boolean. - `forwarding_email_enabled` (string) **(required)**: Whether each submission gets a Terac forwarding email address: the string "enabled" or "disabled", not a boolean. - `max_responses_per_expert` (integer, nullable) **(required)** - `feasibility_request_id` (string, nullable) **(required)** - `project_id` (string) **(required)** - `created_at` (string) **(required)** - `updated_at` (string) **(required)** - `ending_at` (string, nullable) **(required)**: Target completion date for recruitment. - `launched_at` (string, nullable) **(required)** - `links` (object) **(required)** - `self` (string) **(required)** - `launch` (string, nullable) **(required)** - `submissions` (string) **(required)** - `dashboard` (object) **(required)**: Pages a person opens in a browser. `self`, `launch` and `submissions` above are API paths for you, not for them - never give a customer one of those. - `study` (string, nullable) **(required)**: The study's own page. It routes itself by status: an editable draft opens the builder, anything else opens submissions. Never null. Give this when you do not know which tab the customer needs; when `draft_editor` is set, prefer that instead, since it opens the same builder the customer would review in. Null if Terac cannot resolve the organization or project this link belongs to. - `draft_editor` (string, nullable) **(required)**: Opens this draft in the study builder so a human can review the whole setup (targeting, screener, tasks, price) before anything launches. Set exactly when `submissions` here is null, never both. Give this to the customer after you build a draft, instead of launching. Null if Terac cannot resolve the organization or project this link belongs to. - `submissions` (string, nullable) **(required)**: Who applied, who is in progress, who is awaiting review. Set exactly when `draft_editor` here is null. Do not read that as the opportunity being live: the `status` field reports several settled states (cancelled, expired, failed) as `draft`, so read status from `status` and treat this only as a link. Give this when the customer asks about progress or has submissions to review. Null if Terac cannot resolve the organization or project this link belongs to. - `recruitment` (string, nullable) **(required)**: The screener questions and quotas every applicant is filtered against. Set exactly when `draft_editor` here is null. Give this when the customer wants to check who is being screened in or out. Null if Terac cannot resolve the organization or project this link belongs to. - `task` (string, nullable) **(required)**: What a participant is actually asked to do - task type, duration, and task link. Set exactly when `draft_editor` here is null. Give this when the customer asks what the participants see. Null if Terac cannot resolve the organization or project this link belongs to. - `settings` (string, nullable) **(required)**: The study's own settings: recruitment window, participant count, and the controls to stop or delete it. Set exactly when `draft_editor` here is null. Give this when the customer wants to change or end a running study rather than look at its results. Pause and resume are not here - they are on the opportunities list. Null if Terac cannot resolve the organization or project this link belongs to. - `submission_stats` (object) - `total` (integer) **(required)** - `in_progress` (integer) **(required)** - `awaiting_review` (integer) **(required)** - `approved` (integer) **(required)** - `rejected` (integer) **(required)** - `quota_progress` (array) - `screening_question` (string) **(required)** - `targets` (array) **(required)** - `value` (string) **(required)** - `count` (integer) **(required)** - `current` (integer) **(required)** - `type` (string) **(required)**: How the count is enforced: exact, minimum or maximum. Tells you whether "current" nearing "count" means a cap is about to close or a floor is about to be met. - `cross_quota_progress` (array): Fill progress for each cross-question quota cell (live studies). - `label` (string) **(required)**: Human label for this cell, e.g. "Brazil x Agency". - `conditions` (array) **(required)**: The conditions, one per dimension: one for a simple per-answer quota, two+ to interlock a cross-tab across distinct questions. - `screening_question` (string) **(required)**: Key of a screening_questions[].key this condition tests (one dimension of the cross-tab). - `answer` (string) **(required)**: An answer of that question, by its label. For a grid source use "Row: Column". - `operator` (string) **(required)**: one of: `eq`, `ne`: "eq" = selected that answer, "ne" = did not. - `join` (string) **(required)**: one of: `and`, `or` - `target` (integer) **(required)**: Target participant count for this cell. - `quota_type` (string) **(required)**: one of: `exact`, `minimum`, `maximum`: "exact" = hit exactly, "minimum" = at least, "maximum" = no more than. - `dimension` (string): Cells sharing a dimension are summed as one interlocked cross-tab quota; use one token per cross-tab. - `current` (integer) **(required)**: Approved participants matching this cell so far. - `screening_stats` (array): Per-question screen-out rates and answer breakdown (live studies). - `screening_question` (string) **(required)**: The `screening_questions[].key`. - `graded` (integer) **(required)**: Screeners GRADED here, not answers given: a branching screener stops at the first failure. - `rejected` (integer) **(required)**: Rejected by this question. - `rejection_rate` (number) **(required)**: `rejected` / `graded`, 0 to 1. - `answers` (array) **(required)**: How often each answer was chosen, for spotting a screener that is too narrow. - `text` (string) **(required)**: The answer as written on the question, not its stored value. - `selected` (integer) **(required)** - `share` (number) **(required)**: `selected` / `graded`, between 0 and 1. ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # Update opportunity (https://terac.com/docs/developers/reference/updateOpportunity) `PATCH https://terac.com/api/external/v2/opportunities/{opportunityId}` Edits a draft opportunity; send only the fields you are changing. A list you send (`tasks`, `filters`, `screening_questions`, `quotas`, `cross_quotas`) replaces the stored one rather than merging into it, so read the opportunity first and send the whole list back. `quotas` and `cross_quotas` are only accepted alongside `screening_questions`. A new `expected_days_to_complete` moves the deadline without re-pricing. A request that changes nothing returns 400, and a launched opportunity returns 409. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Path Parameters - `opportunityId` (string) **(required)**: ID of the opportunity to update ## Request Body (required) `Content-Type: application/json` - `title` (string): New participant-facing name - `internal_title` (string): New internal-only name - `description` (string): New description - `num_participants` (integer): New number of completed submissions you need (1-1000 for self-serve). - `business_type` (string): one of: `b2c`, `b2b`: New audience type: "b2c" = consumer, "b2b" = professional/business - `customer_screening_review` (string): one of: `auto_invite`, `manual_review`: Whether qualified applicants wait for your decision ("manual_review") or are invited automatically ("auto_invite"). Applies to applicants who qualify after this call; anyone already invited stays invited. - `device_types` (array): REPLACES the allowed device list wholesale. Omitting this leaves the current restriction untouched (unlike create, where omitting means desktop, mobile_ios, mobile_android and other). - values: `desktop`, `mobile_ios`, `mobile_android`, `tablet_ios`, `tablet_android`, `other` - `tasks` (array): REPLACES the entire task list; it is not merged. Send every task you want to keep, so read the current tasks with terac_get_opportunity first. - `sequence` (integer) **(required)**: 1-based order in which the participant performs this task - `task_type` (string) **(required)**: one of: `interview`, `file_upload`, `activity`: Pick by WHERE the answer lands, not by what the work is called. Work through these in order and stop at the first that fits: 1. You have a page where they do the work and submit it -> "interview". REQUIRES task_url, that page. 2. No such page, and the deliverable is a file (document, recording, screenshot) -> "file_upload": the expert uploads files through Terac and you read them off the submission, so it needs no task_url. REQUIRES instructions and accepted_mime_types. 3. No such page and the deliverable is not a file -> ask the customer for a URL. The task is still an "interview"; you simply cannot create it until they give you that page. Do not fall through to another type while you wait. 4. You want no output at all -> "activity". It lands nowhere: Terac records the button press and whatever the expert read, decided or wrote is gone. Research, auditing a site, judging output, writing something: all real work, so all end at step 1, 2 or 3. Never reach step 4 to escape step 3, because "activity" does not collect the work, it discards it. None of these book a human-moderated call; that is not available through this API. - `review_type` (string) **(required)**: one of: `auto_approve`, `manual_review`, `self_report`: How completion is reviewed and when the expert is paid: "auto_approve" = accepted and paid automatically, but ONLY when the task has a task_url whose provider redirects to the completion callback below; a task with no URL has no way to fire it and falls back to manual review; "self_report" = the participant attests completion, which auto-approves and pays them (you cannot withhold payment based on your own quality signal); "manual_review" = the submission goes to AWAITING_REVIEW and is paid only when you call approve (reject withholds payment). Choose manual_review when you need to gate payout on your own verification. - `task_url` (string): Fully-qualified URL (https://...) of the page where the expert BOTH does the work AND submits their answer. REQUIRED on "interview". Not used by "file_upload", which collects its files in Terac. The customer hosts that page, not Terac, and it owes two things: SHOW whatever the expert has to read, and give them an INPUT to answer with. The answer arrives there. If they have no such page, that is what to ask them for. Not a reference to read: a repo, doc or homepage is not a task, because there is nothing to submit from it. A Terac task screen hosts no form and no text box, so a task without this returns only a button press and the expert's work is lost. Files are the exception: use "file_upload". Pasting the material into description does not collect it, and neither does screening_questions. Terac appends tracking params per participant (submissionId, teracSubmissionId, taskId) so you can attribute an answer; the tool description carries the callback contract. - `title` (string): Short label for this task - `description` (string): What the participant should do in this task - `duration_minutes` (integer): Estimated minutes to complete this task; drives pricing, so a duration you guess is a price you invent. Take it from whoever is commissioning the work. Required except for "file_upload", which carries no time estimate. - `instructions` (string): REQUIRED on "file_upload": what the expert should upload, in words shown above the drop zone. Ignored by other task types. - `accepted_mime_types` (array): REQUIRED on "file_upload": MIME types the expert may upload. Exact types ("application/pdf"), family wildcards ("image/*") and "*/*" for any file all work. Ignored by other task types. - `min_files` (integer): For "file_upload": fewest files the expert must upload before they can submit. Defaults to 1. - `max_files` (integer): For "file_upload": most files the expert may upload; must be >= min_files and at most 20. Defaults to 1. - `max_file_size_bytes` (integer): For "file_upload": per-file size ceiling in bytes. Defaults to 25 MB. - `filters` (array): REPLACES every hard filter; it is not merged. Send the full targeting list you want to keep. An empty array drops all of them and leaves the study recruiting everyone, so it is accepted only alongside unrestricted_audience: true. - `unrestricted_audience` (boolean): Set true in the same call that sends an empty filters array, to record that the customer wants every restriction gone. It decides what THIS call does to the audience, so it is rejected on a call that leaves filters alone, and rejected alongside a non-empty filters list. - `screening_questions` (array): REPLACES the entire screener; it is not merged. Send every question you want to keep, so read the current screener with terac_get_opportunity first. - `key` (string): Stable identifier for this question, referenced by quotas[].screening_question and display conditions. Defaults to q0, q1, ... by position if omitted. - `text` (string) **(required)**: The screening question text - `question_rich_text` (string): Optional Markdown (GFM) version of the prompt, used as the display copy when set (bold, bullet lists, ...). `text` stays required and remains the plaintext source of truth for the voice agent and search, so keep the two in sync. - `pick` (string) **(required)**: one of: `one`, `any`, `boolean`, `text`, `grid`: "one" = single-select, "any" = multi-select, "boolean" = yes/no, "text" = open-ended freeform response (no answer options; responses route to manual review), "grid" = matrix (put rows/columns on `grid`, omit answers). - `answers` (array): Answer options, each with qualify_logic. Required for choice questions (pick "one" | "any" | "boolean"): at least two. Omit for pick: "text" and pick: "grid". - `text` (string) **(required)**: The answer option shown to the participant - `qualify_logic` (string) **(required)**: one of: `may`, `must`, `must_one_of`, `reject`, `review`: How this answer affects qualification: "may" = acceptable; "must" = this exact answer is required (valid with pick "one" or "any"); "must_one_of" = counts toward a required any-of group (multi-select, pick: "any"); "reject" = disqualifies; "review" = flag for manual review (routes an otherwise-qualified applicant to customer review instead of auto-inviting; only acts on auto-invite studies). IMPORTANT, single-select collapse: on pick "one" and pick "boolean" only one answer can be selected, so an answer can be neither individually required nor ignored - "may", "must" and "must_one_of" all persist as the same qualifying disposition and all read back as "must". A single-select whose answers are all "may"/"must"/"must_one_of" therefore screens nobody out. Put "reject" on the answers that must screen an applicant out (or "review" to route them to a human), or use pick "any" when you need a different disposition per answer. - `allow_free_text` (boolean): Selecting this answer reveals a write-in box ("Other, please specify") whose typed value is captured with the response. Eligibility is unchanged - the answer keeps its qualify_logic. Applies to pick "one" and "any"; ignored for pick "boolean". - `grid` (object): Required when pick is "grid"; omit otherwise. - `pick` (string): one of: `one`, `any`: How many columns per row: "one" (default) = a single column per row; "any" = any number of columns per row (select all that apply per row). - `rows` (array) **(required)** - `key` (string): Stable row key; defaults to r0, r1, ... by position. - `text` (string) **(required)**: Row label, e.g. a platform name - `columns` (array) **(required)** - `value` (string): Stable column value; defaults to c0, c1, ... by position. - `text` (string) **(required)**: Column label shared across rows - `cell_actions` (array): Per-cell qualify logic. Omit a cell for neutral (may). - `row` (string) **(required)**: A row key from rows[] - `column` (string) **(required)**: A column value from columns[] - `qualify_logic` (string) **(required)**: one of: `may`, `must`, `must_one_of`, `reject` - `min_qualifying` (integer): Multi-select (pick: "any") only. Require at least this many "must_one_of" answers to be selected to qualify, e.g. min_qualifying: 2 with three must_one_of answers means "select 2+ of these three". Omitted or 1 = the default "at least one of" behavior. Ignored for other pick types. - `display_condition` (object): Show this question only if earlier answers match ("ask only if"). Conditions reference EARLIER questions by key. - `conditions` (array) **(required)** - `screening_question` (string) **(required)**: The key of an EARLIER question this one depends on - `answer` (string) **(required)**: An answer of that earlier question. For a grid source use "Row: Column". - `operator` (string): one of: `eq`, `ne`: "eq" = answered that value, "ne" = did not. - `join` (string): one of: `and`, `or` - `conditional_rules` (array): Cross-question consistency / anti-falsification checks that reject or flag when answers match a pattern (e.g. "if role is Store Manager but none of the management tasks are selected here, flag for review"). Write one all-"and" rule per role. Reference an earlier question with screening_question; omit it to test this question's own selections. - `conditions` (array) **(required)** - `screening_question` (string): Key of the question this condition reads. Omit to test an answer on THIS question (a self-check). - `answer` (string) **(required)**: An answer by its label. For a grid source use "Row: Column". - `operator` (string): one of: `eq`, `ne`: "eq" = selected that answer, "ne" = did not. - `join` (string): one of: `and`, `or` - `outcome` (string) **(required)**: one of: `reject`, `review`: "reject" screens the participant out, "review" flags for a human, when the rule matches. - `skip_rules` (array): Branching: when a rule's conditions hold, jump straight to target_question (or end the screener when it is null), skipping everything in between. Conditions test THIS question's answers, and the target must be a later question. Not available for pick "text" or "grid". - `conditions` (array) **(required)** - `screening_question` (string) **(required)**: The key of the question this rule lives on. A rule tests its OWN answers; use display_condition to gate a question on an earlier one. - `answer` (string) **(required)**: One of that question's answers (its text; "Yes"/"No" for pick: "boolean"). - `operator` (string) **(required)**: one of: `eq`, `ne`: "eq" = answered that value, "ne" = did not. - `join` (string) **(required)**: one of: `and`, `or` - `target_question` (string, nullable) **(required)**: Key of the question to jump to when the conditions hold; null ends the screener. Must be a LATER question - the screener only walks forward. - `allow_paste` (boolean): Open-ended (pick: "text") only. Whether participants may paste into the freeform field. Pasting is blocked by default to discourage low-effort copied responses; set true for questions that ask for a pasted value (e.g. a link). Ignored for choice questions. - `quotas` (array): REPLACES every per-answer target count. Only accepted alongside screening_questions in the SAME call, because a quota attaches to one of those answers. - `screening_question` (string) **(required)**: The key of the screening question this quota applies to (must match a screening_questions[].key) - `targets` (array) **(required)**: Per-answer target counts for this question - `value` (string) **(required)**: The answer option text (matches a screening answer's text) this quota targets - `count` (integer) **(required)**: How many approved submissions to allow/require for this answer - `type` (string): one of: `exact`, `minimum`, `maximum`: How the count is enforced: "minimum" = at least this many (a floor; recruiting prioritizes the answer while it is short); "maximum" = at most this many (a cap; further applicants who pick this answer are screened out once it is full); "exact" = both. Omit for "minimum". - `cross_quotas` (array): REPLACES every cross-question (cross-tab) quota. Only accepted alongside screening_questions in the SAME call, because each cell references those answers. Send an empty array to drop all cross-quotas. - `label` (string) **(required)**: Human label for this cell, e.g. "Brazil x Agency". - `conditions` (array) **(required)**: The conditions, one per dimension: one for a simple per-answer quota, two+ to interlock a cross-tab across distinct questions. - `screening_question` (string) **(required)**: Key of a screening_questions[].key this condition tests (one dimension of the cross-tab). - `answer` (string) **(required)**: An answer of that question, by its label. For a grid source use "Row: Column". - `operator` (string): one of: `eq`, `ne`: "eq" = selected that answer, "ne" = did not. - `join` (string): one of: `and`, `or` - `target` (integer) **(required)**: Target participant count for this cell. - `quota_type` (string): one of: `exact`, `minimum`, `maximum`: "exact" = hit exactly, "minimum" = at least, "maximum" = no more than. - `dimension` (string): Cells sharing a dimension are summed as one interlocked cross-tab quota; use one token per cross-tab. - `expected_days_to_complete` (integer): New recruitment window in calendar days counted from today (min 5). Moves the deadline only; it does not re-price the opportunity. ## Responses ### 200: The draft as stored after the edit. - `id` (string) **(required)** - `title` (string) **(required)** - `internal_title` (string, nullable) **(required)** - `description` (string, nullable) **(required)** - `status` (string) **(required)**: one of: `draft`, `pre_recruiting`, `active`, `fulfilled`, `paused`, `stopped`, `completed`, `expired`, `cancelled`, `failed`: Decides which calls are allowed: - `draft`: edit, launch or delete it. An opportunity Terac is still preparing from a quote reads `draft` too. - `pre_recruiting`: taking applicants ahead of a launch set up in the dashboard. Stop only. - `active`: pause or stop. - `paused`: resume or stop. - `fulfilled`: target reached; settles to `completed`. - `completed`, `stopped`, `expired`, `cancelled`: ended. - `failed`: its launch never finished. Create a new opportunity. - `num_participants` (integer) **(required)** - `estimated_duration_minutes` (integer) **(required)** - `filters` (array) **(required)** - `screening_questions` (array) **(required)** - `key` (string) **(required)**: Identifier for this question, yours or assigned. Comes back as `screening_answers[].key`, so answers join to their question. Opaque. - `text` (string) **(required)** - `question_rich_text` (string): Markdown version of the prompt, when the author wrote one. `text` stays the plaintext source of truth. - `pick` (string) **(required)**: one of: `one`, `any`, `boolean`, `text`, `grid` - `answers` (array) - `text` (string) **(required)** - `qualify_logic` (string) **(required)**: one of: `may`, `must`, `must_one_of`, `reject`, `review`: The disposition that is actually stored, which is not always the one that was sent. On a single-select (pick "one", which is also how a yes/no question reads back) every qualifying answer reads back as "must", because "may", "must" and "must_one_of" collapse to one disposition where only one answer can be picked. So a "may" answer reading back as "must" is the stored truth, not a mistranslation - rewriting it to "may" will not restore a filter. Only "reject" and "review" screen or flag on a single-select; switch the question to pick "any" if you need per-answer dispositions. - `allow_free_text` (boolean): Selecting this answer reveals a write-in box ("Other, please specify"). - `grid` (object) - `pick` (string): one of: `one`, `any` - `rows` (array) **(required)** - `key` (string) - `text` (string) **(required)** - `columns` (array) **(required)** - `value` (string) - `text` (string) **(required)** - `cell_actions` (array) - `row` (string) **(required)** - `column` (string) **(required)** - `qualify_logic` (string) **(required)**: one of: `may`, `must`, `must_one_of`, `reject`, `review` - `min_qualifying` (integer) - `display_condition` (object) - `conditions` (array) **(required)** - `screening_question` (string) **(required)**: The key of an EARLIER question this one depends on - `answer` (string) **(required)**: An answer of that earlier question. For a grid source use "Row: Column". - `operator` (string) **(required)**: one of: `eq`, `ne`: "eq" = answered that value, "ne" = did not. - `join` (string) **(required)**: one of: `and`, `or` - `conditional_rules` (array) - `conditions` (array) **(required)** - `screening_question` (string) - `answer` (string) **(required)** - `operator` (string) **(required)**: one of: `eq`, `ne` - `join` (string) **(required)**: one of: `and`, `or` - `outcome` (string) **(required)**: one of: `reject`, `review` - `skip_rules` (array): Branching authored here or in the dashboard, in the same shape create accepts, so it can be sent straight back on an update. Omitted when the question has none - an absent field means no branching, never that branching was dropped on read. - `conditions` (array) **(required)** - `screening_question` (string) **(required)**: The key of the question this rule lives on. A rule tests its OWN answers; use display_condition to gate a question on an earlier one. - `answer` (string) **(required)**: One of that question's answers (its text; "Yes"/"No" for pick: "boolean"). - `operator` (string) **(required)**: one of: `eq`, `ne`: "eq" = answered that value, "ne" = did not. - `join` (string) **(required)**: one of: `and`, `or` - `target_question` (string, nullable) **(required)**: Key of the question to jump to when the conditions hold; null ends the screener. Must be a LATER question - the screener only walks forward. - `allow_paste` (boolean) - `quotas` (array) **(required)** - `screening_question` (string) **(required)**: The key of the screening question this quota applies to (must match a screening_questions[].key) - `targets` (array) **(required)**: Per-answer target counts for this question - `value` (string) **(required)**: The answer option text (matches a screening answer's text) this quota targets - `count` (integer) **(required)**: How many approved submissions to allow/require for this answer - `type` (string): one of: `exact`, `minimum`, `maximum`: How the count is enforced: "minimum" = at least this many (a floor; recruiting prioritizes the answer while it is short); "maximum" = at most this many (a cap; further applicants who pick this answer are screened out once it is full); "exact" = both. Omit for "minimum". - `cross_quotas` (array) **(required)** - `label` (string) **(required)**: Human label for this cell, e.g. "Brazil x Agency". - `conditions` (array) **(required)**: The conditions, one per dimension: one for a simple per-answer quota, two+ to interlock a cross-tab across distinct questions. - `screening_question` (string) **(required)**: Key of a screening_questions[].key this condition tests (one dimension of the cross-tab). - `answer` (string) **(required)**: An answer of that question, by its label. For a grid source use "Row: Column". - `operator` (string) **(required)**: one of: `eq`, `ne`: "eq" = selected that answer, "ne" = did not. - `join` (string) **(required)**: one of: `and`, `or` - `target` (integer) **(required)**: Target participant count for this cell. - `quota_type` (string) **(required)**: one of: `exact`, `minimum`, `maximum`: "exact" = hit exactly, "minimum" = at least, "maximum" = no more than. - `dimension` (string): Cells sharing a dimension are summed as one interlocked cross-tab quota; use one token per cross-tab. - `tasks` (array) **(required)** - `sequence` (integer) **(required)** - `task_type` (string) **(required)**: Any stored task type, so wider than the set you can create: interview, survey, file_upload, activity, scheduled_interview, agreement, join_slack, slack_handoff, e_signature. Only interview, file_upload and activity are accepted on create. - `review_type` (string) **(required)** - `task_url` (string, nullable) **(required)**: null when the task type carries no URL. - `participant_url_template` (string): task_url with the tracking params Terac appends per participant (submissionId, teracSubmissionId, taskId), shown as {submissionId}/{taskId} macros. Omitted when there is no task_url. - `title` (string) - `description` (string) - `duration_minutes` (integer) **(required)** - `available_after_sequence` (integer): This task unlocks only after that task is approved. - `available_after_delay_minutes` (integer): Wait this long after the gating task before unlocking. - `provider` (string): Scheduled interview only: the booking provider. - `calendar_owner` (string): Scheduled interview only: whose calendar is booked (researcher or expert). - `event_type_url` (string): Scheduled interview only: the booking link. - `instructions` (string): File upload only: what to submit. - `accepted_mime_types` (array): "file_upload" only, and absent on every other task type. RESOLVED rather than echoed: a task created without this reads back with the default actually enforced, not an empty field. - `min_files` (integer): "file_upload" only, and absent on every other task type. Resolved to the enforced default when the task did not set one. - `max_files` (integer): "file_upload" only, and absent on every other task type. Resolved to the enforced default when the task did not set one. - `max_file_size_bytes` (integer): "file_upload" only, and absent on every other task type. Resolved to the enforced default when the task did not set one. - `pricing` (object, nullable) **(required)** - `cost_per_participant_cents` (integer) **(required)** - `total_cost_cents` (integer) **(required)** - `currency` (string) **(required)**: one of: `usd` - `funding` (object) - `affordable` (boolean) **(required)**: Covers the cost, overdraft included. False blocks nothing. - `draws_on_credit` (boolean) **(required)**: Affordable but goes negative, invoiced later. Say so. - `balance_cents` (integer) **(required)**: Balance now. - `shortfall_cents` (integer) **(required)**: Gap after overdraft, 0 when affordable. - `finance_url` (string, nullable) **(required)**: Where billing adds credit. - `device_types` (array) **(required)** - `business_type` (string, nullable) **(required)** - `candidate_profile` (string, nullable) **(required)**: Free-text profile of the ideal candidate, when set. - `required_accounts` (array) **(required)**: Accounts an expert must have linked to apply (e.g. linkedin). - `video_required` (string) **(required)**: Whether the expert's camera is needed: none, requested, required. - `screen_share_required` (string) **(required)**: Whether a screen share is needed: none, requested, required. - `customer_screening_review` (string) **(required)**: auto_invite = qualified applicants are invited automatically; manual_review = you pick who to invite. - `pay_frequency` (string) **(required)**: How the incentive is framed: one_time, hourly, or per_task. - `screening_option_randomization` (string) **(required)**: Whether screening answer options are shown in random order: the string "enabled" or "disabled", not a boolean. - `forwarding_email_enabled` (string) **(required)**: Whether each submission gets a Terac forwarding email address: the string "enabled" or "disabled", not a boolean. - `max_responses_per_expert` (integer, nullable) **(required)** - `feasibility_request_id` (string, nullable) **(required)** - `project_id` (string) **(required)** - `created_at` (string) **(required)** - `updated_at` (string) **(required)** - `ending_at` (string, nullable) **(required)**: Target completion date for recruitment. - `launched_at` (string, nullable) **(required)** - `links` (object) **(required)** - `self` (string) **(required)** - `launch` (string, nullable) **(required)** - `submissions` (string) **(required)** - `dashboard` (object) **(required)**: Pages a person opens in a browser. `self`, `launch` and `submissions` above are API paths for you, not for them - never give a customer one of those. - `study` (string, nullable) **(required)**: The study's own page. It routes itself by status: an editable draft opens the builder, anything else opens submissions. Never null. Give this when you do not know which tab the customer needs; when `draft_editor` is set, prefer that instead, since it opens the same builder the customer would review in. Null if Terac cannot resolve the organization or project this link belongs to. - `draft_editor` (string, nullable) **(required)**: Opens this draft in the study builder so a human can review the whole setup (targeting, screener, tasks, price) before anything launches. Set exactly when `submissions` here is null, never both. Give this to the customer after you build a draft, instead of launching. Null if Terac cannot resolve the organization or project this link belongs to. - `submissions` (string, nullable) **(required)**: Who applied, who is in progress, who is awaiting review. Set exactly when `draft_editor` here is null. Do not read that as the opportunity being live: the `status` field reports several settled states (cancelled, expired, failed) as `draft`, so read status from `status` and treat this only as a link. Give this when the customer asks about progress or has submissions to review. Null if Terac cannot resolve the organization or project this link belongs to. - `recruitment` (string, nullable) **(required)**: The screener questions and quotas every applicant is filtered against. Set exactly when `draft_editor` here is null. Give this when the customer wants to check who is being screened in or out. Null if Terac cannot resolve the organization or project this link belongs to. - `task` (string, nullable) **(required)**: What a participant is actually asked to do - task type, duration, and task link. Set exactly when `draft_editor` here is null. Give this when the customer asks what the participants see. Null if Terac cannot resolve the organization or project this link belongs to. - `settings` (string, nullable) **(required)**: The study's own settings: recruitment window, participant count, and the controls to stop or delete it. Set exactly when `draft_editor` here is null. Give this when the customer wants to change or end a running study rather than look at its results. Pause and resume are not here - they are on the opportunities list. Null if Terac cannot resolve the organization or project this link belongs to. - `submission_stats` (object) - `total` (integer) **(required)** - `in_progress` (integer) **(required)** - `awaiting_review` (integer) **(required)** - `approved` (integer) **(required)** - `rejected` (integer) **(required)** - `quota_progress` (array) - `screening_question` (string) **(required)** - `targets` (array) **(required)** - `value` (string) **(required)** - `count` (integer) **(required)** - `current` (integer) **(required)** - `type` (string) **(required)**: How the count is enforced: exact, minimum or maximum. Tells you whether "current" nearing "count" means a cap is about to close or a floor is about to be met. - `cross_quota_progress` (array): Fill progress for each cross-question quota cell (live studies). - `label` (string) **(required)**: Human label for this cell, e.g. "Brazil x Agency". - `conditions` (array) **(required)**: The conditions, one per dimension: one for a simple per-answer quota, two+ to interlock a cross-tab across distinct questions. - `screening_question` (string) **(required)**: Key of a screening_questions[].key this condition tests (one dimension of the cross-tab). - `answer` (string) **(required)**: An answer of that question, by its label. For a grid source use "Row: Column". - `operator` (string) **(required)**: one of: `eq`, `ne`: "eq" = selected that answer, "ne" = did not. - `join` (string) **(required)**: one of: `and`, `or` - `target` (integer) **(required)**: Target participant count for this cell. - `quota_type` (string) **(required)**: one of: `exact`, `minimum`, `maximum`: "exact" = hit exactly, "minimum" = at least, "maximum" = no more than. - `dimension` (string): Cells sharing a dimension are summed as one interlocked cross-tab quota; use one token per cross-tab. - `current` (integer) **(required)**: Approved participants matching this cell so far. - `screening_stats` (array): Per-question screen-out rates and answer breakdown (live studies). - `screening_question` (string) **(required)**: The `screening_questions[].key`. - `graded` (integer) **(required)**: Screeners GRADED here, not answers given: a branching screener stops at the first failure. - `rejected` (integer) **(required)**: Rejected by this question. - `rejection_rate` (number) **(required)**: `rejected` / `graded`, 0 to 1. - `answers` (array) **(required)**: How often each answer was chosen, for spotting a screener that is too narrow. - `text` (string) **(required)**: The answer as written on the question, not its stored value. - `selected` (integer) **(required)** - `share` (number) **(required)**: `selected` / `graded`, between 0 and 1. ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # Delete a draft opportunity (https://terac.com/docs/developers/reference/deleteOpportunity) `DELETE https://terac.com/api/external/v2/opportunities/{opportunityId}` Deletes a draft opportunity, which was never charged, and drops it from `GET /opportunities`. A launched opportunity returns 409; end one with `POST /opportunities/{opportunityId}/stop` instead. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Path Parameters - `opportunityId` (string) **(required)**: ID of the opportunity, as returned by `POST /opportunities` or `GET /opportunities`. ## Responses ### 200: Confirms the deletion, with a link to the organization's remaining opportunities. - `id` (string) **(required)** - `deleted` (boolean) **(required)**: one of: `true` - `dashboard_url` (string, nullable) **(required)**: Every opportunity in the organization, across all projects, filterable by status. Give this when the customer asks where they can see everything they have running, or when they want to pause or resume something - those two controls live on a list row here, not on the study's own pages. Null if Terac cannot resolve the organization or project this link belongs to. ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # Launch opportunity (https://terac.com/docs/developers/reference/launchOpportunity) `POST https://terac.com/api/external/v2/opportunities/{opportunityId}/launch` Launches a draft opportunity: charges your organization's balance and starts recruiting. The charge is the participant incentive for every one of `num_participants` plus the platform fee, as `pricing` on the draft shows, and `funding` says beforehand whether the balance covers it. The response is the opportunity, already `active`, so there is nothing to poll for. Only a `draft` can be launched; any other status returns 409. A draft that cannot run is refused with 412 before anything is charged, and the message says what to fix: the balance does not cover the charge, the screener is missing or nobody can pass it, the participant budget is under the $5 minimum, or the feasibility request it was priced from is not answered yet. Unused budget is refunded when the opportunity is stopped or completes. A screener is recommended. A draft without `screening_questions` launches only when Terac has enabled that for your organization and you send `without_screener: true`. Unless Terac runs its AI screening interview on it, everyone who matches its filters then goes straight to the task, so the tasks that deliver the work have to be `review_type: manual_review` and `customer_screening_review` has to be `auto_invite`: nobody is paid until you approve their submission with `POST /submissions/{submissionId}/approve`. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Path Parameters - `opportunityId` (string) **(required)**: ID of the opportunity, as returned by `POST /opportunities` or `GET /opportunities`. ## Request Body (required) `Content-Type: application/json` - `without_screener` (boolean): Launch an opportunity that has no screening_questions. Honored only when Terac has enabled it for your organization, and ignored on an opportunity that has a screener. Unless Terac runs its AI screening interview on it, everyone who matches the filters then goes straight to the task, so the tasks that deliver the work have to be review_type manual_review and customer_screening_review auto_invite, and nobody is paid until you approve their submission. A screener is recommended: send this only when none is needed. ## Responses ### 200: The launched opportunity, now `active`. - `id` (string) **(required)** - `title` (string) **(required)** - `internal_title` (string, nullable) **(required)** - `description` (string, nullable) **(required)** - `status` (string) **(required)**: one of: `draft`, `pre_recruiting`, `active`, `fulfilled`, `paused`, `stopped`, `completed`, `expired`, `cancelled`, `failed`: Decides which calls are allowed: - `draft`: edit, launch or delete it. An opportunity Terac is still preparing from a quote reads `draft` too. - `pre_recruiting`: taking applicants ahead of a launch set up in the dashboard. Stop only. - `active`: pause or stop. - `paused`: resume or stop. - `fulfilled`: target reached; settles to `completed`. - `completed`, `stopped`, `expired`, `cancelled`: ended. - `failed`: its launch never finished. Create a new opportunity. - `num_participants` (integer) **(required)** - `estimated_duration_minutes` (integer) **(required)** - `filters` (array) **(required)** - `screening_questions` (array) **(required)** - `key` (string) **(required)**: Identifier for this question, yours or assigned. Comes back as `screening_answers[].key`, so answers join to their question. Opaque. - `text` (string) **(required)** - `question_rich_text` (string): Markdown version of the prompt, when the author wrote one. `text` stays the plaintext source of truth. - `pick` (string) **(required)**: one of: `one`, `any`, `boolean`, `text`, `grid` - `answers` (array) - `text` (string) **(required)** - `qualify_logic` (string) **(required)**: one of: `may`, `must`, `must_one_of`, `reject`, `review`: The disposition that is actually stored, which is not always the one that was sent. On a single-select (pick "one", which is also how a yes/no question reads back) every qualifying answer reads back as "must", because "may", "must" and "must_one_of" collapse to one disposition where only one answer can be picked. So a "may" answer reading back as "must" is the stored truth, not a mistranslation - rewriting it to "may" will not restore a filter. Only "reject" and "review" screen or flag on a single-select; switch the question to pick "any" if you need per-answer dispositions. - `allow_free_text` (boolean): Selecting this answer reveals a write-in box ("Other, please specify"). - `grid` (object) - `pick` (string): one of: `one`, `any` - `rows` (array) **(required)** - `key` (string) - `text` (string) **(required)** - `columns` (array) **(required)** - `value` (string) - `text` (string) **(required)** - `cell_actions` (array) - `row` (string) **(required)** - `column` (string) **(required)** - `qualify_logic` (string) **(required)**: one of: `may`, `must`, `must_one_of`, `reject`, `review` - `min_qualifying` (integer) - `display_condition` (object) - `conditions` (array) **(required)** - `screening_question` (string) **(required)**: The key of an EARLIER question this one depends on - `answer` (string) **(required)**: An answer of that earlier question. For a grid source use "Row: Column". - `operator` (string) **(required)**: one of: `eq`, `ne`: "eq" = answered that value, "ne" = did not. - `join` (string) **(required)**: one of: `and`, `or` - `conditional_rules` (array) - `conditions` (array) **(required)** - `screening_question` (string) - `answer` (string) **(required)** - `operator` (string) **(required)**: one of: `eq`, `ne` - `join` (string) **(required)**: one of: `and`, `or` - `outcome` (string) **(required)**: one of: `reject`, `review` - `skip_rules` (array): Branching authored here or in the dashboard, in the same shape create accepts, so it can be sent straight back on an update. Omitted when the question has none - an absent field means no branching, never that branching was dropped on read. - `conditions` (array) **(required)** - `screening_question` (string) **(required)**: The key of the question this rule lives on. A rule tests its OWN answers; use display_condition to gate a question on an earlier one. - `answer` (string) **(required)**: One of that question's answers (its text; "Yes"/"No" for pick: "boolean"). - `operator` (string) **(required)**: one of: `eq`, `ne`: "eq" = answered that value, "ne" = did not. - `join` (string) **(required)**: one of: `and`, `or` - `target_question` (string, nullable) **(required)**: Key of the question to jump to when the conditions hold; null ends the screener. Must be a LATER question - the screener only walks forward. - `allow_paste` (boolean) - `quotas` (array) **(required)** - `screening_question` (string) **(required)**: The key of the screening question this quota applies to (must match a screening_questions[].key) - `targets` (array) **(required)**: Per-answer target counts for this question - `value` (string) **(required)**: The answer option text (matches a screening answer's text) this quota targets - `count` (integer) **(required)**: How many approved submissions to allow/require for this answer - `type` (string): one of: `exact`, `minimum`, `maximum`: How the count is enforced: "minimum" = at least this many (a floor; recruiting prioritizes the answer while it is short); "maximum" = at most this many (a cap; further applicants who pick this answer are screened out once it is full); "exact" = both. Omit for "minimum". - `cross_quotas` (array) **(required)** - `label` (string) **(required)**: Human label for this cell, e.g. "Brazil x Agency". - `conditions` (array) **(required)**: The conditions, one per dimension: one for a simple per-answer quota, two+ to interlock a cross-tab across distinct questions. - `screening_question` (string) **(required)**: Key of a screening_questions[].key this condition tests (one dimension of the cross-tab). - `answer` (string) **(required)**: An answer of that question, by its label. For a grid source use "Row: Column". - `operator` (string) **(required)**: one of: `eq`, `ne`: "eq" = selected that answer, "ne" = did not. - `join` (string) **(required)**: one of: `and`, `or` - `target` (integer) **(required)**: Target participant count for this cell. - `quota_type` (string) **(required)**: one of: `exact`, `minimum`, `maximum`: "exact" = hit exactly, "minimum" = at least, "maximum" = no more than. - `dimension` (string): Cells sharing a dimension are summed as one interlocked cross-tab quota; use one token per cross-tab. - `tasks` (array) **(required)** - `sequence` (integer) **(required)** - `task_type` (string) **(required)**: Any stored task type, so wider than the set you can create: interview, survey, file_upload, activity, scheduled_interview, agreement, join_slack, slack_handoff, e_signature. Only interview, file_upload and activity are accepted on create. - `review_type` (string) **(required)** - `task_url` (string, nullable) **(required)**: null when the task type carries no URL. - `participant_url_template` (string): task_url with the tracking params Terac appends per participant (submissionId, teracSubmissionId, taskId), shown as {submissionId}/{taskId} macros. Omitted when there is no task_url. - `title` (string) - `description` (string) - `duration_minutes` (integer) **(required)** - `available_after_sequence` (integer): This task unlocks only after that task is approved. - `available_after_delay_minutes` (integer): Wait this long after the gating task before unlocking. - `provider` (string): Scheduled interview only: the booking provider. - `calendar_owner` (string): Scheduled interview only: whose calendar is booked (researcher or expert). - `event_type_url` (string): Scheduled interview only: the booking link. - `instructions` (string): File upload only: what to submit. - `accepted_mime_types` (array): "file_upload" only, and absent on every other task type. RESOLVED rather than echoed: a task created without this reads back with the default actually enforced, not an empty field. - `min_files` (integer): "file_upload" only, and absent on every other task type. Resolved to the enforced default when the task did not set one. - `max_files` (integer): "file_upload" only, and absent on every other task type. Resolved to the enforced default when the task did not set one. - `max_file_size_bytes` (integer): "file_upload" only, and absent on every other task type. Resolved to the enforced default when the task did not set one. - `pricing` (object, nullable) **(required)** - `cost_per_participant_cents` (integer) **(required)** - `total_cost_cents` (integer) **(required)** - `currency` (string) **(required)**: one of: `usd` - `funding` (object) - `affordable` (boolean) **(required)**: Covers the cost, overdraft included. False blocks nothing. - `draws_on_credit` (boolean) **(required)**: Affordable but goes negative, invoiced later. Say so. - `balance_cents` (integer) **(required)**: Balance now. - `shortfall_cents` (integer) **(required)**: Gap after overdraft, 0 when affordable. - `finance_url` (string, nullable) **(required)**: Where billing adds credit. - `device_types` (array) **(required)** - `business_type` (string, nullable) **(required)** - `candidate_profile` (string, nullable) **(required)**: Free-text profile of the ideal candidate, when set. - `required_accounts` (array) **(required)**: Accounts an expert must have linked to apply (e.g. linkedin). - `video_required` (string) **(required)**: Whether the expert's camera is needed: none, requested, required. - `screen_share_required` (string) **(required)**: Whether a screen share is needed: none, requested, required. - `customer_screening_review` (string) **(required)**: auto_invite = qualified applicants are invited automatically; manual_review = you pick who to invite. - `pay_frequency` (string) **(required)**: How the incentive is framed: one_time, hourly, or per_task. - `screening_option_randomization` (string) **(required)**: Whether screening answer options are shown in random order: the string "enabled" or "disabled", not a boolean. - `forwarding_email_enabled` (string) **(required)**: Whether each submission gets a Terac forwarding email address: the string "enabled" or "disabled", not a boolean. - `max_responses_per_expert` (integer, nullable) **(required)** - `feasibility_request_id` (string, nullable) **(required)** - `project_id` (string) **(required)** - `created_at` (string) **(required)** - `updated_at` (string) **(required)** - `ending_at` (string, nullable) **(required)**: Target completion date for recruitment. - `launched_at` (string, nullable) **(required)** - `links` (object) **(required)** - `self` (string) **(required)** - `launch` (string, nullable) **(required)** - `submissions` (string) **(required)** - `dashboard` (object) **(required)**: Pages a person opens in a browser. `self`, `launch` and `submissions` above are API paths for you, not for them - never give a customer one of those. - `study` (string, nullable) **(required)**: The study's own page. It routes itself by status: an editable draft opens the builder, anything else opens submissions. Never null. Give this when you do not know which tab the customer needs; when `draft_editor` is set, prefer that instead, since it opens the same builder the customer would review in. Null if Terac cannot resolve the organization or project this link belongs to. - `draft_editor` (string, nullable) **(required)**: Opens this draft in the study builder so a human can review the whole setup (targeting, screener, tasks, price) before anything launches. Set exactly when `submissions` here is null, never both. Give this to the customer after you build a draft, instead of launching. Null if Terac cannot resolve the organization or project this link belongs to. - `submissions` (string, nullable) **(required)**: Who applied, who is in progress, who is awaiting review. Set exactly when `draft_editor` here is null. Do not read that as the opportunity being live: the `status` field reports several settled states (cancelled, expired, failed) as `draft`, so read status from `status` and treat this only as a link. Give this when the customer asks about progress or has submissions to review. Null if Terac cannot resolve the organization or project this link belongs to. - `recruitment` (string, nullable) **(required)**: The screener questions and quotas every applicant is filtered against. Set exactly when `draft_editor` here is null. Give this when the customer wants to check who is being screened in or out. Null if Terac cannot resolve the organization or project this link belongs to. - `task` (string, nullable) **(required)**: What a participant is actually asked to do - task type, duration, and task link. Set exactly when `draft_editor` here is null. Give this when the customer asks what the participants see. Null if Terac cannot resolve the organization or project this link belongs to. - `settings` (string, nullable) **(required)**: The study's own settings: recruitment window, participant count, and the controls to stop or delete it. Set exactly when `draft_editor` here is null. Give this when the customer wants to change or end a running study rather than look at its results. Pause and resume are not here - they are on the opportunities list. Null if Terac cannot resolve the organization or project this link belongs to. - `submission_stats` (object) - `total` (integer) **(required)** - `in_progress` (integer) **(required)** - `awaiting_review` (integer) **(required)** - `approved` (integer) **(required)** - `rejected` (integer) **(required)** - `quota_progress` (array) - `screening_question` (string) **(required)** - `targets` (array) **(required)** - `value` (string) **(required)** - `count` (integer) **(required)** - `current` (integer) **(required)** - `type` (string) **(required)**: How the count is enforced: exact, minimum or maximum. Tells you whether "current" nearing "count" means a cap is about to close or a floor is about to be met. - `cross_quota_progress` (array): Fill progress for each cross-question quota cell (live studies). - `label` (string) **(required)**: Human label for this cell, e.g. "Brazil x Agency". - `conditions` (array) **(required)**: The conditions, one per dimension: one for a simple per-answer quota, two+ to interlock a cross-tab across distinct questions. - `screening_question` (string) **(required)**: Key of a screening_questions[].key this condition tests (one dimension of the cross-tab). - `answer` (string) **(required)**: An answer of that question, by its label. For a grid source use "Row: Column". - `operator` (string) **(required)**: one of: `eq`, `ne`: "eq" = selected that answer, "ne" = did not. - `join` (string) **(required)**: one of: `and`, `or` - `target` (integer) **(required)**: Target participant count for this cell. - `quota_type` (string) **(required)**: one of: `exact`, `minimum`, `maximum`: "exact" = hit exactly, "minimum" = at least, "maximum" = no more than. - `dimension` (string): Cells sharing a dimension are summed as one interlocked cross-tab quota; use one token per cross-tab. - `current` (integer) **(required)**: Approved participants matching this cell so far. - `screening_stats` (array): Per-question screen-out rates and answer breakdown (live studies). - `screening_question` (string) **(required)**: The `screening_questions[].key`. - `graded` (integer) **(required)**: Screeners GRADED here, not answers given: a branching screener stops at the first failure. - `rejected` (integer) **(required)**: Rejected by this question. - `rejection_rate` (number) **(required)**: `rejected` / `graded`, 0 to 1. - `answers` (array) **(required)**: How often each answer was chosen, for spotting a screener that is too narrow. - `text` (string) **(required)**: The answer as written on the question, not its stored value. - `selected` (integer) **(required)** - `share` (number) **(required)**: `selected` / `graded`, between 0 and 1. ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # Pause opportunity (https://terac.com/docs/developers/reference/pauseOpportunity) `POST https://terac.com/api/external/v2/opportunities/{opportunityId}/pause` Pauses an active opportunity, so it stops taking new applicants. Participants already working can still finish and submit, and nothing is charged or refunded. Resume it with `POST /opportunities/{opportunityId}/resume`. Only an `active` opportunity can be paused; any other status returns 409. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Path Parameters - `opportunityId` (string) **(required)**: ID of the opportunity, as returned by `POST /opportunities` or `GET /opportunities`. ## Responses ### 200: The opportunity, now `paused`. - `id` (string) **(required)** - `title` (string) **(required)** - `internal_title` (string, nullable) **(required)** - `description` (string, nullable) **(required)** - `status` (string) **(required)**: one of: `draft`, `pre_recruiting`, `active`, `fulfilled`, `paused`, `stopped`, `completed`, `expired`, `cancelled`, `failed`: Decides which calls are allowed: - `draft`: edit, launch or delete it. An opportunity Terac is still preparing from a quote reads `draft` too. - `pre_recruiting`: taking applicants ahead of a launch set up in the dashboard. Stop only. - `active`: pause or stop. - `paused`: resume or stop. - `fulfilled`: target reached; settles to `completed`. - `completed`, `stopped`, `expired`, `cancelled`: ended. - `failed`: its launch never finished. Create a new opportunity. - `num_participants` (integer) **(required)** - `estimated_duration_minutes` (integer) **(required)** - `filters` (array) **(required)** - `screening_questions` (array) **(required)** - `key` (string) **(required)**: Identifier for this question, yours or assigned. Comes back as `screening_answers[].key`, so answers join to their question. Opaque. - `text` (string) **(required)** - `question_rich_text` (string): Markdown version of the prompt, when the author wrote one. `text` stays the plaintext source of truth. - `pick` (string) **(required)**: one of: `one`, `any`, `boolean`, `text`, `grid` - `answers` (array) - `text` (string) **(required)** - `qualify_logic` (string) **(required)**: one of: `may`, `must`, `must_one_of`, `reject`, `review`: The disposition that is actually stored, which is not always the one that was sent. On a single-select (pick "one", which is also how a yes/no question reads back) every qualifying answer reads back as "must", because "may", "must" and "must_one_of" collapse to one disposition where only one answer can be picked. So a "may" answer reading back as "must" is the stored truth, not a mistranslation - rewriting it to "may" will not restore a filter. Only "reject" and "review" screen or flag on a single-select; switch the question to pick "any" if you need per-answer dispositions. - `allow_free_text` (boolean): Selecting this answer reveals a write-in box ("Other, please specify"). - `grid` (object) - `pick` (string): one of: `one`, `any` - `rows` (array) **(required)** - `key` (string) - `text` (string) **(required)** - `columns` (array) **(required)** - `value` (string) - `text` (string) **(required)** - `cell_actions` (array) - `row` (string) **(required)** - `column` (string) **(required)** - `qualify_logic` (string) **(required)**: one of: `may`, `must`, `must_one_of`, `reject`, `review` - `min_qualifying` (integer) - `display_condition` (object) - `conditions` (array) **(required)** - `screening_question` (string) **(required)**: The key of an EARLIER question this one depends on - `answer` (string) **(required)**: An answer of that earlier question. For a grid source use "Row: Column". - `operator` (string) **(required)**: one of: `eq`, `ne`: "eq" = answered that value, "ne" = did not. - `join` (string) **(required)**: one of: `and`, `or` - `conditional_rules` (array) - `conditions` (array) **(required)** - `screening_question` (string) - `answer` (string) **(required)** - `operator` (string) **(required)**: one of: `eq`, `ne` - `join` (string) **(required)**: one of: `and`, `or` - `outcome` (string) **(required)**: one of: `reject`, `review` - `skip_rules` (array): Branching authored here or in the dashboard, in the same shape create accepts, so it can be sent straight back on an update. Omitted when the question has none - an absent field means no branching, never that branching was dropped on read. - `conditions` (array) **(required)** - `screening_question` (string) **(required)**: The key of the question this rule lives on. A rule tests its OWN answers; use display_condition to gate a question on an earlier one. - `answer` (string) **(required)**: One of that question's answers (its text; "Yes"/"No" for pick: "boolean"). - `operator` (string) **(required)**: one of: `eq`, `ne`: "eq" = answered that value, "ne" = did not. - `join` (string) **(required)**: one of: `and`, `or` - `target_question` (string, nullable) **(required)**: Key of the question to jump to when the conditions hold; null ends the screener. Must be a LATER question - the screener only walks forward. - `allow_paste` (boolean) - `quotas` (array) **(required)** - `screening_question` (string) **(required)**: The key of the screening question this quota applies to (must match a screening_questions[].key) - `targets` (array) **(required)**: Per-answer target counts for this question - `value` (string) **(required)**: The answer option text (matches a screening answer's text) this quota targets - `count` (integer) **(required)**: How many approved submissions to allow/require for this answer - `type` (string): one of: `exact`, `minimum`, `maximum`: How the count is enforced: "minimum" = at least this many (a floor; recruiting prioritizes the answer while it is short); "maximum" = at most this many (a cap; further applicants who pick this answer are screened out once it is full); "exact" = both. Omit for "minimum". - `cross_quotas` (array) **(required)** - `label` (string) **(required)**: Human label for this cell, e.g. "Brazil x Agency". - `conditions` (array) **(required)**: The conditions, one per dimension: one for a simple per-answer quota, two+ to interlock a cross-tab across distinct questions. - `screening_question` (string) **(required)**: Key of a screening_questions[].key this condition tests (one dimension of the cross-tab). - `answer` (string) **(required)**: An answer of that question, by its label. For a grid source use "Row: Column". - `operator` (string) **(required)**: one of: `eq`, `ne`: "eq" = selected that answer, "ne" = did not. - `join` (string) **(required)**: one of: `and`, `or` - `target` (integer) **(required)**: Target participant count for this cell. - `quota_type` (string) **(required)**: one of: `exact`, `minimum`, `maximum`: "exact" = hit exactly, "minimum" = at least, "maximum" = no more than. - `dimension` (string): Cells sharing a dimension are summed as one interlocked cross-tab quota; use one token per cross-tab. - `tasks` (array) **(required)** - `sequence` (integer) **(required)** - `task_type` (string) **(required)**: Any stored task type, so wider than the set you can create: interview, survey, file_upload, activity, scheduled_interview, agreement, join_slack, slack_handoff, e_signature. Only interview, file_upload and activity are accepted on create. - `review_type` (string) **(required)** - `task_url` (string, nullable) **(required)**: null when the task type carries no URL. - `participant_url_template` (string): task_url with the tracking params Terac appends per participant (submissionId, teracSubmissionId, taskId), shown as {submissionId}/{taskId} macros. Omitted when there is no task_url. - `title` (string) - `description` (string) - `duration_minutes` (integer) **(required)** - `available_after_sequence` (integer): This task unlocks only after that task is approved. - `available_after_delay_minutes` (integer): Wait this long after the gating task before unlocking. - `provider` (string): Scheduled interview only: the booking provider. - `calendar_owner` (string): Scheduled interview only: whose calendar is booked (researcher or expert). - `event_type_url` (string): Scheduled interview only: the booking link. - `instructions` (string): File upload only: what to submit. - `accepted_mime_types` (array): "file_upload" only, and absent on every other task type. RESOLVED rather than echoed: a task created without this reads back with the default actually enforced, not an empty field. - `min_files` (integer): "file_upload" only, and absent on every other task type. Resolved to the enforced default when the task did not set one. - `max_files` (integer): "file_upload" only, and absent on every other task type. Resolved to the enforced default when the task did not set one. - `max_file_size_bytes` (integer): "file_upload" only, and absent on every other task type. Resolved to the enforced default when the task did not set one. - `pricing` (object, nullable) **(required)** - `cost_per_participant_cents` (integer) **(required)** - `total_cost_cents` (integer) **(required)** - `currency` (string) **(required)**: one of: `usd` - `funding` (object) - `affordable` (boolean) **(required)**: Covers the cost, overdraft included. False blocks nothing. - `draws_on_credit` (boolean) **(required)**: Affordable but goes negative, invoiced later. Say so. - `balance_cents` (integer) **(required)**: Balance now. - `shortfall_cents` (integer) **(required)**: Gap after overdraft, 0 when affordable. - `finance_url` (string, nullable) **(required)**: Where billing adds credit. - `device_types` (array) **(required)** - `business_type` (string, nullable) **(required)** - `candidate_profile` (string, nullable) **(required)**: Free-text profile of the ideal candidate, when set. - `required_accounts` (array) **(required)**: Accounts an expert must have linked to apply (e.g. linkedin). - `video_required` (string) **(required)**: Whether the expert's camera is needed: none, requested, required. - `screen_share_required` (string) **(required)**: Whether a screen share is needed: none, requested, required. - `customer_screening_review` (string) **(required)**: auto_invite = qualified applicants are invited automatically; manual_review = you pick who to invite. - `pay_frequency` (string) **(required)**: How the incentive is framed: one_time, hourly, or per_task. - `screening_option_randomization` (string) **(required)**: Whether screening answer options are shown in random order: the string "enabled" or "disabled", not a boolean. - `forwarding_email_enabled` (string) **(required)**: Whether each submission gets a Terac forwarding email address: the string "enabled" or "disabled", not a boolean. - `max_responses_per_expert` (integer, nullable) **(required)** - `feasibility_request_id` (string, nullable) **(required)** - `project_id` (string) **(required)** - `created_at` (string) **(required)** - `updated_at` (string) **(required)** - `ending_at` (string, nullable) **(required)**: Target completion date for recruitment. - `launched_at` (string, nullable) **(required)** - `links` (object) **(required)** - `self` (string) **(required)** - `launch` (string, nullable) **(required)** - `submissions` (string) **(required)** - `dashboard` (object) **(required)**: Pages a person opens in a browser. `self`, `launch` and `submissions` above are API paths for you, not for them - never give a customer one of those. - `study` (string, nullable) **(required)**: The study's own page. It routes itself by status: an editable draft opens the builder, anything else opens submissions. Never null. Give this when you do not know which tab the customer needs; when `draft_editor` is set, prefer that instead, since it opens the same builder the customer would review in. Null if Terac cannot resolve the organization or project this link belongs to. - `draft_editor` (string, nullable) **(required)**: Opens this draft in the study builder so a human can review the whole setup (targeting, screener, tasks, price) before anything launches. Set exactly when `submissions` here is null, never both. Give this to the customer after you build a draft, instead of launching. Null if Terac cannot resolve the organization or project this link belongs to. - `submissions` (string, nullable) **(required)**: Who applied, who is in progress, who is awaiting review. Set exactly when `draft_editor` here is null. Do not read that as the opportunity being live: the `status` field reports several settled states (cancelled, expired, failed) as `draft`, so read status from `status` and treat this only as a link. Give this when the customer asks about progress or has submissions to review. Null if Terac cannot resolve the organization or project this link belongs to. - `recruitment` (string, nullable) **(required)**: The screener questions and quotas every applicant is filtered against. Set exactly when `draft_editor` here is null. Give this when the customer wants to check who is being screened in or out. Null if Terac cannot resolve the organization or project this link belongs to. - `task` (string, nullable) **(required)**: What a participant is actually asked to do - task type, duration, and task link. Set exactly when `draft_editor` here is null. Give this when the customer asks what the participants see. Null if Terac cannot resolve the organization or project this link belongs to. - `settings` (string, nullable) **(required)**: The study's own settings: recruitment window, participant count, and the controls to stop or delete it. Set exactly when `draft_editor` here is null. Give this when the customer wants to change or end a running study rather than look at its results. Pause and resume are not here - they are on the opportunities list. Null if Terac cannot resolve the organization or project this link belongs to. - `submission_stats` (object) - `total` (integer) **(required)** - `in_progress` (integer) **(required)** - `awaiting_review` (integer) **(required)** - `approved` (integer) **(required)** - `rejected` (integer) **(required)** - `quota_progress` (array) - `screening_question` (string) **(required)** - `targets` (array) **(required)** - `value` (string) **(required)** - `count` (integer) **(required)** - `current` (integer) **(required)** - `type` (string) **(required)**: How the count is enforced: exact, minimum or maximum. Tells you whether "current" nearing "count" means a cap is about to close or a floor is about to be met. - `cross_quota_progress` (array): Fill progress for each cross-question quota cell (live studies). - `label` (string) **(required)**: Human label for this cell, e.g. "Brazil x Agency". - `conditions` (array) **(required)**: The conditions, one per dimension: one for a simple per-answer quota, two+ to interlock a cross-tab across distinct questions. - `screening_question` (string) **(required)**: Key of a screening_questions[].key this condition tests (one dimension of the cross-tab). - `answer` (string) **(required)**: An answer of that question, by its label. For a grid source use "Row: Column". - `operator` (string) **(required)**: one of: `eq`, `ne`: "eq" = selected that answer, "ne" = did not. - `join` (string) **(required)**: one of: `and`, `or` - `target` (integer) **(required)**: Target participant count for this cell. - `quota_type` (string) **(required)**: one of: `exact`, `minimum`, `maximum`: "exact" = hit exactly, "minimum" = at least, "maximum" = no more than. - `dimension` (string): Cells sharing a dimension are summed as one interlocked cross-tab quota; use one token per cross-tab. - `current` (integer) **(required)**: Approved participants matching this cell so far. - `screening_stats` (array): Per-question screen-out rates and answer breakdown (live studies). - `screening_question` (string) **(required)**: The `screening_questions[].key`. - `graded` (integer) **(required)**: Screeners GRADED here, not answers given: a branching screener stops at the first failure. - `rejected` (integer) **(required)**: Rejected by this question. - `rejection_rate` (number) **(required)**: `rejected` / `graded`, 0 to 1. - `answers` (array) **(required)**: How often each answer was chosen, for spotting a screener that is too narrow. - `text` (string) **(required)**: The answer as written on the question, not its stored value. - `selected` (integer) **(required)** - `share` (number) **(required)**: `selected` / `graded`, between 0 and 1. ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # Resume opportunity (https://terac.com/docs/developers/reference/resumeOpportunity) `POST https://terac.com/api/external/v2/opportunities/{opportunityId}/resume` Resumes a paused opportunity, so it takes new applicants again. Nothing is charged: the budget taken at launch still covers it. Only a `paused` opportunity can be resumed; any other status returns 409. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Path Parameters - `opportunityId` (string) **(required)**: ID of the opportunity, as returned by `POST /opportunities` or `GET /opportunities`. ## Responses ### 200: The opportunity, `active` again. - `id` (string) **(required)** - `title` (string) **(required)** - `internal_title` (string, nullable) **(required)** - `description` (string, nullable) **(required)** - `status` (string) **(required)**: one of: `draft`, `pre_recruiting`, `active`, `fulfilled`, `paused`, `stopped`, `completed`, `expired`, `cancelled`, `failed`: Decides which calls are allowed: - `draft`: edit, launch or delete it. An opportunity Terac is still preparing from a quote reads `draft` too. - `pre_recruiting`: taking applicants ahead of a launch set up in the dashboard. Stop only. - `active`: pause or stop. - `paused`: resume or stop. - `fulfilled`: target reached; settles to `completed`. - `completed`, `stopped`, `expired`, `cancelled`: ended. - `failed`: its launch never finished. Create a new opportunity. - `num_participants` (integer) **(required)** - `estimated_duration_minutes` (integer) **(required)** - `filters` (array) **(required)** - `screening_questions` (array) **(required)** - `key` (string) **(required)**: Identifier for this question, yours or assigned. Comes back as `screening_answers[].key`, so answers join to their question. Opaque. - `text` (string) **(required)** - `question_rich_text` (string): Markdown version of the prompt, when the author wrote one. `text` stays the plaintext source of truth. - `pick` (string) **(required)**: one of: `one`, `any`, `boolean`, `text`, `grid` - `answers` (array) - `text` (string) **(required)** - `qualify_logic` (string) **(required)**: one of: `may`, `must`, `must_one_of`, `reject`, `review`: The disposition that is actually stored, which is not always the one that was sent. On a single-select (pick "one", which is also how a yes/no question reads back) every qualifying answer reads back as "must", because "may", "must" and "must_one_of" collapse to one disposition where only one answer can be picked. So a "may" answer reading back as "must" is the stored truth, not a mistranslation - rewriting it to "may" will not restore a filter. Only "reject" and "review" screen or flag on a single-select; switch the question to pick "any" if you need per-answer dispositions. - `allow_free_text` (boolean): Selecting this answer reveals a write-in box ("Other, please specify"). - `grid` (object) - `pick` (string): one of: `one`, `any` - `rows` (array) **(required)** - `key` (string) - `text` (string) **(required)** - `columns` (array) **(required)** - `value` (string) - `text` (string) **(required)** - `cell_actions` (array) - `row` (string) **(required)** - `column` (string) **(required)** - `qualify_logic` (string) **(required)**: one of: `may`, `must`, `must_one_of`, `reject`, `review` - `min_qualifying` (integer) - `display_condition` (object) - `conditions` (array) **(required)** - `screening_question` (string) **(required)**: The key of an EARLIER question this one depends on - `answer` (string) **(required)**: An answer of that earlier question. For a grid source use "Row: Column". - `operator` (string) **(required)**: one of: `eq`, `ne`: "eq" = answered that value, "ne" = did not. - `join` (string) **(required)**: one of: `and`, `or` - `conditional_rules` (array) - `conditions` (array) **(required)** - `screening_question` (string) - `answer` (string) **(required)** - `operator` (string) **(required)**: one of: `eq`, `ne` - `join` (string) **(required)**: one of: `and`, `or` - `outcome` (string) **(required)**: one of: `reject`, `review` - `skip_rules` (array): Branching authored here or in the dashboard, in the same shape create accepts, so it can be sent straight back on an update. Omitted when the question has none - an absent field means no branching, never that branching was dropped on read. - `conditions` (array) **(required)** - `screening_question` (string) **(required)**: The key of the question this rule lives on. A rule tests its OWN answers; use display_condition to gate a question on an earlier one. - `answer` (string) **(required)**: One of that question's answers (its text; "Yes"/"No" for pick: "boolean"). - `operator` (string) **(required)**: one of: `eq`, `ne`: "eq" = answered that value, "ne" = did not. - `join` (string) **(required)**: one of: `and`, `or` - `target_question` (string, nullable) **(required)**: Key of the question to jump to when the conditions hold; null ends the screener. Must be a LATER question - the screener only walks forward. - `allow_paste` (boolean) - `quotas` (array) **(required)** - `screening_question` (string) **(required)**: The key of the screening question this quota applies to (must match a screening_questions[].key) - `targets` (array) **(required)**: Per-answer target counts for this question - `value` (string) **(required)**: The answer option text (matches a screening answer's text) this quota targets - `count` (integer) **(required)**: How many approved submissions to allow/require for this answer - `type` (string): one of: `exact`, `minimum`, `maximum`: How the count is enforced: "minimum" = at least this many (a floor; recruiting prioritizes the answer while it is short); "maximum" = at most this many (a cap; further applicants who pick this answer are screened out once it is full); "exact" = both. Omit for "minimum". - `cross_quotas` (array) **(required)** - `label` (string) **(required)**: Human label for this cell, e.g. "Brazil x Agency". - `conditions` (array) **(required)**: The conditions, one per dimension: one for a simple per-answer quota, two+ to interlock a cross-tab across distinct questions. - `screening_question` (string) **(required)**: Key of a screening_questions[].key this condition tests (one dimension of the cross-tab). - `answer` (string) **(required)**: An answer of that question, by its label. For a grid source use "Row: Column". - `operator` (string) **(required)**: one of: `eq`, `ne`: "eq" = selected that answer, "ne" = did not. - `join` (string) **(required)**: one of: `and`, `or` - `target` (integer) **(required)**: Target participant count for this cell. - `quota_type` (string) **(required)**: one of: `exact`, `minimum`, `maximum`: "exact" = hit exactly, "minimum" = at least, "maximum" = no more than. - `dimension` (string): Cells sharing a dimension are summed as one interlocked cross-tab quota; use one token per cross-tab. - `tasks` (array) **(required)** - `sequence` (integer) **(required)** - `task_type` (string) **(required)**: Any stored task type, so wider than the set you can create: interview, survey, file_upload, activity, scheduled_interview, agreement, join_slack, slack_handoff, e_signature. Only interview, file_upload and activity are accepted on create. - `review_type` (string) **(required)** - `task_url` (string, nullable) **(required)**: null when the task type carries no URL. - `participant_url_template` (string): task_url with the tracking params Terac appends per participant (submissionId, teracSubmissionId, taskId), shown as {submissionId}/{taskId} macros. Omitted when there is no task_url. - `title` (string) - `description` (string) - `duration_minutes` (integer) **(required)** - `available_after_sequence` (integer): This task unlocks only after that task is approved. - `available_after_delay_minutes` (integer): Wait this long after the gating task before unlocking. - `provider` (string): Scheduled interview only: the booking provider. - `calendar_owner` (string): Scheduled interview only: whose calendar is booked (researcher or expert). - `event_type_url` (string): Scheduled interview only: the booking link. - `instructions` (string): File upload only: what to submit. - `accepted_mime_types` (array): "file_upload" only, and absent on every other task type. RESOLVED rather than echoed: a task created without this reads back with the default actually enforced, not an empty field. - `min_files` (integer): "file_upload" only, and absent on every other task type. Resolved to the enforced default when the task did not set one. - `max_files` (integer): "file_upload" only, and absent on every other task type. Resolved to the enforced default when the task did not set one. - `max_file_size_bytes` (integer): "file_upload" only, and absent on every other task type. Resolved to the enforced default when the task did not set one. - `pricing` (object, nullable) **(required)** - `cost_per_participant_cents` (integer) **(required)** - `total_cost_cents` (integer) **(required)** - `currency` (string) **(required)**: one of: `usd` - `funding` (object) - `affordable` (boolean) **(required)**: Covers the cost, overdraft included. False blocks nothing. - `draws_on_credit` (boolean) **(required)**: Affordable but goes negative, invoiced later. Say so. - `balance_cents` (integer) **(required)**: Balance now. - `shortfall_cents` (integer) **(required)**: Gap after overdraft, 0 when affordable. - `finance_url` (string, nullable) **(required)**: Where billing adds credit. - `device_types` (array) **(required)** - `business_type` (string, nullable) **(required)** - `candidate_profile` (string, nullable) **(required)**: Free-text profile of the ideal candidate, when set. - `required_accounts` (array) **(required)**: Accounts an expert must have linked to apply (e.g. linkedin). - `video_required` (string) **(required)**: Whether the expert's camera is needed: none, requested, required. - `screen_share_required` (string) **(required)**: Whether a screen share is needed: none, requested, required. - `customer_screening_review` (string) **(required)**: auto_invite = qualified applicants are invited automatically; manual_review = you pick who to invite. - `pay_frequency` (string) **(required)**: How the incentive is framed: one_time, hourly, or per_task. - `screening_option_randomization` (string) **(required)**: Whether screening answer options are shown in random order: the string "enabled" or "disabled", not a boolean. - `forwarding_email_enabled` (string) **(required)**: Whether each submission gets a Terac forwarding email address: the string "enabled" or "disabled", not a boolean. - `max_responses_per_expert` (integer, nullable) **(required)** - `feasibility_request_id` (string, nullable) **(required)** - `project_id` (string) **(required)** - `created_at` (string) **(required)** - `updated_at` (string) **(required)** - `ending_at` (string, nullable) **(required)**: Target completion date for recruitment. - `launched_at` (string, nullable) **(required)** - `links` (object) **(required)** - `self` (string) **(required)** - `launch` (string, nullable) **(required)** - `submissions` (string) **(required)** - `dashboard` (object) **(required)**: Pages a person opens in a browser. `self`, `launch` and `submissions` above are API paths for you, not for them - never give a customer one of those. - `study` (string, nullable) **(required)**: The study's own page. It routes itself by status: an editable draft opens the builder, anything else opens submissions. Never null. Give this when you do not know which tab the customer needs; when `draft_editor` is set, prefer that instead, since it opens the same builder the customer would review in. Null if Terac cannot resolve the organization or project this link belongs to. - `draft_editor` (string, nullable) **(required)**: Opens this draft in the study builder so a human can review the whole setup (targeting, screener, tasks, price) before anything launches. Set exactly when `submissions` here is null, never both. Give this to the customer after you build a draft, instead of launching. Null if Terac cannot resolve the organization or project this link belongs to. - `submissions` (string, nullable) **(required)**: Who applied, who is in progress, who is awaiting review. Set exactly when `draft_editor` here is null. Do not read that as the opportunity being live: the `status` field reports several settled states (cancelled, expired, failed) as `draft`, so read status from `status` and treat this only as a link. Give this when the customer asks about progress or has submissions to review. Null if Terac cannot resolve the organization or project this link belongs to. - `recruitment` (string, nullable) **(required)**: The screener questions and quotas every applicant is filtered against. Set exactly when `draft_editor` here is null. Give this when the customer wants to check who is being screened in or out. Null if Terac cannot resolve the organization or project this link belongs to. - `task` (string, nullable) **(required)**: What a participant is actually asked to do - task type, duration, and task link. Set exactly when `draft_editor` here is null. Give this when the customer asks what the participants see. Null if Terac cannot resolve the organization or project this link belongs to. - `settings` (string, nullable) **(required)**: The study's own settings: recruitment window, participant count, and the controls to stop or delete it. Set exactly when `draft_editor` here is null. Give this when the customer wants to change or end a running study rather than look at its results. Pause and resume are not here - they are on the opportunities list. Null if Terac cannot resolve the organization or project this link belongs to. - `submission_stats` (object) - `total` (integer) **(required)** - `in_progress` (integer) **(required)** - `awaiting_review` (integer) **(required)** - `approved` (integer) **(required)** - `rejected` (integer) **(required)** - `quota_progress` (array) - `screening_question` (string) **(required)** - `targets` (array) **(required)** - `value` (string) **(required)** - `count` (integer) **(required)** - `current` (integer) **(required)** - `type` (string) **(required)**: How the count is enforced: exact, minimum or maximum. Tells you whether "current" nearing "count" means a cap is about to close or a floor is about to be met. - `cross_quota_progress` (array): Fill progress for each cross-question quota cell (live studies). - `label` (string) **(required)**: Human label for this cell, e.g. "Brazil x Agency". - `conditions` (array) **(required)**: The conditions, one per dimension: one for a simple per-answer quota, two+ to interlock a cross-tab across distinct questions. - `screening_question` (string) **(required)**: Key of a screening_questions[].key this condition tests (one dimension of the cross-tab). - `answer` (string) **(required)**: An answer of that question, by its label. For a grid source use "Row: Column". - `operator` (string) **(required)**: one of: `eq`, `ne`: "eq" = selected that answer, "ne" = did not. - `join` (string) **(required)**: one of: `and`, `or` - `target` (integer) **(required)**: Target participant count for this cell. - `quota_type` (string) **(required)**: one of: `exact`, `minimum`, `maximum`: "exact" = hit exactly, "minimum" = at least, "maximum" = no more than. - `dimension` (string): Cells sharing a dimension are summed as one interlocked cross-tab quota; use one token per cross-tab. - `current` (integer) **(required)**: Approved participants matching this cell so far. - `screening_stats` (array): Per-question screen-out rates and answer breakdown (live studies). - `screening_question` (string) **(required)**: The `screening_questions[].key`. - `graded` (integer) **(required)**: Screeners GRADED here, not answers given: a branching screener stops at the first failure. - `rejected` (integer) **(required)**: Rejected by this question. - `rejection_rate` (number) **(required)**: `rejected` / `graded`, 0 to 1. - `answers` (array) **(required)**: How often each answer was chosen, for spotting a screener that is too narrow. - `text` (string) **(required)**: The answer as written on the question, not its stored value. - `selected` (integer) **(required)** - `share` (number) **(required)**: `selected` / `graded`, between 0 and 1. ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # Stop opportunity (https://terac.com/docs/developers/reference/stopOpportunity) `POST https://terac.com/api/external/v2/opportunities/{opportunityId}/stop` Permanently ends an opportunity and refunds its unused budget to your organization's balance. Pending invitations are withdrawn and recruitment cannot be restarted, so pause instead if you may want to continue. Submissions still awaiting your review block the stop: approve or reject them first. On an hourly or per-task opportunity, the stop itself approves every expert already paid on it whose submission is still open. Only an `active`, `paused` or `pre_recruiting` opportunity can be stopped; any other status returns 409. Delete a draft instead. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Path Parameters - `opportunityId` (string) **(required)**: ID of the opportunity, as returned by `POST /opportunities` or `GET /opportunities`. ## Request Body (required) `Content-Type: application/json` - `reason` (string): Why you are stopping it, up to 5,000 characters. Recorded with the stop, the refund and each withdrawn invitation. ## Responses ### 200: The opportunity, now `stopped`. - `id` (string) **(required)** - `title` (string) **(required)** - `internal_title` (string, nullable) **(required)** - `description` (string, nullable) **(required)** - `status` (string) **(required)**: one of: `draft`, `pre_recruiting`, `active`, `fulfilled`, `paused`, `stopped`, `completed`, `expired`, `cancelled`, `failed`: Decides which calls are allowed: - `draft`: edit, launch or delete it. An opportunity Terac is still preparing from a quote reads `draft` too. - `pre_recruiting`: taking applicants ahead of a launch set up in the dashboard. Stop only. - `active`: pause or stop. - `paused`: resume or stop. - `fulfilled`: target reached; settles to `completed`. - `completed`, `stopped`, `expired`, `cancelled`: ended. - `failed`: its launch never finished. Create a new opportunity. - `num_participants` (integer) **(required)** - `estimated_duration_minutes` (integer) **(required)** - `filters` (array) **(required)** - `screening_questions` (array) **(required)** - `key` (string) **(required)**: Identifier for this question, yours or assigned. Comes back as `screening_answers[].key`, so answers join to their question. Opaque. - `text` (string) **(required)** - `question_rich_text` (string): Markdown version of the prompt, when the author wrote one. `text` stays the plaintext source of truth. - `pick` (string) **(required)**: one of: `one`, `any`, `boolean`, `text`, `grid` - `answers` (array) - `text` (string) **(required)** - `qualify_logic` (string) **(required)**: one of: `may`, `must`, `must_one_of`, `reject`, `review`: The disposition that is actually stored, which is not always the one that was sent. On a single-select (pick "one", which is also how a yes/no question reads back) every qualifying answer reads back as "must", because "may", "must" and "must_one_of" collapse to one disposition where only one answer can be picked. So a "may" answer reading back as "must" is the stored truth, not a mistranslation - rewriting it to "may" will not restore a filter. Only "reject" and "review" screen or flag on a single-select; switch the question to pick "any" if you need per-answer dispositions. - `allow_free_text` (boolean): Selecting this answer reveals a write-in box ("Other, please specify"). - `grid` (object) - `pick` (string): one of: `one`, `any` - `rows` (array) **(required)** - `key` (string) - `text` (string) **(required)** - `columns` (array) **(required)** - `value` (string) - `text` (string) **(required)** - `cell_actions` (array) - `row` (string) **(required)** - `column` (string) **(required)** - `qualify_logic` (string) **(required)**: one of: `may`, `must`, `must_one_of`, `reject`, `review` - `min_qualifying` (integer) - `display_condition` (object) - `conditions` (array) **(required)** - `screening_question` (string) **(required)**: The key of an EARLIER question this one depends on - `answer` (string) **(required)**: An answer of that earlier question. For a grid source use "Row: Column". - `operator` (string) **(required)**: one of: `eq`, `ne`: "eq" = answered that value, "ne" = did not. - `join` (string) **(required)**: one of: `and`, `or` - `conditional_rules` (array) - `conditions` (array) **(required)** - `screening_question` (string) - `answer` (string) **(required)** - `operator` (string) **(required)**: one of: `eq`, `ne` - `join` (string) **(required)**: one of: `and`, `or` - `outcome` (string) **(required)**: one of: `reject`, `review` - `skip_rules` (array): Branching authored here or in the dashboard, in the same shape create accepts, so it can be sent straight back on an update. Omitted when the question has none - an absent field means no branching, never that branching was dropped on read. - `conditions` (array) **(required)** - `screening_question` (string) **(required)**: The key of the question this rule lives on. A rule tests its OWN answers; use display_condition to gate a question on an earlier one. - `answer` (string) **(required)**: One of that question's answers (its text; "Yes"/"No" for pick: "boolean"). - `operator` (string) **(required)**: one of: `eq`, `ne`: "eq" = answered that value, "ne" = did not. - `join` (string) **(required)**: one of: `and`, `or` - `target_question` (string, nullable) **(required)**: Key of the question to jump to when the conditions hold; null ends the screener. Must be a LATER question - the screener only walks forward. - `allow_paste` (boolean) - `quotas` (array) **(required)** - `screening_question` (string) **(required)**: The key of the screening question this quota applies to (must match a screening_questions[].key) - `targets` (array) **(required)**: Per-answer target counts for this question - `value` (string) **(required)**: The answer option text (matches a screening answer's text) this quota targets - `count` (integer) **(required)**: How many approved submissions to allow/require for this answer - `type` (string): one of: `exact`, `minimum`, `maximum`: How the count is enforced: "minimum" = at least this many (a floor; recruiting prioritizes the answer while it is short); "maximum" = at most this many (a cap; further applicants who pick this answer are screened out once it is full); "exact" = both. Omit for "minimum". - `cross_quotas` (array) **(required)** - `label` (string) **(required)**: Human label for this cell, e.g. "Brazil x Agency". - `conditions` (array) **(required)**: The conditions, one per dimension: one for a simple per-answer quota, two+ to interlock a cross-tab across distinct questions. - `screening_question` (string) **(required)**: Key of a screening_questions[].key this condition tests (one dimension of the cross-tab). - `answer` (string) **(required)**: An answer of that question, by its label. For a grid source use "Row: Column". - `operator` (string) **(required)**: one of: `eq`, `ne`: "eq" = selected that answer, "ne" = did not. - `join` (string) **(required)**: one of: `and`, `or` - `target` (integer) **(required)**: Target participant count for this cell. - `quota_type` (string) **(required)**: one of: `exact`, `minimum`, `maximum`: "exact" = hit exactly, "minimum" = at least, "maximum" = no more than. - `dimension` (string): Cells sharing a dimension are summed as one interlocked cross-tab quota; use one token per cross-tab. - `tasks` (array) **(required)** - `sequence` (integer) **(required)** - `task_type` (string) **(required)**: Any stored task type, so wider than the set you can create: interview, survey, file_upload, activity, scheduled_interview, agreement, join_slack, slack_handoff, e_signature. Only interview, file_upload and activity are accepted on create. - `review_type` (string) **(required)** - `task_url` (string, nullable) **(required)**: null when the task type carries no URL. - `participant_url_template` (string): task_url with the tracking params Terac appends per participant (submissionId, teracSubmissionId, taskId), shown as {submissionId}/{taskId} macros. Omitted when there is no task_url. - `title` (string) - `description` (string) - `duration_minutes` (integer) **(required)** - `available_after_sequence` (integer): This task unlocks only after that task is approved. - `available_after_delay_minutes` (integer): Wait this long after the gating task before unlocking. - `provider` (string): Scheduled interview only: the booking provider. - `calendar_owner` (string): Scheduled interview only: whose calendar is booked (researcher or expert). - `event_type_url` (string): Scheduled interview only: the booking link. - `instructions` (string): File upload only: what to submit. - `accepted_mime_types` (array): "file_upload" only, and absent on every other task type. RESOLVED rather than echoed: a task created without this reads back with the default actually enforced, not an empty field. - `min_files` (integer): "file_upload" only, and absent on every other task type. Resolved to the enforced default when the task did not set one. - `max_files` (integer): "file_upload" only, and absent on every other task type. Resolved to the enforced default when the task did not set one. - `max_file_size_bytes` (integer): "file_upload" only, and absent on every other task type. Resolved to the enforced default when the task did not set one. - `pricing` (object, nullable) **(required)** - `cost_per_participant_cents` (integer) **(required)** - `total_cost_cents` (integer) **(required)** - `currency` (string) **(required)**: one of: `usd` - `funding` (object) - `affordable` (boolean) **(required)**: Covers the cost, overdraft included. False blocks nothing. - `draws_on_credit` (boolean) **(required)**: Affordable but goes negative, invoiced later. Say so. - `balance_cents` (integer) **(required)**: Balance now. - `shortfall_cents` (integer) **(required)**: Gap after overdraft, 0 when affordable. - `finance_url` (string, nullable) **(required)**: Where billing adds credit. - `device_types` (array) **(required)** - `business_type` (string, nullable) **(required)** - `candidate_profile` (string, nullable) **(required)**: Free-text profile of the ideal candidate, when set. - `required_accounts` (array) **(required)**: Accounts an expert must have linked to apply (e.g. linkedin). - `video_required` (string) **(required)**: Whether the expert's camera is needed: none, requested, required. - `screen_share_required` (string) **(required)**: Whether a screen share is needed: none, requested, required. - `customer_screening_review` (string) **(required)**: auto_invite = qualified applicants are invited automatically; manual_review = you pick who to invite. - `pay_frequency` (string) **(required)**: How the incentive is framed: one_time, hourly, or per_task. - `screening_option_randomization` (string) **(required)**: Whether screening answer options are shown in random order: the string "enabled" or "disabled", not a boolean. - `forwarding_email_enabled` (string) **(required)**: Whether each submission gets a Terac forwarding email address: the string "enabled" or "disabled", not a boolean. - `max_responses_per_expert` (integer, nullable) **(required)** - `feasibility_request_id` (string, nullable) **(required)** - `project_id` (string) **(required)** - `created_at` (string) **(required)** - `updated_at` (string) **(required)** - `ending_at` (string, nullable) **(required)**: Target completion date for recruitment. - `launched_at` (string, nullable) **(required)** - `links` (object) **(required)** - `self` (string) **(required)** - `launch` (string, nullable) **(required)** - `submissions` (string) **(required)** - `dashboard` (object) **(required)**: Pages a person opens in a browser. `self`, `launch` and `submissions` above are API paths for you, not for them - never give a customer one of those. - `study` (string, nullable) **(required)**: The study's own page. It routes itself by status: an editable draft opens the builder, anything else opens submissions. Never null. Give this when you do not know which tab the customer needs; when `draft_editor` is set, prefer that instead, since it opens the same builder the customer would review in. Null if Terac cannot resolve the organization or project this link belongs to. - `draft_editor` (string, nullable) **(required)**: Opens this draft in the study builder so a human can review the whole setup (targeting, screener, tasks, price) before anything launches. Set exactly when `submissions` here is null, never both. Give this to the customer after you build a draft, instead of launching. Null if Terac cannot resolve the organization or project this link belongs to. - `submissions` (string, nullable) **(required)**: Who applied, who is in progress, who is awaiting review. Set exactly when `draft_editor` here is null. Do not read that as the opportunity being live: the `status` field reports several settled states (cancelled, expired, failed) as `draft`, so read status from `status` and treat this only as a link. Give this when the customer asks about progress or has submissions to review. Null if Terac cannot resolve the organization or project this link belongs to. - `recruitment` (string, nullable) **(required)**: The screener questions and quotas every applicant is filtered against. Set exactly when `draft_editor` here is null. Give this when the customer wants to check who is being screened in or out. Null if Terac cannot resolve the organization or project this link belongs to. - `task` (string, nullable) **(required)**: What a participant is actually asked to do - task type, duration, and task link. Set exactly when `draft_editor` here is null. Give this when the customer asks what the participants see. Null if Terac cannot resolve the organization or project this link belongs to. - `settings` (string, nullable) **(required)**: The study's own settings: recruitment window, participant count, and the controls to stop or delete it. Set exactly when `draft_editor` here is null. Give this when the customer wants to change or end a running study rather than look at its results. Pause and resume are not here - they are on the opportunities list. Null if Terac cannot resolve the organization or project this link belongs to. - `submission_stats` (object) - `total` (integer) **(required)** - `in_progress` (integer) **(required)** - `awaiting_review` (integer) **(required)** - `approved` (integer) **(required)** - `rejected` (integer) **(required)** - `quota_progress` (array) - `screening_question` (string) **(required)** - `targets` (array) **(required)** - `value` (string) **(required)** - `count` (integer) **(required)** - `current` (integer) **(required)** - `type` (string) **(required)**: How the count is enforced: exact, minimum or maximum. Tells you whether "current" nearing "count" means a cap is about to close or a floor is about to be met. - `cross_quota_progress` (array): Fill progress for each cross-question quota cell (live studies). - `label` (string) **(required)**: Human label for this cell, e.g. "Brazil x Agency". - `conditions` (array) **(required)**: The conditions, one per dimension: one for a simple per-answer quota, two+ to interlock a cross-tab across distinct questions. - `screening_question` (string) **(required)**: Key of a screening_questions[].key this condition tests (one dimension of the cross-tab). - `answer` (string) **(required)**: An answer of that question, by its label. For a grid source use "Row: Column". - `operator` (string) **(required)**: one of: `eq`, `ne`: "eq" = selected that answer, "ne" = did not. - `join` (string) **(required)**: one of: `and`, `or` - `target` (integer) **(required)**: Target participant count for this cell. - `quota_type` (string) **(required)**: one of: `exact`, `minimum`, `maximum`: "exact" = hit exactly, "minimum" = at least, "maximum" = no more than. - `dimension` (string): Cells sharing a dimension are summed as one interlocked cross-tab quota; use one token per cross-tab. - `current` (integer) **(required)**: Approved participants matching this cell so far. - `screening_stats` (array): Per-question screen-out rates and answer breakdown (live studies). - `screening_question` (string) **(required)**: The `screening_questions[].key`. - `graded` (integer) **(required)**: Screeners GRADED here, not answers given: a branching screener stops at the first failure. - `rejected` (integer) **(required)**: Rejected by this question. - `rejection_rate` (number) **(required)**: `rejected` / `graded`, 0 to 1. - `answers` (array) **(required)**: How often each answer was chosen, for spotting a screener that is too narrow. - `text` (string) **(required)**: The answer as written on the question, not its stored value. - `selected` (integer) **(required)** - `share` (number) **(required)**: `selected` / `graded`, between 0 and 1. ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # List submissions for an opportunity (https://terac.com/docs/developers/reference/listSubmissions) `GET https://terac.com/api/external/v2/opportunities/{opportunityId}/submissions` Lists an opportunity's submissions, newest first, each with its status and screening answers. Filter to one `status`; the `awaiting_review` ones wait on you to approve or reject them. Applicants still in the screener, or waiting on your invite decision, are not listed; the latter are in `GET /opportunities/{opportunityId}/applicants`. Returns up to `limit` per page; while `pagination.has_more` is true, pass `pagination.next_cursor` back as `cursor` for the next page. For totals, read `submission_stats` on the opportunity rather than counting pages. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Path Parameters - `opportunityId` (string) **(required)**: ID of the opportunity, as returned by `POST /opportunities` or `GET /opportunities`. ## Query Parameters - `limit` (integer): Most items to return in one page, from 1 to 100. Defaults to 25. - `cursor` (string): `pagination.next_cursor` from the previous page, to fetch the page after it. Omit it for the first page. - `status` (string: one of: `screen_passed`, `screened_out`, `in_progress`, `awaiting_review`, `approved`, `rejected`, `abandoned`): "screened_out" never reached the work, "rejected" was not accepted or was taken off a project, "abandoned" stopped part-way. ## Responses ### 200: One page of submissions, with the dashboard page where they are reviewed. - `data` (array) **(required)** - `id` (string) **(required)** - `opportunity_id` (string) **(required)** - `status` (string) **(required)**: one of: `screen_passed`, `screened_out`, `in_progress`, `awaiting_review`, `approved`, `rejected`, `abandoned`: "screened_out" never reached the work, "rejected" was not accepted or was taken off a project, "abandoned" stopped part-way. - `participant_id` (string) **(required)** - `created_at` (string) **(required)** - `updated_at` (string) **(required)** - `participant_name` (string, nullable) **(required)**: The participant's name. Null when your organization hides participant identity in Terac, or when we hold none. - `participant_linkedin_url` (string, nullable) **(required)**: The participant's LinkedIn profile URL. Null when your organization hides participant identity in Terac, or when they have none on file. - `participant_background` (object, nullable) **(required)**: The participant's work and education history, with names and contact details removed. Null when we hold no history for them. - `experiences` (array) **(required)**: Their work history. - `company` (string) **(required)** - `role` (string, nullable) **(required)** - `started_at` (string, nullable) **(required)** - `ended_at` (string, nullable) **(required)** - `description` (string, nullable) **(required)**: What they did there, with contact details removed. - `educations` (array) **(required)**: Their education history. - `school` (string) **(required)** - `degree` (string, nullable) **(required)** - `major` (string, nullable) **(required)** - `screening_outcome` (string, nullable) **(required)**: one of: `passed`, `failed`, `review`, `null`: The screener's overall verdict; "review" means qualified but flagged for a human. null while it is unfinished or absent. - `screening_answers` (array) **(required)**: Every answer given, with each verdict. Empty when the screener recorded nothing. - `key` (string): The `screening_questions[].key` this answers. Absent only on legacy panel-backed screenings, which stored answers per insight. - `question` (string) **(required)**: The prompt as asked, in plain text. - `answer` (array) **(required)** - `outcome` (string, nullable) **(required)**: one of: `qualify`, `reject`, `not_important`, `review`, `null`: The verdict this answer received; do not re-derive it from `qualify_logic`. null if a branching screener never graded it. - `pagination` (object) **(required)** - `next_cursor` (string, nullable) **(required)** - `has_more` (boolean) **(required)** - `dashboard_url` (string, nullable) **(required)**: Who applied, who is in progress, who is awaiting review. Set exactly when `draft_editor` here is null. Do not read that as the opportunity being live: the `status` field reports several settled states (cancelled, expired, failed) as `draft`, so read status from `status` and treat this only as a link. Give this when the customer asks about progress or has submissions to review. Null if Terac cannot resolve the organization or project this link belongs to. ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # List applicants awaiting your invite decision (https://terac.com/docs/developers/reference/listApplicants) `GET https://terac.com/api/external/v2/opportunities/{opportunityId}/applicants` Applicants who passed screening and are waiting on your decision to invite them. They are deliberately absent from `GET /opportunities/{opportunityId}/submissions`: an applicant here has not been invited yet, so they hold no submission status, and `GET /submissions/{id}` returns 404 for them. This collection is where they exist until you decide. Act on one with `POST /submissions/{submissionId}/invite` or `/decline`. Inviting makes them `screen_passed` and starts their tasks; declining makes them `screened_out`. Either way they leave this collection and appear in the submissions listing from then on. An opportunity on `manual_review` routes every qualified applicant here. One on `auto_invite` still routes an individual applicant here when a screening answer you marked `review` flags them, so poll this even when you did not opt into reviewing everyone. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Path Parameters - `opportunityId` (string) **(required)**: ID of the opportunity, as returned by `POST /opportunities` or `GET /opportunities`. ## Query Parameters - `limit` (integer): Most items to return in one page, from 1 to 100. Defaults to 25. - `cursor` (string): `pagination.next_cursor` from the previous page, to fetch the page after it. Omit it for the first page. ## Responses ### 200: One page of applicants awaiting your decision, each with their screening answers. - `data` (array) **(required)** - `id` (string) **(required)** - `opportunity_id` (string) **(required)** - `participant_id` (string) **(required)** - `screening_outcome` (string, nullable) **(required)**: one of: `passed`, `failed`, `review`, `null`: The screener's overall verdict; "review" means qualified but flagged for a human. null while it is unfinished or absent. - `screening_answers` (array) **(required)**: Every answer given, with each verdict. Empty when the screener recorded nothing. - `key` (string): The `screening_questions[].key` this answers. Absent only on legacy panel-backed screenings, which stored answers per insight. - `question` (string) **(required)**: The prompt as asked, in plain text. - `answer` (array) **(required)** - `outcome` (string, nullable) **(required)**: one of: `qualify`, `reject`, `not_important`, `review`, `null`: The verdict this answer received; do not re-derive it from `qualify_logic`. null if a branching screener never graded it. - `created_at` (string) **(required)** - `updated_at` (string) **(required)** - `pagination` (object) **(required)** - `next_cursor` (string, nullable) **(required)** - `has_more` (boolean) **(required)** - `dashboard_url` (string, nullable) **(required)**: Who applied, who is in progress, who is awaiting review. Set exactly when `draft_editor` here is null. Do not read that as the opportunity being live: the `status` field reports several settled states (cancelled, expired, failed) as `draft`, so read status from `status` and treat this only as a link. Give this when the customer asks about progress or has submissions to review. Null if Terac cannot resolve the organization or project this link belongs to. ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # Invite an applicant awaiting your decision (https://terac.com/docs/developers/reference/inviteApplicant) `POST https://terac.com/api/external/v2/submissions/{submissionId}/invite` Invites an applicant sitting in your applicant-review queue, which materializes their tasks and notifies them. The submission becomes `screen_passed`. Returns 409 if the applicant is not awaiting your decision, which includes one you have already decided on. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Path Parameters - `submissionId` (string) **(required)**: ID of an applicant awaiting your invite decision: the `id` on their applicant row. ## Request Body (required) `Content-Type: application/json` - `reason` (string): Why, recorded on the submission's history for your own team. Optional. ## Responses ### 200: The applicant's submission, now `screen_passed`. - `id` (string) **(required)** - `status` (string) **(required)**: one of: `screen_passed`, `screened_out`, `in_progress`, `awaiting_review`, `approved`, `rejected`, `abandoned`: Where the applicant landed: `screen_passed` when invited, `screened_out` when declined. - `dashboard_url` (string, nullable) **(required)**: Who applied, who is in progress, who is awaiting review. Set exactly when `draft_editor` here is null. Do not read that as the opportunity being live: the `status` field reports several settled states (cancelled, expired, failed) as `draft`, so read status from `status` and treat this only as a link. Give this when the customer asks about progress or has submissions to review. Null if Terac cannot resolve the organization or project this link belongs to. ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # Decline an applicant awaiting your decision (https://terac.com/docs/developers/reference/declineApplicant) `POST https://terac.com/api/external/v2/submissions/{submissionId}/decline` Declines an applicant sitting in your applicant-review queue. The submission becomes `screened_out` and they are not invited. Returns 409 if the applicant is not awaiting your decision, which includes one you have already decided on. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Path Parameters - `submissionId` (string) **(required)**: ID of an applicant awaiting your invite decision: the `id` on their applicant row. ## Request Body (required) `Content-Type: application/json` - `reason` (string): Why, recorded on the submission's history for your own team. Optional. ## Responses ### 200: The applicant's submission, now `screened_out`. - `id` (string) **(required)** - `status` (string) **(required)**: one of: `screen_passed`, `screened_out`, `in_progress`, `awaiting_review`, `approved`, `rejected`, `abandoned`: Where the applicant landed: `screen_passed` when invited, `screened_out` when declined. - `dashboard_url` (string, nullable) **(required)**: Who applied, who is in progress, who is awaiting review. Set exactly when `draft_editor` here is null. Do not read that as the opportunity being live: the `status` field reports several settled states (cancelled, expired, failed) as `draft`, so read status from `status` and treat this only as a link. Give this when the customer asks about progress or has submissions to review. Null if Terac cannot resolve the organization or project this link belongs to. ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # Get submission details (https://terac.com/docs/developers/reference/getSubmission) `GET https://terac.com/api/external/v2/submissions/{submissionId}` Returns one submission with its status, screening answers and per-task progress. An applicant still in the screener, or waiting on your invite decision, returns 404 here; the latter are listed in `GET /opportunities/{opportunityId}/applicants`. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Path Parameters - `submissionId` (string) **(required)**: ID of the submission, as listed by `GET /opportunities/{opportunityId}/submissions`. ## Responses ### 200: The submission, with its screening answers and per-task progress. - `id` (string) **(required)** - `opportunity_id` (string) **(required)** - `status` (string) **(required)**: one of: `screen_passed`, `screened_out`, `in_progress`, `awaiting_review`, `approved`, `rejected`, `abandoned`: "screened_out" never reached the work, "rejected" was not accepted or was taken off a project, "abandoned" stopped part-way. - `participant_id` (string) **(required)** - `created_at` (string) **(required)** - `updated_at` (string) **(required)** - `participant_name` (string, nullable) **(required)**: The participant's name. Null when your organization hides participant identity in Terac, or when we hold none. - `participant_linkedin_url` (string, nullable) **(required)**: The participant's LinkedIn profile URL. Null when your organization hides participant identity in Terac, or when they have none on file. - `participant_background` (object, nullable) **(required)**: The participant's work and education history, with names and contact details removed. Null when we hold no history for them. - `experiences` (array) **(required)**: Their work history. - `company` (string) **(required)** - `role` (string, nullable) **(required)** - `started_at` (string, nullable) **(required)** - `ended_at` (string, nullable) **(required)** - `description` (string, nullable) **(required)**: What they did there, with contact details removed. - `educations` (array) **(required)**: Their education history. - `school` (string) **(required)** - `degree` (string, nullable) **(required)** - `major` (string, nullable) **(required)** - `screening_outcome` (string, nullable) **(required)**: one of: `passed`, `failed`, `review`, `null`: The screener's overall verdict; "review" means qualified but flagged for a human. null while it is unfinished or absent. - `screening_answers` (array) **(required)**: Every answer given, with each verdict. Empty when the screener recorded nothing. - `key` (string): The `screening_questions[].key` this answers. Absent only on legacy panel-backed screenings, which stored answers per insight. - `question` (string) **(required)**: The prompt as asked, in plain text. - `answer` (array) **(required)** - `outcome` (string, nullable) **(required)**: one of: `qualify`, `reject`, `not_important`, `review`, `null`: The verdict this answer received; do not re-derive it from `qualify_logic`. null if a branching screener never graded it. - `dashboard_url` (string, nullable) **(required)**: Who applied, who is in progress, who is awaiting review. Set exactly when `draft_editor` here is null. Do not read that as the opportunity being live: the `status` field reports several settled states (cancelled, expired, failed) as `draft`, so read status from `status` and treat this only as a link. Give this when the customer asks about progress or has submissions to review. Null if Terac cannot resolve the organization or project this link belongs to. - `tasks` (array) **(required)** - `sequence` (integer) **(required)** - `task_type` (string) **(required)** - `status` (string) **(required)** ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # Approve a submission (https://terac.com/docs/developers/reference/approveSubmission) `POST https://terac.com/api/external/v2/submissions/{submissionId}/approve` Approves a submission that is `awaiting_review`, accepting the reviewed work. The expert is paid when the submission lands in `approved`, once all of its work is accepted. It stays `in_progress` while tasks remain, and on an hourly or per-task opportunity, where approval puts the expert on the project and they are paid through payout runs instead. The response carries the status it lands in. A submission in any other status returns 409. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Path Parameters - `submissionId` (string) **(required)**: ID of the submission, as listed by `GET /opportunities/{opportunityId}/submissions`. ## Responses ### 200: The submission's status after the approval. - `id` (string) **(required)** - `status` (string) **(required)**: one of: `screen_passed`, `screened_out`, `in_progress`, `awaiting_review`, `approved`, `rejected`, `abandoned`: "screened_out" never reached the work, "rejected" was not accepted or was taken off a project, "abandoned" stopped part-way. - `dashboard_url` (string, nullable) **(required)**: Who applied, who is in progress, who is awaiting review. Set exactly when `draft_editor` here is null. Do not read that as the opportunity being live: the `status` field reports several settled states (cancelled, expired, failed) as `draft`, so read status from `status` and treat this only as a link. Give this when the customer asks about progress or has submissions to review. Null if Terac cannot resolve the organization or project this link belongs to. ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # Reject a submission (https://terac.com/docs/developers/reference/rejectSubmission) `POST https://terac.com/api/external/v2/submissions/{submissionId}/reject` Rejects a submission that is `awaiting_review`, so the expert is not paid for that work and the submission becomes `rejected`. `rejection_category` and `rejection_reason` are optional, but they are the only record of why the work went unpaid. A submission in any other status returns 409. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Path Parameters - `submissionId` (string) **(required)**: ID of the submission to reject ## Request Body (required) `Content-Type: application/json` - `rejection_category` (string): one of: `low_quality`, `failed_instructions`, `incomplete`, `suspicious_patterns`, `other`: Optional category for the rejection. Strongly recommended so non-payment has an audit trail. - `rejection_reason` (string): Optional free-text reason, ideally referencing the success/failure criteria the submission missed. Strongly recommended; improves grading and documents why the expert was not paid. ## Responses ### 200: The submission, now `rejected`. - `id` (string) **(required)** - `status` (string) **(required)**: one of: `screen_passed`, `screened_out`, `in_progress`, `awaiting_review`, `approved`, `rejected`, `abandoned`: "screened_out" never reached the work, "rejected" was not accepted or was taken off a project, "abandoned" stopped part-way. - `dashboard_url` (string, nullable) **(required)**: Who applied, who is in progress, who is awaiting review. Set exactly when `draft_editor` here is null. Do not read that as the opportunity being live: the `status` field reports several settled states (cancelled, expired, failed) as `draft`, so read status from `status` and treat this only as a link. Give this when the customer asks about progress or has submissions to review. Null if Terac cannot resolve the organization or project this link belongs to. ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # Create a test submission in a sandbox (https://terac.com/docs/developers/reference/createTestSubmission) `POST https://terac.com/api/external/v2/opportunities/{opportunityId}/test-submissions` Creates a test submission in a sandbox organization, so your webhook endpoint receives the events a real participant would trigger. Terac adds a test participant and moves it through the opportunity a few seconds at a time: it answers the screener, starts the task and finishes it. Each step sends the same `submission.status.change` webhook a real participant's step sends, signed and retried the same way. Nobody is contacted and nobody is paid. Only a subscription created with a key from the same sandbox organization receives these webhooks. The participant stops wherever the next move is yours: inviting or declining an applicant, approving or rejecting the work, or your own completion redirect when `task_completion` is `redirect`. Your call sends the next webhook. After you invite an applicant, the participant carries on with the task. The test submission shows in the sandbox dashboard under the name "Test Participant" and counts in the opportunity's numbers. It does not count toward a quota, and it cannot be removed. Returns 403 outside a sandbox organization, and 409 when the opportunity is not active. Returns 422 when Terac cannot create the test submission you asked for on this opportunity, and the `message` says why: for example `failed` with no screener, `passed` with screening questions that no answer passes, or `redirect` when the first task has no `task_url`. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Path Parameters - `opportunityId` (string) **(required)**: ID of the opportunity, as returned by `POST /opportunities` or `GET /opportunities`. ## Request Body (required) `Content-Type: application/json` - `screening_outcome` (string): one of: `passed`, `failed`, `filtered_out`: How screening goes for the test participant. "passed" (the default) gets through the screener. "failed" answers the screener and is screened out. "filtered_out" is screened out on its profile before the screener, and is allowed even when the opportunity has no filters. - `task_completion` (string): one of: `automatic`, `redirect`: Who finishes the task, for a test participant that reaches it. "automatic" (the default): Terac finishes it. "redirect": Terac starts the task and stops, and you finish it by opening `task_url` and letting your own completion redirect bring the participant back to Terac. ## Responses ### 200: The test submission, which a test participant is now taking through the opportunity. - `id` (string) **(required)**: ID of the test submission. `GET /submissions/{submissionId}` returns 404 while the test participant is still in screening. - `opportunity_id` (string) **(required)** - `task_url` (string, nullable) **(required)**: The first task's link with this submission's IDs filled in, for you to open once the submission is `in_progress`. Set when `task_completion` is "redirect" and `screening_outcome` is "passed"; null otherwise. - `dashboard_url` (string, nullable) **(required)**: Who applied, who is in progress, who is awaiting review. Set exactly when `draft_editor` here is null. Do not read that as the opportunity being live: the `status` field reports several settled states (cancelled, expired, failed) as `draft`, so read status from `status` and treat this only as a link. Give this when the customer asks about progress or has submissions to review. Null if Terac cannot resolve the organization or project this link belongs to. ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # List feasibility requests (https://terac.com/docs/developers/reference/listFeasibilityRequests) `GET https://terac.com/api/external/v2/feasibility/requests` List your organization's feasibility requests, newest first. Optionally filter by status (RECEIVED / RESPONDED / WON / LOST / NOT_PURSUED) and page through results with limit and offset. Each request includes its costPerParticipant (the CPI), which is null until the request has been priced (status RESPONDED). ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Query Parameters - `status` (string: one of: `RECEIVED`, `RESPONDED`, `WON`, `LOST`, `NOT_PURSUED`): Return only requests in this status. `GET /feasibility/requests/{requestId}` says what each one means. - `limit` (integer): Most requests to return, from 1 to 100. Defaults to 50. - `offset` (integer): How many requests to skip before this page, newest first. Defaults to 0. ## Responses ### 200: One page of feasibility requests, newest first. - `count` (integer) **(required)**: How many requests this page holds, not a total. Page with offset until a page comes back shorter than the limit. - `requests` (array) **(required)** - `id` (string) **(required)** - `status` (string) **(required)**: one of: `RECEIVED`, `RESPONDED`, `WON`, `LOST`, `NOT_PURSUED` - `source` (string) **(required)** - `taskDescription` (string) **(required)** - `panelDescription` (string) **(required)** - `submissionCount` (integer, nullable) **(required)** - `timelineHours` (integer, nullable) **(required)** - `payRateFrequency` (string, nullable) **(required)**: one of: `ONE_TIME`, `HOURLY`, `PER_TASK`, `null`: What costPerParticipant is denominated in once priced: ONE_TIME (per complete), HOURLY, or PER_TASK. Null until the brief states it. - `loiMinutes` (integer, nullable) **(required)**: Minutes one participant spends on the task. Distinct from timelineHours, which is Terac's turnaround. - `costPerParticipant` (string, nullable) **(required)** - `respondedAt` (string, nullable) **(required)** - `createdAt` (string) **(required)** - `dashboard_url` (string, nullable) **(required)**: The organization's feasibility requests and the incentives Terac quoted back. There is no page for a single request, so this list is the only feasibility link; give it after you request feasibility or when the customer asks where a quote went. Null if Terac cannot resolve the organization or project this link belongs to. ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # Submit a feasibility request (https://terac.com/docs/developers/reference/requestFeasibility) `POST https://terac.com/api/external/v2/feasibility/requests` Submit a feasibility request: can Terac source a panel for this task, and at what CPI (cost per participant). This is an async request-reply. The response comes back immediately with status RECEIVED and no CPI (costPerParticipant is null). Terac prices it out of band, either automatically within seconds or by routing it to a person, so poll GET /feasibility/requests/{requestId} rather than assuming a turnaround. Only what you send here is priced: taskDescription, panelDescription, and the count and timeline. The filters, screening questions and tasks on the opportunity you build later are not part of the brief that was priced, so a confirmed CPI applies to the scope described here and not to a materially different one. Once RESPONDED, create the opportunity with POST /opportunities and pass feasibility_request_id to price it from that confirmed CPI (Terac skips the autonomous estimate), then launch it. The request stays RESPONDED until that study launches, and is closed as won at launch rather than at create. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Request Body (required) `Content-Type: application/json` - `taskDescription` (string) **(required)**: What participants will do (interview, survey, usability test, ...) and for how long, up to 10,000 characters. Half of the brief that gets priced. - `panelDescription` (string) **(required)**: Who the participants must be: role, seniority, industry, market, and anything that rules someone out, up to 10,000 characters. The other half of the priced brief. - `submissionCount` (integer): How many completed participants you need, if known. - `timelineHours` (integer): The turnaround you are asking Terac for, in hours. - `payRateFrequency` (string): one of: `ONE_TIME`, `HOURLY`, `PER_TASK`: How the work is paid: ONE_TIME (per complete), HOURLY, or PER_TASK. Omit when the brief does not say; do not guess. - `loiMinutes` (integer): Minutes one participant spends on the task. Not the turnaround. - `requestorEmail` (string): Who Terac should contact about this request. Defaults to the email of the API key's owner. ## Responses ### 200: The new request, `RECEIVED` and not priced yet, with the dashboard page where its price will land. - `id` (string) **(required)** - `status` (string) **(required)**: one of: `RECEIVED`, `RESPONDED`, `WON`, `LOST`, `NOT_PURSUED` - `source` (string) **(required)** - `taskDescription` (string) **(required)** - `panelDescription` (string) **(required)** - `submissionCount` (integer, nullable) **(required)** - `timelineHours` (integer, nullable) **(required)** - `payRateFrequency` (string, nullable) **(required)**: one of: `ONE_TIME`, `HOURLY`, `PER_TASK`, `null`: What costPerParticipant is denominated in once priced: ONE_TIME (per complete), HOURLY, or PER_TASK. Null until the brief states it. - `loiMinutes` (integer, nullable) **(required)**: Minutes one participant spends on the task. Distinct from timelineHours, which is Terac's turnaround. - `costPerParticipant` (string, nullable) **(required)** - `respondedAt` (string, nullable) **(required)** - `createdAt` (string) **(required)** - `dashboard_url` (string, nullable) **(required)**: The organization's feasibility requests and the incentives Terac quoted back. There is no page for a single request, so this list is the only feasibility link; give it after you request feasibility or when the customer asks where a quote went. Null if Terac cannot resolve the organization or project this link belongs to. ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # Get a feasibility request (https://terac.com/docs/developers/reference/getFeasibilityRequest) `GET https://terac.com/api/external/v2/feasibility/requests/{requestId}` Retrieve one of your feasibility requests by id. Use this to poll for the CPI after submitting. Status decides whether there is a price to read: - RECEIVED: not priced yet, costPerParticipant is null. - RESPONDED: priced. costPerParticipant is the confirmed all-in CPI per participant. - WON: already used by a study that launched. - LOST or NOT_PURSUED: closed without a usable price. Both are terminal, so stop polling. No reason is returned on this endpoint. Once RESPONDED, create the opportunity with POST /opportunities passing feasibility_request_id to price it from that confirmed CPI, then launch it. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Path Parameters - `requestId` (string) **(required)**: ID of the feasibility request, as returned by `POST /feasibility/requests`. ## Responses ### 200: The request, with its CPI once it is priced. - `id` (string) **(required)** - `status` (string) **(required)**: one of: `RECEIVED`, `RESPONDED`, `WON`, `LOST`, `NOT_PURSUED` - `source` (string) **(required)** - `taskDescription` (string) **(required)** - `panelDescription` (string) **(required)** - `submissionCount` (integer, nullable) **(required)** - `timelineHours` (integer, nullable) **(required)** - `payRateFrequency` (string, nullable) **(required)**: one of: `ONE_TIME`, `HOURLY`, `PER_TASK`, `null`: What costPerParticipant is denominated in once priced: ONE_TIME (per complete), HOURLY, or PER_TASK. Null until the brief states it. - `loiMinutes` (integer, nullable) **(required)**: Minutes one participant spends on the task. Distinct from timelineHours, which is Terac's turnaround. - `costPerParticipant` (string, nullable) **(required)** - `respondedAt` (string, nullable) **(required)** - `createdAt` (string) **(required)** - `dashboard_url` (string, nullable) **(required)**: The organization's feasibility requests and the incentives Terac quoted back. There is no page for a single request, so this list is the only feasibility link; give it after you request feasibility or when the customer asks where a quote went. Null if Terac cannot resolve the organization or project this link belongs to. ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # Get organization context (https://terac.com/docs/developers/reference/getOrganizationContext) `GET https://terac.com/api/external/v2/organizations/current/context` Returns a markdown briefing on the organization your API key belongs to. It covers identity, balance and where to add credit, dashboard links, projects, opportunity counts, the operating guide for building an opportunity, and any policies the organization set for task and panel descriptions. Identity, balance and dashboard links also come back as structured fields. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Responses ### 200: The organization's briefing as markdown, with its identity, balance and dashboard links as fields. - `markdown` (string) **(required)** - `organizationId` (string) **(required)** - `organizationName` (string) **(required)** - `organizationSlug` (string) **(required)** - `balanceDollars` (number) **(required)** - `dashboard` (object) **(required)**: The organization's pages in the researcher dashboard, as absolute URLs. Never null - this organization's slug is what builds them. The same URLs appear under `## Dashboard` in the markdown. - `home` (string) **(required)**: The organization's landing page in the dashboard, with recent activity across every project. Give this when the customer wants somewhere to start rather than a specific study or page. - `opportunities` (string) **(required)**: Every opportunity in the organization, across all projects, filterable by status. Give this when the customer asks where they can see everything they have running, or when they want to pause or resume something - those two controls live on a list row here, not on the study's own pages. - `drafts` (string) **(required)**: The same opportunities list, pre-filtered to drafts. Give this when the customer wants to find the drafts you built without hunting through launched studies. - `feasibility` (string) **(required)**: The organization's feasibility requests and the incentives Terac quoted back. There is no page for a single request, so this list is the only feasibility link; give it after you request feasibility or when the customer asks where a quote went. - `finance` (string) **(required)**: Where the organization checks its balance and adds credit. Give this when the balance will not cover a launch. Requires billing permission; a member without it gets a 404. - `api_keys` (string) **(required)**: Where the organization creates and revokes the API keys that authenticate this connection. Give this when the customer needs a new key or wants to revoke one, never for anything about a study. ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # List subscribable webhook event types (https://terac.com/docs/developers/reference/listWebhookEventTypes) `GET https://terac.com/api/external/v2/hooks/event-types` Lists the event types a webhook subscription can receive, each with a description. Read this rather than hardcoding a list: new event types are added without a breaking change, and appear here first. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Responses ### 200: Every event type a subscription can receive. - `data` (array) **(required)** - `event_type` (string) **(required)**: one of: `submission.status.change`, `submission.approved` - `description` (string) **(required)** ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # List webhook subscriptions (https://terac.com/docs/developers/reference/listWebhookSubscriptions) `GET https://terac.com/api/external/v2/hooks/subscriptions` Lists every webhook subscription in your organization, one per target URL, with its event types and whether it is confirmed and enabled. Not paginated. Signing secrets are left out; read one from `GET /hooks/subscriptions/{subscriptionId}/secret`. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Responses ### 200: Every webhook subscription in the organization. - `data` (array) **(required)** - `id` (string) **(required)** - `target_url` (string) **(required)** - `event_types` (array) **(required)**: Every event type this endpoint receives. PATCH replaces the whole list. - values: `submission.status.change`, `submission.approved` - `is_enabled` (boolean) **(required)** - `confirmed_at` (string, nullable) **(required)**: When this subscription passed the confirmation ping. Null means it receives nothing yet. - `disabled_at` (string, nullable) **(required)** - `disabled_reason` (string, nullable) **(required)**: Set when Terac disabled the subscription rather than you, after sustained delivery failure. - `created_at` (string) **(required)** - `updated_at` (string) **(required)** ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # Create a webhook subscription (https://terac.com/docs/developers/reference/createWebhookSubscription) `POST https://terac.com/api/external/v2/hooks/subscriptions` Creates a webhook subscription for one target URL and returns its signing secret. Created unconfirmed, so it receives nothing until you call the confirm endpoint and Terac's ping to target_url returns 2xx. A URL that already has a subscription returns 409; patch that one's event_types instead. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Request Body (required) `Content-Type: application/json` - `target_url` (string) **(required)**: A public https URL that answers 2xx. Private, loopback, link-local and internal addresses are refused, because confirming makes Terac issue a request to it. One subscription per URL; add event types to the existing one rather than creating a second. Confirm the subscription before it receives anything. - `event_types` (array) **(required)**: Which events this URL receives. Read GET /hooks/event-types/ for the current list. - values: `submission.status.change`, `submission.approved` ## Responses ### 200: The unconfirmed subscription, with its signing secret in full. - `id` (string) **(required)** - `target_url` (string) **(required)** - `event_types` (array) **(required)**: Every event type this endpoint receives. PATCH replaces the whole list. - values: `submission.status.change`, `submission.approved` - `is_enabled` (boolean) **(required)** - `confirmed_at` (string, nullable) **(required)**: When this subscription passed the confirmation ping. Null means it receives nothing yet. - `disabled_at` (string, nullable) **(required)** - `disabled_reason` (string, nullable) **(required)**: Set when Terac disabled the subscription rather than you, after sustained delivery failure. - `created_at` (string) **(required)** - `updated_at` (string) **(required)** - `secret` (string) **(required)**: The signing secret in full, returned here and by the rotate call only. Store it now; you can also read it back from GET /hooks/subscriptions/{id}/secret. ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # Get a webhook subscription (https://terac.com/docs/developers/reference/getWebhookSubscription) `GET https://terac.com/api/external/v2/hooks/subscriptions/{subscriptionId}` Returns one webhook subscription: its target URL, event types, and whether it is confirmed and enabled. disabled_reason is set when Terac disabled it after sustained delivery failure; patch is_enabled to true to turn it back on. The signing secret is left out. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Path Parameters - `subscriptionId` (string) **(required)**: ID of the webhook subscription, as returned when it was created or listed. ## Responses ### 200: The subscription. - `id` (string) **(required)** - `target_url` (string) **(required)** - `event_types` (array) **(required)**: Every event type this endpoint receives. PATCH replaces the whole list. - values: `submission.status.change`, `submission.approved` - `is_enabled` (boolean) **(required)** - `confirmed_at` (string, nullable) **(required)**: When this subscription passed the confirmation ping. Null means it receives nothing yet. - `disabled_at` (string, nullable) **(required)** - `disabled_reason` (string, nullable) **(required)**: Set when Terac disabled the subscription rather than you, after sustained delivery failure. - `created_at` (string) **(required)** - `updated_at` (string) **(required)** ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # Confirm a webhook subscription (https://terac.com/docs/developers/reference/confirmWebhookSubscription) `POST https://terac.com/api/external/v2/hooks/subscriptions/{subscriptionId}` Confirms a webhook subscription by POSTing one signed webhook.ping to its target_url. Answer 2xx and the subscription starts receiving events; any other outcome returns 412 with the reason, and it stays unconfirmed. This is also the way to test a receiver end to end, since it exercises the real signature headers. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Path Parameters - `subscriptionId` (string) **(required)**: ID of the webhook subscription, as returned when it was created or listed. ## Responses ### 200: The subscription, now confirmed. - `id` (string) **(required)** - `target_url` (string) **(required)** - `event_types` (array) **(required)**: Every event type this endpoint receives. PATCH replaces the whole list. - values: `submission.status.change`, `submission.approved` - `is_enabled` (boolean) **(required)** - `confirmed_at` (string, nullable) **(required)**: When this subscription passed the confirmation ping. Null means it receives nothing yet. - `disabled_at` (string, nullable) **(required)** - `disabled_reason` (string, nullable) **(required)**: Set when Terac disabled the subscription rather than you, after sustained delivery failure. - `created_at` (string) **(required)** - `updated_at` (string) **(required)** ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # Update a webhook subscription (https://terac.com/docs/developers/reference/patchWebhookSubscription) `PATCH https://terac.com/api/external/v2/hooks/subscriptions/{subscriptionId}` Updates a webhook subscription's target URL, event types or enabled state, and returns it unchanged when the body sets none. event_types replaces the current list rather than adding to it. Changing target_url clears the confirmation, since the new host has not accepted a ping yet. Set is_enabled true to recover a subscription Terac disabled after sustained failure. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Path Parameters - `subscriptionId` (string) **(required)**: ID of the webhook subscription, as returned when it was created or listed. ## Request Body (required) `Content-Type: application/json` - `target_url` (string): A new public https URL for this subscription. Private, loopback, link-local and internal addresses are refused. Changing it clears the confirmation, so confirm again before it receives events. - `event_types` (array): Replaces the current list rather than adding to it. - values: `submission.status.change`, `submission.approved` - `is_enabled` (boolean): Set true to re-enable a subscription Terac disabled after sustained failure. ## Responses ### 200: The subscription as stored after the update. - `id` (string) **(required)** - `target_url` (string) **(required)** - `event_types` (array) **(required)**: Every event type this endpoint receives. PATCH replaces the whole list. - values: `submission.status.change`, `submission.approved` - `is_enabled` (boolean) **(required)** - `confirmed_at` (string, nullable) **(required)**: When this subscription passed the confirmation ping. Null means it receives nothing yet. - `disabled_at` (string, nullable) **(required)** - `disabled_reason` (string, nullable) **(required)**: Set when Terac disabled the subscription rather than you, after sustained delivery failure. - `created_at` (string) **(required)** - `updated_at` (string) **(required)** ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # Delete a webhook subscription (https://terac.com/docs/developers/reference/deleteWebhookSubscription) `DELETE https://terac.com/api/external/v2/hooks/subscriptions/{subscriptionId}` Deletes a webhook subscription. Terac stops sending to it at once, including retries already queued, and its past deliveries stay readable in `GET /hooks/events`. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Path Parameters - `subscriptionId` (string) **(required)**: ID of the webhook subscription, as returned when it was created or listed. ## Responses ### 200: Confirms the deletion. - `id` (string) **(required)** - `deleted` (boolean) **(required)** ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # Read a subscription's signing secret (https://terac.com/docs/developers/reference/getWebhookSubscriptionSecret) `GET https://terac.com/api/external/v2/hooks/subscriptions/{subscriptionId}/secret` Returns a subscription's current signing secret, for recovering one you no longer have. Rotate instead if you believe it leaked, since reading it does not invalidate anything. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Path Parameters - `subscriptionId` (string) **(required)**: ID of the webhook subscription, as returned when it was created or listed. ## Responses ### 200: The subscription's current signing secret. - `secret` (string) **(required)**: The signing secret in full. Verify a delivery with base64(HMAC-SHA256(secret, X-Terac-Request-Timestamp + raw body)). ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # Rotate a subscription's signing secret (https://terac.com/docs/developers/reference/rotateWebhookSubscriptionSecret) `POST https://terac.com/api/external/v2/hooks/subscriptions/{subscriptionId}/secret` Replaces a subscription's signing secret and returns the new one. Takes effect immediately and with no overlap window: the next attempt of every delivery, including one already queued, is signed with the new secret, so deliveries fail your signature check until your receiver has it. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Path Parameters - `subscriptionId` (string) **(required)**: ID of the webhook subscription, as returned when it was created or listed. ## Responses ### 200: The new signing secret, in effect already. - `secret` (string) **(required)**: The signing secret in full. Verify a delivery with base64(HMAC-SHA256(secret, X-Terac-Request-Timestamp + raw body)). ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # List webhook deliveries (https://terac.com/docs/developers/reference/listWebhookDeliveries) `GET https://terac.com/api/external/v2/hooks/events` Lists webhook deliveries to your subscriptions, newest first, optionally for one subscription_id. One row per delivery, updated in place across retries, so id is the X-Event-ID your endpoint saw and attempt_count is how many tries it took. Confirmation pings are not logged here. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Query Parameters - `limit` (integer): Most items to return in one page, from 1 to 100. Defaults to 25. - `cursor` (string): `pagination.next_cursor` from the previous page, to fetch the page after it. Omit it for the first page. - `subscription_id` (string): Return only deliveries to this subscription ID. ## Responses ### 200: One page of deliveries, newest first. - `data` (array) **(required)** - `id` (string) **(required)**: Also the X-Event-ID header, stable across retries of this delivery. - `subscription_id` (string) **(required)** - `event_type` (string) **(required)** - `resource_id` (string) **(required)** - `status` (string) **(required)**: one of: `pending`, `delivered`, `failed` - `attempt_count` (integer) **(required)** - `response_status` (integer, nullable) **(required)** - `error_message` (string, nullable) **(required)** - `created_at` (string) **(required)** - `last_attempt_at` (string, nullable) **(required)** - `delivered_at` (string, nullable) **(required)** - `pagination` (object) **(required)** - `next_cursor` (string, nullable) **(required)** - `has_more` (boolean) **(required)** ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # Get tracked hours for an opportunity (https://terac.com/docs/developers/reference/getOpportunityHours) `GET https://terac.com/api/external/v2/opportunities/{opportunityId}/hours` Hours each expert worked on this opportunity, one row per person and date. Time spent on your other opportunities, and time clocked in but not spent on work, is not included. The current day is partial and refreshes through the day; `last_updated_at` says when the hours were last refreshed. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Path Parameters - `opportunityId` (string) **(required)**: ID of the opportunity, as returned by `POST /opportunities` or `GET /opportunities`. ## Query Parameters - `from` (string) **(required)**: Start of the window, inclusive: an ISO 8601 UTC timestamp such as `2026-09-08T00:00:00Z`, or a bare `2026-09-08` read as midnight UTC. A person's day counts when the instant it opened in their own tracker timezone falls in the window, so a day is never returned in halves. - `to` (string) **(required)**: End of the window, exclusive, in the same UTC form: a day that opened exactly at `to` belongs to the next window, so consecutive requests neither double count nor skip. At most 92 days after `from`. ## Responses ### 200: Tracked hours per person and date, with totals for the range. - `last_updated_at` (string, nullable) **(required)**: When these hours were last refreshed, regardless of the range asked for. Null when no hours have ever been recorded for this opportunity, which tells an empty `data` apart from a range nobody worked in. - `total_seconds` (integer) **(required)**: Every row's `seconds` added together, for the range you asked for. - `total_hours` (number) **(required)**: `total_seconds` converted once. Not the rows' `hours` added together, which drifts by minutes over a few thousand rows. - `data` (array) **(required)** - `participant_id` (string, nullable) **(required)**: The Terac participant these hours belong to. Null for a worker we cannot yet match to an account; their hours still count toward the totals. - `submission_id` (string, nullable) **(required)**: Their submission on this opportunity. Null when they hold no active one. - `email` (string, nullable) **(required)**: The participant's email. Null when we hold none for them. - `date` (string) **(required)**: `YYYY-MM-DD`, the day this work is credited to, in `timezone`. - `timezone` (string) **(required)**: The IANA timezone `date` is expressed in for this person. - `seconds` (integer) **(required)**: Seconds this participant worked on this opportunity on this date. The authoritative figure: derive your own totals from this rather than from `hours`. - `hours` (number) **(required)**: `seconds` converted once and rounded to two decimals. Adding rounded hours across many rows drifts; add `seconds` and convert at the end. ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # Get tracked hours for one submission (https://terac.com/docs/developers/reference/getSubmissionHours) `GET https://terac.com/api/external/v2/submissions/{submissionId}/hours` Hours this submission's participant worked on its opportunity, one row per date. Time spent on your other opportunities, and time clocked in but not spent on work, is not included. The current day is partial and refreshes through the day; `last_updated_at` says when the hours were last refreshed. Returns exactly this submission's participant's rows from the opportunity response for the same range. ## Authorization Requires a Bearer token via the `Authorization` header. ``` Authorization: Bearer ``` ## Path Parameters - `submissionId` (string) **(required)**: ID of the submission, as listed by `GET /opportunities/{opportunityId}/submissions`. ## Query Parameters - `from` (string) **(required)**: Start of the window, inclusive: an ISO 8601 UTC timestamp such as `2026-09-08T00:00:00Z`, or a bare `2026-09-08` read as midnight UTC. A person's day counts when the instant it opened in their own tracker timezone falls in the window, so a day is never returned in halves. - `to` (string) **(required)**: End of the window, exclusive, in the same UTC form: a day that opened exactly at `to` belongs to the next window, so consecutive requests neither double count nor skip. At most 92 days after `from`. ## Responses ### 200: This participant's tracked hours per date, with totals for the range. - `last_updated_at` (string, nullable) **(required)**: When these hours were last refreshed, regardless of the range asked for. Null when no hours have ever been recorded for this opportunity, which tells an empty `data` apart from a range nobody worked in. - `total_seconds` (integer) **(required)**: Every row's `seconds` added together, for the range you asked for. - `total_hours` (number) **(required)**: `total_seconds` converted once. Not the rows' `hours` added together, which drifts by minutes over a few thousand rows. - `data` (array) **(required)** - `participant_id` (string, nullable) **(required)**: The Terac participant these hours belong to. Null for a worker we cannot yet match to an account; their hours still count toward the totals. - `submission_id` (string, nullable) **(required)**: Their submission on this opportunity. Null when they hold no active one. - `email` (string, nullable) **(required)**: The participant's email. Null when we hold none for them. - `date` (string) **(required)**: `YYYY-MM-DD`, the day this work is credited to, in `timezone`. - `timezone` (string) **(required)**: The IANA timezone `date` is expressed in for this person. - `seconds` (integer) **(required)**: Seconds this participant worked on this opportunity on this date. The authoritative figure: derive your own totals from this rather than from `hours`. - `hours` (number) **(required)**: `seconds` converted once and rounded to two decimals. Adding rounded hours across many rows drifts; add `seconds` and convert at the end. ### 400: Invalid input data - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code. - `message` (string) **(required)**: What went wrong. ### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level). - One of: API key error (403) - `error` (object) **(required)** - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code. - `message` (string) **(required)**: What went wrong. - One of: Insufficient access error (403) - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 404: Not found - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** ### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`. - `error` (object) **(required)** - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code. - `message` (string) **(required)**: What went wrong. ### 500: Internal server error - `message` (string) **(required)**: The error message - `code` (string) **(required)**: The error code - `issues` (array): An array of issues that were responsible for the error - `message` (string) **(required)** # Pricing (https://terac.com/docs/researchers/finance/pricing) How Terac pricing works, including per-participant costs, platform fees, and cost estimation. Terac pricing is based on per-participant payouts plus a platform fee. Each participant earns an incentive, which you either set yourself or have the Terac team price, and Terac adds its fee on top. ## Pricing Structure [#pricing-structure] | Component | Description | | ------------------------- | -------------------------------------------------------------- | | **Participant payout** | The incentive each approved participant earns | | **Platform fee** | Terac's fee for sourcing, screening, and managing participants | | **Total per participant** | Payout + platform fee | > When a quote is given as a single cost per participant (sometimes called CPI), > that figure is the **total**: the participant's incentive plus > Terac's platform fee. The incentive is what the participant receives; the > platform fee covers sourcing, screening, and managing them. Longer or more > specialized studies carry a higher incentive because they are harder to > recruit for. ## Setting Compensation [#setting-compensation] When you create an opportunity, its Review step asks **How do you want to set the incentive?** Choose **Send to the Terac team** to have the Terac team price it, or **Set my own incentive** to set it yourself, starting from Terac's suggested price. Either way, nothing is charged until you launch, and you can change the incentive any time before then. See [Creating an Opportunity](https://terac.com/docs/researchers/opportunities/creating-an-opportunity) for both paths. If you set it yourself, consider: * **Market rates** for the type of work (surveys pay less than multi-hour interviews) * **Participant expertise** level required (specialized experts expect higher compensation) * **Time commitment** (longer tasks should pay proportionally more) * **Complexity** of the work (file uploads, multi-part tasks, etc.) ## Cost Estimation [#cost-estimation] Before launching, Terac provides a cost estimate: **Estimated total = (participant payout + platform fee) x participant cap** You can also request a quote programmatically: `POST /v2/quotes` returns a priced quote, and `GET /v2/quotes/{quoteId}` retrieves it later. See the [API documentation](https://terac.com/docs/developers/guides). ## When You Are Charged [#when-you-are-charged] Credits are only deducted when you approve a submission. You are never charged for: * Participants who are screened out * Participants who abandon the opportunity * Participants whose submissions are rejected * Participants who time out ## Re-runs and Platform Issues [#re-runs-and-platform-issues] You are not charged for work that fails because of a platform issue. If an interview or task has to be re-run because of a technical problem on Terac's side, that re-run is not deducted from your balance. If you believe you were charged for a submission affected by a platform issue, contact support and it will be reviewed. ## Adjusting Pricing [#adjusting-pricing] You can change the per-participant payout on an active opportunity. The new rate applies to participants who have not yet been approved. Participants already approved keep their original rate. If you need to adjust the cost per participant on a study after launch (for example, to raise the incentive for a hard-to-reach audience), your Terac contact can update it for you. ## What's Next? [#whats-next] - [Credits](https://terac.com/docs/researchers/finance/credits): Manage your credit balance - [Invoices](https://terac.com/docs/researchers/finance/invoices): View your transaction history # Credits (https://terac.com/docs/researchers/finance/credits) Purchase, manage, and track your credit balance for funding opportunities. Credits are the currency you use to pay participants on Terac. You buy credits in advance and they are deducted when you approve participant submissions. ## Adding Credits [#adding-credits] 1. **Go to Finance** Navigate to your organization Settings and select **Finance**. 2. **Click Top Up** Enter the amount of credits you want to add. Payment is processed via Stripe. 3. **Credits are available immediately** Once payment is confirmed, credits appear in your balance and can be used to launch opportunities. ## Keeping a Buffer [#keeping-a-buffer] Credits do not top up on their own, so keep a buffer above what your live studies need. Check the balance on the Finance page before you launch, and top up manually when it runs low. If you want a standing arrangement so live studies never stall on funds, talk to your Terac contact. ## How Credits Are Used [#how-credits-are-used] Credits are deducted when you **approve** a participant's submission: | Event | Credit Impact | | ------------------------ | ---------------------------------------------- | | Participant approved | Per-participant payout + platform fee deducted | | Participant rejected | No credits deducted | | Participant screened out | No credits deducted | | Opportunity cancelled | Unused credits remain in your balance | You are never charged for participants who do not complete or are not approved. ## Checking Your Balance [#checking-your-balance] Your current credit balance is shown on the **Finance** page in organization settings, alongside your transaction history and usage over time. The cost breakdown in the builder tells you whether your balance covers a launch before you commit to it. ## Credit Transactions [#credit-transactions] The Finance page shows a full transaction history: * Top-up purchases * Per-opportunity deductions * Refunds (if applicable) Each transaction shows the date, amount, opportunity name, and remaining balance. ## Opportunity Budget Estimates [#opportunity-budget-estimates] Before launching an opportunity, Terac shows an estimated budget based on: * Per-participant payout you set * Platform fee * Participant cap This helps you ensure you have enough credits before going live. > The estimate is a maximum. Your actual spend depends on how many participants > are approved. If you cap at 50 participants but only approve 35, you are only > charged for 35. ## What's Next? [#whats-next] - [Pricing](https://terac.com/docs/researchers/finance/pricing): Understand how costs are calculated - [Invoices](https://terac.com/docs/researchers/finance/invoices): View receipts and transaction history - [Create an Opportunity](https://terac.com/docs/researchers/opportunities/creating-an-opportunity): Use your credits to launch research # Finance & Credits (https://terac.com/docs/researchers/finance) Understand how Terac billing works, including credits, pricing, and transaction history. Terac uses a credits-based billing system. You purchase credits upfront, and they are deducted as participants are approved in your opportunities. - [Credits](https://terac.com/docs/researchers/finance/credits): Purchase, manage, and track your credit balance - [Pricing](https://terac.com/docs/researchers/finance/pricing): How per-participant costs are calculated - [Invoices](https://terac.com/docs/researchers/finance/invoices): View transaction history and receipts ## Quick Reference [#quick-reference] | Concept | Description | | ---------------------- | -------------------------------------------------------- | | Credits | Prepaid balance used to fund opportunities | | Per-participant payout | The amount each approved participant earns | | Platform fee | Terac's fee on top of participant payouts | | Opportunity budget | Estimated total cost based on payout and participant cap | # Invoices (https://terac.com/docs/researchers/finance/invoices) View transaction history, per-opportunity costs, and credit purchase receipts. The Finance page in your organization settings provides a complete view of all financial transactions. ## Transaction History [#transaction-history] The credit transactions table shows every financial event: | Column | Description | | ----------- | ---------------------------------------------------------- | | Date | When the transaction occurred | | Type | Top-up, opportunity deduction, or refund | | Amount | Credits added or deducted | | Balance | Running credit balance after the transaction | | Opportunity | Which opportunity the deduction applies to (if applicable) | ## Per-Opportunity Costs [#per-opportunity-costs] You can view costs broken down by opportunity to understand where your credits are going: * Total spent per opportunity * Number of approved participants * Average cost per participant * Remaining budget vs. cap ## Usage Analytics [#usage-analytics] The Finance page also includes usage trends: * Credit spend over time * Opportunities launched per month * Participants approved per month These analytics help you plan budgets and forecast future credit needs. ## Receipts [#receipts] Credit top-up payments processed through Stripe generate receipts that are available in your Stripe billing portal. Contact your organization admin if you need access. ## What's Next? [#whats-next] - [Credits](https://terac.com/docs/researchers/finance/credits): Add more credits to your balance - [Pricing](https://terac.com/docs/researchers/finance/pricing): Understand how costs are calculated # Create Your Organization (https://terac.com/docs/researchers/getting-started/create-organization) Set up your organization on Terac to start managing research projects and team members. An organization is your team's workspace on Terac. It holds your projects, opportunities, team members, billing, and integrations. Every researcher belongs to at least one organization. ## Creating an Organization [#creating-an-organization] 1. **Sign up or log in** Go to [terac.com](https://terac.com) and create an account using Google, LinkedIn, Facebook, or Microsoft. If you already have an account, log in. 2. **Set up your organization** After signing in, you'll be prompted to create an organization. Provide: * **Organization name** - your company or team name * **Logo** - optional, displayed to participants in opportunity listings 3. **Invite your team** Add team members by email. Each member gets a role that controls what they can access. See [Members and Roles](https://terac.com/docs/researchers/team/members-and-roles) for details. ## Organization Settings [#organization-settings] Once created, configure your organization from **Settings** in the sidebar: | Setting | Description | | ------------------- | ----------------------------------------------- | | General | Organization name, logo, and research context | | Members | Invite, remove, and manage team member roles | | Roles & Permissions | See what each role can do | | Finance | Credit balance, top-up, and transaction history | | API Keys | Generate keys for the Terac API | | Integrations | Connect Slack | | Security | MFA enforcement and SSO configuration | | Activity | Audit log of organization actions | ## Research Context [#research-context] In the General settings, you can set a **Research Context** for your organization. This is background information about your company and research goals that the AI moderator uses when conducting screening interviews on your behalf. A well-written context helps the AI ask better follow-up questions and evaluate participant fit more accurately. ## Multiple Organizations [#multiple-organizations] You can belong to multiple organizations. Use the organization switcher in the top-left of the dashboard to move between them. Each organization has its own billing, members, and projects. ## What's Next? [#whats-next] - [Create a Project](https://terac.com/docs/researchers/getting-started/create-project): Start organizing your research - [Invite Your Team](https://terac.com/docs/researchers/team/members-and-roles): Add collaborators and set roles - [Add Credits](https://terac.com/docs/researchers/finance/credits): Fund your account to launch opportunities # Create a Project (https://terac.com/docs/researchers/getting-started/create-project) Projects group related opportunities, guides, and recordings together for organized research management. Projects are the primary way to organize your research within an organization. Each project contains its own opportunities (opportunities), research guides, and interview recordings. ## Creating a Project [#creating-a-project] 1. **Navigate to your organization** From the dashboard, make sure you're in the correct organization using the switcher in the top-left. 2. **Create a new project** Click the **New Project** button. Provide a name and optional description for your project. 3. **Set project context** Optionally add a research context that describes the goals of this project. This context is used alongside the organization context to help the AI moderator conduct more relevant screening interviews. ## What Projects Contain [#what-projects-contain] Each project has its own: | Section | Description | | ------------- | --------------------------------------------------------------- | | Opportunities | Opportunities and tasks you create for participants | | Guides | Research guides with discussion scripts and stimulus materials | | Guides | Research guides, and the interviews and insights under each one | | Settings | Project-specific configuration | ## Organizing Your Research [#organizing-your-research] Here are some common ways to structure projects: * **By product area** - one project per product you're researching (e.g., "Mobile App", "Checkout Flow") * **By research phase** - group by stage (e.g., "Discovery", "Validation", "Post-Launch") * **By team** - if different teams run independent research, give each their own project * **By client** - for agencies running research on behalf of clients ## Project Settings [#project-settings] Each project has its own settings page where you can: * Update the project name and description * Set project-level research context * Archive the project when research is complete ## What's Next? [#whats-next] - [Create an Opportunity](https://terac.com/docs/researchers/opportunities/creating-an-opportunity): Launch your first opportunity in this project - [Set Up a Research Guide](https://terac.com/docs/researchers/opportunities/research-guides): Build discussion guides for AI-moderated interviews - [Platform Overview](https://terac.com/docs/researchers/getting-started/platform-overview): Learn your way around the dashboard # Welcome to Terac (https://terac.com/docs/researchers/getting-started) Everything you need to get started running research on Terac, from creating your organization to launching your first opportunity. Terac is an AI-native panel infrastructure that recruits, screens, and delivers verified experts to your research. Whether you need participants for user interviews, usability tests, surveys, or data labeling tasks, Terac handles the entire pipeline so you can focus on insights. ## What Terac Does for You [#what-terac-does-for-you] - **Source participants**: Reach verified experts matched to your exact criteria through demographic filters, attestations, and screening questions. - **Screen automatically**: AI-moderated voice interviews and configurable screeners qualify participants before they reach your opportunity. - **Manage submissions**: Track every participant from application through completion with built-in review workflows and quality controls. - **Pay on approval**: Credits-based billing with per-participant pricing. You only pay for approved work. ## How It Works [#how-it-works] 1. **Create your organization** Set up your team workspace at [terac.com](https://terac.com). Invite collaborators and configure roles. 2. **Create a project** Projects group related opportunities together. Each project has its own opportunities, guides, and recordings. 3. **Design your opportunity** Define what you need: participant criteria, screening questions, tasks, and compensation. 4. **Launch recruitment** Activate sourcing channels and Terac begins matching qualified experts to your opportunity. 5. **Review and approve** Review submissions, approve quality work, and export your results. Participants are paid automatically on approval. ## Quick Start Checklist [#quick-start-checklist] > New to Terac? Complete these steps and you'll be ready to launch your > first opportunity. * [ ] Create your organization * [ ] Add credits to your account * [ ] Create your first project * [ ] Set up an opportunity with screening criteria * [ ] Launch recruitment ## What's Next? [#whats-next] - [Create Your Organization](https://terac.com/docs/researchers/getting-started/create-organization): Set up your team workspace - [Create a Project](https://terac.com/docs/researchers/getting-started/create-project): Organize your research - [Platform Overview](https://terac.com/docs/researchers/getting-started/platform-overview): Learn your way around the dashboard - [Create Your First Opportunity](https://terac.com/docs/researchers/opportunities/creating-an-opportunity): Design and launch an opportunity ## Need Help? [#need-help] * Check our [FAQ](https://terac.com/docs/researchers/support/faq) for common questions * Review the [Glossary](https://terac.com/docs/researchers/support/glossary) for terminology * [Contact us](https://terac.com/docs/researchers/support/contact) for direct support # Platform Overview (https://terac.com/docs/researchers/getting-started/platform-overview) A guided tour of the Terac researcher dashboard, including navigation, key pages, and workflows. This page walks you through the main areas of the Terac platform so you can find what you need quickly. ## Dashboard Navigation [#dashboard-navigation] The left sidebar is your primary navigation. The top group is your organization workspace; below it, you drill into a project. ### Organization Level [#organization-level] | Page | What It Does | | ------------- | ----------------------------------------------------------------- | | Overview | Recent activity across your organization | | Feasibility | Check how many panel experts match a set of filters | | Opportunities | All opportunities across the organization | | Settings | Organization name, logo, research context, security, integrations | | Finance | Credit balance, top-up, transaction history, usage | | Members | Invite, remove, and manage team member roles | | API Keys | Generate and revoke keys for the Terac API | | Activity | Organization-wide audit log | ### Project Level [#project-level] | Page | What It Does | | --------------- | --------------------------------------- | | Opportunities | Opportunities belonging to this project | | Research Guides | Discussion guides for this project | | Settings | Project-specific configuration | ## Opportunity Workflow [#opportunity-workflow] The typical research workflow on Terac follows this path: Create Opportunity → Configure Screening → Set Filters → Launch Recruitment → Review Submissions → Export Results ### Key Tabs Within an Opportunity [#key-tabs-within-an-opportunity] The Submissions tab is your command center: it opens by default and shows the whole participant funnel by stage, with review actions inline. | Tab | Purpose | | ----------- | ------------------------------------------------------------------------- | | Submissions | Every submission by funnel stage, with status tracking and review actions | | Task | Configure what participants need to complete | | Recruitment | Hard filters, candidate profile, and screeners | | Settings | Opportunity name, pricing, and advanced configuration | > While an opportunity is still a draft it opens in the full-screen builder > instead of these tabs. The tabs appear once it is launched. ## Research Guides [#research-guides] Research guides let you define structured discussion scripts that the AI moderator follows during screening or task interviews. Each guide includes: * **Discussion guide** - the questions and flow the AI follows * **Screening policy** - rules for auto-qualifying or disqualifying participants * **Stimulus materials** - images, videos, or files shown to participants during interviews * **Insights** - AI-generated analysis and themes extracted from completed interviews ## Finding the Right Experts [#finding-the-right-experts] You do not browse the panel directly. You describe who you need through [filters](https://terac.com/docs/researchers/recruitment/filters), a candidate profile, and [screeners](https://terac.com/docs/researchers/opportunities/screening), and Terac matches and delivers experts against that definition. To sanity-check the size of an audience before you build a study, use the **Feasibility** page. ## What's Next? [#whats-next] - [Create an Opportunity](https://terac.com/docs/researchers/opportunities/creating-an-opportunity): Build your first opportunity - [Set Up Recruitment](https://terac.com/docs/researchers/recruitment): Learn how to find the right participants - [Review Submissions](https://terac.com/docs/researchers/results/submissions): Manage participant work # API Keys (https://terac.com/docs/researchers/integrations/api-keys) Generate and manage API keys for programmatic access to Terac. API keys let you interact with Terac programmatically through the [Terac API](https://terac.com/docs/developers/guides). Use them to automate opportunity creation, manage recruitment, and retrieve submission data. ## Creating an API Key [#creating-an-api-key] 1. **Go to Settings** Navigate to your organization's Settings page and select **API Keys**. 2. **Generate a key** Click **Create API Key**. Give it a descriptive name so you can identify it later (e.g., "Production Integration", "Staging Tests"). 3. **Copy the key** The full key is shown only once. Copy it immediately and store it securely. You cannot retrieve the full key later. ## Using API Keys [#using-api-keys] Include your API key in the `Authorization` header of every request: ``` Authorization: Bearer YOUR_API_KEY ``` ## Key Scope [#key-scope] API keys are scoped to your organization. A single key can access all projects and opportunities within the organization. ## Rate Limits [#rate-limits] API requests are rate-limited to **100 requests per minute** per key. If you exceed this limit, requests return a `429 RATE_LIMITED` error. Wait and retry with exponential backoff. ## Key Management [#key-management] | Action | Description | | ---------- | ----------------------------------------- | | **Rotate** | Generate a new key and revoke the old one | | **Revoke** | Immediately disable a key | | **Rename** | Update the key's display name | ## Security Best Practices [#security-best-practices] * Never commit API keys to version control * Use environment variables to store keys in your applications * Rotate keys periodically * Use separate keys for production and development * Revoke keys immediately if they may be compromised ## What's Next? [#whats-next] - [API Documentation](https://terac.com/docs/developers/guides): Full API reference and guides - [Security](https://terac.com/docs/researchers/team/security): Organization security settings # External Surveys & Redirect URLs (https://terac.com/docs/researchers/integrations/external-surveys) Connect a third-party survey or task tool to Terac so completions, screen-outs, and over-quota exits are tracked automatically. When your task lives on another platform (a Qualtrics or SurveyMonkey survey, your own product, or a hosted usability test), Terac sends qualified participants to that platform and needs to know how each one finished. Redirect URLs are how the two platforms talk to each other. This is the most common setup question for teams running their own survey or task tool, so it is worth getting right before you launch. ## How It Works [#how-it-works] Terac sends participant → Participant completes task → Your tool redirects back → Terac records the result 1. A qualified participant clicks through to your survey. Terac appends their **submission ID** to the task URL. 2. The participant completes (or is screened out of) your survey. 3. At the end, your survey **redirects the participant's browser** back to Terac's callback URL with the submission ID and a result. 4. Terac records the outcome and moves the submission to **Work review**, **Approved** or **Rejected**, depending on the result and the task's review type. > The callback is a browser redirect, not a server-to-server webhook. Your > survey tool only needs to send the participant's browser to a URL when > they finish. No API call or authentication is required. ## Step 1: Capture the Submission ID [#step-1-capture-the-submission-id] When Terac forwards a participant to your task URL, it appends these query parameters: | Parameter | Description | | ------------------- | ------------------------------------------------------ | | `teracSubmissionId` | The participant's unique submission ID (preferred key) | | `submissionId` | The same value, for tools that read this key instead | | `taskId` | The ID of the task within the opportunity | Configure your survey tool to **capture `teracSubmissionId`** as a hidden field or embedded data value so you can pass it back at the end. Both `teracSubmissionId` and `submissionId` carry the same value, so either one works. Enter your task link exactly as your survey tool gives it to you. Terac adds the parameters above on the way out, so there is no need to append `teracSubmissionId` to the link yourself. ### Putting the ID in your own parameter [#putting-the-id-in-your-own-parameter] Some tools identify participants through a parameter of their own, and hand you a link with that parameter left blank — for example `…&sourceId=terac&sourceParticipantId=`. **Paste it as-is:** a link that ends in an empty parameter gets the submission ID filled into it for each participant. To place the ID anywhere else — a parameter that is not last, or a segment of the path — write `{TERAC_SUBMISSION_ID}` where the value belongs: ``` https://app.example.com/sample/collect?sourceParticipantId={TERAC_SUBMISSION_ID}&sourceId=terac ``` `{TERAC_TASK_ID}` works the same way for the task ID. Both work on any link, and the parameters from the table above are still appended alongside your own. > **Warning** > > Some survey tools drop unknown query parameters on their hosted pages. If > yours does, the submission ID will not reach your survey. Tell your Terac > contact which tool you use and we will configure a pass-through for it. ## Step 2: Redirect Back on Completion [#step-2-redirect-back-on-completion] At the end of your survey, redirect the participant to: ``` https://terac.com/api/external/callback?teracSubmissionId={{teracSubmissionId}}&result=completed ``` Replace `{{teracSubmissionId}}` with the submission ID you captured in Step 1. The same base URL is used for every study, so you only configure it once per survey tool. ### Result Values [#result-values] Set `result` to match how the participant finished: | `result` | Use when | Effect on the submission | | -------------- | ------------------------------------------------------------- | ---------------------------------------- | | `completed` | The participant finished the survey | Depends on the task's review type, below | | `screened_out` | The participant failed your in-survey screener | Rejected, with the reason screened out | | `quota_full` | The relevant quota was already full when they arrived | Rejected, with the reason quota full | | `rejected` | The participant was disqualified for quality or fraud reasons | Rejected | If `result` is omitted, Terac assumes `completed`. ### `completed` Does Not Mean Approved [#completed-does-not-mean-approved] A completion redirect tells Terac the work was **done**, not that it was acceptable. What happens next is set by the task's review type, which is a per-task setting: | Task review type | Shown in the builder as | Where a `completed` redirect leaves the submission | | ---------------- | ----------------------- | -------------------------------------------------------- | | `manual_review` | not offered, API only | **Work review**, waiting for you to approve or reject it | | `auto_approve` | Needs approval | **Approved**, and the participant is paid | | `self_report` | Expert approves | **Approved**, and the participant is paid | Once they have opened your survey, participants also see an **I completed the task** button on Terac, unless Terac has turned it off for your organization. On a `self_report` task it approves the submission and pays the participant, with no redirect needed. On the other two it moves the submission to **Work review**, and on an `auto_approve` task a `completed` redirect then approves it. ### Reviewing external work before anyone is paid [#reviewing-external-work-before-anyone-is-paid] `manual_review` is the review type that holds a completed submission for you, and today it is only settable **through the API**: ```json { "sequence": 1, "task_type": "interview", "review_type": "manual_review", "task_url": "https://…" } ``` Send the `completed` redirect as normal. It moves the submission into Work review, and you approve or reject it yourself, in the dashboard or with `POST /submissions/{submissionId}/approve`. An opportunity launched without a screener has to use `manual_review` on its survey task, unless Terac runs its AI screening interview on it. Everyone who matches its filters reaches your survey, so a redirect alone must never be what pays them. In the dashboard, launching such an opportunity sets its **Needs approval** tasks to `manual_review` for you. See [Launching Without a Screener](https://terac.com/docs/developers/guides/screening-questions#launching-without-a-screener). > **Warning** > > On every other opportunity, the dashboard's task builder does not offer > `manual_review`. Its picker has two options, and on a task with a URL both > settle the submission from the redirect without waiting for you: **Needs > approval** pays out on a `completed` callback, and **Expert approves** lets > the participant approve their own work. A task that stored `manual_review` > still shows as **Needs approval** there, with Redirect URLs that say a > completed redirect waits for your approval, and changing its link or its > review setting in the builder replaces `manual_review` with one of those two. > > So if gating payout matters to you, build the task through the API, and leave > its link and review setting alone in the builder. Tell us if you need this in > the dashboard. The callback itself can never approve a `manual_review` task, whichever way it was created. That part is deliberate: the callback is an unauthenticated browser redirect, and anything a participant's own browser can trigger must not release payment for work you have not seen. > Set up redirects for every exit path your survey can take, not just > completion. Without a `screened_out` or `quota_full` redirect, those > participants look the same as people who simply abandoned the survey. ## Step 3: Test Your Redirect [#step-3-test-your-redirect] Right after you launch, before participants reach your survey, check the redirect yourself. On the opportunity's **Task** tab, find the task's **Redirect URLs** and click **Run a demo redirect**. We open your task link in a new tab with a demo ID added to it, in place of a real submission ID (`teracSubmissionId=demo_…`). Your redirect sends it back like any other ID, so you don't need to add the demo ID anywhere. Using the API or MCP? Once the opportunity is live, its response includes `links.dashboard.task`. Open that link and run the demo there. Go through your survey and exit the way you want to test. When your tool redirects back, you see either **Your redirect works**, or what your tool sent and the redirect URL to use instead. Run it again for each exit path. > A demo run records nothing in Terac and no participant is affected. It does > create a real response in your own tool, marked with `demo_run`, so you can > find and delete it there. ## What Happens Without Redirect URLs [#what-happens-without-redirect-urls] If a participant reaches your survey but Terac never receives a callback, the submission has no completion signal from your tool. It stays **In Progress** until the participant clicks **I completed the task**, which moves it to **Work review**, or approves it on a `self_report` task. On an opportunity with **redirect URLs enabled**, a submission sitting in Work review with no callback evidence is **auto-rejected after 6 hours**. Setting up the completion redirect is what prevents qualified participants from being auto-rejected and keeps your recruitment moving. The same 6-hour sweep never approves a `manual_review` task. One that reached Work review through a `completed` redirect stays there until you decide, however long you take over it. ### Is Redirect URLs Enabled On My Opportunity? [#is-redirect-urls-enabled-on-my-opportunity] Almost certainly not, unless you asked us to turn it on. It is a per-opportunity setting that is **off by default**, and it is not currently self-serve: there is no toggle in the dashboard and no field on the API, so it can only be switched on by Terac. Ask us if you want it. While it is off, nothing is auto-rejected for a missing callback. A participant who clicks **I completed the task** without your survey sending them back waits in Work review for you to resolve, and one who never comes back stays In Progress. That is the behaviour to assume unless you have arranged otherwise. Configuring the `completed` redirect is worth doing either way. On an `auto_approve` task it is what approves finished work without you, and on a `manual_review` task it puts finished participants in front of you without relying on them to click anything. ## Setup Checklist [#setup-checklist] * [ ] Your task URL accepts a query parameter and your tool keeps it through to the end of the survey * [ ] Your survey captures `teracSubmissionId` as hidden / embedded data * [ ] The end-of-survey redirect points to the Terac callback URL with the captured ID * [ ] A `completed` redirect is configured * [ ] `screened_out` and `quota_full` redirects are configured for those exit paths * [ ] A demo redirect shows **Your redirect works** for each exit path * [ ] You ran a soft launch (a few participants) to confirm completions show up before scaling to your full sample > Run a small soft launch first. Send a handful of participants through and > confirm they appear as Completed in the Submissions tab before opening the > study to your full target. ## What's Next? [#whats-next] - [Submissions](https://terac.com/docs/researchers/results/submissions): Track submission states and completion - [Reviewing Work](https://terac.com/docs/researchers/results/reviewing-work): How approvals and auto-review work - [API Keys](https://terac.com/docs/researchers/integrations/api-keys): Programmatic access to Terac # Integrations (https://terac.com/docs/researchers/integrations) Connect Terac with your existing tools including API access, Slack, and calendar integrations. Terac integrates with the tools your team already uses to streamline your research workflow. - [API Keys](https://terac.com/docs/researchers/integrations/api-keys): Programmatic access to Terac - [External Surveys](https://terac.com/docs/researchers/integrations/external-surveys): Track completions from a third-party survey or task tool - [Slack](https://terac.com/docs/researchers/integrations/slack): Get completion notifications in Slack ## Available Integrations [#available-integrations] | Integration | Description | | -------------------- | ------------------------------------------------------------ | | **Terac API** | Full programmatic access to create and manage opportunities | | **External surveys** | Redirect URLs that track completions from a third-party tool | | **Slack** | Interview and opportunity completion notices to one channel | ## Setting Up Integrations [#setting-up-integrations] All integrations are managed from the **Integrations** page in your organization settings. Each integration has its own setup flow and configuration options. # Slack (https://terac.com/docs/researchers/integrations/slack) Connect Terac to Slack so your team hears about completed interviews and fulfilled opportunities without checking the dashboard. The Slack integration posts Terac completion notices into one channel in your workspace, so your team sees progress without opening the dashboard. ## Setting Up Slack [#setting-up-slack] 1. **Go to Integrations** Navigate to your organization's Settings page and select **Integrations**. 2. **Connect Slack** Click **Connect Slack** and authorize Terac in your Slack workspace. 3. **Pick a channel** Select the channel that should receive notifications. Terac posts to this one channel for the whole organization. ## What Terac Posts [#what-terac-posts] | Event | What You See | | -------------------------- | --------------------------------------------------------------- | | Interview completed | A participant finished an AI-moderated interview, with a link | | Opportunity target reached | An opportunity hit its participant target, with completed count | > There is one destination channel per organization, and the two notices above > are what Terac sends today. There is no per-event or per-opportunity routing. > If you need notifications on other events, tell your Terac contact so it can > be prioritized. ## Disconnecting [#disconnecting] You can disconnect Slack or change the destination channel at any time from the same Integrations page. Disconnecting revokes Terac's access token and stops all posts. ## What's Next? [#whats-next] - [API Keys](https://terac.com/docs/researchers/integrations/api-keys): Set up programmatic access - [External Surveys](https://terac.com/docs/researchers/integrations/external-surveys): Track completions from a third-party tool # Terac MCP (https://terac.com/docs/researchers/mcp) Connect any AI assistant or agent to Terac. Price studies, launch recruitment, and review submissions in plain language through the Model Context Protocol. The Terac MCP server exposes Terac's API as a set of tools that any [Model Context Protocol](https://modelcontextprotocol.io) client can call. Connect Claude, Claude Code, Cursor, VS Code, or ChatGPT and your AI assistant can source verified experts, launch recruitment, and review work on your behalf, all in plain language. > **Using Claude or ChatGPT?** Terac is published in both directories, so it > installs in one click with nothing to paste: [Terac in > Claude](https://claude.ai/directory/terac) or [Terac in > ChatGPT](https://chatgpt.com/plugins/plugin_asdk_app_699562017464819186082ec0bff33f17). > For every other client, see [Install](https://terac.com/docs/researchers/mcp/install). ## What You Can Do [#what-you-can-do] - **Price any study**: Describe who you need and submit a feasibility request. Our team prices it and returns a cost per participant before you commit any budget. - **Launch recruitment**: Turn a priced request into a live opportunity. Terac sources and screens matching experts automatically. - **Target with filters**: Narrow by verified demographic, geographic, and professional attributes instead of free-text descriptions. - **Review and pay**: List submissions, inspect the work, and approve or reject. Approved experts are paid automatically. ## Endpoint [#endpoint] Connect your client to: `https://terac.com/api/mcp` The server speaks streamable HTTP and authenticates with either an organization API key or browser-based OAuth. See [Install](https://terac.com/docs/researchers/mcp/install) for client-by-client setup. ## A Typical Flow [#a-typical-flow] Once connected, ask your assistant to run the workflow end to end: Get context → Request feasibility → Poll for price → Create draft → Launch → Approve & pay Always start a session by calling `terac_get_context`. It returns your organization identity, credit balance, projects, and policies, and grounds every tool call that follows. ## Tool Catalog [#tool-catalog] The tools are grouped by what they touch. Each one carries full inline guidance that your assistant reads automatically, so you rarely need to name a tool yourself. | Group | Tools | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | Organization | `terac_get_context` | | Projects | `terac_list_projects`, `terac_create_project`, `terac_get_project`, `terac_update_project` | | Targeting filters | `terac_list_filters`, `terac_get_filter_options` | | Feasibility | `terac_request_feasibility`, `terac_get_feasibility_request`, `terac_list_feasibility_requests` | | Drafts & launch | `terac_create_opportunity`, `terac_update_opportunity`, `terac_launch_draft_opportunity`, `terac_delete_opportunity` | | Lifecycle | `terac_list_opportunities`, `terac_get_opportunity`, `terac_pause_opportunity`, `terac_resume_opportunity`, `terac_stop_opportunity` | | Submissions | `terac_get_submissions`, `terac_get_submission`, `terac_approve_submission`, `terac_reject_submission` | ### How Pricing Works [#how-pricing-works] Pricing is not autonomous. `terac_request_feasibility` submits your audience and task to the Terac team; it does **not** return a price immediately. Your assistant then polls `terac_get_feasibility_request` until `status` is `RESPONDED` and `costPerParticipant` is set. A person prices it, typically within about an hour, longer for niche or multi-market audiences. Polling more often does not make it arrive sooner. Once you have a price, build the study with `terac_create_opportunity` and take it live with `terac_launch_draft_opportunity`. ## Safe by Design [#safe-by-design] Every tool is annotated so your client knows whether it reads or writes, and whether a write can be undone. Destructive actions (`terac_stop_opportunity`, `terac_reject_submission`, `terac_delete_opportunity`) are flagged so your client can ask you to confirm before running them. > **Warning** > > There are no webhooks yet. To track progress, your assistant polls > `terac_get_opportunity` and `terac_get_submissions` rather than waiting for a > callback. ## What's Next? [#whats-next] - [Install](https://terac.com/docs/researchers/mcp/install): Connect Claude, Claude Code, Cursor, VS Code, or ChatGPT - [API Keys](https://terac.com/docs/researchers/integrations/api-keys): Generate and manage keys for programmatic access - [Create a Project](https://terac.com/docs/researchers/getting-started/create-project): Organize the opportunities your agent will launch # Install (https://terac.com/docs/researchers/mcp/install) Connect the Terac MCP server to Claude, Claude Code, Cursor, VS Code, ChatGPT, or any MCP client. This guide connects the Terac MCP server to your AI assistant so it can price, launch, and manage research on your behalf. Pick your client below; setup takes a couple of minutes. ## Prerequisites [#prerequisites] Before connecting, make sure you have: 1. **A Terac account and organization** [Sign in to Terac](https://terac.com) and [create your organization](https://terac.com/docs/researchers/getting-started/create-organization) if you have not already. The MCP acts on behalf of a single organization. Connecting through the Claude or ChatGPT directory listing is the exception: if you have no organization yet, it walks you through creating one and then returns you to finish connecting. 2. **A way to authenticate** The server accepts two methods, and you only need one: * **API key** (recommended) is a single secret you paste into your client config. It works in every client, including headless and CI environments. [Generate one](https://terac.com/docs/researchers/integrations/api-keys) under your organization's **Settings → API Keys**. Keys start with `tk_`. * **OAuth** is a browser-based login with no key to manage. It is supported by clients that implement the MCP OAuth flow (Claude, Claude Code, Cursor, VS Code, ChatGPT). Your client opens a browser, you sign in, and you are done. > Not sure which to use? Start with an **API key**. It is the most portable > option and the only one that works for automation and CI. ## The Server Endpoint [#the-server-endpoint] Every client below connects to the same URL: `https://terac.com/api/mcp` ## Connect Your Client [#connect-your-client] ### Claude Terac is published in the Claude directory, so there is no server URL to paste. 1. **Open the Terac app** Go to [Terac in the Claude directory](https://claude.ai/directory/terac), or search Claude's directory for **Terac**. 2. **Connect your account** Choose to connect and complete the sign-in when prompted. If you do not have a Terac organization yet, you can create one during this step and you will be returned to finish connecting. 3. **Pick your organization** Terac asks which organization the app should act on behalf of. That choice is remembered, and everything the app does afterwards is scoped to it. Terac's tools are now available in the chat. Try asking: *"Use Terac to get my organization context."* > The same listing covers Claude Desktop and Claude on the web. Prefer to wire > up the raw MCP server instead? Add `https://terac.com/api/mcp` as a custom > connector under **Settings → Connectors → Add custom connector**, or use the > **Other (mcp-remote)** tab. Both reach the same server; the directory listing > is simply fewer steps. To use an API key instead, or on a build without the connector UI, open **Settings → Developer → Edit Config** and bridge through `mcp-remote`: ```json title="claude_desktop_config.json" { "mcpServers": { "terac": { "command": "npx", "args": [ "-y", "mcp-remote", "https://terac.com/api/mcp", "--header", "x-api-key:tk_your_api_key" ] } } } ``` Restart Claude Desktop after saving. ### Claude Code The fastest path is the CLI. Run this from any project, or add `--scope user` to make it available everywhere: ```bash title="Add with an API key" claude mcp add --transport http terac https://terac.com/api/mcp \ --header "x-api-key: tk_your_api_key" ``` Prefer browser login? Omit the header and authenticate with OAuth instead: ```bash title="Add with OAuth" claude mcp add --transport http terac https://terac.com/api/mcp ``` Then run `/mcp` inside Claude Code and select **terac** to complete the sign-in. To share the server with your team, add it with `--scope project`, which writes a committed `.mcp.json`: ```json title=".mcp.json" { "mcpServers": { "terac": { "type": "http", "url": "https://terac.com/api/mcp", "headers": { "x-api-key": "${TERAC_API_KEY}" } } } } ``` Reference `${TERAC_API_KEY}` so the secret stays in your environment, not in the committed file. ### Cursor Create or edit `~/.cursor/mcp.json` for all projects, or `.cursor/mcp.json` inside a single project: ```json title="~/.cursor/mcp.json" { "mcpServers": { "terac": { "url": "https://terac.com/api/mcp", "headers": { "x-api-key": "tk_your_api_key" } } } } ``` To keep the key out of the file, reference an environment variable instead: `"x-api-key": "${env:TERAC_API_KEY}"`. Prefer OAuth? Drop the `headers` block and leave only the `url`. Cursor opens a browser to sign in the first time the server is used. Then open **Settings → MCP** and confirm **terac** shows a green dot. ### VS Code With GitHub Copilot's agent mode, create `.vscode/mcp.json` in your workspace. The `inputs` block prompts for your key once and stores it securely instead of writing it to disk: ```json title=".vscode/mcp.json" { "inputs": [ { "type": "promptString", "id": "terac-api-key", "description": "Terac API key", "password": true } ], "servers": { "terac": { "type": "http", "url": "https://terac.com/api/mcp", "headers": { "x-api-key": "${input:terac-api-key}" } } } } ``` Click **Start** above the server entry, or run **MCP: List Servers** from the Command Palette. For OAuth, remove the `inputs` and `headers` blocks; VS Code opens a browser to authorize on first connect. ### ChatGPT Terac is published in the ChatGPT app directory, so there is no server URL to paste and no developer mode to enable. 1. **Open the Terac app** Go to [Terac in the ChatGPT app directory](https://chatgpt.com/plugins/plugin_asdk_app_699562017464819186082ec0bff33f17), or search ChatGPT's apps for **Terac**. 2. **Connect your account** Choose to connect and complete the sign-in when prompted. If you do not have a Terac organization yet, you can create one during this step and you will be returned to finish connecting. 3. **Pick your organization** Terac asks which organization the app should act on behalf of. That choice is remembered, and everything the app does afterwards is scoped to it. Terac's tools are now available in chat. Try asking: *"Use Terac to get my organization context."* > Prefer to wire up the raw MCP server instead, or on a plan without app > support? Use the **Other (mcp-remote)** tab, or add > `https://terac.com/api/mcp` as a custom connector under **Settings → Apps & > Connectors**. Both reach the same server; the app is simply fewer steps. ### Other (mcp-remote) Any client that can run a stdio command reaches Terac through [`mcp-remote`](https://www.npmjs.com/package/mcp-remote), which bridges a local command to the remote HTTP server and handles OAuth: ```json title="mcp config" { "mcpServers": { "terac": { "command": "npx", "args": ["-y", "mcp-remote", "https://terac.com/api/mcp"] } } } ``` That uses browser-based OAuth. To pass an API key instead, append a header argument: ```json title="With an API key" { "mcpServers": { "terac": { "command": "npx", "args": [ "-y", "mcp-remote", "https://terac.com/api/mcp", "--header", "x-api-key:tk_your_api_key" ] } } } ``` If your client supports native HTTP transport, skip `mcp-remote` and point it directly at `https://terac.com/api/mcp` with an `x-api-key` header. ## Verify the Connection [#verify-the-connection] Confirm the tools are live before relying on them. 1. **Check the server connected** In Claude Code, run `claude mcp list` (or `/mcp` in a session). In Cursor, VS Code, Claude, or ChatGPT, open the MCP or connector settings and look for **terac** with a connected status and a non-zero tool count. 2. **Run the first tool** Ask your assistant: *"Use Terac to get my organization context."* It should call `terac_get_context` and return your organization name, credit balance, and projects. If it does, you are connected. ## Troubleshooting [#troubleshooting] ### 401 Unauthorized or 'Authentication required' Your API key is missing, malformed, or revoked. Confirm the header name is exactly `x-api-key`, the value starts with `tk_`, and the key is still active under **Settings → API Keys**. If you used an environment variable, make sure it is set in the shell or app that launched the client. ### Server shows 'failed' or no tools Check that the URL is `https://terac.com/api/mcp` (note the `/api/mcp` path) and the transport is HTTP, not stdio, unless you are using `mcp-remote`. Restart the client after editing a config file. In Claude Code, `claude mcp get terac` prints the resolved configuration. ### OAuth browser window does not open Some clients print a URL instead of opening a browser; copy it manually. Make sure pop-ups are allowed. If sign-in succeeds but tools still fail, the session may be bound to a different organization. Remove and re-add the server, then re-authenticate. ### 429 Rate limited The MCP shares the API rate limit of 100 requests per minute per key. Space out high-volume operations and retry with exponential backoff. Contact support for an increase. ## What's Next? [#whats-next] - [Terac MCP Overview](https://terac.com/docs/researchers/mcp): See the full tool catalog and launch workflow - [API Keys](https://terac.com/docs/researchers/integrations/api-keys): Rotate, scope, and secure your keys - [Create an Opportunity](https://terac.com/docs/researchers/opportunities/creating-an-opportunity): Understand what your agent is launching # Creating an Opportunity (https://terac.com/docs/researchers/opportunities/creating-an-opportunity) Build a study with the AI copilot or the step-by-step builder, then launch it to verified experts. An opportunity is a job an expert can apply to. You define who you want, what they will do, and how much they earn; Terac sources verified experts and collects their submissions. There are two ways to build one: * **Start from a brief (fastest).** Describe your study to the AI copilot in plain language, or upload a brief, and it writes the fields for you. * **Build step-by-step.** Move through Setup, Audience, Tasks, and Review yourself, with the copilot available whenever you want a hand. Both edit the same underlying study, so you can mix them freely. The copilot makes its changes directly in the form, so nothing is hidden and everything stays editable. ## Draft it with the copilot [#draft-it-with-the-copilot] From **Opportunities**, click **Create** and pick a type to open the builder. The **Copilot** sits in the right rail. Describe who you want to talk to in plain English, including the role, the company profile, how many people you need, and the incentive. Send it, and the copilot fills in the form: the opportunity name, the internal name, and an expert-facing description. ![The copilot drafting an opportunity from a brief](https://terac.com/docs/researchers/creating-an-opportunity/02-copilot.png) The copilot works incrementally and tells you what it changed. Keep going in plain language to set the audience, add a screener, define the task, and set the incentive, or jump into any step and adjust the fields yourself. > Already have a brief written up? Use the attach button in the copilot to > upload it instead of typing. It accepts PDFs, images, and text files (up to 3 > MB), and you can send with just the file attached, the copilot builds the > study from what the document specifies. ## Choose your opportunity type [#choose-your-opportunity-type] Picking a type seeds the right steps and task defaults. You can change everything later. ![Choosing an opportunity type](https://terac.com/docs/researchers/creating-an-opportunity/01-type-picker.png) * **Unmoderated** - a task each expert completes on their own time, such as a usability test or an external link. * **Human-moderated** - a live call scheduled with each expert. The task is a "Schedule a call" block. ## Step 1: Setup [#step-1-setup] The Setup step holds the basics: * **Opportunity name** - public; experts may see this. Make it specific and compelling. * **Internal name** - only your team sees it. Use it to organize studies. * **Short description** - the expert-facing summary, up to 280 characters. Keep it clear and avoid biasing language; never state what qualifies, since every applicant reads it before the screener. * **Devices allowed** - restrict to Desktop or Mobile only when the task requires it. ## Step 2: Audience [#step-2-audience] The Audience step is where you decide who qualifies. ![Setting the audience, filters, and screener](https://terac.com/docs/researchers/creating-an-opportunity/03-audience.png) | Field | Description | | ----------------- | --------------------------------------------------------------------------------------------- | | Experts | How many submissions you want | | Filters | Hard requirements; experts who do not match cannot apply, so only require what you truly need | | Candidate profile | Like a job posting, used to source, rank and screen experts; never shown to them | | Required accounts | Verified connections (such as LinkedIn) an expert must link before applying | | Screeners | Soft qualification questions; mark answers as qualify, reject, or set a per-answer quota | Keep screeners short, use plain language, and include a "None of the above" option where it helps. See [Screening](https://terac.com/docs/researchers/opportunities/screening) and [Filters](https://terac.com/docs/researchers/recruitment/filters) for the full detail. ## Step 3: Tasks [#step-3-tasks] The Tasks step defines what experts actually do, in order. Give each task a clear, expert-facing title and description. ![Defining the task](https://terac.com/docs/researchers/creating-an-opportunity/04-tasks.png) * For an **unmoderated** opportunity, the task is something experts do on their own time: an in-app activity, or an external link to your survey or prototype (paste the URL experts open). * For a **human-moderated** opportunity, the task is **Schedule a call**: pick a scheduling provider (Calendly, Google Calendar, or Microsoft Bookings) and paste your booking link. See [Tasks](https://terac.com/docs/researchers/opportunities/tasks) for every task type. ## Step 4: Review and launch [#step-4-review-and-launch] The Review step asks **How do you want to set the incentive?** The incentive is what each expert earns, and you can change it any time before you launch. * **Priced by the Terac team** (recommended). Click **Send to the Terac team**. The step shows **Your study is with the pricing team** until the price is in, and **Cancel request** turns it back into a draft you can price again. When the price lands, **Here's what your study needs to fill** shows the incentive per expert, the number of experts, the fee per expert, and the total. Click **Accept and launch**, or **Adjust the price** first. * **Set the incentive yourself**. Click **Set my own incentive**. **Incentive / expert** is pre-filled with Terac's suggested price, and **What you'll pay** shows the incentive times the number of experts, the platform fee, and the **Grand total**. You can go below the suggestion after confirming **Launch below the suggested price?**, but not under $5. Click **Launch at my price**. Nothing is charged until you launch. Launch unlocks only when every step is complete (each shows a checkmark) and the opportunity has at least one screening question. If Terac has enabled launching without a screener for your organization, an opportunity with no screening questions can launch too, after you confirm it. See [Launching Without a Screener](https://terac.com/docs/researchers/opportunities/screening#launching-without-a-screener). > Make sure your organization has enough > [credits](https://terac.com/docs/researchers/finance/credits) to cover the grand total before > you launch. ## Best practices [#best-practices] * Lead with the copilot to get a complete draft in seconds, then refine by hand. * Only add hard filters you genuinely need; everything else belongs in screeners. * Keep screeners short and write a strong, specific listing description. * Pay a fair incentive; it is the biggest lever on response rate and quality. ## Duplicating an Opportunity [#duplicating-an-opportunity] To run a similar study with different parameters, use the **Duplicate** action on any existing opportunity. It copies the full configuration, including screeners, filters, and task setup, so you adjust only what is different. ## What's Next? [#whats-next] - [Set Up Screening](https://terac.com/docs/researchers/opportunities/screening): Design qualification criteria - [Configure Recruitment](https://terac.com/docs/researchers/recruitment): Choose how to find participants - [Opportunity Lifecycle](https://terac.com/docs/researchers/opportunities/opportunity-lifecycle): Understand opportunity states and transitions # Opportunities (https://terac.com/docs/researchers/opportunities) Create, configure, and manage research opportunities on Terac. Opportunities are the core unit of research on Terac. An opportunity defines what you need from participants, how they're screened, what tasks they complete, and how much they're paid. ## Opportunity Types [#opportunity-types] Terac supports two types of opportunities: | Type | Best For | | ------------------- | --------------------------------------------------------------------------- | | **Unmoderated** | A task each expert completes on their own time: a survey, activity, or link | | **Human-moderated** | A live call each expert books with you through your scheduling link | Picking a type seeds the right steps and task defaults. Everything stays editable afterwards. ## Opportunity Lifecycle [#opportunity-lifecycle] Every opportunity moves through a predictable set of stages: Draft → Active → Paused → Completed * **Draft** - the opportunity is being configured and has not been launched yet * **Active** - recruitment is live and participants can apply * **Paused** - recruitment is temporarily stopped; existing participants can still complete work * **Completed** - the opportunity is closed and no new submissions are accepted See [Opportunity Lifecycle](https://terac.com/docs/researchers/opportunities/opportunity-lifecycle) for the full breakdown of transitions and what each state means. ## What Goes Into an Opportunity [#what-goes-into-an-opportunity] - [Screening](https://terac.com/docs/researchers/opportunities/screening): Define who qualifies with screening questions and auto-grading rules - [Tasks](https://terac.com/docs/researchers/opportunities/tasks): Configure what participants need to complete - [Research Guides](https://terac.com/docs/researchers/opportunities/research-guides): Build discussion scripts for AI-moderated interviews - [Creating an Opportunity](https://terac.com/docs/researchers/opportunities/creating-an-opportunity): Step-by-step walkthrough of opportunity creation ## Key Concepts [#key-concepts] * **Screening questions** - qualification questions asked during the application flow. Can be auto-graded or manually reviewed. * **Filters** - demographic and geographic criteria that determine which experts see your opportunity. * **Tasks** - the actual work participants complete (surveys, file uploads, interviews). * **Submissions** - a participant's end-to-end journey through your opportunity, from application to completion. * **Research guides** - structured discussion scripts the AI moderator follows during interviews. # Screening (https://terac.com/docs/researchers/opportunities/screening) How to design screening questions and configure auto-grading to qualify the right participants. Screening determines which applicants are qualified for your opportunity. You can combine demographic filters (handled before participants even see the opportunity) with screening questions (asked during the application flow). ## How Screening Works [#how-screening-works] Filters → Screening Questions → AI Interview → Qualified 1. **Filters** narrow the audience before they see the opportunity. Only experts matching your demographic, geographic, and professional criteria can view and apply. 2. **Screening questions** are asked during the application flow. They can be auto-graded or manually reviewed. 3. **AI screening interviews** (optional) use your research guide to conduct a voice conversation that evaluates fit. ## Screening Question Types [#screening-question-types] | Type | Format | Use Case | | ----------------- | ---------------------------- | ------------------------------- | | **Single select** | One answer from a list | "What is your primary role?" | | **Multi-select** | Multiple answers from a list | "Which tools do you use daily?" | | **Yes/No** | Boolean toggle | "Do you own a smartphone?" | ### Designing Good Screening Questions [#designing-good-screening-questions] > Write screening questions so the qualifying answer is not obvious. If > participants can guess what you want to hear, the screening loses its value. * Include plausible distractor answers alongside qualifying ones * Avoid leading phrasing that hints at the correct response * Keep questions concise and unambiguous * Test your questions with a colleague before launching ## Qualification Logic [#qualification-logic] Each answer option carries a disposition that determines what selecting it does: | Disposition | Behavior | | ---------------------- | ------------------------------------------------------------------------------------------ | | **Must select one of** | At least one option tagged this way must be selected. The default qualifying rule | | **Must select** | This exact option must be selected. Multi-select only, and every one tagged must be chosen | | **May select** | Acceptable; neither qualifies nor disqualifies | | **Reject** | Selecting this option disqualifies the participant | | **Flag for review** | Diverts an otherwise-qualified applicant to a human decision instead of auto-qualifying | > On a single-select question there is only one answer to give, so **Must > select** and **May select** have no meaning there. Use **Must select one of** > for the qualifying answers and **Reject** for the disqualifying ones. ### Auto-Grading [#auto-grading] Terac evaluates answers against these dispositions as soon as they are submitted: * If the qualifying conditions are met and no **reject** answers are selected, the participant passes * If any **reject** answer is selected, the participant is screened out immediately * If a **flag for review** answer is selected, the applicant is routed to a human instead * Results are instant with no manual review required ### Reviewing Applicants Yourself [#reviewing-applicants-yourself] Under **Applicant review** in the builder's Audience step or in Settings, choose **Manual review** to review screening responses before an applicant starts work, or **Auto-approve** to let qualified applicants start without your review. Auto-approve hides this step from the pipeline. This is separate from **Terac review**, Terac's internal screening review. Switching to Auto-approve does not approve applicants already waiting for review or override answers marked **Flag for review**. These applicants remain available in **All** and the **Applicant review** status filter, where you can approve or reject them even though the pipeline step is hidden. Terac staff use the same verdict controls in the builder and Settings: **When the judge says Approve** offers **Manual review** or **Auto-approve**, and **When the judge says Reject** offers **Manual review** or **Auto-reject**. Enabling Auto-reject in Settings asks for confirmation before rejecting pending applicants. ## Launching Without a Screener [#launching-without-a-screener] A screener is recommended, because it decides who reaches your paid task. Some studies need none, for example when the task screens participants itself. For those, Terac can let your organization launch opportunities with no screening questions. Ask your Terac contact to turn it on. Until then, an opportunity needs at least one screening question to launch. Once it is on, the builder keeps recommending a screener but no longer requires one. An opportunity with no screening questions shows **Add a screener for better matches** with the screener questions and on the Review step, with a button that adds one. Clicking **Launch at my price** or **Accept and launch** then asks **Launch without a screener?**. Only **Launch without a screener** launches it; **Add a screener** takes you back to the questions. Without a screener, everyone who matches your filters goes straight to the task, so the builder makes sure you approve the work before anyone is paid: * **Needs approval** holds every submission for your review, including on an external link, where a `completed` redirect moves the submission to Work review instead of paying it. * **Expert approves** is not available. A task already set to it is flagged, and the launch waits until you set it to **Needs approval**. Steps that only confirm something, such as an agreement or joining Slack, keep working as before, as long as one task waits for your review. * **Applicant review** has to be **Auto-approve**. Holding applicants for review needs screening responses to review, so **Manual review** is not available. You approve each submission from the Submissions tab, and the participant is paid once you do. If Terac runs its AI screening interview on your opportunity, applicants are still screened before the task, so launching only asks you to confirm. The same confirmation applies when you convert a pre-recruiting opportunity with **Convert to Active**. Once an opportunity without a screener is live, **Applicant review** stays on **Auto-approve** in Settings. To launch through the API, see [Launching Without a Screener](https://terac.com/docs/developers/guides/screening-questions#launching-without-a-screener). ## Quotas [#quotas] Quotas let you control how many participants you need for specific screening answers. This is useful when you need a balanced sample. For example, if you need 60 participants total with at least 20 from each of three experience levels: * Set the total participant cap to 60 * Add a quota for each experience level with a target of 20 Recruitment continues until all quota targets are met and the total cap is reached. Without quotas, recruitment is first-come-first-served. See the [API documentation](https://terac.com/docs/developers/guides/quotas) for programmatic quota configuration. ## AI Screening Interviews [#ai-screening-interviews] For opportunities that need deeper qualification, you can attach a research guide with a screening policy. The AI moderator will: 1. Conduct a voice interview following your discussion guide 2. Evaluate responses against your screening policy 3. Auto-qualify or disqualify based on the policy rules This is particularly useful when qualification depends on nuanced answers that simple multiple-choice questions cannot capture. See [Research Guides](https://terac.com/docs/researchers/opportunities/research-guides) for how to set up discussion guides and screening policies. ## What's Next? [#whats-next] - [Configure Filters](https://terac.com/docs/researchers/recruitment/filters): Target participants by demographics and geography - [Set Up Tasks](https://terac.com/docs/researchers/opportunities/tasks): Define what qualified participants need to complete - [Review Submissions](https://terac.com/docs/researchers/results/submissions): Manage the review workflow # Research Guides (https://terac.com/docs/researchers/opportunities/research-guides) Build structured discussion guides for AI-moderated interviews, including screening policies, stimulus materials, and insight generation. Research guides are structured discussion scripts that the AI moderator follows during interviews. They define the conversation flow, screening criteria, and stimulus materials for qualitative research. ## Creating a Research Guide [#creating-a-research-guide] 1. **Navigate to your project** Go to the project where you want the guide and click **New Guide**. 2. **Write the discussion guide** Define the questions and flow the AI moderator will follow. Structure your guide with sections and questions, just as you would for a human moderator. 3. **Add stimulus materials** Upload images, videos, or files that should be shown to participants during the interview. These can be presented at specific points in the conversation. 4. **Set the screening policy** Define rules that determine whether a participant qualifies based on their interview responses. 5. **Publish a version** When your guide is ready, publish a version to lock it in. You can continue editing and publish new versions as needed. ## Discussion Guide Structure [#discussion-guide-structure] A discussion guide typically includes: * **Introduction** - what the interview is about, how long it will take * **Warm-up questions** - easy questions to get the conversation started * **Core questions** - the main research questions you need answered * **Stimulus prompts** - points where images, videos, or prototypes are shown * **Wrap-up** - closing questions and next steps The AI moderator uses your guide as a framework but adapts naturally based on participant responses. It will ask follow-up questions, probe deeper on interesting answers, and skip sections that are not relevant. ### Follow-up Questions [#follow-up-questions] You can control how much the moderator probes. The follow-up setting on a question is the number of additional probing questions the moderator may ask after the participant's initial answer (for example, "What makes you say that?"). More follow-ups produce richer qualitative depth; fewer keep the interview shorter and more structured. ### Screen Sharing [#screen-sharing] If you need to see what a participant does (for example, walking through your product or a prototype), attach the relevant link or media to that section of the guide. The moderator requests the screen share at that point in the conversation, and the recording is captured alongside the interview. Screen recording requires a desktop browser. See [Technical Requirements](https://terac.com/docs/researchers/results/interviews) for participant-side constraints. ## Screening Policies [#screening-policies] A screening policy attached to your guide defines automatic qualification rules based on interview responses. The AI evaluates each interview against your policy and recommends whether the participant should pass or fail screening. This is particularly useful for: * Qualifying participants based on nuanced, open-ended answers * Screening for domain expertise that cannot be captured in multiple-choice questions * Detecting low-effort or dishonest responses ## Stimulus Materials [#stimulus-materials] You can upload stimulus materials that are shown to participants during the interview: | Material Type | Use Case | | ------------- | ------------------------------------------- | | **Images** | Screenshots, mockups, logos, product photos | | **Videos** | Walkthroughs, ads, product demos | | **Files** | Documents, prototypes, reference materials | Stimulus materials are presented at the points you specify in the discussion guide. You can also search the web for images directly from the guide editor. ## Version History [#version-history] Guides support version control: * **Draft** - your working copy that you can edit freely * **Published versions** - snapshots locked for use in opportunities * **Revert** - roll back to any previous published version > Always publish a new version before linking a guide to an opportunity. > Opportunities reference a specific published version, so changes to the draft > do not affect active opportunities. ## Insights [#insights] After interviews are completed, Terac can generate AI-powered insights from the transcripts. Insights include: * **Themes** - recurring patterns across multiple interviews * **Key quotes** - notable participant statements with timestamps * **Evidence** - links from insights back to the specific interview moments that support them You can also create insights manually and tag interview evidence to support your findings. ## What's Next? [#whats-next] - [Configure Screening](https://terac.com/docs/researchers/opportunities/screening): Set up qualification criteria for your opportunity - [View Insights](https://terac.com/docs/researchers/results/insights): Analyze interview data with AI-generated insights - [Review Interviews](https://terac.com/docs/researchers/results/interviews): Watch recordings and read transcripts # Opportunity Lifecycle (https://terac.com/docs/researchers/opportunities/opportunity-lifecycle) Understanding opportunity states, transitions, and what happens at each stage. Every opportunity moves through a series of states from creation to completion. Understanding these states helps you manage your research effectively. ## Opportunity States [#opportunity-states] Draft → Active → Paused → Completed | State | Description | | ------------- | ---------------------------------------------------------------------------------------------- | | **Draft** | The opportunity is being configured. It is not visible to participants. | | **Active** | Recruitment is live. Participants can discover and apply to the opportunity. | | **Paused** | Recruitment is temporarily stopped. Existing participants can still complete in-progress work. | | **Completed** | The opportunity is closed. No new applications or submissions are accepted. | ## State Transitions [#state-transitions] ### Draft to Active [#draft-to-active] Launching an opportunity moves it from Draft to Active. Launch unlocks only once every step of the builder is complete, and Terac verifies: * The opportunity has a name and an expert-facing description * An incentive per expert is set * Your organization has enough credits to cover the grand total ### Active to Paused [#active-to-paused] Pausing an opportunity stops new recruitment while letting current participants finish. Use this when: * You've reached enough participants temporarily * You need to adjust screening criteria * You want to review early submissions before continuing ### Paused to Active [#paused-to-active] Resuming a paused opportunity reactivates recruitment. All configuration remains unchanged. ### Active or Paused to Completed [#active-or-paused-to-completed] Stopping an opportunity closes it permanently. Outstanding submissions in progress will still be processed, but no new applications are accepted. > **Warning** > > Stopping an opportunity cannot be undone. If you want to temporarily halt > recruitment, use Pause instead. ## Submission States Within an Opportunity [#submission-states-within-an-opportunity] While the opportunity itself has a lifecycle, each participant's submission also moves through its own states: Screening → Not started → In Progress → Work review → Approved For the full list of submission states and what each one means, see [Submissions](https://terac.com/docs/researchers/results/submissions). ## Adjusting a Live Opportunity [#adjusting-a-live-opportunity] You can modify most settings on an active opportunity without pausing: * **Participant cap** - increase or decrease the target number * **Filters** - adjust demographic or geographic targeting * **Compensation** - change the per-participant payout (applies to new participants only) * **Screening questions** - edit questions on a live study > Editing screening questions or broadening filters on a live study does not > re-evaluate participants who have already been screened. Changes apply only to > participants screened after the change. When you broaden filters, experts who > were previously out of scope can now qualify and may be invited. Larger changes to task structure are best made by pausing the opportunity first so participants do not encounter the task mid-edit. ## What's Next? [#whats-next] - [Creating an Opportunity](https://terac.com/docs/researchers/opportunities/creating-an-opportunity): Build a new opportunity from scratch - [Manage Submissions](https://terac.com/docs/researchers/results/submissions): Track participant progress - [Recruitment](https://terac.com/docs/researchers/recruitment): How participants find your opportunity # Tasks (https://terac.com/docs/researchers/opportunities/tasks) Configure the work participants complete after passing screening. Tasks define what participants actually do in your opportunity. After a participant passes screening, they're presented with the task you've configured. ## Task Types [#task-types] | Type | Description | Best For | | ------------------- | -------------------------------------------------- | ---------------------------------------- | | **Survey** | Structured questions answered in the app | Quick data collection, opinion gathering | | **File Upload** | Participants submit files | Screenshots, documents, code samples | | **Interview** | AI-moderated voice conversation | In-depth qualitative research | | **Activity** | An in-app activity or an external link you provide | Usability tests, your own survey tool | | **Schedule a call** | Participant books a slot on your calendar link | Human-moderated sessions | | **Agreement** | Participant accepts terms or signs a document | NDAs and consent before work begins | An opportunity can string several tasks together in order, so you can combine these: for example an agreement, then a survey, then an interview. ## Configuring Tasks [#configuring-tasks] ### Survey Tasks [#survey-tasks] Surveys consist of questions that participants answer directly in the Terac interface. You can include various question formats and add instructions for each section. ### File Upload Tasks [#file-upload-tasks] For file uploads, specify: * What files you need (type, format, size constraints) * Clear instructions on what to submit * Any examples or templates To collect everything at once, open the **⋯** menu on the opportunity in your Opportunities list and choose **Download all files**. You get one zip with two folders: **Approved** holds the files from approved file upload tasks, and **Pending review** holds the files still waiting for your review. Inside each, there is a folder per submission. ### Interview Tasks [#interview-tasks] Interview tasks use the AI moderator to conduct structured conversations. You'll need a [research guide](https://terac.com/docs/researchers/opportunities/research-guides) to define: * The discussion script the AI follows * Stimulus materials to show during the interview (images, videos, prototypes) * Expected duration ### Multiple Tasks [#multiple-tasks] An opportunity can contain several tasks, presented in the order you define. For example, you might ask participants to: 1. Complete a survey about their background 2. Upload screenshots of their workflow 3. Participate in a follow-up interview Give each task a clear, expert-facing title and description so participants know what they are agreeing to before they start. ## Timing [#timing] Recruitment runs until you reach your participant target or you stop the opportunity, so there is no per-task deadline for you to set. Participants who start and go quiet are eventually marked abandoned or timed out and their slot is freed. If your fieldwork has to close by a specific date, say so during the [feasibility check](https://terac.com/docs/researchers/recruitment/feasibility) so recruitment is paced to hit it. ## Review Methods [#review-methods] After participants submit their work, it is resolved according to the review method set on each task: | Method | Description | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | **Manual review** | The submission waits in Work review until you approve or reject it | | **Auto-approve** | A `completed` redirect from your external tool approves it and pays the participant. On a task without a redirect it waits for you instead | | **Self-report** | The participant's own completion approves it and pays them, with no review from you | The task builder offers two of these. **Expert approves** is self-report. **Needs approval** is manual review, except on an external-link task, where it is auto-approve; manual review on an external link is set through the API. See [External Surveys & Redirect URLs](https://terac.com/docs/researchers/integrations/external-surveys). On an opportunity launching without a screener, **Needs approval** is manual review on every task, external links included, and **Expert approves** is not available, so you approve each submission before anyone is paid. See [Launching Without a Screener](https://terac.com/docs/researchers/opportunities/screening#launching-without-a-screener). See [Reviewing Work](https://terac.com/docs/researchers/results/reviewing-work) for the workflow. ## What's Next? [#whats-next] - [Research Guides](https://terac.com/docs/researchers/opportunities/research-guides): Build discussion scripts for AI interviews - [Reviewing Work](https://terac.com/docs/researchers/results/reviewing-work): Learn the approval workflow - [Opportunity Lifecycle](https://terac.com/docs/researchers/opportunities/opportunity-lifecycle): Understand what happens at each stage # Filters (https://terac.com/docs/researchers/recruitment/filters) Target the right participants using demographic, geographic, and professional filters. Filters determine which experts can see and apply to your opportunity. Only experts matching all of your filters will have the opportunity appear in their listing. ## How Filters Work [#how-filters-work] Filters are applied before participants see your opportunity. They act as hard requirements: if an expert does not match, they never see the opportunity. This is different from screening questions, which are asked during the application flow. Think of filters as "who can apply" and screening questions as "who qualifies among those who apply." ## Filter Categories [#filter-categories] ### Geographic [#geographic] | Filter | Type | Description | | ------------- | ------------ | ----------------------------------------- | | Country | Multi-select | Country of residence | | Country of IP | Multi-select | Country detected from IP address | | State | Multi-select | State or province (requires country) | | City | Multi-select | City (requires country, optionally state) | > Geographic filters cascade: state options depend on the selected country, and > city options depend on the selected country and state. ### Demographic [#demographic] | Filter | Type | Description | | ------------------ | ------------- | -------------------------- | | Age | Integer range | Minimum and maximum age | | Sex | Select | Biological sex | | Gender | Multi-select | Gender identity | | Language | Multi-select | Languages spoken | | Household Income | Select | Income bracket | | Level of Education | Select | Highest education level | | Marital Status | Select | Relationship status | | Children | Select | Whether they have children | | Home Owner | Select | Home ownership status | ### Professional [#professional] | Filter | Type | Description | | ----------------- | ------------ | ------------------------------------- | | Job Function | Multi-select | Primary job area | | Industry | Multi-select | Industry sector | | Seniority | Select | Career level | | Company Size | Select | Employer size | | Work Setting | Select | Remote, hybrid, or in-person | | Employment Status | Select | Full-time, part-time, freelance, etc. | ### Participation History [#participation-history] | Filter | Type | Description | | ------------------------- | --------- | --------------------------------------------------------------- | | Has taken opportunity | Reference | Only include experts who participated in a specific opportunity | | Has not taken opportunity | Reference | Exclude experts who participated in a specific opportunity | ## Checking Filter Reach [#checking-filter-reach] Before launching your opportunity, you can see how many experts in the Terac panel match your filters. This helps you: * Avoid overly narrow filter combinations that return few matches * Estimate how long recruitment will take * Decide whether to relax certain criteria Filter match counts live on the **Feasibility** page in your organization. See [Feasibility & Coverage](https://terac.com/docs/researchers/recruitment/feasibility). ## Adjusting Filters After Launch [#adjusting-filters-after-launch] You can update filters on an active opportunity. Changes take effect immediately: * **Loosening filters** (removing criteria) exposes the opportunity to more experts * **Tightening filters** (adding criteria) reduces the pool but does not affect participants who have already applied ## What's Next? [#whats-next] - [Feasibility & Coverage](https://terac.com/docs/researchers/recruitment/feasibility): Check how many experts match your filters - [Set Up Screening](https://terac.com/docs/researchers/opportunities/screening): Add qualification questions - [API Filter Reference](https://terac.com/docs/developers/guides/filters): Programmatic filter configuration # Feasibility & Coverage (https://terac.com/docs/researchers/recruitment/feasibility) Check whether Terac can fill your audience before you launch, what a niche request needs, and typical recruitment timelines. Before you commit to a study, it helps to know whether your audience is reachable, how long it will take, and what it will cost. This is the most common pre-launch question, especially for niche or hard-to-reach audiences. ## Checking Feasibility Yourself [#checking-feasibility-yourself] For most audiences you can gauge feasibility directly in the dashboard. The **Feasibility** page in your organization shows how many experts in the Terac panel match a given set of demographic, geographic, and professional [filters](https://terac.com/docs/researchers/recruitment/filters). Adjust the filters and the available-expert count updates as you go. Use it to avoid over-narrow filter combinations and to estimate how quickly recruitment will fill. ## Requesting a Feasibility Check for Niche Audiences [#requesting-a-feasibility-check-for-niche-audiences] If your audience is specialized (for example, a specific professional credential, an enterprise role, or a narrow geography), Terac may need to run targeted outreach beyond the existing panel. To get an accurate quote and timeline quickly, share: | Detail | Why it matters | | ------------------- | ------------------------------------------------------------------------------------- | | **Audience** | Exactly who qualifies, including any must-have criteria | | **Methodology** | Survey, AI-moderated interview, human-moderated session, multi-part, or in-field task | | **Length (LOI)** | Expected length of interview or time on task, which drives the incentive | | **Sample size (n)** | How many completes you need | | **Geography** | Countries or regions in scope | | **Timeline** | When you need fieldwork to finish | With these, Terac can confirm whether the audience is feasible and return a [cost per participant](https://terac.com/docs/researchers/finance/pricing) and an estimated timeline. > Length of interview and audience difficulty are the biggest drivers of both > feasibility and incentive. A longer or more specialized study needs a higher > incentive to recruit reliably. ## Typical Timelines [#typical-timelines] Timelines depend on how common your audience is and how long the task takes. As general guidance: * **Common consumer and standard professional audiences** typically fill within about a week, with the first completes often arriving within a few days of launch. * **Niche or custom audiences** that require targeted outreach typically take longer, on the order of one to two weeks. Your Terac contact will give you a specific estimate for your study as part of the feasibility check, and recruiters keep your study's delivery estimate updated in the dashboard once it is live. ## Coverage [#coverage] Terac maintains a verified panel with broad consumer coverage and specialized professional (B2B) coverage across many industries and seniority levels. For audiences that are not yet well represented in the panel, Terac runs targeted recruitment to source and verify new experts before sending them to your study. If you are unsure whether an audience is in scope, ask for a feasibility check. It is the fastest way to find out. ## What's Next? [#whats-next] - [Filters](https://terac.com/docs/researchers/recruitment/filters): Define who qualifies for your opportunity - [Recruitment](https://terac.com/docs/researchers/recruitment): How Terac reaches participants - [Pricing](https://terac.com/docs/researchers/finance/pricing): How cost per participant is built # Recruitment (https://terac.com/docs/researchers/recruitment) How Terac finds the right participants for your opportunity, and the targeting you control. Recruitment is the process of getting qualified participants into your opportunity. You define who qualifies; Terac sources and screens matching experts and delivers their submissions. ## Recruitment Pipeline [#recruitment-pipeline] Define Filters → Experts Apply → Screen → Qualified 1. **Define filters** to narrow your target audience by demographics, geography, and professional attributes 2. **Experts apply** once your opportunity is live and matched to them 3. **Screen** applicants with questions, quotas, or AI-moderated interviews 4. **Qualified** participants proceed to complete your opportunity tasks ## Recruitment Tools [#recruitment-tools] - [Feasibility & Coverage](https://terac.com/docs/researchers/recruitment/feasibility): Check whether your audience is reachable before you launch - [Filters](https://terac.com/docs/researchers/recruitment/filters): Target participants by age, location, job function, and more ## How Terac Reaches Participants [#how-terac-reaches-participants] Terac sources participants for your opportunity through its verified panel and, where an audience needs it, through targeted recruitment of new experts. Channel selection and pacing are handled by the Terac recruitment team as part of delivering your study; you control the audience definition through [filters](https://terac.com/docs/researchers/recruitment/filters) and [screeners](https://terac.com/docs/researchers/opportunities/screening). If you have your own audience you want to recruit from, or you want participants sourced a particular way, raise it with your Terac contact during the [feasibility check](https://terac.com/docs/researchers/recruitment/feasibility). ## Monitoring Recruitment [#monitoring-recruitment] The Recruitment tab within each opportunity is where you review and adjust your targeting: hard filters, the candidate profile, and screeners. Filter match counts (how many experts in the panel match your criteria) are available on the organization's Feasibility page. Progress against your participant target is shown in the Submissions tab, broken down by funnel stage. # Avoiding Bias (https://terac.com/docs/researchers/research-methods/avoiding-bias) Recognize common research biases, distinguish systematic from random error, and use pilot testing to catch problems before they cost you a full study. Bias is anything that pushes your results away from the truth in a consistent direction. Unlike random noise, it does not average out with a bigger sample. This page covers the biases most likely to affect online studies and the single most effective way to catch them: piloting. ## Common Biases [#common-biases] | Bias | What it is | Mitigation | | ---------------------------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | | **Confirmation bias** | Designing or interpreting a study to favor what you already believe | [Preregister](https://terac.com/docs/researchers/research-methods/preregistration); predefine analyses | | **Question-order bias** | Earlier questions changing answers to later ones | Randomize or counterbalance order | | **Method bias** | An artifact of how you measured, not the construct itself | Measure key variables more than one way | | **Social desirability bias** | Participants answering to look good rather than honestly | Stress anonymity; normalize phrasing | | **Selection bias** | The people who took part differ systematically from your target population | Define and report your sample; reason about who is missing | ## Systematic vs Random Error [#systematic-vs-random-error] Every measurement carries error. The two kinds behave very differently. * **Random error** is noise: unpredictable, scattered in both directions. It reduces precision but does not shift your average. More data and reliable instruments shrink it. * **Systematic error** is bias: a consistent offset in one direction. It shifts your result regardless of sample size. You cannot fix it with more participants; you have to fix the source. > A bigger sample helps with random error and does nothing for systematic error. > This is why design quality matters more than raw numbers. ## Why Pilot Testing Matters [#why-pilot-testing-matters] A pilot is a small run of your full study before you commit your budget. It is the cheapest insurance you can buy, and it catches problems that are invisible from the inside: * Questions that participants read differently than you intended. * Branching or display logic that sends people down the wrong path. * Instructions that are unclear or attention checks that are unfair. * Timing estimates that are wildly off, which affects fair pay. * Data that does not export or merge the way you expected. ### Running a Pilot on Terac [#running-a-pilot-on-terac] 1. Create your opportunity with a small [participant cap](https://terac.com/docs/researchers/opportunities/creating-an-opportunity), often 5 to 10 people. 2. Consider targeting experienced participants for a cleaner technical read. 3. Inspect the raw output end to end: does every field arrive, in the format you expect? 4. Fix what you found, then launch the full study, excluding pilot participants if their data should not be combined. > **Warning** > > Decide in advance whether pilot data will be included in your final dataset. > If you change the instrument after piloting, the pilot responses are not > comparable and should be excluded. ## Fair Attention Checks [#fair-attention-checks] Attention checks reduce one kind of error but can introduce another if they are unfair. A fair check tests whether someone read instructions that genuinely matter for the task. An unfair check (ambiguous wording, trick phrasing) fails attentive participants and adds noise. Pilot your checks alongside the rest of the instrument, and treat a high failure rate as a signal that the check, not the participant, may be the problem. ## What's Next? [#whats-next] - [Online Research Validity](https://terac.com/docs/researchers/research-methods/online-research-validity): The strengths and limits of online samples - [Writing Good Surveys](https://terac.com/docs/researchers/research-methods/writing-good-surveys): Reduce bias at the question level # Analysis and Inference (https://terac.com/docs/researchers/research-methods/analysis-and-inference) Significance testing, Bayesian methods, sharper predictions, and what it takes to build a real theory. Choose your analysis framework before you collect data, and commit to it. This page covers the two dominant statistical frameworks, how to make your predictions more informative, and what separates a theory from a description. ## Two Statistical Frameworks [#two-statistical-frameworks] ### Null Hypothesis Significance Testing [#null-hypothesis-significance-testing] **NHST** asks: if there were truly no effect, how surprising is the data I observed? The answer is the **p-value**. A small p-value (below a threshold such as .05 or .01) leads you to reject the null hypothesis. Two errors are always in play: * **Type I error (false positive):** concluding there is an effect when there is not. Controlled by your alpha (for example, 5%). * **Type II error (false negative):** missing an effect that is really there. Tied to [statistical power](https://terac.com/docs/researchers/research-methods/sampling-and-power). > **Warning** > > A p-value tells you how incompatible the data are with the null hypothesis. It > is not the probability that your hypothesis is true, and statistical > significance is not the same as practical importance. ### Bayesian Statistics [#bayesian-statistics] Bayesian analysis asks a different question: given the data, how much should I believe each competing hypothesis? The **Bayes factor** quantifies the relative evidence for one hypothesis over another. A key advantage: Bayesian methods can provide evidence **for** a null (no-effect) hypothesis, not just fail to reject it. NHST can never confirm "no effect"; it can only fail to find one. When distinguishing "no evidence of an effect" from "evidence of no effect" matters, Bayesian methods are the better fit. | | NHST | Bayesian | | ------------------------- | --------------------------------------- | ------------------------ | | **Core output** | p-value | Bayes factor / posterior | | **Can support the null?** | No | Yes | | **Needs priors?** | No | Yes | | **Common tools** | Most stats packages, G\*Power for power | JASP, R, Stan | Neither framework is universally correct. Pick the one that answers your question, and state it in your [preregistration](https://terac.com/docs/researchers/research-methods/preregistration). ## Make Sharper Predictions [#make-sharper-predictions] A theory that predicts only "there will be a difference" is hard to falsify, because almost any non-zero result confirms it. You make your theory more useful, and more testable, by predicting the size of an effect: * **Point prediction:** the effect will be approximately X. * **Range prediction:** the effect will fall between X and Y. Specific predictions give clear falsification criteria and stop you from reporting trivially small but statistically significant results as meaningful. ## From Findings to Theory [#from-findings-to-theory] A single significant result is a finding, not a theory. Aim for **converging evidence**: results that hold across different methods, measures, and samples. Triangulating with multiple approaches guards against any one method's quirks. A genuine theory explains more than what happened. It accounts for: * **Why** the effect occurs (the mechanism). * **How** it operates (the process). * **When** it holds and when it breaks down (the boundary conditions). Raw data, a list of variables, or a bare hypothesis are inputs to a theory, not the theory itself. ## What's Next? [#whats-next] - [Avoiding Bias](https://terac.com/docs/researchers/research-methods/avoiding-bias): Keep error out of your measurements - [Preregistration](https://terac.com/docs/researchers/research-methods/preregistration): Commit to your analysis before collecting data # Research Methods (https://terac.com/docs/researchers/research-methods) A practical best-practice guide for designing rigorous studies on Terac, from framing your question to analyzing your results. Good data starts long before you launch an opportunity. This guide walks through the methodological decisions that determine whether your results are trustworthy: how you frame your question, write your items, choose your sample, and interpret what you find. It is written to be useful whether you are running a quick survey or a multi-wave study with domain experts. None of it is required to use Terac, but following it will materially improve the quality of your data. > These pages cover research design. For how to configure the platform itself, > see [Creating an > Opportunity](https://terac.com/docs/researchers/opportunities/creating-an-opportunity), > [Screening](https://terac.com/docs/researchers/opportunities/screening), and > [Filters](https://terac.com/docs/researchers/recruitment/filters). ## The Four Phases [#the-four-phases] A well-run study moves through four phases. Each page below maps to one of them. Design → Build → Sample → Analyze 1. **Design** the study. Decide whether you are exploring or confirming, define your population, and commit to a plan. 2. **Build** the instrument. Write clear, unbiased questions and lay them out so participants can answer accurately. 3. **Sample** the right people, at the right size, with enough statistical power to detect what you care about. 4. **Analyze** with a framework you chose in advance, and aim for conclusions that hold up. ## Start Here [#start-here] - [Research Design](https://terac.com/docs/researchers/research-methods/research-design): Exploratory vs confirmatory research, defining your target population, and avoiding HARKing - [Preregistration](https://terac.com/docs/researchers/research-methods/preregistration): Lock in your hypotheses and analysis plan before you collect data - [Writing Good Surveys](https://terac.com/docs/researchers/research-methods/writing-good-surveys): Phrasing, question order, response scales, and visual design - [Sampling and Power](https://terac.com/docs/researchers/research-methods/sampling-and-power): Sampling methods, representativeness, and how big your sample needs to be - [Analysis and Inference](https://terac.com/docs/researchers/research-methods/analysis-and-inference): Significance testing, Bayesian methods, and building a real theory - [Avoiding Bias](https://terac.com/docs/researchers/research-methods/avoiding-bias): Common biases, measurement error, and why piloting matters - [Online Research Validity](https://terac.com/docs/researchers/research-methods/online-research-validity): The strengths and limits of online samples, and when they fit your question # Online Research Validity (https://terac.com/docs/researchers/research-methods/online-research-validity) The strengths and limits of online samples, how they compare to lab studies, and when they are the right tool for your question. Online research platforms have become a mainstream tool across the social and behavioral sciences. Used well, they produce data comparable in quality to lab studies, and far faster. Used carelessly, they inherit known limitations. This page covers both sides so you can judge whether an online sample fits your question. ## Advantages [#advantages] * **Speed:** recruitment that takes weeks in a lab can complete in hours, so you can iterate on designs quickly. * **Reach:** access to participants well beyond a single campus or city, including niche populations you could not easily recruit locally. * **Diversity:** online pools are typically more varied in age, background, and occupation than the convenience samples lab studies often rely on. * **Comparability to the lab:** across many replications, online studies recover classic effects at quality comparable to in-person data collection. * **Lower cost per participant** and less logistical overhead. ## Limitations [#limitations] No sampling method is free of limitations. The ones most relevant to online panels: | Limitation | What it means | What helps | | ------------------------ | -------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | | **Rapid-responder bias** | The fastest responders may differ from the broader pool | Avoid treating speed as quality; keep studies open long enough | | **WEIRD skew** | Pools lean Western, Educated, Industrialized, Rich, Democratic | State your population; use [filters](https://terac.com/docs/researchers/recruitment/filters) deliberately | | **Selection bias** | People who join panels differ from non-joiners | Report who is and is not represented | | **Satisficing** | Some participants do the minimum to finish | Fair attention checks, sensible pay, clear instructions | > WEIRD stands for Western, Educated, Industrialized, Rich, and Democratic. It > is a reminder that conclusions from one population may not transfer to all > humans. Name your target population explicitly rather than implying > universality. ## Is Online Research Legitimate? [#is-online-research-legitimate] Yes, when the question suits it and the study is well designed. The evidence base is now large: properly run online studies replicate established findings and meet standards expected by peer reviewers. Legitimacy comes from design, transparency, and data-quality safeguards, not from the venue being online or in person. Online samples are a strong fit for surveys, experiments with self-contained tasks, attitude and preference measurement, and reaching specialized or geographically dispersed populations. They are a weaker fit for studies that require tightly controlled physical environments, specialized equipment, or in-person observation. ## Strengthening Online Data Quality [#strengthening-online-data-quality] Several practices, several of them built into Terac, narrow the gap between online and lab data: * **Verified participants** reduce fraud and duplicate accounts. See [Attestations](https://terac.com/docs/experts/how-to/attestations) for how identity and credentials are verified. * **Fair attention and comprehension checks** filter low-effort responses without penalizing honest ones. See [Avoiding Bias](https://terac.com/docs/researchers/research-methods/avoiding-bias). * **Quality review** of submissions, manual or AI-assisted, catches problems before they enter your dataset. See [Reviewing Work](https://terac.com/docs/researchers/results/reviewing-work). * **Fair pay and clear instructions** reduce satisficing and dropout. ## Reporting Your Sample [#reporting-your-sample] When you write up an online study, state where the sample came from, the population it represents, the size and how you arrived at it, your exclusion rules, and the quality safeguards you used. Transparency on these points is what lets reviewers and readers trust the data. ## What's Next? [#whats-next] - [Sampling and Power](https://terac.com/docs/researchers/research-methods/sampling-and-power): Size your sample to your question - [Avoiding Bias](https://terac.com/docs/researchers/research-methods/avoiding-bias): Keep systematic error out of your results # Preregistration (https://terac.com/docs/researchers/research-methods/preregistration) Lock in your hypotheses and analysis plan before you collect data to guard against p-hacking and HARKing. Preregistration means documenting your research plan, your hypotheses, your design, and your analysis, and timestamping it in a public archive **before** you collect or look at your data. It is the strongest single thing you can do to make a confirmatory study credible. ## Why Preregister [#why-preregister] When the analysis plan is fixed in advance, you remove the researcher degrees of freedom that quietly inflate false positives: * **p-hacking:** trying many analyses and reporting only the one that crossed significance. * **HARKing:** presenting a post hoc hypothesis as an a priori one. See [Research Design](https://terac.com/docs/researchers/research-methods/research-design). * **Selective reporting:** dropping conditions, outcomes, or exclusions that did not work out. A preregistration lets a reader distinguish what you predicted from what you discovered. Both are valuable, but only the first counts as confirmatory evidence. ## What to Include [#what-to-include] A useful preregistration is specific enough that someone else could run your analysis without asking you anything: | Section | What to specify | | -------------- | ----------------------------------------------------------------------------- | | **Hypotheses** | The exact predictions, stated directionally where possible | | **Design** | Conditions, manipulations, and how participants are assigned | | **Sample** | Target size, how you arrived at it, and stopping rule | | **Measures** | Every variable and exactly how it is computed | | **Exclusions** | Rules for removing participants or responses, written before you see the data | | **Analysis** | The specific test for each hypothesis, including how you handle covariates | > Decide your participant exclusion rules in advance. Removing data after seeing > results, even for defensible reasons, reopens the door to bias. ## Where to Preregister [#where-to-preregister] Independent registries timestamp your plan so it cannot be edited silently: * **OSF (Open Science Framework):** flexible, widely used, supports embargoes. * **AsPredicted:** a short standardized form, good for simple designs. ## Registered Reports [#registered-reports] A **Registered Report** goes one step further. You submit your introduction, methods, and analysis plan to a journal for peer review *before* collecting data. If the plan is sound, the journal grants **in-principle acceptance**, committing to publish the results regardless of how they turn out. This removes publication bias toward positive findings and rewards good design over lucky results. ## Preregistration and Terac [#preregistration-and-terac] A few platform settings are worth fixing in your preregistration because they cannot change once an opportunity is live: * Your [participant cap](https://terac.com/docs/researchers/opportunities/creating-an-opportunity) and any [quotas](https://terac.com/docs/researchers/opportunities/screening). * Your [filters](https://terac.com/docs/researchers/recruitment/filters) and screening criteria. * Your attention and quality checks, which double as preregistered exclusion criteria. ## What's Next? [#whats-next] - [Sampling and Power](https://terac.com/docs/researchers/research-methods/sampling-and-power): Justify your target sample size before you register it - [Analysis and Inference](https://terac.com/docs/researchers/research-methods/analysis-and-inference): Choose the analysis you will commit to # Research Design (https://terac.com/docs/researchers/research-methods/research-design) Decide whether you are exploring or confirming, define your target population, and avoid common design traps like HARKing. Before you write a single question, be clear about what kind of study you are running. The biggest threats to credible findings are introduced here, at the design stage, not during analysis. ## Exploratory vs Confirmatory Research [#exploratory-vs-confirmatory-research] Every study leans one of two ways, and conflating them is one of the most common mistakes in applied research. | | Exploratory | Confirmatory | | ---------------------- | ------------------------------------------------------- | -------------------------------------------------------------- | | **Goal** | Discover possible relationships and generate hypotheses | Test a specific prediction stated in advance | | **Mindset** | Open, flexible, descriptive | Pre-committed, falsifiable | | **Output** | Hypotheses worth testing later | Evidence for or against a hypothesis | | **Risk if mislabeled** | Treating a chance pattern as a confirmed result | Discarding genuine discoveries because they were not predicted | Exploratory work is valuable. Problems only arise when an exploratory finding is reported as though it were confirmatory. ### HARKing [#harking] **HARKing** stands for Hypothesizing After the Results are Known: running an analysis, finding a pattern, and then writing it up as though you had predicted it all along. It inflates false positives because a hypothesis built to fit the data will almost always fit the data. > **Warning** > > If you only formed a hypothesis after seeing the results, say so. Report it as > exploratory and, ideally, confirm it in a fresh study before drawing strong > conclusions. The cleanest defense against HARKing is to write your hypotheses down before you collect data. See [Preregistration](https://terac.com/docs/researchers/research-methods/preregistration). ## Defining Your Target Population [#defining-your-target-population] Your **target population** is the group you want your conclusions to apply to. Define it concretely in terms of observable criteria, because those criteria become your recruitment settings on Terac. Work through three questions: 1. **Who** are you studying? Age range, location, profession, behavior, or experience level. 2. **Why** those people? Tie each criterion back to your research question. If a criterion does not change who should qualify, drop it. 3. **How** will you reach them? Map each criterion to a [filter](https://terac.com/docs/researchers/recruitment/filters) or a [screening question](https://terac.com/docs/researchers/opportunities/screening). > Filters narrow who can see your opportunity before they apply. Screening > questions qualify people during the application flow. Use filters for hard > demographic or professional requirements, and screening questions for anything > that needs a judged answer. ### Operationalizing Your Constructs [#operationalizing-your-constructs] A construct is the abstract thing you care about (for example, "brand loyalty" or "comfort with AI tools"). Operationalizing means turning it into something you can actually measure with concrete items. State, in advance, exactly how each construct maps to the questions you will ask. If two researchers would measure your construct differently, your definition is not specific enough yet. ## A Pre-Build Checklist [#a-pre-build-checklist] Before moving on to building your instrument, you should be able to answer: * Is this study exploratory or confirmatory? * What is my exact research question or hypothesis? * Who is my target population, in observable criteria? * How does each criterion map to a filter or screening question? * How will I measure each construct? ## What's Next? [#whats-next] - [Preregistration](https://terac.com/docs/researchers/research-methods/preregistration): Commit to your plan before collecting data - [Writing Good Surveys](https://terac.com/docs/researchers/research-methods/writing-good-surveys): Turn your constructs into clear questions # Sampling and Power (https://terac.com/docs/researchers/research-methods/sampling-and-power) Choose a sampling method, understand representativeness, and work out how large your sample needs to be. Who you recruit, and how many, determines what you can conclude. This page covers the sampling choices behind a study and how to size it so you can actually detect the effect you care about. ## Sampling Methods [#sampling-methods] Sampling methods fall into two families. ### Probability Sampling [#probability-sampling] Every member of the population has a known, non-zero chance of selection. These methods support the strongest statistical generalization. * **Simple random:** everyone has an equal chance. * **Stratified random:** divide the population into strata (for example, age groups) and sample within each, guaranteeing representation. * **Systematic:** select every nth member from an ordered list. ### Non-Probability Sampling [#non-probability-sampling] Selection is based on availability or judgment. Faster and cheaper, but generalization rests on argument rather than pure probability. * **Convenience:** whoever is readily available. * **Quota:** fill predetermined counts for specific subgroups. * **Purposive:** deliberately target people with specific characteristics. > Online panels, including Terac, are non-probability samples by nature. You can > still get close to a target distribution using > [filters](https://terac.com/docs/researchers/recruitment/filters) and > [quotas](https://terac.com/docs/researchers/opportunities/screening), and you strengthen > generalization by reasoning explicitly about who is and is not represented. ## Representativeness [#representativeness] A sample is **representative** when its composition on the characteristics that matter mirrors the population you want to generalize to. Representativeness is always relative to specific variables: a sample can be representative for age and sex while skewing on income or geography. To approximate it on Terac: 1. Identify the variables your conclusions depend on. 2. Use [filters](https://terac.com/docs/researchers/recruitment/filters) to constrain the audience to your target population. 3. Use [quotas](https://terac.com/docs/researchers/opportunities/screening) to balance subgroups so no single group dominates. 4. Report the composition you achieved, and be explicit about variables you did not control. ## Statistical Power [#statistical-power] **Statistical power** is the probability of detecting an effect that is genuinely there. Underpowered studies waste effort and produce unreliable results, with positive findings that often fail to replicate. Power depends on four interacting factors. Fix any three and the fourth is determined: | Factor | Effect on power | | ------------------------------ | ------------------------------------------------------ | | **Significance level (alpha)** | A stricter alpha (for example .01 vs .05) lowers power | | **Effect size** | Larger true effects are easier to detect | | **Sample size** | More participants increase power | | **The statistical test** | Some tests extract more information than others | Aim for power between **.80 and .95**. Below .80 you are likely to miss real effects; chasing much higher power can demand impractically large samples. ### Sizing Your Sample Before You Launch [#sizing-your-sample-before-you-launch] Work it backwards: 1. Decide the **smallest effect size** that would be meaningful for your question. 2. Pick your **alpha** (commonly .05) and **target power** (commonly .80). 3. Use a power calculator to get the required **sample size**. Free tools like **G\*Power** handle the common designs. 4. Set your [participant cap](https://terac.com/docs/researchers/opportunities/creating-an-opportunity) to that number, and add a margin for exclusions you defined in your [preregistration](https://terac.com/docs/researchers/research-methods/preregistration). > **Warning** > > Decide your sample size in advance and commit to it. Peeking at results and > stopping once they cross significance inflates false positives. If you need an > adaptive stopping rule, plan it as a sequential design up front. ## What's Next? [#whats-next] - [Analysis and Inference](https://terac.com/docs/researchers/research-methods/analysis-and-inference): Choose the framework you will analyze with - [Online Research Validity](https://terac.com/docs/researchers/research-methods/online-research-validity): Understand what an online sample can and cannot tell you # Writing Good Surveys (https://terac.com/docs/researchers/research-methods/writing-good-surveys) Phrase questions clearly, order them to minimize bias, choose the right response scales, and lay out your instrument for accurate answers. The wording, order, and layout of your questions shape the answers you get. Small changes can move results more than the effect you are trying to measure. This page covers how to write items that capture what participants actually think. ## Phrasing Questions [#phrasing-questions] Each item should measure one thing, in plain language, without nudging the participant toward an answer. | Avoid | Why | Better | | ---------------------------------------------------------------------- | ----------------------------------------------------------------- | --------------------------------------------- | | **Double-barreled:** "Was the app fast and easy to use?" | Bundles two things; you cannot tell which one the answer is about | Ask about speed and ease separately | | **Leading:** "How much did you enjoy the new feature?" | Presumes enjoyment | "How did you feel about the new feature?" | | **Double negatives:** "Do you disagree that it should not be removed?" | Hard to parse, easy to misread | Rephrase positively | | **Loaded or jargon terms** | Different people read them differently | Use neutral, common words | | **Vague quantifiers:** "often", "sometimes" | Mean different things to different people | Give concrete anchors ("2 to 3 times a week") | > A good rule: if a colleague who has not seen your study could misread an item, > rewrite it. Test every item on someone before launching. ## Ordering Questions [#ordering-questions] Earlier questions prime later answers. A few principles: * **Open with easy, non-sensitive questions** to build comfort and momentum. * **Group related questions** so participants are not switching context constantly. * **Place sensitive questions later**, once some trust is established, but before fatigue sets in. * **Watch for carryover:** asking about a topic can change how someone answers a related question. If order might matter, **randomize** question or block order so any effect is spread evenly rather than confounded with content. ## Response Scales [#response-scales] ### Likert Scales [#likert-scales] A **Likert scale** measures agreement or intensity along an ordered set of options, typically five or seven points (for example, "Strongly disagree" to "Strongly agree"). They capture nuance that yes/no items miss. * **Five points** are easy to answer; **seven** capture more gradation. * Decide deliberately whether to include a **neutral midpoint**. A midpoint is honest when "neither" is a real position, but it can also be a place for low-effort participants to hide. * Label every point, not just the ends, so the scale means the same thing to everyone. * Keep the scale direction and labeling consistent across your survey. ### Choosing a Format [#choosing-a-format] Match the response format to the construct: ordered options for intensity, multiple choice for categories, numeric entry for counts. Mismatched formats (for example, forcing a continuous quantity into three buckets) throw away information you cannot recover later. ## Sensitive Questions [#sensitive-questions] For topics people may be reluctant to answer honestly: * **Emphasize anonymity** and explain how responses are used. * **Normalize the behavior** in the phrasing so the question does not feel accusatory. * **Provide an opt-out** ("Prefer not to say") rather than forcing an answer. * For potentially distressing topics, **signpost support resources** in your debrief. ## Visual Design [#visual-design] How a survey looks affects how carefully it is answered: * **Readability first:** legible font size, strong color contrast, generous spacing. * **One idea per screen** for complex items reduces errors. * **Progress bars** help, especially on longer or repetitive studies, by setting expectations and reducing dropout. * **Mobile matters:** many participants answer on phones, so check your instrument on a small screen. ## Attention and Quality Checks [#attention-and-quality-checks] Well-designed attention checks help you identify low-effort responses, but they must be fair: they should test attention to instructions that matter for the task, not trick participants with ambiguous wording. Build them in deliberately and pilot them. See [Avoiding Bias](https://terac.com/docs/researchers/research-methods/avoiding-bias) for more, and [Screening](https://terac.com/docs/researchers/opportunities/screening) for how qualification logic works on Terac. ## What's Next? [#whats-next] - [Sampling and Power](https://terac.com/docs/researchers/research-methods/sampling-and-power): Decide who to recruit and how many - [Avoiding Bias](https://terac.com/docs/researchers/research-methods/avoiding-bias): Pilot your instrument before you commit # Results & Review (https://terac.com/docs/researchers/results) Review participant submissions, manage approvals, watch interview recordings, and generate insights. Once participants start completing your opportunity, their work flows into the review pipeline. This section covers how to manage submissions, review quality, and extract insights. ## Review Workflow [#review-workflow] Submission Received → Review → Approve or Reject → Payout 1. **Submission received** - a participant completes their task and submits work 2. **Review** - the submission is resolved by the review method set on your opportunity 3. **Approve or reject** - accepted work triggers a payout; rejected work can include a reason 4. **Payout** - approved participants are paid automatically from your credit balance ## Review Tools [#review-tools] - [Submissions](https://terac.com/docs/researchers/results/submissions): Track and manage all participant submissions - [Reviewing Work](https://terac.com/docs/researchers/results/reviewing-work): The approval workflow and quality controls - [Interviews](https://terac.com/docs/researchers/results/interviews): Watch recordings and read transcripts - [Insights](https://terac.com/docs/researchers/results/insights): AI-generated themes and analysis from interviews # Insights (https://terac.com/docs/researchers/results/insights) Generate AI-powered insights from interview data, including themes, key quotes, and evidence-backed analysis. Insights turn your raw interview data into structured findings. Terac's AI analyzes completed interviews to surface themes, patterns, and notable quotes. ## How Insights Work [#how-insights-work] After interviews are completed, the AI analyzes transcripts across all participants to identify: * **Themes** - recurring patterns or topics that appear across multiple interviews * **Key quotes** - notable participant statements that support or illustrate a theme * **Evidence** - direct links from insights back to the specific interview moments ## Generating Insights [#generating-insights] 1. **Navigate to your guide** Open the research guide linked to your opportunity and go to the **Insights** tab. 2. **Generate insights** Click **Generate Insights** to start the AI analysis. The AI reviews all completed interview transcripts and produces a set of themes with supporting evidence. 3. **Review and refine** Review the generated insights. You can: * Edit theme names and descriptions * Add or remove evidence links * Create new insights manually * Delete insights that are not relevant ## Insight Types [#insight-types] The AI suggests an insight type for each finding. Common types include: | Type | Description | | --------------- | --------------------------------------------------- | | **Pain Point** | A problem or frustration participants experience | | **Behavior** | A pattern in how participants act or make decisions | | **Need** | Something participants want or require | | **Opportunity** | A potential area for improvement or innovation | | **Preference** | A stated preference for how something should work | ## Evidence Linking [#evidence-linking] Each insight is backed by evidence from specific interview moments. Evidence includes: * The participant who said it * A timestamp link to the exact moment in the recording * The relevant transcript excerpt This lets you trace any finding back to its source for validation. ## Manual Insights [#manual-insights] You can also create insights manually: 1. Watch an interview and identify a notable moment 2. Create an insight and describe the finding 3. Link the interview moment as evidence 4. Tag it with an insight type This is useful for observations that the AI might not surface automatically. ## Insights with Stats [#insights-with-stats] The insights view shows aggregate statistics: * How many interviews support each theme * Which themes are most common * Distribution of insight types Use these stats to prioritize findings by prevalence and impact. ## What's Next? [#whats-next] - [Watch Interviews](https://terac.com/docs/researchers/results/interviews): Review the source recordings - [Research Guides](https://terac.com/docs/researchers/opportunities/research-guides): Refine your discussion guide based on findings - [Submissions](https://terac.com/docs/researchers/results/submissions): Track overall opportunity progress # Reviewing Work (https://terac.com/docs/researchers/results/reviewing-work) The approval workflow for evaluating participant submissions, including manual review, screening review, and quality controls. Reviewing submissions is how you ensure the quality of participant work. Terac provides multiple review methods to match different opportunity needs. ## Review Methods [#review-methods] | Method | Description | Best For | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------- | | **Manual review** | The submission waits in Work review until you approve or reject it | Complex or nuanced work | | **Auto-approve** | A `completed` redirect from your external tool approves it and pays the participant. On a task without a redirect it waits for you instead | High-volume external surveys | | **Self-report** | The participant's own completion approves it and pays them, with no review from you | Steps where the participant's word is enough | Review method is set per task, so one opportunity can mix them. In the task builder, **Expert approves** is self-report and **Needs approval** is manual review, except on an external-link task, where it is auto-approve. Manual review on an external link is set through the API; see [External Surveys & Redirect URLs](https://terac.com/docs/researchers/integrations/external-surveys). ## Manual Review Workflow [#manual-review-workflow] 1. **Open the submission** Navigate to the Submissions tab and open the **Work review** stage, then click a submission. 2. **Review the work** Examine the participant's submission: * Survey responses * Uploaded files * Interview recordings and transcripts Uploaded files are renamed after the task and the submission, for example `upload_your_files-.pdf`, so a file you save still says where it came from. 3. **Make a decision** Choose one of three actions: * **Approve** - the work meets your standards; the participant is paid * **Reject** - the work does not meet standards; optionally provide a reason * **Strike** - flag serious quality issues (repeated low effort, dishonesty) ## Screening Review [#screening-review] If your opportunity routes applicants to you rather than auto-inviting them, screening responses land in a review queue. For each applicant you can: * **Approve screening** - the participant advances to the task * **Reject screening** - the participant is screened out ## AI-Assisted Screening Review [#ai-assisted-screening-review] For screening, Terac can evaluate an applicant's interview responses against the screening policy attached to your research guide and return a recommendation of approve, needs review, or reject. Applicants the policy is confident about are resolved automatically; the rest are routed to a human. See [Research Guides](https://terac.com/docs/researchers/opportunities/research-guides). This applies to screening decisions. Post-task work review follows the review method set on the opportunity. ## Auto-Review for External Tasks [#auto-review-for-external-tasks] When your task runs on a third-party tool and you have configured [redirect URLs](https://terac.com/docs/researchers/integrations/external-surveys), submissions are reviewed automatically from the result your tool sends back: * A `completed` callback **approves** the submission and triggers the payout. * A `screened_out`, `quota_full`, or `rejected` callback closes the submission without charging you. If redirect URLs are enabled but no callback arrives, the submission stays in **Work review** and is auto-rejected after 6 hours. This is why configuring the completion redirect matters: without it, qualified participants are rejected automatically. ## Participant Ratings [#participant-ratings] After reviewing a submission, you can rate the participant on a quality scale. Ratings are visible in the participant's profile and influence their standing for future opportunities. High-quality participants are more likely to be recommended for your next opportunity. ## Kick Out [#kick-out] If a participant is problematic (unresponsive, submitting spam), you can remove them from the opportunity with **Kick Out Participant**. This marks their submission as abandoned and frees the slot, and they can still be re-invited later. On an opportunity paid per hour or per task, kicking out an expert who is in progress asks **Reject and offboard this expert?** instead. Their submission is rejected, they are offboarded and told so, and payouts already made aren't taken back. ## Reset Participation [#reset-participation] In rare cases where a participant needs to restart (e.g., technical issues during a task), you can reset their participation. This returns their submission to the beginning of the task phase. > **Warning** > > Resetting participation cannot be undone. Only use this when a genuine > technical issue prevented the participant from completing their work fairly. ## What's Next? [#whats-next] - [View Interviews](https://terac.com/docs/researchers/results/interviews): Review recordings and transcripts - [Generate Insights](https://terac.com/docs/researchers/results/insights): Extract themes from completed research - [Submissions](https://terac.com/docs/researchers/results/submissions): Track submission status # Interviews (https://terac.com/docs/researchers/results/interviews) Watch AI-moderated interview recordings, read transcripts, and review participant responses. When your opportunity includes AI-moderated interviews, Terac records the entire session. You can watch recordings, read transcripts, and use interview data to inform your research decisions. ## Accessing Interviews [#accessing-interviews] Interviews are accessible from two places: * **Opportunity Submissions tab** - click any submission that includes an interview to view the recording * **Research guide Interviews tab** - browse every interview run against that guide ## Interview Data [#interview-data] Each interview record includes: | Data | Description | | --------------- | ------------------------------------------ | | **Recording** | Audio/video playback of the full interview | | **Transcript** | Full text transcript with timestamps | | **Summary** | AI-generated summary of key points | | **Duration** | Total interview length | | **Participant** | Link to the participant's profile | ## Watching Recordings [#watching-recordings] The recording player supports: * Play, pause, and scrub through the conversation * Jump to specific moments using the transcript * View participant and AI moderator turns separately ## Transcripts [#transcripts] Transcripts are generated automatically and include: * Speaker labels (participant vs. AI moderator) * Timestamps for each segment * Full text of everything said during the interview Use the transcript to quickly scan for relevant content without watching the entire recording. ## AI Summaries [#ai-summaries] Each interview gets an AI-generated summary that highlights: * Key topics discussed * Notable responses * Potential concerns or standout answers Summaries are designed to help you triage interviews quickly, deciding which ones deserve a full review. ## Interview Review [#interview-review] For opportunities where interviews are the primary task, you can approve or reject participants based on their interview performance. The review interface shows the recording, transcript, and summary side by side with the approval controls. See [Reviewing Work](https://terac.com/docs/researchers/results/reviewing-work) for details on the approval workflow. ## Technical Requirements [#technical-requirements] A few participant-side requirements affect interview quality. Account for them when you design a study and when you brief participants: | Requirement | Detail | | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Browser** | A current Chrome-based desktop browser is the most reliable. Some browsers (for example, Arc) can fail to connect to the voice session. | | **Screen recording** | Screen sharing and screen recording work on **desktop**. Mobile operating systems restrict screen capture, so studies that require a screen recording should be run on desktop. | | **Microphone** | Participants must grant microphone access for the voice interview. A blocked mic produces a session with no audio. | | **Connection** | A stable connection is needed for the live voice session. Brief drop-outs are recoverable, but a sustained loss can end the session early. | > If a study must capture the participant's screen during a specific part > of the conversation, set that up in your research guide so the screen share is > requested at the right moment. See [Research > Guides](https://terac.com/docs/researchers/opportunities/research-guides). ## Linking to Insights [#linking-to-insights] Interview moments can be linked to insights as evidence. When you identify a key quote or observation, you can tag it and associate it with a theme in your insights board. See [Insights](https://terac.com/docs/researchers/results/insights) for details. ## What's Next? [#whats-next] - [Generate Insights](https://terac.com/docs/researchers/results/insights): Extract themes from interview data - [Reviewing Work](https://terac.com/docs/researchers/results/reviewing-work): Approve or reject interview submissions - [Research Guides](https://terac.com/docs/researchers/opportunities/research-guides): Build discussion scripts for interviews # Submissions (https://terac.com/docs/researchers/results/submissions) Track participant submissions from application through completion, with filtering and status management. A submission represents a single participant's complete journey through your opportunity, from the moment they apply to final approval or rejection. ## Submission States [#submission-states] Each submission moves through a series of states: Screening → Not started → In Progress → Work review → Approved These are the stages you see as tabs on the Submissions table. | State | Description | | -------------------- | -------------------------------------------------------------------------- | | **Screening** | Screening questions or screening interview in progress | | **Terac review** | Screening complete, being reviewed by Terac before you see the applicant | | **Applicant review** | Waiting on you to review screening responses and decide who can start work | | **Not started** | Applicant qualified but has not begun the task yet | | **In Progress** | Participant is working on the task | | **Work review** | Task submitted; waiting on the review decision | | **Approved** | Work accepted; payout triggered | | **Not selected** | Did not qualify at screening | | **Rejected** | Work was reviewed and turned down | | **Returned** | Sent back to the participant to redo | | **Abandoned** | Participant left without completing | | **Timed Out** | Participant ran out of time | | **Cancelled** | Submission cancelled by you or by Terac | > **Not selected** and **Rejected** are different. Not selected means the person > did not qualify at screening and no work was done. Rejected means work was > submitted, reviewed, and turned down. **Applicant review** is your decision before an applicant starts work, not a review before their initial invitation. Terac's internal view calls this same stage **Customer review** to distinguish it from **Terac review**. ## How Completion Is Detected [#how-completion-is-detected] For tasks completed inside Terac, such as file uploads and activities, Terac knows when a participant finishes and advances the submission to **Work review**. It goes straight to **Approved**, and the participant is paid, only when the task's review type is `self_report` (**Expert approves** in the task builder). `auto_approve` does not approve work done inside Terac: it pays automatically only on a `completed` redirect from a third-party tool, and on any other task it waits in Work review like `manual_review`. Review type is set per task, not per opportunity, so one opportunity can mix them. For tasks hosted on a third-party tool (your own survey, product, or usability platform), Terac depends on a **redirect URL** to learn the outcome. When the participant finishes, your tool redirects them back to Terac with their submission ID and a result, which advances the submission. > **Warning** > > If a participant reaches a third-party task and no completion callback is > received, their submission stays **In Progress**, or waits in **Work review** > for you if they clicked **I completed the task** on Terac. Configuring the > completion redirect is what prevents qualified participants from sitting > there. On the small number of opportunities where Terac has enabled > redirect-URL auto-resolution, a submission in Work review with no callback is > auto-rejected after 6 hours instead. See [External Surveys & Redirect > URLs](https://terac.com/docs/researchers/integrations/external-surveys). ## Submissions Table [#submissions-table] The Submissions tab in each opportunity shows all submissions with: * Participant name and profile link * Current state * Screening status * Task completion status * Submission date * Review actions ### Filtering [#filtering] You can filter submissions by: * **State** - show only submissions in a specific state * **Date range** - filter by submission date * **Screening result** - passed, failed, or pending ### Sorting [#sorting] Sort by submission date, participant name, or state to find what you need. ## Submission Details [#submission-details] Clicking a submission opens the detail view where you can: * See the participant's profile and demographics * Review their screening answers * View their task submission (files, survey responses, interview recording) * Approve or reject the work * Add notes or feedback * Rate the participant's quality ## Exporting [#exporting] You can export the submissions in the current view to CSV from the Submissions tab, for analysis or for merging with data from your own tools. Approvals and rejections are made one submission at a time so each decision is deliberate. If you have a large volume to work through and want help, contact your Terac contact. ## Submission Counts [#submission-counts] The funnel tabs across the top of the Submissions table show live counts by stage, giving you a quick picture of your opportunity's progress: * How many participants are in screening * How many are working on tasks * How many are awaiting your review * How many have been approved or rejected ## What's Next? [#whats-next] - [Reviewing Work](https://terac.com/docs/researchers/results/reviewing-work): Learn the approval workflow - [Opportunity Lifecycle](https://terac.com/docs/researchers/opportunities/opportunity-lifecycle): Understand opportunity states - [Finance](https://terac.com/docs/researchers/finance/credits): How approvals affect your credit balance # Contact (https://terac.com/docs/researchers/support/contact) How to reach the Terac team for support, feedback, and business inquiries. ## Email Support [#email-support] For questions, issues, or feedback, email us at **[support@terac.com](mailto:support@terac.com)**. ### What to Include [#what-to-include] To help us resolve your issue quickly, include: * Your organization name * The opportunity name or ID (if applicable) * A description of the problem or question * Screenshots (if relevant) ## Response Times [#response-times] We aim to respond as quickly as possible, and prioritize by plan and by the severity of the issue. Tell us up front if an issue is blocking a live study or a payment so it gets triaged accordingly. Committed target response times apply to Enterprise subscriptions and are set out in the [Support Terms](https://terac.com/legal/support-terms). Your tier is named on your order form. ## In-App Support [#in-app-support] The researcher dashboard has no chat widget. For quick questions, bug reports, and feature suggestions, email **[support@terac.com](mailto:support@terac.com)** or ask your Terac contact. ## Business Inquiries [#business-inquiries] For enterprise plans, custom integrations, partnerships, or volume pricing, email **[sales@terac.com](mailto:sales@terac.com)**. ## What Support Can Help With [#what-support-can-help-with] * Account and billing questions * Opportunity configuration and troubleshooting * Technical issues with the platform * Questions about participant quality * API integration help ## What Support Cannot Help With [#what-support-cannot-help-with] * Individual participant disputes (handled through the review workflow) * Tax advice (consult a tax professional) * Custom development work (contact sales for enterprise needs) # Glossary (https://terac.com/docs/researchers/support/glossary) Key terms and definitions for researchers using the Terac platform. ## A [#a] ### Attestation [#attestation] A verified credential on an expert's profile, such as government ID verification, a verified phone number, or a connected professional account. Attestations are earned once and reused across opportunities. ## C [#c] ### Cost Per Participant (CPI) [#cost-per-participant-cpi] The total cost for one approved participant: the participant's incentive plus Terac's platform fee. Sometimes quoted as a single number. ### Credits [#credits] The currency used to pay for participant work on Terac. Credits are purchased upfront and deducted when submissions are approved. ## D [#d] ### Discussion Guide [#discussion-guide] A structured script of questions and prompts that the AI moderator follows during interviews. Part of a research guide. ## E [#e] ### Expert [#expert] A verified individual in the Terac panel who can participate in opportunities. Also called a panelist. ## F [#f] ### Feasibility [#feasibility] Whether Terac can reach and recruit your target audience within your timeline, and at what cost. Checked before launch via filter match counts, the audience feasibility tool, or a feasibility request for niche audiences. ### Filter [#filter] A demographic, geographic, or professional criterion that determines which experts can see and apply to an opportunity. Filters are applied before experts see the opportunity. ## I [#i] ### Incentive [#incentive] The reward a participant receives for completing an opportunity, paid out on approval. The incentive is one part of the cost per participant; Terac's platform fee is the other. ### Insight [#insight] An AI-generated or manually created finding from interview data. Insights include themes, key quotes, and evidence links. ### Internal Panel [#internal-panel] Terac's pool of verified experts, sourced and vetted by Terac and matched to your opportunity by your filters and screeners. ## M [#m] ### MFA [#mfa] Multi-factor authentication. An optional security setting that requires a second form of verification when logging in. ## O [#o] ### Opportunity [#opportunity] A research project posted on Terac with defined criteria, tasks, and compensation that experts can apply to. ### Organization [#organization] The top-level workspace on Terac. Contains projects, opportunities, team members, billing, and settings. ## P [#p] ### Participant Cap [#participant-cap] The maximum number of participants an opportunity will recruit. Recruitment stops when this cap is reached. ### Platform Fee [#platform-fee] Terac's fee on top of participant payouts, covering sourcing, screening, and platform services. ### Project [#project] A container within an organization that groups related opportunities, research guides, and recordings. ## Q [#q] ### Quota [#quota] A target number of participants for specific screening answer combinations, used to ensure a balanced sample. ## R [#r] ### RBAC [#rbac] Role-based access control. The system that determines what each team member can do within an organization. ### Redirect URL [#redirect-url] Also called a callback URL. The URL a third-party survey or task tool sends a participant back to when they finish, so Terac can record the outcome (completed, screened out, over-quota, or rejected) and review the submission. ### Research Guide [#research-guide] A comprehensive document containing a discussion guide, screening policy, stimulus materials, and generated insights. Used to power AI-moderated interviews. ## S [#s] ### Screener [#screener] A qualification question asked during the application flow. Can be auto-graded or manually reviewed. ### Screening Policy [#screening-policy] Rules attached to a research guide that determine whether a participant qualifies based on their interview responses. ### SSO [#sso] Single sign-on. Allows members to authenticate through an organization's existing identity provider. ### Stimulus [#stimulus] Images, videos, or files shown to participants during AI-moderated interviews. ### Submission [#submission] A participant's end-to-end journey through an opportunity, from application to completion. Tracks state, screening results, task work, and review decisions. ## Quick Reference [#quick-reference] | Term | Definition | | ----------- | ----------------------------------------------------- | | Attestation | Verified credential on an expert profile | | Credits | Prepaid balance for funding opportunities | | Expert | Verified panelist who participates in opportunities | | Filter | Hard requirement that controls opportunity visibility | | Opportunity | A research project experts can apply to | | Screener | Qualification question in the application flow | | Submission | A participant's journey through an opportunity | # FAQ (https://terac.com/docs/researchers/support/faq) Frequently asked questions from researchers using Terac. ## Account & Organization [#account--organization] ### How do I create an organization? Sign up at [terac.com](https://terac.com) and follow the prompts to set up your organization. See [Create Your Organization](https://terac.com/docs/researchers/getting-started/create-organization) for a full walkthrough. ### Can I belong to multiple organizations? Yes. Use the organization switcher in the top-left of the dashboard to move between organizations. Each has its own billing, members, and projects. ### How do I invite team members? Go to Settings, then Members, and enter the email address of the person you want to invite. They will receive an email with a link to join. See [Members and Roles](https://terac.com/docs/researchers/team/members-and-roles). ### What roles are available? Owner, Admin, and Member. Owner and Admin can build, launch, and review studies and manage billing and members; Member is read-only by default, so anyone who needs to run studies needs Admin. See [Members and Roles](https://terac.com/docs/researchers/team/members-and-roles) for the full permissions table. ## Opportunities [#opportunities] ### How do I create an opportunity? Navigate to a project and click **New Opportunity**. Follow the creation flow to set up your title, description, compensation, screening, and filters. See [Creating an Opportunity](https://terac.com/docs/researchers/opportunities/creating-an-opportunity). ### Can I edit an opportunity after launching it? Yes. You can adjust the participant cap, filters, compensation, and screening questions on an active opportunity. Already-screened participants are not re-evaluated when you change screening or filters. Larger changes to task structure are best made while the opportunity is paused. See [Opportunity Lifecycle](https://terac.com/docs/researchers/opportunities/opportunity-lifecycle). ### How do I pause or stop an opportunity? From the opportunity, use the status controls in the header to pause or stop. Pausing is temporary and can be reversed. Stopping is permanent. See [Opportunity Lifecycle](https://terac.com/docs/researchers/opportunities/opportunity-lifecycle). ### Can I duplicate an opportunity? Yes. Use the **Duplicate** action from any existing opportunity to create a copy with all the same settings. Adjust what you need and launch. ### What opportunity types are there? Two: **unmoderated**, where each expert completes a task on their own time (a survey, an in-app activity, or an external link), and **human-moderated**, where each expert books a live call with you. Picking a type seeds the right task defaults; everything stays editable afterwards. See [Creating an Opportunity](https://terac.com/docs/researchers/opportunities/creating-an-opportunity). ## Recruitment & Screening [#recruitment--screening] ### How do filters work? Filters determine which experts can see your opportunity. Only experts matching all your filters will have the opportunity appear in their listing. Filters are applied before participants see the opportunity. See [Filters](https://terac.com/docs/researchers/recruitment/filters). ### What is the difference between filters and screening questions? Filters control who can see and apply to your opportunity (hard requirements). Screening questions are asked during the application flow and evaluate whether an applicant qualifies (soft requirements). ### How many participants can I recruit? You set a participant cap when creating your opportunity. Recruitment continues until the cap is reached or you stop the opportunity. You can adjust the cap on an active opportunity. ### Can I invite specific experts? Not directly. You describe the audience you need through filters, a candidate profile, and screeners, and Terac matches and invites experts against that definition. If you need particular people or a re-contact of a previous study's participants, ask your Terac contact. See [Recruitment](https://terac.com/docs/researchers/recruitment). ### What are quotas? Quotas let you control how many participants you need for specific screening answers, ensuring a balanced sample. Quotas are set per screening question. See [Screening](https://terac.com/docs/researchers/opportunities/screening). ### How do I know if Terac can recruit my audience? For most audiences, the Feasibility page in your organization shows how many matching experts exist for a given set of filters. For niche or hard-to-reach audiences, request a feasibility check and share your audience, methodology, length, sample size, geography, and timeline. See [Feasibility & Coverage](https://terac.com/docs/researchers/recruitment/feasibility). ### How long does recruitment take? It depends on how common your audience is and how long the task is. Common audiences typically fill within about a week, with first completes often within a few days. Niche audiences that need targeted outreach usually take one to two weeks. Your study's delivery estimate is shown in the dashboard once it is live. See [Feasibility & Coverage](https://terac.com/docs/researchers/recruitment/feasibility). ### Can I edit screening questions after launching? Yes. You can edit screening questions on a live study. Participants who were already screened are not re-evaluated, so the change applies only to participants screened after it. See [Opportunity Lifecycle](https://terac.com/docs/researchers/opportunities/opportunity-lifecycle). ## Billing & Payments [#billing--payments] ### How does billing work? Terac uses a credits-based system. You purchase credits upfront and they are deducted when you approve participant submissions. See [Credits](https://terac.com/docs/researchers/finance/credits). ### When am I charged? Credits are only deducted when you approve a submission. You are never charged for screened-out, rejected, abandoned, or timed-out participants. ### How do I avoid running out of credits mid-study? Credits do not top up automatically, so keep a buffer above what your live studies need and top up from the Finance page when the balance runs low. If you want a standing arrangement so live studies never stall on funds, talk to your Terac contact. See [Credits](https://terac.com/docs/researchers/finance/credits). ### How do participants get paid? Payouts are triggered automatically when you approve a submission. Terac handles the payout end to end and participants choose how to receive their earnings from the options available in their region, so you never handle participant payment details. ### What does the cost per participant (CPI) include? When a cost is quoted as a single per-participant figure, that total is the participant's incentive plus Terac's platform fee. The incentive is what the participant earns; the platform fee covers sourcing, screening, and management. Longer or more specialized studies carry higher incentives. See [Pricing](https://terac.com/docs/researchers/finance/pricing). ### Will I be charged if a study has to be re-run due to a technical issue? No. Work that fails because of a platform issue on Terac's side is not charged, and a re-run for that reason is not deducted from your balance. If you think you were charged for a submission affected by a platform issue, contact support. See [Pricing](https://terac.com/docs/researchers/finance/pricing). ## Interviews & Insights [#interviews--insights] ### How does the AI moderator work? The AI moderator conducts voice interviews following your research guide. It asks questions, probes for deeper answers, and evaluates responses against your screening policy. Recordings and transcripts are saved automatically. ### Can I watch interview recordings? Yes. All AI-moderated interviews are recorded. Access them from the opportunity Submissions tab or the research guide's Interviews tab. See [Interviews](https://terac.com/docs/researchers/results/interviews). ### What are insights? Insights are AI-generated themes and findings extracted from completed interviews. They include key quotes and evidence links back to specific interview moments. See [Insights](https://terac.com/docs/researchers/results/insights). ### Why are some interviews missing screen recordings? Screen recording works on desktop. Mobile operating systems restrict screen capture, so a study that requires a screen recording should be run on desktop. A current Chrome-based browser is the most reliable for the voice session. See [Technical Requirements](https://terac.com/docs/researchers/results/interviews). ### How do I access results after a study closes? Completed interviews, recordings, transcripts, and AI summaries stay available in the opportunity Submissions tab and the research guide's Interviews tab after a study closes. You can also generate insight reports from the completed data. See [Interviews](https://terac.com/docs/researchers/results/interviews) and [Insights](https://terac.com/docs/researchers/results/insights). ## Integrations [#integrations] ### Does Terac have an API? Yes. The Terac API provides full programmatic access to create and manage opportunities, recruitment, and submissions. See the [Developer Docs](https://terac.com/docs/developers/guides). ### Can I connect Slack? Yes. The Slack integration sends real-time notifications about opportunity activity to your Slack workspace. See [Slack](https://terac.com/docs/researchers/integrations/slack). ### What other integrations are available? Terac integrates with Slack and Google Calendar. API keys are available for custom integrations. See [Integrations](https://terac.com/docs/researchers/integrations). ### How do I track completions from my own survey or task tool? Use redirect URLs. Terac sends each participant to your tool with their submission ID attached, and at the end your tool redirects them back to Terac's callback URL with a result (completed, screened out, over-quota, or rejected). Without it, completions cannot be detected and submissions are auto-rejected after sitting unconfirmed. See [External Surveys & Redirect URLs](https://terac.com/docs/researchers/integrations/external-surveys). ## Data & Privacy [#data--privacy] ### What participant data do I get? By default, Terac does not surface participant personally identifiable information such as full name or email in your results. Participants are identified by their submission and profile. See [Security](https://terac.com/docs/researchers/team/security). ### Can I recruit participants under 18? No. Terac is for adults: under the [Terms of Service](https://terac.com/legal/terms-of-service), people under 18 are not permitted to register for or use the Services, so every participant delivered to your study is 18 or older. If your research needs a minor audience, raise it with your Terac contact before building the study. See [Security](https://terac.com/docs/researchers/team/security). # Support (https://terac.com/docs/researchers/support) Get help with Terac, find answers to common questions, and contact the team. Find answers to common questions, look up terminology, or reach out to the Terac team. - [FAQ](https://terac.com/docs/researchers/support/faq): Answers to the most common researcher questions - [Glossary](https://terac.com/docs/researchers/support/glossary): Key terms and definitions - [Contact](https://terac.com/docs/researchers/support/contact): Reach the Terac team directly ## Quick Help [#quick-help] | Issue | Where to Look | | ---------------------------- | ---------------------------------------------------------------------------------- | | How to create an opportunity | [Creating an Opportunity](https://terac.com/docs/researchers/opportunities/creating-an-opportunity) | | How to add credits | [Credits](https://terac.com/docs/researchers/finance/credits) | | How to invite team members | [Members and Roles](https://terac.com/docs/researchers/team/members-and-roles) | | How screening works | [Screening](https://terac.com/docs/researchers/opportunities/screening) | | How to use the API | [Developer Docs](https://terac.com/docs/developers/guides) | | How recruitment works | [Recruitment](https://terac.com/docs/researchers/recruitment) | # Team & Organization (https://terac.com/docs/researchers/team) Manage your team members, roles, projects, and organization security settings. Your organization is the workspace where your team collaborates on research. This section covers how to manage members, assign roles, organize projects, and configure security. - [Members and Roles](https://terac.com/docs/researchers/team/members-and-roles): Invite collaborators and control access - [Projects](https://terac.com/docs/researchers/team/projects): Organize research into separate workspaces - [Security](https://terac.com/docs/researchers/team/security): MFA, SSO, and access policies # Members and Roles (https://terac.com/docs/researchers/team/members-and-roles) Invite team members and manage access with role-based permissions. Terac uses role-based access control (RBAC) to manage what each team member can do within your organization. ## Inviting Members [#inviting-members] 1. **Go to Settings** Navigate to your organization's Settings page and select **Members**. 2. **Send an invitation** Enter the email address of the person you want to invite and select their role. 3. **They accept** The invitee receives an email with a link to join your organization. Once they accept, they appear in the members list with the assigned role. ## Roles and Permissions [#roles-and-permissions] Terac has three roles: **Owner**, **Admin**, and **Member**. Owner and Admin differ only around ownership and removing other admins; Member is a read-only role by default. | Permission | Owner | Admin | Member | | ------------------------------ | ----- | ----- | ------ | | View the organization | Yes | Yes | Yes | | Create and edit opportunities | Yes | Yes | No | | Launch, pause, and stop | Yes | Yes | No | | Review and approve submissions | Yes | Yes | No | | Create and edit projects | Yes | Yes | No | | Invite and manage members | Yes | Yes | No | | View finance and add credits | Yes | Yes | No | | Edit organization settings | Yes | Yes | No | | Configure SSO | Yes | Yes | No | | Delete opportunities | Yes | Yes | No | | Remove admins | Yes | No | No | | Transfer ownership | Yes | No | No | | Delete the organization | Yes | No | No | > Member is read-only. Anyone who needs to build, launch, or review studies > needs Admin. You can see exactly which permissions each role carries under > Settings, then **Roles & Permissions**. ## Managing Members [#managing-members] From the Members page you can: * **Change roles** - update a member's role at any time * **Remove members** - revoke access to the organization * **Transfer ownership** - hand over the Owner role to another member (Owner only) ## Pending Invitations [#pending-invitations] Invitations that have not been accepted yet appear in a separate section. You can: * Resend the invitation email * Cancel the invitation * See when it was sent ## Multiple Organizations [#multiple-organizations] Team members can belong to multiple organizations. Each organization has its own roles, billing, and projects. Members switch between organizations using the org switcher in the dashboard. ## What's Next? [#whats-next] - [Projects](https://terac.com/docs/researchers/team/projects): Organize your research by project - [Security](https://terac.com/docs/researchers/team/security): Configure MFA and SSO - [Activity Log](https://terac.com/docs/researchers/team/security): Track organization activity # Projects (https://terac.com/docs/researchers/team/projects) Organize your research into separate project workspaces within your organization. Projects group related opportunities, research guides, and recordings together. They provide a clean separation between different research initiatives. ## Why Use Projects [#why-use-projects] * **Organization** - keep opportunities for different products, teams, or clients separate * **Context** - each project can have its own research context that guides the AI moderator * **Focus** - team members can work within a project without seeing unrelated opportunities ## Creating Projects [#creating-projects] See [Create a Project](https://terac.com/docs/researchers/getting-started/create-project) for a step-by-step guide. ## Project Structure [#project-structure] Each project contains: | Section | Description | | ------------- | ----------------------------------------------- | | Opportunities | Opportunities belonging to this project | | Guides | Research guides and discussion scripts | | Guides | Discussion guides, interviews, and insights | | Settings | Project name, description, and research context | ## Project Settings [#project-settings] From the project settings page you can: * Update the project name and description * Set project-level research context (used by the AI moderator alongside org context) * Archive the project when research is complete ## Archiving Projects [#archiving-projects] When a project is finished, you can archive it. Archived projects: * Are hidden from the main navigation * Retain all data (opportunities, recordings, insights) * Can be unarchived later if needed ## What's Next? [#whats-next] - [Create an Opportunity](https://terac.com/docs/researchers/opportunities/creating-an-opportunity): Launch research within your project - [Members and Roles](https://terac.com/docs/researchers/team/members-and-roles): Manage team access # Security (https://terac.com/docs/researchers/team/security) Configure multi-factor authentication, SSO, and access policies for your organization. Terac provides security features to protect your organization and research data. ## Multi-Factor Authentication (MFA) [#multi-factor-authentication-mfa] ### Enforcing MFA [#enforcing-mfa] Organization admins can require all members to enable MFA. When enforced: * Existing members are prompted to set up MFA on their next login * New members must configure MFA before accessing the organization * Members cannot disable MFA while the enforcement is active ### Setting Up MFA [#setting-up-mfa] Individual users set up MFA from their personal account settings. Terac supports authenticator apps (TOTP) for second-factor verification. ## Single Sign-On (SSO) [#single-sign-on-sso] For organizations that use an identity provider, Terac supports SSO configuration. This lets members authenticate through your company's existing identity system. SSO setup is available in the organization's Security settings. ## Audit Log [#audit-log] The Activity page provides a complete audit trail of actions taken within your organization: * Member logins and role changes * Opportunity creation, launch, and configuration changes * Submission reviews and approvals * Billing transactions * Settings changes Use the audit log to track who did what and when, for compliance or troubleshooting. ## API Key Security [#api-key-security] API keys provide programmatic access to your organization's data. Best practices: * Rotate keys periodically * Use separate keys for different integrations * Revoke keys immediately if compromised * Never share keys in public repositories or chat See [API Keys](https://terac.com/docs/researchers/integrations/api-keys) for management details. ## Participant Data & Privacy [#participant-data--privacy] Terac is built to minimize the personal data exposed about participants: * By default, Terac does **not** surface participant personally identifiable information such as full name or email address in your results. Participants are identified by their submission and profile rather than by direct contact details. * If your study collects any personal information directly (for example, in your own survey), keep it to the minimum your research requires and handle it according to your own privacy obligations. If you are unsure whether a field is acceptable, ask your Terac contact before launching. ### Age [#age] Terac is for adults. Under the [Terms of Service](https://terac.com/legal/terms-of-service), people under 18 are not permitted to register for or use the Services, so every participant delivered to your study is 18 or older. Studies that require a minor audience are out of scope; if your research needs one, raise it with your Terac contact before you build the study. ## What's Next? [#whats-next] - [Members and Roles](https://terac.com/docs/researchers/team/members-and-roles): Manage team access - [API Keys](https://terac.com/docs/researchers/integrations/api-keys): Manage programmatic access # Data Handling (https://terac.com/docs/researchers/trust-and-quality/data-handling) What participant data you receive, how Terac protects it, and your responsibilities as a researcher. When you run a study, you handle data about real people. This page explains what you receive, what you do not, how Terac protects participant data, and the responsibilities that come with collecting it. ## What You Receive [#what-you-receive] When a participant applies and completes your study, you can see the information relevant to your study: * Their screening responses * Their task submissions and uploaded files * Interview recordings and transcripts, where applicable * Profile attributes relevant to your filters ## What You Do Not Receive [#what-you-do-not-receive] Terac is designed so you can study people without holding their raw identity documents. * You **do not** receive participants' government ID images. Identity verification is handled by a third-party partner, and Terac does not share ID documents with researchers. * You **do not** receive contact details beyond what a participant explicitly provides for your study. > Because identity is verified upstream, you get the assurance of a verified > human without taking on the burden and risk of storing sensitive identity > documents yourself. ## How Terac Protects Data [#how-terac-protects-data] Terac protects participant and researcher data with: * Encryption in transit and at rest * Access controls that limit who can see what * Regular security audits * SOC 2 certification Terac does not sell personal data, and does not use participant data to train third-party AI models. For the complete picture, see the [Privacy Policy](https://terac.com/legal/privacy-policy). ## Your Responsibilities [#your-responsibilities] Once participant data reaches your systems, you become responsible for handling it appropriately. The core obligations: 1. **Get informed consent** Obtain consent before collecting data, and make it clear what you collect, how it is used, and how participants can withdraw. Record consent with a timestamp. See [Getting consent practices](https://terac.com/docs/researchers/research-methods/writing-good-surveys) for how to phrase it. 2. **Minimize what you collect** Collect only the data your stated research purpose requires. Less data means less risk for everyone. 3. **Avoid collecting direct identifiers** Do not ask participants for direct personal identifiers (such as full names, home addresses, or contact details) unless it is essential, disclosed, and consented to. Keep participants pseudonymous wherever possible. 4. **Store and share carefully** Apply security proportionate to the sensitivity of your data. Before sharing a dataset publicly (for example, in a repository), strip identifiers and check that no participant can be re-identified. ## Regional Rights [#regional-rights] Participants may have rights under regional law, including GDPR (EU) and CCPA (California), such as access, correction, deletion, and portability. Your study must remain compatible with those rights. If you are unsure how a requirement applies to your study, [contact support](https://terac.com/docs/researchers/support/contact). ## What's Next? [#whats-next] - [Verified Participants](https://terac.com/docs/researchers/trust-and-quality/verified-participants): How identity verification works - [Research Methods](https://terac.com/docs/researchers/research-methods): Design studies that respect participants and produce good data # Trust & Data Quality (https://terac.com/docs/researchers/trust-and-quality) How Terac works to ensure your data comes from real, verified humans, and what you can do to protect quality. The value of your research depends on who is behind each response. Terac is built around a simple premise: the people completing your study should be real, verified humans who are a genuine fit for what you are studying. This section explains the safeguards that support that, and how you can use them. ## The Layers [#the-layers] Data quality on Terac is not a single feature. It is a set of layers that each remove a different risk. Verified people → Fit screening → Quality review → Secure handling | Layer | What it protects against | Where to read more | | ------------------------- | ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | | **Verified participants** | Fake, duplicate, or misrepresented identities | [Verified Participants](https://terac.com/docs/researchers/trust-and-quality/verified-participants) | | **Fit screening** | The right humans, but the wrong people for your study | [Screening](https://terac.com/docs/researchers/opportunities/screening), [Filters](https://terac.com/docs/researchers/recruitment/filters) | | **Quality review** | Low-effort, incomplete, or dishonest submissions | [Quality Controls](https://terac.com/docs/researchers/trust-and-quality/quality-controls) | | **Secure handling** | Mishandled or over-collected personal data | [Data Handling](https://terac.com/docs/researchers/trust-and-quality/data-handling) | ## Your Part [#your-part] The platform handles identity and provides the review tooling, but study-level quality is a shared responsibility. The practices that make the biggest difference: * **Screen for fit, not just identity.** Use [filters](https://terac.com/docs/researchers/recruitment/filters) for hard requirements and [screening questions](https://terac.com/docs/researchers/opportunities/screening) for judged criteria. * **Design fair checks.** Attention and comprehension checks help, but only when they are fair. See [Avoiding Bias](https://terac.com/docs/researchers/research-methods/avoiding-bias). * **Review deliberately.** Choose a [review method](https://terac.com/docs/researchers/results/reviewing-work) that matches the stakes of your study. * **Pay fairly and instruct clearly.** Both reduce low-effort responding. ## Start Here [#start-here] - [Verified Participants](https://terac.com/docs/researchers/trust-and-quality/verified-participants): How identity and attestations work - [Quality Controls](https://terac.com/docs/researchers/trust-and-quality/quality-controls): Review methods, AI moderation, and quality signals - [Data Handling](https://terac.com/docs/researchers/trust-and-quality/data-handling): What data you receive, and your responsibilities # Verified Participants (https://terac.com/docs/researchers/trust-and-quality/verified-participants) How Terac verifies the people in its panel, and how you can require specific verified credentials for your study. Every participant on Terac is a member of the panel who has completed onboarding. Verification is what lets you trust that an applicant is a real, distinct person, and optionally that they meet specific verified criteria before they can apply. ## Attestations [#attestations] An **attestation** is a verified badge on a participant's profile. It proves the participant has cleared a specific check. Attestations are earned once and reused across every opportunity, so participants are not re-verified for each study. | Attestation | What it proves | | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | | **Government ID** | The participant verified a government-issued ID with a face match. This is the strongest identity signal and most opportunities require it. | | **Phone** | The participant confirmed a phone number via a one-time SMS code. | | **Professional / social profile** | The participant connected a real professional or social account (such as LinkedIn). | Identity verification is handled by a dedicated third-party verification partner. Terac does not store images of participants' ID documents. For the participant-side flow, see [Understanding Attestations](https://terac.com/docs/experts/how-to/attestations). ## Requiring Verified Accounts for Your Study [#requiring-verified-accounts-for-your-study] In the Audience step of the builder you can require **connected accounts**: an applicant must link a real LinkedIn, Google, Facebook, or Microsoft account before they can apply. Use LinkedIn when professional authenticity matters, or a work identity provider when you need applicants tied to a real workplace. Each requirement narrows your eligible pool, so only require what your study genuinely needs. Recruitment respects the requirement automatically: applicants who have not connected the account cannot start your study, and there is nothing further to check at review time. > Government ID and phone verification are handled by Terac during panel > onboarding rather than set per study. If your study needs a specific > verification standard, raise it during the [feasibility > check](https://terac.com/docs/researchers/recruitment/feasibility) so the audience is scoped to > it. > Required accounts are hard requirements applied before a participant can > apply. They differ from [screening > questions](https://terac.com/docs/researchers/opportunities/screening), which qualify > participants on judged answers during the application flow. ## Preventing Duplicate Participation [#preventing-duplicate-participation] Identity verification also underpins one-person-one-account integrity. To keep a single participant from entering the same study twice, rely on the platform's participation controls rather than trying to detect duplicates yourself after the fact. See [Submissions](https://terac.com/docs/researchers/results/submissions) for how participation is tracked. ## What's Next? [#whats-next] - [Quality Controls](https://terac.com/docs/researchers/trust-and-quality/quality-controls): Keep low-effort work out of your dataset - [Filters](https://terac.com/docs/researchers/recruitment/filters): Target the right audience before they apply # Quality Controls (https://terac.com/docs/researchers/trust-and-quality/quality-controls) The review methods, AI moderation, and quality signals Terac gives you to keep low-effort and dishonest work out of your dataset. Verification confirms who a participant is. Quality controls confirm that the work they submitted is genuine and meets your standards. Terac gives you several tools, from fully manual to AI-assisted, so you can match the level of scrutiny to the stakes of your study. ## Choosing a Review Method [#choosing-a-review-method] | Method | How it works | Best for | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------- | | **Manual review** | The submission waits in Work review until you approve or reject it | Complex or nuanced work | | **Auto-approve** | A `completed` redirect from your external tool approves it and pays the participant. On a task without a redirect it waits for you instead | High-volume external surveys | | **Self-report** | The participant's own completion approves it and pays them, with no review from you | Steps where the participant's word is enough | Review method is set per task. See [External Surveys & Redirect URLs](https://terac.com/docs/researchers/integrations/external-surveys) for how each one treats a completion redirect. See [Reviewing Work](https://terac.com/docs/researchers/results/reviewing-work) for the full workflow. ## Decisions and Signals [#decisions-and-signals] When you review a submission, you have more than a binary choice. Each action feeds the signals that protect quality across the platform. * **Approve** the work meets your standards and the participant is paid. * **Reject** the work does not meet standards; you can include a reason. * **Strike** flags serious problems such as repeated low effort or dishonesty. * **Participant ratings** let you score quality after review. Ratings influence a participant's standing and whether they are recommended for your future opportunities. * **Kick out** removes an unresponsive or spamming participant and frees their slot. > Because ratings and strikes carry forward, high-quality participants surface > more often in your future studies and the panel improves over time. Reserve > rejections and strikes for genuine quality problems. ## AI Moderation and Verification [#ai-moderation-and-verification] For studies that need deeper assurance than a form submission can provide, the **AI moderator** conducts voice interviews that screen, probe, and verify in real time. Beyond gathering qualitative depth, moderated interviews are well suited to verifying claims and surfacing inconsistencies on longer or higher-stakes tasks, where a static submission alone is easier to game. You attach a research guide with a screening policy, and the moderator evaluates responses against it. See [Research Guides](https://terac.com/docs/researchers/opportunities/research-guides) and [Interviews](https://terac.com/docs/researchers/results/interviews). ## Attention and Comprehension Checks [#attention-and-comprehension-checks] Attention and comprehension checks are your first line of defense against low-effort responding inside a task. They work best when they are **fair**: they should test attention to instructions that genuinely matter, not trick attentive participants with ambiguous wording. Pilot your checks alongside the rest of your study, and treat a high failure rate as a sign that the check, not the participant, may be at fault. See [Avoiding Bias](https://terac.com/docs/researchers/research-methods/avoiding-bias). ## A Layered Approach [#a-layered-approach] No single check is sufficient on its own. The strongest setups combine: 1. Required [attestations](https://terac.com/docs/researchers/trust-and-quality/verified-participants) so applicants are verified. 2. [Filters and screening](https://terac.com/docs/researchers/opportunities/screening) so they fit your population. 3. Fair in-task checks so low-effort work is visible. 4. A [review method](https://terac.com/docs/researchers/results/reviewing-work) matched to your stakes. > **Warning** > > Quality assessment is about converging signals, not one number. Avoid > rejecting on a single ambiguous indicator. Weigh completion behavior, check > performance, and content together before acting. ## What's Next? [#whats-next] - [Reviewing Work](https://terac.com/docs/researchers/results/reviewing-work): The full approval workflow - [Data Handling](https://terac.com/docs/researchers/trust-and-quality/data-handling): What you receive and your responsibilities # Account Standing, Holds, and Appeals (https://terac.com/docs/experts/policies/account-standing) What keeps your account in good standing, what can lead to a suspension, and how to get help if your account is restricted. Most experts never have to think about this page. Your account is active by default, and ordinary participation keeps it that way. This page explains what good standing means, what can put it at risk, and what to do if your account is ever restricted. ## What Good Standing Means [#what-good-standing-means] An account in good standing can browse opportunities, apply, complete studies, and cash out normally. You keep your account in good standing by: * Keeping **one account per person** * Providing **accurate** profile and identity information * Giving **honest, genuine** responses in studies * **Following study instructions** and completing the work you start * **Keeping your sign-in secure** and never sharing access to your account ## What Does Not Put Your Account at Risk [#what-does-not-put-your-account-at-risk] Some normal outcomes worry people unnecessarily. The following are part of participating and do not, on their own, harm your standing: * **Being screened out** of a study you did not match * An **occasional rejected submission** * **Leaving** a study you chose not to finish See [Understanding Submissions](https://terac.com/docs/experts/working/submissions) for what each of these means. ## What Can Lead to a Suspension or Ban [#what-can-lead-to-a-suspension-or-ban] Account actions are reserved for serious or repeated problems. These include: * Creating **multiple or duplicate accounts** * **Fraudulent activity** or attempting to manipulate or game studies * Providing **false identity or demographic information** * Giving **knowingly false, scripted, or AI-generated responses** in interviews or surveys * **Sharing, selling, or renting** your account, or using someone else's These violate our [Terms of Service](https://terac.com/docs/experts/policies/terms). A ban removes access to the platform, and when an account is closed for cause, pending earnings, and in some cases earlier earnings, may be withheld. > **Warning** > > Account closures for cause are permanent. Because each person may only have > one account, we cannot create a replacement account after a closure. ## How Account Reviews Work [#how-account-reviews-work] Bans are decided by a person on our team, who reviews the account first. Some limits are automatic, such as not being able to use Terac from a region where it is not available yet. If your account is restricted, you will see a notice the next time you sign in. ## If You Think Your Account Was Restricted by Mistake [#if-you-think-your-account-was-restricted-by-mistake] If your account has been restricted and you believe it is an error, you can ask us to review it: 1. Email **[support@terac.com](mailto:support@terac.com)** from the email on your account, since you may not be able to sign in to chat with Sophia 2. Include your account email and any relevant details or screenshots 3. Our team will review your case and respond We cannot guarantee an outcome, but every appeal is read by a person. ## What's Next? [#whats-next] * Read the [Terms of Service summary](https://terac.com/docs/experts/policies/terms) * Learn to spot scams in [Staying Safe](https://terac.com/docs/experts/support/staying-safe) * Review [Understanding Submissions](https://terac.com/docs/experts/working/submissions) # How Terac Uses Your Data (https://terac.com/docs/experts/policies/data-usage) What data we collect, how it's used, and your privacy rights. This page summarizes what data Terac collects, how we use it, and your privacy rights. Our [Privacy Policy](https://terac.com/legal/privacy-policy) is the complete version, and it governs if anything here reads differently. ## What Data We Collect [#what-data-we-collect] ### Profile Information [#profile-information] When you create an account and complete onboarding, we collect: * **Identity:** Name, email, phone number * **Professional background:** Work experience, education, skills, and your preferred hourly rate * **Resume:** If you choose to upload one * **Connected accounts:** LinkedIn, Facebook, or the other accounts you sign in with * **Availability:** Your Google Calendar free times and booking link, if you connect them * **Verification data:** Your identity check, run by our verification partner ### Participation Data [#participation-data] When you take part in studies, we collect: * **Screening responses:** Answers to qualification questions * **Task submissions:** Survey responses and uploaded files * **Interview recordings:** Video of your camera and shared screen, audio, and transcripts from AI interviews * **Time tracking:** On hourly projects, the time and screenshots the Insightful app records while your timer runs * **Messages:** Your conversations with Sophia and with our support team * **Behavioral data:** How you interact with tasks, such as timing and completion patterns ### Technical Data [#technical-data] We automatically collect: * **Device information:** Browser type, operating system * **Connection data:** IP address, location (country/region) * **Usage data:** Pages visited, features used ### Payment Data [#payment-data] For payouts, we collect: * **Email address:** To set up your payout account with Whop * **Identity and tax information:** Collected by Whop directly during payout verification. This includes a government ID and, for participants in the United States, an SSN. Terac does not collect or store tax forms. * **Bank details:** Collected by Whop directly, in its own forms, when you add a payout method If Whop cannot pay out in your country, your payouts go through Virtual Incentives instead, which receives your email to send you your reward. ## How We Use Your Data [#how-we-use-your-data] ### Matching and Opportunities [#matching-and-opportunities] We use your data to: * Match you with relevant research opportunities * Check you meet study requirements * Show your profile to researchers when you apply ### AI Processing [#ai-processing] We use AI to: * Extract information from your resume * Enrich your profile with LinkedIn data * Conduct interviews through our AI interviewer * Answer your questions through Sophia, our assistant * Check submission quality > Our AI features are provided through third-party AI service providers, which > process your information on our behalf. The Privacy Policy names them and > explains how your information is handled. ### Research Delivery [#research-delivery] Your participation data is shared with researchers for their studies. This includes: * Screening responses * Task submissions * Interview recordings and transcripts Researchers use this data for their research purposes as described in each study. ### Platform Operations [#platform-operations] We use data to: * Process payments * Communicate with you * Improve our platform * Prevent fraud ## Data Sharing [#data-sharing] ### With Researchers [#with-researchers] When you apply to a study, researchers can see: * Your profile information relevant to the study * Your screening responses * Your task submissions and interview transcripts ### With Service Providers [#with-service-providers] We share data with the providers that run parts of Terac for us, including: * **Whop:** for payouts (they receive your email, and collect identity and tax details from you directly to verify you before your first cash out) * **Virtual Incentives:** for payouts in countries Whop does not cover (they receive your email) * **Persona:** for identity verification (they receive your ID image and selfie) * **LiveKit and our speech and AI providers:** for interviews and Sophia * **Text messaging providers:** for the texts we send you * **Insightful:** for time tracking on hourly projects * **Email and analytics providers** The Privacy Policy describes who we share data with, including the categories of personal information we have sold or shared in the past 12 months as some US state privacy laws define those terms, and how to opt out. ### We Never [#we-never] * Share your identity documents with researchers * Share your contact information with researchers without your consent ## Data Security [#data-security] We protect your data with: * Encryption in transit and at rest * Secure access controls * Regular security audits * SOC 2 certification ## Data Retention [#data-retention] ### Active Accounts [#active-accounts] We retain your data while your account is active and for a reasonable period after for legal and operational purposes. ### Deleted Accounts [#deleted-accounts] When your account is deleted, you can no longer sign in, and the account is closed. We keep some records as our Privacy Policy describes and as the law requires. An email address that belonged to a deleted account cannot be used to sign up again. ### Research Data [#research-data] Participation data (submissions, recordings, transcripts) may be retained by researchers according to their data retention policies. ## Your Rights [#your-rights] ### Access [#access] You can view your data in your profile and on your Earnings page. ### Correction [#correction] You can update your profile information at any time from **Profile** in your dashboard. ### Deletion [#deletion] You can ask for your account to be deleted. [Ask Sophia](https://terac.com/docs/experts/support/contact), and our team will take care of it. ### Portability [#portability] You can request an export of your data. ### Opt-Out [#opt-out] You can opt out of certain communications under **Profile → Communications**. To stop text messages, reply STOP. ## Regional Rights [#regional-rights] ### United States [#united-states] Residents of some US states, including California, have additional rights, such as the right to know, to delete, and to opt out of the sale or sharing of personal information. The Privacy Policy explains them and how to exercise them. ### European Union [#european-union] EU residents have rights under GDPR including access, rectification, erasure, and portability. ### Other Regions [#other-regions] Additional rights may apply based on your location. ## Cookies [#cookies] We use cookies for: * Authentication * Preferences * Analytics and advertising You can manage cookie preferences in your browser and in our cookie settings. See the [Cookie Policy](https://terac.com/legal/cookie-policy). ## Changes to This Policy [#changes-to-this-policy] We may update this policy periodically. We'll notify you of significant changes via email or in-app notification. ## Questions [#questions] For privacy-related questions: * Review our [Privacy Policy](https://terac.com/legal/privacy-policy) * [Ask Sophia](https://terac.com/docs/experts/support/contact) # Policies (https://terac.com/docs/experts/policies) Important policies and terms for participating on Terac. Understanding our policies helps you participate successfully and know what to expect. Here are the key policies for Terac experts. - [Referral Policy](https://terac.com/docs/experts/policies/referral-policy): Terms and conditions for earning referral commissions - [Payout Policy](https://terac.com/docs/experts/policies/payout-policy): How payments work, timing, and requirements - [Rewards and Fair Pay](https://terac.com/docs/experts/policies/rewards): How rewards are set, when you get paid, and how review works - [Account Standing](https://terac.com/docs/experts/policies/account-standing): What keeps your account in good standing, and how to appeal a restriction - [Your Wellbeing](https://terac.com/docs/experts/policies/wellbeing): Sensitive study content, your choices, and where to find support - [Data Usage](https://terac.com/docs/experts/policies/data-usage): How we collect and use your information - [Terms of Service](https://terac.com/docs/experts/policies/terms): Summary of your rights and obligations ## Quick Reference [#quick-reference] | Policy | Key Points | | ------------- | ------------------------------------------------------------------------------------------------------------------- | | **Referrals** | 20% commission by default, up to $1,000 per referral | | **Payouts** | Through Whop: move your balance to your payout balance, then withdraw it to your bank (typically 1-3 business days) | | **Rewards** | Paid for approved work, $12.50 minimum cashout, ask us after 7 days | | **Account** | One account per person, honest info; closures for cause are permanent | | **Wellbeing** | Sensitive content should be disclosed upfront, and you can always decline | | **Data** | Used to match you with opportunities and deliver research; see the Privacy Policy | | **Terms** | Follow guidelines, provide honest responses | ## Legal Documents [#legal-documents] These pages are summaries. The complete legal terms are: * [Terms of Service](https://terac.com/legal/terms-of-service) * [Privacy Policy](https://terac.com/legal/privacy-policy) * [Opportunity Policy](https://terac.com/legal/opportunity-policy) * [Cookie Policy](https://terac.com/legal/cookie-policy) ## Questions? [#questions] If you have questions about our policies: * Check the [FAQ](https://terac.com/docs/experts/support/faq) * [Ask Sophia](https://terac.com/docs/experts/support/contact) # Rewards and Fair Pay (https://terac.com/docs/experts/policies/rewards) How study rewards are set, when you get paid, and the protections that make sure you are paid fairly. Terac is built so you know what a study pays before you start, and so approved work is paid promptly. This page explains how rewards work and the protections in place to keep things fair. ## You See the Pay Before You Apply [#you-see-the-pay-before-you-apply] Every opportunity shows its pay and what you will be doing in its details before you apply. You never have to start a study to find out what it pays. Open any opportunity on **Explore** at [terac.com/me/opportunities](https://terac.com/me/opportunities) to see the reward, the kind of work, and the full description. ## You Are Paid for Approved Work [#you-are-paid-for-approved-work] * On a **one-time opportunity**, your earnings are added to your Terac balance shortly after your work is approved. * On a **project-based opportunity**, paid by the hour or per task, you are paid on the project's payout schedule. See [Project-Based Work](https://terac.com/docs/experts/working/projects). * On an opportunity you joined through **another research panel**, that panel usually pays you directly, and the payment does not appear in your Terac balance. You can cash out from your [Earnings page](https://terac.com/me/earnings) once you reach the minimum. > The minimum to cash out is **$12.50**. Earnings from multiple > submissions add up toward it, so smaller studies add up. ## How Long Review Takes [#how-long-review-takes] After you submit, your work goes into review. How long it takes depends on the opportunity and on who reviews it: our automated review, our team, or the research team. The screen you land on after submitting says what happens next, and we email you when there is a decision. > If your submission has been awaiting review for more than 7 days, [ask > Sophia](https://terac.com/docs/experts/support/contact) and we will chase it up for you. ## Being Screened Out [#being-screened-out] Screening questions confirm you match what a study needs. If you are screened out, it means you did not match the criteria for that particular study, and that study is not paid. Being screened out is normal, it is not a judgment on you, and it does not affect your account, unless your answers misrepresented you. Keep applying to opportunities that fit your background. See [completing screeners](https://terac.com/docs/experts/working/screeners) for tips. ## Bonus Payments [#bonus-payments] Some studies include bonus payments on top of the base reward, for things like multi-part studies, follow-up sessions, or exceptional contributions. Bonuses appear in your [transaction history](https://terac.com/docs/experts/how-to/payments). A bonus a researcher adds to an interview shows as **Interview bonus** under **All Transactions**. If a study told you a bonus was coming and it has not arrived after the work was approved, [ask Sophia](https://terac.com/docs/experts/support/contact) with the name of the study. ## If You Think You Were Underpaid or a Payment Is Missing [#if-you-think-you-were-underpaid-or-a-payment-is-missing] Work through these steps: 1. **Check the submission status.** Open [your history](https://terac.com/me/opportunities/history) and confirm the work was approved, not still in review or screened out. 2. **Check who pays for it.** If you joined the opportunity through another research panel, that panel pays you, not Terac. 3. **Check your balance and transaction history.** Approved earnings and bonuses appear on your [Earnings page](https://terac.com/me/earnings). On a project, check its **Payout** tab and next payout date. 4. **Check how long it has been in review.** If it has been awaiting review for more than 7 days, mention that in the next step and we will chase it up. 5. **Ask Sophia.** If the numbers still do not look right, [ask Sophia](https://terac.com/docs/experts/support/contact). She can see your balance and your cash outs, and hands anything she can't sort out to our team. ## Taxes [#taxes] Earnings from Terac may be taxable depending on where you live and your total income. You are responsible for reporting your income according to your local tax laws. See the [Payout Policy](https://terac.com/docs/experts/policies/payout-policy) for how tax details are collected. ## What's Next? [#whats-next] * Learn the mechanics of getting your money in [Setting Up Payments](https://terac.com/docs/experts/how-to/payments) * Read the full [Payout Policy](https://terac.com/docs/experts/policies/payout-policy) * Understand each status in [Understanding Submissions](https://terac.com/docs/experts/working/submissions) # Your Wellbeing and Sensitive Content (https://terac.com/docs/experts/policies/wellbeing) How Terac looks after participants, what to expect from sensitive study content, and where to find support. Your wellbeing matters to us. Research covers a wide range of topics, and a small number of studies discuss subjects that some people find sensitive or distressing. This page explains what to expect and where to get help. ## What Sensitive Content Means [#what-sensitive-content-means] Most studies are everyday surveys, interviews, and tasks. Occasionally, a study may involve: * **Sensitive topics** such as health, personal experiences, trauma, or other difficult subjects * **Mature or graphic material** such as strong language or content that some people find upsetting ## What to Expect Before You Start [#what-to-expect-before-you-start] Before you apply, review the opportunity description and requirements in the detail panel. Studies that involve sensitive or potentially distressing content should disclose this upfront so you can make an informed choice. If something is unclear, you do not have to apply. ## You Can Always Decline or Stop [#you-can-always-decline-or-stop] Participation is your choice. If a study's content is not right for you: * You do not have to apply * If you have not started, you can simply move on to other opportunities * If you have started and are not comfortable continuing, stop, and [ask Sophia](https://terac.com/docs/experts/support/contact) to close the application for you. On a study agreement step, you can also choose **I'm not interested** Leaving a study you are not comfortable with does not harm your account standing. That study is not paid, but there is no penalty. > If a study contains content that is abusive, unethical, or that was not > disclosed in the description, please [tell > Sophia](https://terac.com/docs/experts/support/contact) so we can review it. ## If You Need Support [#if-you-need-support] If a study leaves you feeling distressed, or you are struggling for any reason, help is available. These are free, confidential services you can reach right now: ### United States [#united-states] * **988 Suicide and Crisis Lifeline:** call or text **988** * **Crisis Text Line:** text **HOME** to **741741** ### United Kingdom and Ireland [#united-kingdom-and-ireland] * **Samaritans:** call **116 123** (free, 24/7) ### Anywhere [#anywhere] * **Find a Helpline:** [findahelpline.com](https://findahelpline.com) lists free, confidential support lines by country > **Warning** > > If you or someone else is in immediate danger, contact your local emergency > services right away. These are independent organizations, not part of Terac. We share them because your safety comes first. ## What's Next? [#whats-next] * Learn how to [leave an application](https://terac.com/docs/experts/working/submissions#leaving-an-application) * Review [Staying Safe: Spotting Fraud and Scams](https://terac.com/docs/experts/support/staying-safe) * See how we handle your information in [How Terac Uses Your Data](https://terac.com/docs/experts/policies/data-usage) # Payout Policy (https://terac.com/docs/experts/policies/payout-policy) How Terac payments work, including timing, methods, and requirements. This policy explains how you get paid for completing opportunities on Terac. For step-by-step instructions, see [Setting Up Payments](https://terac.com/docs/experts/how-to/payments). ## Payment Partner [#payment-partner] Terac pays out through **Whop**. Whop handles identity verification, payout methods, and the transfer to your bank. In the few countries Whop cannot pay out to, cash outs go through **Virtual Incentives** instead: after you cash out, you receive an email with a link to choose your payment method. Your Earnings page shows whichever one applies to you. ## How Payments Work [#how-payments-work] ### Step 1: Earn [#step-1-earn] Get your work approved. Earnings for approved work, referral commissions, and bonuses are added to your Terac balance. ### Step 2: Verify your identity [#step-2-verify-your-identity] Before your first cash out, Whop runs a one-time identity check. It asks for personal details and a government ID, and participants in the United States are also asked for tax information such as an SSN. Whop requires this before it can send money to a bank account. You start it from your Earnings page or from [Profile → Identity & Security](https://terac.com/me/settings/identity). It is the same check in both places. Your earnings stay in your Terac balance until it clears. ### Step 3: Move to your payout balance [#step-3-move-to-your-payout-balance] From your Earnings page, transfer any amount at or above the minimum from your Terac balance to your payout balance. This is free and usually instant. ### Step 4: Withdraw [#step-4-withdraw] Withdraw your payout balance to your bank. Choose your method, review the fee, and confirm. ## Payout Timeline [#payout-timeline] | Event | Timeframe | | ------------------------------- | -------------------------------------------------- | | One-time work approved | Added to your Terac balance shortly after approval | | Hourly or per-task project work | Paid on the project's payout schedule | | Move to payout balance | Usually instant | | Withdrawal to bank (standard) | Typically 1–3 business days | ## Limits [#limits] | Limit | Amount | | ---------------- | ----------------------------- | | Minimum cash out | $12.50 | | Maximum per day | $10,000.00 | The current minimum is also displayed on your Earnings page. Earnings from multiple submissions accumulate toward it. The daily maximum counts everything you move out of your Terac balance in one day and resets at midnight UTC. On Whop there is no separate limit per cash out: you can move the whole daily maximum at once, or cash out as many times as you like within it. On Virtual Incentives, each cash out can be from $5.00 up to $2,000.00. Your Earnings page shows how much you have left today and when it resets. ## Fees [#fees] Moving funds from your Terac balance to your payout balance is free. Whop may charge a fee on the withdrawal itself, depending on the method: standard bank transfers carry a small flat fee, and instant transfers cost more. The exact fee is shown in the withdrawal screen before you confirm. ## Identity Verification [#identity-verification] Whop's verification is required before your first cash out and is not something Terac can complete or waive on your behalf. Until it passes, your earnings cannot be moved out of your Terac balance, so nothing can get stuck part way. If you verified your identity on Terac before payouts moved to Whop, that check was with a different provider and you will be asked to verify once more. If it fails, check that the name, date of birth, and address you submitted match your government ID exactly. Mismatches and typos are the most common cause. You can retry from your Earnings page or from [Profile → Identity & Security](https://terac.com/me/settings/identity). If it keeps failing, [ask Sophia](https://terac.com/docs/experts/support/contact). She hands it to our team, who can check the status on Whop's side and escalate to them. If the Earnings page shows **Account on hold**, the identity document you verified with is already used on another Terac account. Retrying the check does not clear this; [ask Sophia](https://terac.com/docs/experts/support/contact) instead. If a cash out instead says **Your cash out is on hold while we review your account**, that is a separate review by our team. Your balance stays on Terac until it is done, and verifying again does not clear it; [ask Sophia](https://terac.com/docs/experts/support/contact) and she will pass it on. ## Failed Payouts [#failed-payouts] A cash out can fail if: * Whop rejects your account email address. It refuses some addresses Terac accepts, such as a `+` alias, so switch your primary email under [Profile → Identity & Security](https://terac.com/me/settings/identity) rather than retrying the same one * Whop's identity verification has not passed yet * Your account is flagged for review * The cash out would take you past the daily maximum * There is a technical issue on the provider's side If a cash out is refused before anything moves, your Terac balance is untouched. If the transfer itself fails, the amount is returned to your Terac balance automatically, and you'll see **Failed to create payout. Your balance has been restored.** Whop can also fail a transfer after accepting it. When that happens, the amount comes back to your Terac balance automatically, usually within 30 minutes, and your transaction history shows **Withdrawal reversal: Whop transfer failed**. Either way, you have not lost money. If you believe a cash out was deducted without arriving, [ask Sophia](https://terac.com/docs/experts/support/contact) and we will trace it. If a cash out would take you past the $10,000.00 daily maximum, the message tells you how much you can still move today and when the limit resets. Your balance is safe and you can cash out again after the reset. If you see "Cash out is temporarily disabled and will be back soon," we have paused cash outs for everyone while we fix an issue. Your balance is safe. Repeated attempts do not speed it up. If it persists for more than a day, [ask Sophia](https://terac.com/docs/experts/support/contact). ## Reversed Payments [#reversed-payments] A payment for approved one-time work can be reversed if the submission is later rejected in review. The reversal is shown as a **Clawback** in your transaction history. ## Paid by Another Panel [#paid-by-another-panel] If you joined an opportunity through another research panel, that panel usually pays you directly, under its own terms. That payment never appears in your Terac balance, and questions about it go to that panel. ## Tax Considerations [#tax-considerations] ### Your Responsibility [#your-responsibility] You take part in opportunities as an independent contractor, not as an employee of Terac or its clients (see section 16 of the [Terms of Service](https://terac.com/legal/terms-of-service)). Earnings from Terac may be taxable income. You are responsible for: * Tracking your earnings * Reporting income to tax authorities * Paying applicable taxes ### Tax Information [#tax-information] Whop collects the tax details it needs directly from you during verification. In the United States this includes an SSN, because Whop cannot open a payout account for a US individual without one. Terac does not collect tax forms from you and does not withhold tax from your earnings: you receive the full approved amount in your Terac balance. ### International [#international] Tax requirements vary by country. Consult a local tax professional for guidance. ## Currency [#currency] Earnings are tracked in USD. Conversion may apply when you withdraw, depending on your country and chosen method. ## Refunds and Disputes [#refunds-and-disputes] ### Can I cancel a cash out? [#can-i-cancel-a-cash-out] Moving funds to your payout balance cannot be reversed from Terac, and the money cannot be moved back to your Terac balance. You can leave it in your payout balance and withdraw it later. ### Disputed amounts [#disputed-amounts] If you believe your earnings are incorrect, [ask Sophia](https://terac.com/docs/experts/support/contact) with the name of the opportunity in question. ## Account Status [#account-status] You must maintain an account in good standing to receive payouts: * No terms of service violations * No fraudulent activity * Active account (not suspended or terminated) * An identity document that is not already used on another account ## Policy Changes [#policy-changes] Terac may update this policy from time to time. We'll notify users of significant changes via email or in-app notification. ## Questions [#questions] For payout questions: * Review [Setting Up Payments](https://terac.com/docs/experts/how-to/payments) * Check [Troubleshooting](https://terac.com/docs/experts/support/troubleshooting) * [Ask Sophia](https://terac.com/docs/experts/support/contact) # Referral Program Policy (https://terac.com/docs/experts/policies/referral-policy) Terms and conditions for the Terac expert referral program. This policy outlines the terms and conditions of Terac's referral program for experts. ## Program Overview [#program-overview] The Terac Referral Program allows you to earn commission when people you refer join Terac and are paid for their work. ## Commission Structure [#commission-structure] ### Commission Rate [#commission-rate] By default, you earn **20%** of what Terac pays your referral for approved work. Some opportunities advertise a higher referral rate for that opportunity. If you referred someone through one of those, you earn the higher rate on what they are paid for that opportunity, and 20% on everything else. The rate is shown wherever you copy the link. ### Earnings Cap [#earnings-cap] The maximum you can earn from a single referral is **$1,000**. The cap is set when they sign up. Once you've earned $1,000 from a specific referral, you stop earning commission from that person. You keep what you've already earned, unless a submission that earned it is later rejected in review (see below). ### No Limit on Referrals [#no-limit-on-referrals] There's no limit to how many people you can refer. Each referral has their own $1,000 cap. ## Eligibility [#eligibility] ### Who Can Participate [#who-can-participate] * Any expert with a Terac account can participate * You don't need to have completed opportunities yourself * Both new and existing experts can refer others ### Who Qualifies as a Referral [#who-qualifies-as-a-referral] For someone to count as your referral: * They must create a new Terac account * They must use your referral link during sign-up * They cannot have an existing Terac account ### When You Earn Commission [#when-you-earn-commission] You earn commission each time Terac pays your referral: * When their work on a one-time opportunity is approved and paid * With each payout on an hourly or per-task project Work Terac does not pay for, such as an opportunity they joined through another research panel, does not earn a commission. Neither does work on an opportunity whose organization does not take referrals, whichever of your links your referral signed up through. If an approved one-time submission that earned you a commission is later rejected in review, that commission may be reversed. ## How to Refer [#how-to-refer] ### Get Your Link [#get-your-link] * Open an opportunity's details and copy the link in its **Refer & Earn** section. Opportunities that don't take referrals have no link. * Go to [terac.com/me/referrals](https://terac.com/me/referrals) and click **Share your link** for your general link See [Referring Others](https://terac.com/docs/experts/how-to/referrals) for every way to get and share a link. ### Share Your Link [#share-your-link] Share your link via: * Direct message to friends or colleagues * Social media posts * Email to your network * Professional communities ### Track Referrals [#track-referrals] View your referrals and what you have earned from them on your referrals page, at [terac.com/me/referrals](https://terac.com/me/referrals). ## Commission Payment [#commission-payment] Referral earnings are added to your regular Terac balance. You can [cash out](https://terac.com/docs/experts/how-to/payments) referral earnings the same way as any other earnings. ## Prohibited Activities [#prohibited-activities] The following activities are not allowed and may result in program suspension: ### Self-Referral [#self-referral] Creating multiple accounts to refer yourself violates our terms of service and may result in account termination. ### Fake Referrals [#fake-referrals] Creating accounts for people who don't exist or without their knowledge is prohibited. ### Spam [#spam] Mass unsolicited messaging or posting referral links where they're not welcome is not allowed. ### Misleading Claims [#misleading-claims] Making false claims about Terac or the referral program to encourage sign-ups is prohibited. ### Incentive Stacking [#incentive-stacking] Offering additional incentives beyond Terac's referral program (like paying people to sign up) is not allowed. ## Program Changes [#program-changes] Terac reserves the right to: * Modify commission rates * Change the earnings cap * Update program terms * Suspend the program We'll notify users of significant changes via email or in-app notification. ## Tax Considerations [#tax-considerations] Referral earnings are taxable income in most jurisdictions. You're responsible for reporting these earnings according to your local tax laws. ## Termination [#termination] We may suspend or terminate your participation in the referral program if: * You violate these terms * You violate Terac's general Terms of Service * We detect fraudulent activity While your Terac account is banned, you earn no commission. Commission on payments your referrals receive during the ban is not paid later, even if the ban is lifted. Your referral links also stop working during a ban, and people who sign up through them are not counted as your referrals. If the ban is lifted, your links are restored and you earn commission again on payments from then on. ## Questions [#questions] For questions about the referral program: * Check the [referral how-to guide](https://terac.com/docs/experts/how-to/referrals) * [Ask Sophia](https://terac.com/docs/experts/support/contact) # Terms of Service Summary (https://terac.com/docs/experts/policies/terms) A summary of the key terms and conditions for using Terac. This page summarizes the key terms for using Terac as an expert. For the complete legal terms, see our [Terms of Service](https://terac.com/legal/terms-of-service). ## Your Account [#your-account] ### One Account Per Person [#one-account-per-person] You may only have one Terac account. Multiple accounts will result in suspension. ### Accurate Information [#accurate-information] You must provide accurate information in your profile. False information may result in account termination. ### Account Security [#account-security] You're responsible for keeping your account secure. Don't share your login credentials. ## Your Obligations [#your-obligations] ### Honest Participation [#honest-participation] When participating in studies, you must: * Provide truthful responses * Answer questions based on your real experiences * Not fabricate or exaggerate information * Not give scripted or AI-generated responses in interviews or surveys ### Quality Standards [#quality-standards] Your submissions should: * Follow study instructions * Be complete and thoughtful * Meet the stated requirements ### Professional Conduct [#professional-conduct] You agree to: * Communicate respectfully * Not attempt to manipulate or game the system * Not engage in fraudulent activity ### Compliance [#compliance] You must comply with: * These terms of service * All applicable laws * Study-specific guidelines ## What Terac Provides [#what-terac-provides] ### Platform Access [#platform-access] We provide: * Access to research opportunities * Tools to manage your profile and submissions * Payment processing for approved work ### No Guarantees [#no-guarantees] We don't guarantee: * A specific number of opportunities * Approval of any submission * Specific earnings amounts ## Payments [#payments] ### Earning Money [#earning-money] You earn money when your submissions are approved. Payouts are processed through our payout partner, Whop, or through Virtual Incentives in countries Whop does not cover. A payment for approved work can be reversed if the submission is later rejected. ### Payment Terms [#payment-terms] See our [Payout Policy](https://terac.com/docs/experts/policies/payout-policy) for complete payment terms. ### Taxes [#taxes] You're responsible for paying applicable taxes on your earnings. ## Intellectual Property [#intellectual-property] ### Your Submissions [#your-submissions] When you submit work to a study, you grant researchers the right to use your responses for their research purposes. ### Our Platform [#our-platform] Terac's platform, design, and content are our intellectual property. You may not copy or reproduce them. ## Termination [#termination] ### By You [#by-you] You can close your account at any time by [asking Sophia](https://terac.com/docs/experts/support/contact). ### By Us [#by-us] We may suspend or terminate your account if you: * Violate these terms * Engage in fraudulent activity * Provide false information * Create multiple accounts ### Effect of Termination [#effect-of-termination] Upon termination: * You lose access to the platform * Pending earnings, and in some cases earlier earnings, may be withheld (for cause termination) * Your data will be handled per our privacy policy ## Limitations [#limitations] ### Liability [#liability] Terac is not liable for: * Study-related issues outside our control * Actions of researchers or other users * Technical issues or outages * Lost earnings from rejected submissions ### Disputes with Researchers [#disputes-with-researchers] Disputes about study requirements or approvals should first be directed to us. We'll try to help but cannot guarantee resolution. ## Changes [#changes] ### To Terms [#to-terms] We may update these terms. We'll notify you of significant changes. ### To the Platform [#to-the-platform] We may modify features, add or remove opportunities, or change how things work. ## Governing Law [#governing-law] These terms are governed by the laws of the State of Delaware, as set out in our full Terms of Service. ## Dispute Resolution [#dispute-resolution] Disputes should first be addressed with us informally, through our support team. See the full Terms of Service for formal dispute resolution procedures. ## Key Points Summary [#key-points-summary] | Topic | Your Responsibility | | ------------- | -------------------------------------------- | | Account | One account, accurate info, keep secure | | Participation | Be honest, follow instructions, quality work | | Payments | Report taxes, comply with payout terms | | Conduct | Professional, no fraud, follow rules | ## Full Terms [#full-terms] This is a summary only. Please read our complete [Terms of Service](https://terac.com/legal/terms-of-service) for all details. ## Questions [#questions] For questions about terms: * Review the [FAQ](https://terac.com/docs/experts/support/faq) * [Ask Sophia](https://terac.com/docs/experts/support/contact) # Get Help (https://terac.com/docs/experts/support/contact) How to get help from Sophia, Terac's assistant, and how to reach the team. The fastest way to get help is to ask **Sophia**, Terac's assistant. She can see your account, she knows these docs, and she hands anything she can't sort out to our support team. ## Ask Sophia [#ask-sophia] Sign in, then open the chat: * From the **chat bubble** in the bottom-right corner of any page of your dashboard, or of these docs * On the support page at [terac.com/help](https://terac.com/help) * From **Ask Sophia** at the end of any page of these docs If Sophia has texted you, you can also reply to her there. Reply STOP to stop the texts. ### What Sophia can help with [#what-sophia-can-help-with] * **Your money:** your balance, what you have earned, and whether a cash out went through and where it went * **Your opportunities:** where you stand on one, which tasks are done, and who pays for it * **Your projects:** the instructions, FAQs, materials, office hours, and payout schedule in the handbook of an opportunity you are on * **Links:** resending an opportunity's apply link, or sending your referral link * **Screenshots:** send her a screenshot of a screen you are stuck on * **How Terac works:** anything these docs cover ### When she hands it to our team [#when-she-hands-it-to-our-team] When Sophia can't sort something out herself, such as a payment that never arrived, a decision you think is unfair, a broken link, deleting your account, or when you'd rather talk to a person, she passes it to our support team with your conversation attached. A person replies in the same conversation. ### What she can't do [#what-she-cant-do] * Give a timeline for a review * Explain why a submission was rejected * Reopen an opportunity you were screened out of * See payments made by another research panel, or what happens in your payout balance after a transfer ### What to include [#what-to-include] To get help faster, tell her: * The name of the opportunity, if it is about one * What happened, and what you expected to happen * A screenshot, if something on screen looks wrong ## Can't Sign In? [#cant-sign-in] If you can't sign in, or your account has been restricted, email **[support@terac.com](mailto:support@terac.com)** from the email address on your account. Include what you see when you try to sign in. ## Community [#community] * **Slack:** join the [Terac community on Slack](https://join.slack.com/t/teraccommunity/shared_invite/zt-3vrh9xvoj-uLN9hcYkhA9ezKrSnaXijA) to ask questions, swap tips, and connect with other experts. * **Facebook:** connect with other Terac experts in our [Facebook community](https://www.facebook.com/groups/1037757014918292/). ## System Status [#system-status] If something isn't working for anyone, check [status.terac.com](https://status.terac.com). The support page at [terac.com/help](https://terac.com/help) also shows the current status. ## Business Inquiries [#business-inquiries] * **Partnerships:** visit [terac.com/contact](https://terac.com/contact) * **Press and brand:** visit [terac.com/brand](https://terac.com/brand) * **Careers:** see our open roles at [terac.com/about](https://terac.com/about#roles) ## What We Can't Help With [#what-we-cant-help-with] Some things are outside our support scope: * **Tax advice:** consult a tax professional * **Legal advice:** consult a lawyer * **Payments from another research panel:** contact that panel * **Retroactive referral credit:** referrals must use your link at sign-up ## Tips for Getting Help [#tips-for-getting-help] 1. **Be specific**: describe exactly what's happening 2. **Include details**: the opportunity's name, error messages, screenshots 3. **Check first**: many answers are in these docs 4. **Be patient**: when Sophia hands something to our team, a person replies in the same conversation # Frequently Asked Questions (https://terac.com/docs/experts/support/faq) Quick answers to common questions about Terac. Find answers to the most common questions about using Terac. Can't find yours? [Ask Sophia](https://terac.com/docs/experts/support/contact). ## Account & Profile [#account--profile] ### How do I change my email address? Your email comes from the accounts you sign in with. Under **Profile → Identity & Security → Linked Accounts**, click **Set as primary** next to the linked email you want us to use. If the address you want is not linked to your account yet, [ask Sophia](https://terac.com/docs/experts/support/contact). ### Can I have more than one account? No. Each person should have only one Terac account. Multiple accounts violate our terms of service and may result in suspension of all accounts. ### How do I delete my account? [Ask Sophia](https://terac.com/docs/experts/support/contact), and our team will delete it. This is permanent, and the same email address cannot be used to sign up again. ### Why can't I log in? * Use the same provider you signed up with. The one you used last is marked **Last used** on the login page. * Signing in with a provider that uses a different email address creates a new account instead of opening yours. * Allow pop-ups and cookies for terac.com, and make sure you can sign in to the provider itself. * If you see **Account Banned** or **Use your personal email**, follow the message on screen. Still stuck? Email [support@terac.com](mailto:support@terac.com) from the address on your account. ### How do I update my profile? Go to **Profile** in your dashboard. You can update most information at any time. A few answers on your match profile can only be changed every so often. ## Opportunities & Applications [#opportunities--applications] ### Why don't I see any opportunities? * Check **Home**: opportunities you are invited to appear there, not on Explore. * Explore does not show opportunities you have already started or taken part in, or ones that are full. * Most opportunities you are not a fit for appear under **Refer & earn** on Explore. Ones that don't take referrals are not shown. * New opportunities open all the time, so check back regularly. You need a verified phone number to apply. If an opportunity's button reads **Finish your profile to apply**, finish onboarding first. ### Why wasn't I accepted to a study? It is one of two things: * **Screened out**: you did not match what the study needed, or the group you fall into was already full. This is normal, and not a judgment on you. * **Not approved**: your submitted work did not pass review. The screen does not show a reason. Sometimes you qualify but the study fills first. We email you if that happens. Keep applying to studies that match your background. ### How long does it take to hear back after applying? It depends on the opportunity and on who reviews it. The screen you land on after submitting shows what happens next, and we email you when there is a decision. If you have been waiting more than 7 days, [ask Sophia](https://terac.com/docs/experts/support/contact). ### Can I withdraw my application? You can decline an invitation from Home, or choose **I'm not interested** on an opportunity's agreement step. Both close the application for good. For anything else, [ask Sophia](https://terac.com/docs/experts/support/contact). ### How do I know which opportunities I qualify for? Each opportunity's details show **Who Should Apply**. Most opportunities you are not a fit for are listed separately, under **Refer & earn**. ## Payments [#payments] ### When will I get paid? 1. On a one-time opportunity, your earnings are added to your Terac balance shortly after your work is approved. On a project, you are paid on the project's payout schedule. 2. Once you reach the minimum, click **Transfer** on your Earnings page to move your balance to your payout balance. The first time, you verify your identity first. 3. Withdraw your payout balance to your bank from the **Payout balance** panel on the same page. 4. A standard bank transfer typically arrives within 1 to 3 business days. See [Setting Up Payments](https://terac.com/docs/experts/how-to/payments). ### I moved money but it's not in my bank Moving money puts it in your payout balance, not your bank. Withdraw it from the **Payout balance** panel on your Earnings page, which shows the status of each withdrawal. ### I moved funds but my payout balance didn't update If you saw **Transfer processing**, Whop is still confirming the transfer, and your payout balance updates when it does. Otherwise the page refreshes a moment after the transfer; if your payout balance still looks wrong, refresh manually. ### Why am I being asked to verify my identity again? Payouts go through our payout partner Whop, which runs its own one-time identity check before your first cash out. If you verified on Terac before payouts moved to Whop, that check was with a different provider, so you need to verify once more. Your earnings stay in your Terac balance until it passes, and the Earnings page unlocks **Transfer** as soon as it does. ### What payment methods are available? You withdraw your payout balance to your bank through Whop, from the **Payout balance** panel on your Earnings page. The methods available depend on your country, and the options and any fee are shown before you confirm. ### What's the minimum withdrawal amount? The minimum to cash out is **$12.50**. It is also shown on your Earnings page. Earnings from multiple submissions add up toward it. ### Why isn't an opportunity's payment in my Terac balance? If you joined it through another research panel, that panel usually pays you directly, and the payment never appears in your Terac balance. On a project, check its **Payout** tab for the next payout date. ### Are my earnings taxable? Earnings from Terac may be taxable depending on where you live and your total income. You're responsible for reporting income according to your local tax laws. ## Interviews [#interviews] ### What if my microphone or camera isn't working? 1. Allow camera and microphone for terac.com in your browser's site settings, then reload the page 2. Choose the right microphone in your browser or computer sound settings 3. Try Chrome or Edge, which work best 4. Try headphones with a built-in microphone See [AI Interviews](https://terac.com/docs/experts/working/interviews#common-issues-and-solutions). ### Can I retake an interview? Not one you have finished. A screening interview that dropped or that you ended early can be started again from the beginning. ### What language should I use? The language the opportunity is in. If it offers more than one, you choose on the screen before you start. The onboarding interview is in English. ### How long are interviews? The AI screening interview takes about 3 minutes, and the onboarding interview about 10. The estimate is shown before you start. ## Verification [#verification] ### Why do I need to verify my phone number? Phone verification confirms you're a real person and helps prevent duplicate accounts. It is the one required onboarding step, and you need it to apply to opportunities. ### Why do I need to verify my identity? You need a verified identity before you can cash out. It only has to be done once. See [Verification and Connected Accounts](https://terac.com/docs/experts/how-to/attestations). ### What if my ID verification fails? * **Blurry photo:** Retake it in better light * **Expired ID:** Use a valid, unexpired document * **Face doesn't match:** Make sure your face is well lit * **Document not accepted:** Try a different document, such as a passport, driver's license, or national ID If it says **Verification Blocked** or **Needs Review**, retrying won't help. [Ask Sophia](https://terac.com/docs/experts/support/contact). ### Who runs the identity check? Whop, our payout and verification partner, runs it in a secure form on terac.com, so you may see their name. Only ever verify your identity from your Terac settings or your Earnings page. ## Referrals [#referrals] ### When do I get paid for referrals? Each time Terac pays the person you referred for their work, a share of that payment is added to your balance: 20% by default, up to $1,000 per person. Opportunities that don't take referrals don't count. See [Referring Others](https://terac.com/docs/experts/how-to/referrals). ### Does my referral need to use my link? Yes. They must use your referral link when creating their account. There is no code to enter, so if they sign up without the link, you won't receive credit. ### What if my referral already has an account? Referral credit only applies to new accounts. Existing accounts cannot be added as referrals retroactively. ## Still Have Questions? [#still-have-questions] * [Ask Sophia](https://terac.com/docs/experts/support/contact) * Check [Troubleshooting](https://terac.com/docs/experts/support/troubleshooting) for technical issues * Review the [Glossary](https://terac.com/docs/experts/support/glossary) for terminology # Terac Glossary (https://terac.com/docs/experts/support/glossary) Definitions of key terms used on Terac. This glossary explains the terminology you'll encounter on Terac. ## A [#a] ### AI Interviewer [#ai-interviewer] Terac's voice interviewer. It runs the onboarding interview, where it introduces itself as Sophia, and the short AI screening interviews some opportunities use. The interviewer does not decide: a person on our team, or our automated review, assesses the recording and transcript. ### Approval [#approval] When your submitted work is accepted. On a one-time opportunity, earnings are added to your balance once your work is approved. On a project, you are paid on the project's payout schedule. ### Attestation [#attestation] A verified fact about you that opportunities can rely on, such as a verified phone number, a verified identity, or a connected LinkedIn profile. ### Automated Review [#automated-review] Terac's quality system reviewing your work instead of a person. Where it is switched on for an opportunity, it can approve your screening, usually in under a minute, and where rejecting is switched on too, it can reject a screening that falls below the quality bar. It is never based on how long a submission has been waiting. ## B [#b] ### Balance [#balance] Your **Terac balance**: the money you have earned and not yet cashed out, shown on your Earnings page. ### Bonus [#bonus] An additional payment on top of a study's base reward, for example for a follow-up session or an exceptional contribution. Bonuses appear in your transaction history. ## C [#c] ### Camera Quality Check [#camera-quality-check] A quick check before some tasks that your face is visible, the lighting is good, and you are alone. ### Cash Out [#cash-out] Moving money from your Terac balance to your payout balance, then withdrawing it to your bank. ### Commission [#commission] What you earn from referrals: 20% by default of what Terac pays the person you referred, up to $1,000 per person. Some opportunities advertise a higher rate for that opportunity. ## E [#e] ### Earnings [#earnings] Money you've earned from approved work, referral commissions, and bonuses, added to your Terac balance. ### Expert [#expert] A verified participant on Terac who completes research opportunities. Also called a panelist. ### Explore [#explore] The page in your dashboard, at terac.com/me/opportunities, that lists the opportunities open to you. ## H [#h] ### Handbook [#handbook] The home page of a project-based opportunity. It covers how to do the work, how you get paid, and who to contact, with office hours, materials, and, on hourly projects, the hours you plan and track. ## I [#i] ### Identity Verification [#identity-verification] A one-time check of your government-issued photo ID, required before you can cash out. It is run by Whop, our verification partner. ### Insightful [#insightful] The time tracker used on hourly projects. On your computer the app is called Workpuls. ### Interview [#interview] A short AI voice conversation: the onboarding interview, or a screening step on an opportunity. Some opportunities also have a live call you book. ### Invitation [#invitation] An opportunity someone picked you for. Invitations appear on Home, where you can accept or decline them. ## O [#o] ### Onboarding [#onboarding] The setup after you create an account: verifying your phone, connecting LinkedIn or adding your resume, reviewing your profile and hourly rate, and an optional interview. Identity verification comes afterwards. ### Opportunity [#opportunity] A research study you can apply to. Each opportunity has its own requirements, pay, and tasks. ## P [#p] ### Panelist [#panelist] Another term for an expert. A verified participant on Terac who completes research opportunities. ### Payout [#payout] A payment to you: approved work credited to your Terac balance, or money you withdraw to your bank. ### Payout Balance [#payout-balance] Your balance with Whop, our payout partner. You move money into it from your Terac balance, then withdraw it to your bank from the **Payout balance** panel on your Earnings page. ### Planned Hours [#planned-hours] On an hourly project, the hours you mark in **Schedule**, on the project's **Time** tab, as when you plan to work. Your project manager sees them too. They are not paid: only tracked hours are. ### Profile [#profile] Your expert profile: your information, experience, education, skills, and hourly rate. A complete profile helps match you with opportunities. ### Project-Based Opportunity [#project-based-opportunity] Ongoing work over a period of time, paid by the hour or per task on the project's payout schedule. ## R [#r] ### Referral [#referral] A new user who signed up using your referral link. You earn a commission when Terac pays them for their work. ### Rejection [#rejection] When submitted work is not approved in review, or when you are taken off a project, in which case we email you. It can happen after an approval, in which case the payment for a one-time opportunity is reversed. On a project, what you were already paid for hours or tasks you worked stays yours. ## S [#s] ### Screener [#screener] Qualification questions asked early in the application. Screeners determine if you're eligible for a study. ### Screened Out [#screened-out] When you don't meet the screening criteria for a study. This is common and not a reflection on you personally. ### Sophia [#sophia] Terac's assistant. You can chat with her from your dashboard, these docs, or terac.com/help, and she hands anything she can't sort out to our support team. ### Submission [#submission] Your application and work for a specific opportunity. A submission tracks your progress from application through payment. ## T [#t] ### Task [#task] A piece of work within a submission, such as a survey, a file upload, a booked session, an agreement or contract, or a setup step on a project. ### Tracked Hours [#tracked-hours] On an hourly project, the time the Insightful app recorded while your timer was running. Only tracked hours are paid, and a project may pay for only some of them: **Tracked**, on its **Time** tab, shows your **Payable hours**. ## V [#v] ### Virtual Incentives [#virtual-incentives] Our former payment provider, which emailed a reward link when you cashed out. Terac now pays out through [Whop](https://terac.com/docs/experts/support/glossary#whop). Payouts you already received through Virtual Incentives are not affected. ## W [#w] ### Whop [#whop] Terac's payout partner. Whop holds your payout balance, runs a one-time identity verification before your first cash out, and transfers the money to your bank. *** ## Quick Reference [#quick-reference] | Term | Short Definition | | --------------- | ----------------------------------------------- | | Balance | Money earned on Terac and not yet cashed out | | Cash Out | Transfer to your payout balance, then your bank | | Expert/Panelist | Verified participant | | Opportunity | Research study to apply for | | Payout balance | Your balance with Whop, withdrawn to your bank | | Screener | Qualification questions | | Submission | Your application + work | | Task | Individual activity (survey, upload, session) | | Whop | Payout and identity verification partner | ## Missing a Term? [#missing-a-term] If you encounter a term that's not defined here, [ask Sophia](https://terac.com/docs/experts/support/contact). # Support (https://terac.com/docs/experts/support) Get help with your Terac account, opportunities, and payments. Need help? Find answers to common questions here, or ask Sophia, Terac's assistant. - [Get Help](https://terac.com/docs/experts/support/contact): Ask Sophia, or reach our team - [FAQ](https://terac.com/docs/experts/support/faq): Quick answers to common questions - [Glossary](https://terac.com/docs/experts/support/glossary): Terac terminology explained - [Troubleshooting](https://terac.com/docs/experts/support/troubleshooting): Solutions to common technical issues - [Staying Safe](https://terac.com/docs/experts/support/staying-safe): Spot fraud and scams, and protect your account ## Quick Help [#quick-help] ### Common Issues [#common-issues] | Issue | Solution | | -------------------------------------- | --------------------------------------------------------------------------------- | | Can't log in | Use the provider marked **Last used**, or the one you signed up with | | No opportunities showing | Check **Home** for invitations, and verify your phone to apply | | Moved money but it's not in my bank | Withdraw it from the **Payout balance** panel on your Earnings page | | Interview camera or microphone blocked | Allow them in your browser's site settings, then reload. Chrome or Edge work best | ### Helpful Links [#helpful-links] * [How to apply to opportunities](https://terac.com/docs/experts/how-to/apply) * [Understanding submissions](https://terac.com/docs/experts/working/submissions) * [Setting up payments](https://terac.com/docs/experts/how-to/payments) * [AI interviews](https://terac.com/docs/experts/working/interviews) ## Still Need Help? [#still-need-help] If you can't find what you're looking for: 1. Browse the sidebar, or check the [FAQ](https://terac.com/docs/experts/support/faq) 2. Review [Troubleshooting](https://terac.com/docs/experts/support/troubleshooting) 3. [Ask Sophia](https://terac.com/docs/experts/support/contact) # Troubleshooting (https://terac.com/docs/experts/support/troubleshooting) Solutions to common technical issues on Terac. Experiencing technical issues? Find solutions to common problems below. If you're still stuck, [ask Sophia](https://terac.com/docs/experts/support/contact) and send her a screenshot of what you see. ## Interview Issues [#interview-issues] ### Camera or Microphone Blocked [#camera-or-microphone-blocked] **Symptoms:** "Access to your camera and microphone is blocked", "No camera and microphone was found on this device", or the interview won't start **Solutions:** 1. **Allow access in your browser** * Click the lock or site-settings icon in your browser's address bar * Set camera and microphone to **Allow** * Reload the page 2. **On a phone** * Open your phone's settings and allow camera and microphone for your browser * Reload the page 3. **Choose the right microphone** * There is no microphone picker inside the interview * Choose it in your browser's site settings or your computer's sound settings 4. **Try a different browser** * Chrome or Edge work best * Safari and Firefox can work, but some checks are not available in them 5. **Restart your browser** * Close all browser windows * Reopen and try again ### Screen Sharing or Monitor Problems [#screen-sharing-or-monitor-problems] * **You shared the wrong thing:** click **Try again** and choose **Entire screen**, not a window or a tab. * **"External monitor detected":** unplug the extra screen. The message clears once it is disconnected. * **Screen sharing won't start:** refresh the page, or try Chrome or Edge. ### Audio Quality Problems [#audio-quality-problems] **Symptoms:** Choppy audio, echo, feedback **Solutions:** 1. **Reduce background noise**: move somewhere quieter, and close windows and doors 2. **Use headphones**: they prevent echo from your speakers 3. **Move closer to your microphone** 4. **Check your internet connection**: switch to a wired connection if you can, and close bandwidth-heavy apps ### Interview Disconnected [#interview-disconnected] * **A screening interview:** go back to the step and start it again. It starts from the beginning. * **The onboarding interview:** open it again from Home, and you pick up where you left off. * **"We couldn't start the interview":** wait a moment and try again. If you can't get back in, [ask Sophia](https://terac.com/docs/experts/support/contact) with the name of the opportunity and what happened. ## Verification Issues [#verification-issues] ### Phone Verification Not Working [#phone-verification-not-working] **Solutions:** 1. **Send the pre-filled text** * The default is to send us a text: scan the QR code, or tap **Open Messages** on your phone, and send the message without changing it * We detect it as soon as it arrives 2. **Use a code instead** * Choose **Verify with a code instead** to get a 6-digit code by SMS * You can ask for a new code every 60 seconds, even if you change numbers 3. **Check your number** * Make sure you entered the right number, with the country code * Virtual numbers such as Google Voice, and landlines, cannot be used 4. **Reached a code limit** * You can get up to 5 codes a day, sent to at most two different numbers * Past that, choose **Verify through Messages instead**, or email [support@terac.com](mailto:support@terac.com) if you can't use Messages ### ID Verification Failed [#id-verification-failed] **Symptoms:** The identity check rejects your document **Solutions:** 1. **Photo quality** * Ensure good lighting * Keep the ID flat and fully visible, with all corners in the frame * Avoid glare and shadows 2. **Document issues** * Make sure the ID has not expired * Use an accepted document type, such as a passport, driver's license, or national ID * If you live in a different country from your citizenship, use an ID from your country of residence 3. **Try again** * Click **Try Again** and use a clearer photo or a different document If it says **Verification Blocked** or **Needs Review**, retrying won't help. [Ask Sophia](https://terac.com/docs/experts/support/contact). ### LinkedIn Connection Failed [#linkedin-connection-failed] **Symptoms:** one of these messages after you try to connect LinkedIn: * **That LinkedIn account is already connected to another Terac account** * **That LinkedIn account uses a different email** * **LinkedIn access was not granted** * **We could not start connecting your LinkedIn** * **We could not connect your LinkedIn** **Solutions:** 1. **Already connected to another Terac account** * Try a different LinkedIn account, or upload your resume instead * If you think you have signed up before with a different email, [ask Sophia](https://terac.com/docs/experts/support/contact) and include your LinkedIn profile URL 2. **Uses a different email** * Connect the LinkedIn account that matches the email on your Terac account 3. **Access was not granted** * Try again, and when LinkedIn asks, allow Terac to access your profile 4. **Could not start or could not connect** * Check your internet connection and try again * Some browser extensions can interfere, so try a different browser * If it keeps failing, [ask Sophia](https://terac.com/docs/experts/support/contact) ## Payment Issues [#payment-issues] ### Payout Balance Did Not Update [#payout-balance-did-not-update] **Symptoms:** Moved funds from your Terac balance but the payout balance still looks wrong **Solutions:** 1. **Check whether it said Transfer processing** * Whop is still confirming the transfer, and your payout balance updates when it does * There is no need to try again 2. **Refresh the page** * Otherwise the panel refreshes a moment after the transfer, and a manual refresh resolves almost every case 3. **Check your transaction history** * The move appears as a withdrawal from your Terac balance * If your Terac balance was restored instead, the transfer failed and nothing was lost 4. **Ask Sophia** * If the money is in neither balance, [ask Sophia](https://terac.com/docs/experts/support/contact) * Include the date and amount ### Transfer Blocked by Verification [#transfer-blocked-by-verification] **Symptoms:** Your Earnings page shows **Verify identity**, **In review**, or a verification message where the **Transfer** button should be **Solutions:** 1. **Complete the identity check** * It is required once, before your first cash out * Your earnings stay in your Terac balance until it passes, so nothing is stuck * If you verified on Terac before payouts moved to Whop, you need to verify once more 2. **Wait if it says In review** * There is nothing to do: the page checks again on its own and unlocks **Transfer** as soon as it clears 3. **Match your ID exactly** * Name, date of birth, and address must match your government ID * Mismatches and typos are the most common cause of failure 4. **Retry from the Earnings page or your profile** * Click **Try again**, **Finish verifying**, or **Re-verify identity** where Transfer would be * Or restart it under [Profile → Identity & Security](https://terac.com/me/settings/identity) 5. **Ask Sophia** * If it keeps failing, or the page says **Account on hold**, [ask Sophia](https://terac.com/docs/experts/support/contact) * She hands it to our team, who can check the status on Whop's side and escalate ### "Our payout provider rejected your account email address" [#our-payout-provider-rejected-your-account-email-address] **Symptoms:** Error when starting a cash out **Solutions:** 1. **Switch to a different email** * Our payout provider refuses some addresses Terac accepts, such as ones with a `+` alias, so a perfectly valid address can still be rejected * Retrying with the same address gets the same answer every time * Under [Profile → Identity & Security](https://terac.com/me/settings/identity), go to Linked Accounts and click **Set as primary** on a different email, or **Link Another Account** to add one, then try again 2. **Ask Sophia** * If a different address is refused too, or you cannot add one, [ask Sophia](https://terac.com/docs/experts/support/contact) ### "Your cash out is on hold while we review your account" [#your-cash-out-is-on-hold-while-we-review-your-account] **Symptoms:** **Could not move funds to payout balance** (or **Cash Out Failed**) with this message when you try to cash out **Solutions:** 1. **Your balance is safe** * Nothing has left your Terac balance * Trying again gets the same answer until the review is done 2. **Ask Sophia** * [Ask Sophia](https://terac.com/docs/experts/support/contact), and she passes it to our team, who review the account * This is not the same as **Account on hold** on your Earnings page, and verifying your identity again does not clear it ### My Transfer Failed [#my-transfer-failed] If a transfer fails, the full amount goes straight back to your Terac balance, and you'll see **Failed to create payout. Your balance has been restored.** You can try again. Whop can also fail a transfer after accepting it. When that happens, the amount comes back to your Terac balance automatically, usually within 30 minutes, and your transaction history shows **Withdrawal reversal: Whop transfer failed**. ### Money Isn't in My Bank [#money-isnt-in-my-bank] A transfer moves your money to your payout balance, not straight to your bank. Withdraw it from the **Payout balance** panel on your Earnings page, which also shows the status of each withdrawal. Terac cannot see what happens inside your payout balance after the transfer. ### Wrong Payment Amount [#wrong-payment-amount] **Solutions:** 1. **Check your transaction history** on your Earnings page, including any **Clawback** rows for reversed payments 2. **Check the submission's status**: some work may still be in review 3. **Check who pays for it**: if you joined through another research panel, that panel pays you 4. **On a project, check its Payout tab**: projects pay on their own schedule 5. **Ask Sophia**: if the numbers still don't match, [ask Sophia](https://terac.com/docs/experts/support/contact) with the name of the opportunity ## Website Issues [#website-issues] ### Page Not Loading [#page-not-loading] **Symptoms:** Blank page, constant loading, errors **Solutions:** 1. **Refresh the page**: try Shift + Refresh for a hard refresh 2. **Clear your browser cache** 3. **Try a different browser** 4. **Check your internet connection** 5. **Check Terac's status** at [status.terac.com](https://status.terac.com) ### Login Issues [#login-issues] **Symptoms:** Can't sign in, or you land on an empty account **Solutions:** 1. **Use the same provider you signed up with** * The one you used last is marked **Last used** on the login page * Signing in with a provider that uses a different email address creates a new account instead of opening yours 2. **Allow pop-ups and cookies** for terac.com 3. **Check the provider itself** * Make sure you can sign in to LinkedIn, Google, or whichever provider you use 4. **Follow the message on screen** * **Account Banned** or **Use your personal email** explain what to do Still stuck? Email [support@terac.com](mailto:support@terac.com) from the address on your account. ## Still Having Issues? [#still-having-issues] If these solutions don't resolve your problem: 1. **Document the issue**: take screenshots, and note any error messages and what you were doing 2. **Ask Sophia**: [start a chat](https://terac.com/docs/experts/support/contact) and include those details # Staying Safe: Spotting Fraud and Scams (https://terac.com/docs/experts/support/staying-safe) How to recognize scams, protect your account, and report anything suspicious on Terac. Terac is a safe place to do research work, and keeping it that way is a shared effort. This page helps you recognize scams, protect your account, and report anything that looks wrong. ## Terac Will Never Ask You For [#terac-will-never-ask-you-for] * **A password.** You sign in through LinkedIn, Google, Facebook, Microsoft, or GitHub, so there is no Terac password for anyone to ask for. * **A code, by email, chat, or phone.** You only ever enter a code on terac.com, or send the pre-filled text that terac.com opens for you when you verify your phone. Nobody from Terac will ask you to read out or forward a code. * **A payment to participate.** Taking studies is always free. You never pay to apply, to unlock studies, or to receive your earnings. * **Your bank details anywhere outside the payout flow.** Terac itself never collects them. Our payout partner Whop does ask for a government ID and a payout method, but only inside flows you start yourself on Terac: identity verification from your [Earnings page](https://terac.com/me/earnings) or **Profile → Identity & Security**, and adding a payout method in the **Payout balance** panel when you withdraw. Anyone asking for that by email, chat, text, or phone is not us. * **Identity documents in a message.** Identity verification happens only inside a flow you opened yourself on Terac, from **Profile → Identity & Security** or your Earnings page, never by email request. ## Red Flags in a Study or Message [#red-flags-in-a-study-or-message] Be cautious if you are ever asked to: * **Pay money, send a deposit, or buy gift cards** as part of a study * **Share sensitive details that were not disclosed upfront**, such as your full bank details, government document numbers, passwords, or login codes * **Download an unexpected file** or visit an unfamiliar website that feels off * **Move to a channel or payment method that Terac didn't give you**, such as a personal email address, a messaging app, or an outside payment service * Accept an offer that **seems too good to be true** Legitimate studies only use the channels Terac sets up for them: the opportunity's own page, texts or chats from Sophia, a project Slack or other link shown on your opportunity's page, Insightful for time tracking, and our payout partner. Legitimate payouts never require a fee. ## Protecting Your Account [#protecting-your-account] * **Secure your sign-in provider.** Because you sign in through LinkedIn, Google, Facebook, Microsoft, or GitHub, turning on two-factor authentication with that provider also protects your Terac account. * **Turn on Terac two-factor authentication** under **Profile → Identity & Security**. * **Never share access** to your account. Account sharing violates our [Terms of Service](https://terac.com/docs/experts/policies/terms). * **Watch for phishing.** Scammers may send emails that imitate Terac. Genuine Terac email comes from terac.com or one of its subdomains, such as notifications.terac.com, and our support address is `support@terac.com`. Partners such as Whop and Insightful email from their own domains. We will never ask for your credentials by email. > Unsure whether a message is really from us? Ask Sophia at > [terac.com/help](https://terac.com/help). She can see the texts she has sent > you. ## How to Report Something Suspicious [#how-to-report-something-suspicious] * If a **study or message** looks like a scam, [tell Sophia](https://terac.com/docs/experts/support/contact) with details and screenshots. * If you think your **account has been compromised**, [tell Sophia](https://terac.com/docs/experts/support/contact) straight away, or email [support@terac.com](mailto:support@terac.com) if you can't sign in, so we can help secure it. ## What's Next? [#whats-next] * Review [Account Standing, Holds, and Appeals](https://terac.com/docs/experts/policies/account-standing) * See how we protect your information in [How Terac Uses Your Data](https://terac.com/docs/experts/policies/data-usage) * Browse the full [FAQ](https://terac.com/docs/experts/support/faq) # Welcome to Terac (https://terac.com/docs/experts/welcome) Everything you need to get started as a Terac expert, including onboarding, opportunities, payments, and more. Welcome to Terac! We connect verified experts like you with paid research opportunities from leading companies and research platforms worldwide. ## See How Terac Works [#see-how-terac-works] [What is Terac](https://www.youtube.com/embed/9GFXpuJQZ7U) ## What is Terac? [#what-is-terac] Terac is an AI-native panel infrastructure that recruits, screens, and delivers verified experts to research platforms. What you do here is paid research work: a short interview, a survey, or an ongoing project. As an expert on Terac, you can: - **Browse opportunities**: Find studies that match your background and expertise - **Take part in studies**: Everything from quick surveys to ongoing hourly projects - **Earn money**: Get paid for your approved work - **Refer others**: Earn a share of what the people you refer are paid ## The Expert Journey [#the-expert-journey] 1. **Create your account** Sign in at [terac.com/login](https://terac.com/login) with LinkedIn or another provider. There is no password to set. 2. **Complete onboarding** Verify your phone number, connect LinkedIn or add your resume, and review the profile Terac builds for you, including your preferred hourly rate. 3. **Take the onboarding interview** A short voice conversation with Sophia, about 10 minutes, that helps us match you with the right opportunities. You can skip it and take it later from Home. 4. **Browse opportunities** Open **Explore** in your dashboard, or see what is open at [terac.com/opportunities](https://terac.com/opportunities). Opportunities you are invited to appear on **Home**. 5. **Apply and do the work** Each opportunity walks you through its own steps, usually screening questions and then the tasks themselves. 6. **Get paid** Approved work is added to your Terac balance. Verify your identity once, then move your balance to your payout balance on the Earnings page and withdraw it to your bank through Whop. ## Quick Start Checklist [#quick-start-checklist] > New to Terac? Knock these out first and you'll be ready to apply to your first > study. * [ ] Verify your phone number * [ ] Connect LinkedIn or upload your resume * [ ] Review your profile and set your preferred hourly rate * [ ] Take the onboarding interview (optional, about 10 minutes) * [ ] Verify your identity (needed before your first cash out) * [ ] Browse **Explore** and apply to your first opportunity ## What's Next? [#whats-next] - [Create Your Account](https://terac.com/docs/experts/how-to/create-account): Sign up and get started - [Setup Your Profile](https://terac.com/docs/experts/how-to/setup-profile): Complete onboarding and build your profile - [Apply to Opportunities](https://terac.com/docs/experts/how-to/apply): Find and apply to studies - [Set Up Payments](https://terac.com/docs/experts/how-to/payments): Learn how to cash out your earnings ## Need Help? [#need-help] * Ask Sophia, Terac's assistant, from the chat on any page of your dashboard or on these docs. See [Get Help](https://terac.com/docs/experts/support/contact). * Check the [FAQ](https://terac.com/docs/experts/support/faq) for common questions * Review [Troubleshooting](https://terac.com/docs/experts/support/troubleshooting) for technical issues # Working on Studies (https://terac.com/docs/experts/working) Everything you need to know about participating in research studies on Terac. Once you've applied to an opportunity, here's what to expect as you work through the study. ## The Study Lifecycle [#the-study-lifecycle] - [Understanding Submissions](https://terac.com/docs/experts/working/submissions): Track your applications and what each status means - [Completing Screeners](https://terac.com/docs/experts/working/screeners): Pass screening questions to qualify for studies - [Completing Tasks](https://terac.com/docs/experts/working/tasks): Surveys, uploads, agreements, and other research tasks - [Project-Based Work](https://terac.com/docs/experts/working/projects): Handbooks, office hours, and payouts on ongoing projects - [Time Tracking with Insightful](https://terac.com/docs/experts/working/time-tracking): Record your hours on an hourly project - [AI Interviews](https://terac.com/docs/experts/working/interviews): The onboarding interview and AI screening interviews ## Tips for Success [#tips-for-success] - [Best Practices](https://terac.com/docs/experts/working/best-practices): Build a strong profile and improve your approval rate ## Quick Overview [#quick-overview] | Stage | What Happens | | ------------- | --------------------------------------------------------------------- | | **Apply** | Start an application from Home or Explore | | **Screening** | Answer questions to show you qualify (most opportunities) | | **Tasks** | Complete the main research activities | | **Review** | Our team, the research team, or our automated review checks your work | | **Payout** | Get paid for approved work | On a project-based opportunity, after the setup tasks you work from the project's handbook and are paid on its payout schedule. ## Need Help? [#need-help] If you run into trouble during a study: * Check [Troubleshooting](https://terac.com/docs/experts/support/troubleshooting) for common problems * Review the [FAQ](https://terac.com/docs/experts/support/faq) for quick answers * [Ask Sophia](https://terac.com/docs/experts/support/contact) if you're stuck # Project-Based Work (https://terac.com/docs/experts/working/projects) How ongoing projects work, from the project handbook and office hours to tracked hours and payouts. Project-based opportunities are ongoing work over a period of time, paid by the hour or per task. Instead of a single payout when you finish, you are paid on the project's payout schedule for the hours or tasks you complete. ## Getting Started on a Project [#getting-started-on-a-project] A project's application usually ends with a few setup tasks, such as signing a contract, joining the project's Slack, or installing the time tracker. When they are done, you see **You're in**, and from then on you work from the project's handbook. ## The Project Handbook [#the-project-handbook] Open the project from **Home** to reach its page. The handbook is your home for the project: it covers how to do the work, how you get paid, and who to contact. It has these tabs: * **Instructions**: how to do the work. * **Materials**: documents, videos, and other files the project gives you, when it has any. * **Time**: on hourly projects. **Schedule** is where you plan the hours you will work, and **Tracked** shows the time the tracker has recorded. * **Payout**: what you have earned on this project, and what has been paid. Beside the tabs you will find: * **Payout**: **Your rate**, **Paid to date**, and **Next payout**. * **Your schedule**, on hourly projects: the next hours you planned. Click it to open **Schedule**. * **Office hours**, if the project holds them: **Join the call** while one is live, or **Add to calendar** for the next one. * **Who to contact** about the project. * **Project links**, such as the **Project Slack channel**. * **Documents**, including **My contract** if you signed one. On an hourly project, the **Start Tracking Hours** button at the top opens the time tracker. > Sophia can read your project's handbook, so you can ask her about the > instructions, materials, office hours, or payout schedule. ## How Projects Pay [#how-projects-pay] Each project sets its own payout schedule, such as weekly on a set day or monthly on a set date. The **Payout** tab and the **Next payout** card show it. * **Hourly projects** pay for the time you track in Insightful. Only tracked hours are paid, and they are reviewed before they are paid. A project pays for all of your tracked time, for productive time only, or for productive and neutral time; **How payable hours work**, in **Tracked** on the **Time** tab, shows which, with each kind of time marked **PAID** or **NOT PAID**. See [Time Tracking with Insightful](https://terac.com/docs/experts/working/time-tracking). * **Per-task projects** pay for the tasks you complete. Each payout is added to your Terac balance, where you can [cash it out](https://terac.com/docs/experts/how-to/payments). Until your first payment is approved, the Payout tab says **Your first payment will appear here once it is approved.** ## Planning Your Hours [#planning-your-hours] On an hourly project, **Schedule** on the **Time** tab is where you mark when you plan to work, this week or next. You and your project manager both see these hours, so the project knows how much time it can count on. Planning hours is not the same as tracking them: you are paid only for the time you track. * Click **Add hours** to place an hour in the first free spot, then drag, resize, or delete it to fit your plans. * Hours you planned on your other opportunities show too, so you can see your whole week. * Nothing is saved until you click **Save changes**. **Discard** drops your changes. * You can only change this week and next week. * **History** lists every change to your week, newest first. ## Tracked Hours [#tracked-hours] On an hourly project, **Tracked** on the **Time** tab shows your time by week or by day, counted in your own time zone. It leads with your **Payable hours**, then splits your time into **Productive**, **Neutral** (including any app the project has not categorized), **Unproductive**, and **Idle**, where idle is time the tracker was running with no keyboard or mouse activity. Idle time is paid only on a project that pays for all tracked time. New hours appear here within about an hour of being tracked. ## FAQ [#faq] ### When do I get paid on a project? [#when-do-i-get-paid-on-a-project] On the project's payout schedule, shown on its Payout tab. You are not paid when you finish setup: you are paid for the hours or tasks each payout covers. ### Where do I find the project's Slack or office hours? [#where-do-i-find-the-projects-slack-or-office-hours] On the project's page, under **Project links** and **Office hours**. You can also ask Sophia for the office hours link. ### My project is still "In Progress". Is something wrong? [#my-project-is-still-in-progress-is-something-wrong] No. A project stays in progress for as long as you are working on it. ### What happens if I am taken off a project? [#what-happens-if-i-am-taken-off-a-project] We email you that you have been offboarded, and you are taken off everything related to the project, including its time tracker. The project then shows **Rejected** in your history. What you were already paid for the project stays in your Terac balance, and you can keep applying to other opportunities. ### What happens when a project ends? [#what-happens-when-a-project-ends] We email you that it has ended, and you are taken off its time tracker. What you were already paid stays in your Terac balance. ## What's Next? [#whats-next] If your project is hourly, set up [Time Tracking with Insightful](https://terac.com/docs/experts/working/time-tracking). # Completing Screeners (https://terac.com/docs/experts/working/screeners) How to answer screening questions to qualify for research studies. Screening questions help researchers find the right participants for their studies. Here's how they work and how to approach them. ## What Are Screeners? [#what-are-screeners] Screeners are qualification questions asked early in the application. They help researchers: * Check you match the people the study is looking for * Keep study quality high * Find specific experiences or opinions ## How Screening Works [#how-screening-works] Depending on the opportunity, screening is one or more of these: * **A form**: questions shown one at a time. You cannot go back to change an answer, so read each one carefully. * **A short AI interview**: a voice conversation of about 3 minutes with our AI interviewer. You will need your camera and microphone. See [AI Interviews](https://terac.com/docs/experts/working/interviews). * **A file upload**: for example a document or a screenshot the study asks for. Some opportunities also let Sophia ask you the screening questions by text message or in the chat. They are the same questions, graded the same way as the form. Before screening, the **Basic Info** step asks for any details the opportunity needs that are missing from your profile. If they rule you out, you will be told straight away. ## Types of Screener Questions [#types-of-screener-questions] ### Demographic Questions [#demographic-questions] Basic information about you, such as your age range, location, occupation, or industry. ### Experience Questions [#experience-questions] Your background with specific topics: * "Have you used \[product] in the past 6 months?" * "Do you manage a team of 5+ people?" ### Opinion and Behavior Questions [#opinion-and-behavior-questions] Your views and habits on topics relevant to the study: * "How satisfied are you with your current \[solution]?" * "How often do you \[activity]?" ## Tips for Answering Screeners [#tips-for-answering-screeners] ### Be Honest [#be-honest] This is the most important rule. Researchers design screeners to find specific people. If you don't genuinely qualify, you may: * Be caught during the main study * Have your submission rejected * Risk your account being banned, which can follow a single screening where your answers misrepresented you ### Be Accurate and Consistent [#be-accurate-and-consistent] Your answers should match your profile, your other answers in the same screener, and what you have told us before. Inconsistencies are red flags for researchers. ### Read Carefully [#read-carefully] Pay attention to: * Time frames ("in the past 30 days" vs "ever") * Specific quantities ("more than 5" vs "5 or more") * Exclusionary phrases ("none of the above") ## What Happens After Screening [#what-happens-after-screening] ### If You Pass [#if-you-pass] You move on to the next step. Some opportunities review your answers first: automated review shows **We're reviewing your responses** and usually finishes in under a minute, and some reviews are done by our team or by the research team. We email you when you are through. ### If You Don't Pass [#if-you-dont-pass] You'll see **Not a fit for this one**. This means: * You didn't match what this study needed, the group of people you fall into was already full, or, on some opportunities, your answers were declined in review * It's not a judgment on you personally * You can apply to other opportunities > Being screened out is normal. Researchers often have very specific > requirements. Keep applying to studies that match your background. ## FAQ [#faq] ### Why do I keep getting screened out? [#why-do-i-keep-getting-screened-out] Studies often need very specific people, and a study can fill its quota for a group before you apply. Apply to opportunities that closely match your background, and keep your profile complete. ### Can I retake a screener? [#can-i-retake-a-screener] No. Each opportunity takes one attempt. If something technical went wrong, such as an AI interview that could not start or dropped partway through, you can try that step again. ### Do screener responses affect future opportunities? [#do-screener-responses-affect-future-opportunities] Your answers, not whether you passed, are saved to your profile, and they can help us match you with future opportunities. That is another reason to answer honestly. ### How long are screeners? [#how-long-are-screeners] Most take a few minutes. The AI screening interview takes about 3 minutes. ## What's Next? [#whats-next] After passing screening, you'll move to [completing tasks](https://terac.com/docs/experts/working/tasks). # Best Practices (https://terac.com/docs/experts/working/best-practices) Tips for building a strong track record and maximizing your success on Terac. Want to get more opportunities and earn more on Terac? Follow these best practices to stand out. ## Profile Optimization [#profile-optimization] ### Complete Every Section [#complete-every-section] A complete profile gives us more to match you on. From **Profile** in your dashboard, make sure you have: * [ ] A profile photo and your full name * [ ] A verified phone number * [ ] A verified identity * [ ] LinkedIn connected * [ ] Your resume uploaded (PDF) * [ ] Your experience, education, and skills filled in * [ ] Your preferred hourly rate set * [ ] Your Google Calendar connected, or a booking link saved, under **Availability** * [ ] The onboarding interview taken ### Add LinkedIn [#add-linkedin] LinkedIn helps show your professional background: * It fills in your work history for you * It helps researchers trust your experience * It can get you put forward for more work ### Keep Information Current [#keep-information-current] Update your profile when things change: * A new job or role * New skills or certifications * Your preferred hourly rate * Your primary email and phone number, under **Identity & Security** ## Quality Submissions [#quality-submissions] ### Follow Instructions Carefully [#follow-instructions-carefully] Not following instructions is a common reason for rejection: * Read the full task description * Note any specific requirements * Do exactly what's asked ### Be Thorough [#be-thorough] Detailed responses are more valuable: * Answer questions completely * Provide context and examples * Don't give one-word answers to open questions ### Be Authentic [#be-authentic] Researchers value genuine insights: * Share your real experiences * Give honest opinions * Don't try to give "right" answers ### Proofread [#proofread] Before submitting: * Check for typos * Ensure completeness * Review your responses ## Interview Success [#interview-success] ### Technical Preparation [#technical-preparation] * Check your camera and microphone before every interview * Use a quiet, well-lit place * Use a stable internet connection and an up-to-date Chrome or Edge ### Communication [#communication] * Speak clearly and at a moderate pace * Let the interviewer finish before responding * Keep pauses short, and say "let me think" if you need a moment ### Engagement [#engagement] * Stay focused on the questions * Provide detailed, relevant answers * Stay in the interview window until it ends ## Building Your Track Record [#building-your-track-record] ### Consistency [#consistency] * Finish the applications you start * Maintain quality across all tasks * Check Explore regularly for new opportunities ### Reliability [#reliability] * Follow through on commitments, such as sessions you book * Keep the time tracker honest on hourly projects * Respond promptly to any follow-ups ## What Researchers Look For [#what-researchers-look-for] ### Qualified Background [#qualified-background] * Relevant experience for the study topic * A verified identity and connected profiles * A complete and accurate profile ### Quality Responses [#quality-responses] * Thoughtful, detailed answers * Consistency between screening and tasks * Following instructions ### Professionalism [#professionalism] * Completing submissions in a timely manner * Honest, authentic responses * Technical reliability (clear audio, a working camera, a stable connection) ## Common Mistakes to Avoid [#common-mistakes-to-avoid] ### Rushing Through Tasks [#rushing-through-tasks] Take your time. Rushed responses are often lower quality and may be rejected. ### Inconsistent Answers [#inconsistent-answers] Make sure your responses are consistent with: * Your profile information * Other answers in the same study * What you've said in past submissions ### Ignoring Requirements [#ignoring-requirements] Read **Who Should Apply** before you start. If an opportunity is not a fit for you, it is usually listed under **Refer & earn** on Explore instead. ### Starting What You Can't Finish [#starting-what-you-cant-finish] Only apply to opportunities you have time to complete. ### Technical Negligence [#technical-negligence] * Not checking your equipment * Using unstable internet * Taking interviews in noisy or dark places ## What's Next? [#whats-next] Now that you know best practices, review our [policies](https://terac.com/docs/experts/policies) to ensure you understand the terms and conditions. # Completing Tasks (https://terac.com/docs/experts/working/tasks) How to complete research tasks including surveys, uploads, and more. Tasks are the main activities in a study. After you pass screening, or straight away on an opportunity that has none, you complete one or more tasks to earn your payout. On **Home**, each task shows what the next step is, such as **Book your session** or **Study agreement**. ## Task Types [#task-types] ### Surveys and External Tasks [#surveys-and-external-tasks] Many tasks take place on another website, such as a survey: 1. Click **Open survey**. It opens in a new tab. 2. Complete it there. 3. Come back and click **I completed the task**. If you close the tab by mistake, click **Reopen survey**. Some surveys tell us automatically when you finish, and then there is no button to click. Some ask for your Terac submission ID: when they do, a window shows it so you can copy it in. ### File Uploads [#file-uploads] Upload the files the task asks for, such as documents, screenshots, or photos. You can remove a file and add another until you click **Submit files**. * Make sure images are clear and readable * Remove sensitive personal information the task does not need ### Booked Sessions [#booked-sessions] Some studies include a live session. **Schedule your interview** lets you pick a time, and some studies book it for you from your connected Google Calendar. ### Agreements and Contracts [#agreements-and-contracts] * **Study agreement**: read the study's terms and accept them. If you would rather not take part, choose **I'm not interested**. That closes your application for good. * **Sign the contract**: some studies, usually projects, ask you to sign a contract online. Afterwards you can download your copy from the sidebar with **Download signed contract**. ### Project Setup [#project-setup] Project-based studies often start with setup tasks: * **Join the Slack workspace** or **Meet the team on Slack**, to join the project's Slack. * **Set up time tracking**, to install Insightful for an hourly project. See [Time Tracking with Insightful](https://terac.com/docs/experts/working/time-tracking). See [Project-Based Work](https://terac.com/docs/experts/working/projects) for how the rest of a project works. ## Camera Checks and Recording [#camera-checks-and-recording] Before some tasks, a **Camera quality check** makes sure your face is visible, the lighting is good, you are sitting upright, and you are alone. Some tasks also record your screen or camera while you work. When they do, the task tells you first and asks you to allow it. ## Timing [#timing] Tasks are not timed. Each one shows an estimate of how long it takes. Some studies unlock the next task only after the previous one is approved (**Task under review**), or after a set time (**Next task available soon**, which says when). A study can fill, pause, or end while you are partway through. If it fills, you may see **Spot just filled**. Your progress is saved, and you can carry on if a spot opens up again. ## After You Submit [#after-you-submit] You'll see **Task submitted**, and we email you once a decision is made. Some tasks are approved as soon as you confirm them, or as soon as the survey confirms you finished. Others are reviewed by our team or by the research team. > **Warning** > > Submissions cannot be edited after you submit. Double-check your work first. ## Tips for High-Quality Submissions [#tips-for-high-quality-submissions] 1. **Follow instructions exactly**: don't improvise unless asked 2. **Be thorough**: incomplete responses are often rejected 3. **Be genuine**: authentic responses are more valuable 4. **Proofread**: check for typos in written responses 5. **Meet requirements**: make sure uploads meet the size and format asked for ## FAQ [#faq] ### Can I pause and resume a task? [#can-i-pause-and-resume-a-task] Your application saves your progress between tasks. Whether you can pause in the middle of one depends on the task: an external survey, for example, may not save a half-finished answer. ### What if I submit by mistake? [#what-if-i-submit-by-mistake] [Ask Sophia](https://terac.com/docs/experts/support/contact) straight away. The team may be able to help, but there's no guarantee. ### Why was my task rejected? [#why-was-my-task-rejected] Common reasons: * Incomplete responses * Responses didn't follow instructions * Quality didn't meet standards * Technical issues (audio didn't record, file corrupted) ### Can I redo a rejected task? [#can-i-redo-a-rejected-task] Generally no. Each task allows one submission, although a reviewer can sometimes reopen one. ## What's Next? [#whats-next] If your study is a project, read [Project-Based Work](https://terac.com/docs/experts/working/projects). For AI interviews, see [AI Interviews](https://terac.com/docs/experts/working/interviews). # AI Interviews (https://terac.com/docs/experts/working/interviews) What to expect from Terac's AI voice interviews, and how to get set up for one. Terac uses two short AI voice interviews. This guide explains what each one is, how to get set up, and what to do if something goes wrong. ## The Two AI Interviews [#the-two-ai-interviews] ### The onboarding interview [#the-onboarding-interview] The last step of onboarding is a conversation with Sophia, our AI interviewer, about your background and the kind of work you want. It helps us match you with the right opportunities. * It takes about **10 minutes** and runs in English. * It is optional. Choose **Skip for now**, and you can take it later from Home. * If you need to stop, choose **Leave and finish later**. What you have said so far is saved, and next time you pick up where you left off. * Once you have finished it, it cannot be taken again. ### The AI screening interview [#the-ai-screening-interview] Some opportunities screen applicants with a **Quick chat with our AI interviewer**. * It takes about **3 minutes**, sometimes a little longer, and there is no hard time limit. * If the opportunity offers more than one language, you choose it on the screen before you start. Otherwise it runs in the opportunity's language. * Once you have finished it, it cannot be taken again. If it dropped, or you ended it early, you can start it again from the beginning. The interviewer does not decide whether you are approved. A person on our team reviews the recording and transcript, and on some opportunities our automated review can approve or decline it. ### Other interview tasks [#other-interview-tasks] Some opportunities have an interview task that is not one of these. It either opens on another website, or it is a live call you book with **Schedule your interview**. See [Completing Tasks](https://terac.com/docs/experts/working/tasks). ## What You Need [#what-you-need] * **A computer or phone with a camera and microphone.** Both are required: the camera stays on for the whole interview. Some opportunities only run on a computer, and will tell you if you open them on a phone. * **An up-to-date browser.** Chrome or Edge work best. Safari and Firefox can work, but some checks are not available in them. * **A quiet, well-lit place** where your face is clearly visible, with minimal interruptions. * **A stable internet connection.** ## Before You Start [#before-you-start] Both AI interviews open with a short checklist: 1. **Disconnect external monitors** (on a computer, screening interview only). Unplug any extra screens. The check clears itself once they are unplugged. 2. **Share your entire screen**. Pick **Entire screen**, not a window or a tab. On a device that cannot share its screen, this step is skipped. 3. **Allow camera and microphone** when your browser asks. 4. **Check your framing**. Center yourself in the outline, in good light, then click **I'm ready**. Then click **Start interview**. On the onboarding interview, the button reads **Start the interview**, or **Continue the interview** if you are picking up where you left off. ## During the Interview [#during-the-interview] The interviewer introduces itself, then asks questions one at a time, with follow-ups based on your answers. * **Listen to the whole question**, then answer clearly and in your own words. * **Keep pauses short.** A long silence can be taken as the end of your answer. If you need a moment, say "let me think." * **Be specific.** Real examples from your experience are what researchers want. * **Stay in the interview window.** Switching tabs, leaving the window, exiting full screen, muting, turning off your camera, and stopping your screen share are all recorded for the reviewer. The controls at the bottom let you mute, turn your camera on or off, and manage your screen share. **End interview** ends the screening interview straight away, with no confirmation, so only click it when you are done. You can also say you are done, and the interviewer will wrap up. ## Common Issues and Solutions [#common-issues-and-solutions] ### "Access to your camera and microphone is blocked" [#access-to-your-camera-and-microphone-is-blocked] 1. Click the lock or site-settings icon in your browser's address bar. 2. Set camera and microphone to **Allow**. 3. Reload the page. On a phone, open your phone's settings, allow camera and microphone for your browser, then reload the page. ### The wrong microphone is being used [#the-wrong-microphone-is-being-used] Choose your microphone in your browser's site settings or in your computer's sound settings, then reload the page. There is no microphone picker inside the interview. ### "External monitor detected" [#external-monitor-detected] Unplug the extra screen. The message clears once it is disconnected. ### Screen sharing picked the wrong thing [#screen-sharing-picked-the-wrong-thing] If you shared a single window or tab, click **Try again** and choose **Entire screen**, or the name of your monitor. ### The interview did not start, or it disconnected [#the-interview-did-not-start-or-it-disconnected] If you see **We couldn't start the interview**, wait a moment and try again. If a screening interview drops partway through, go back to the step and start it again from the beginning. If the onboarding interview drops, you pick up where you left off. ### Audio quality is poor [#audio-quality-is-poor] Move closer to your microphone, reduce background noise, and use headphones to prevent echo. ## FAQ [#faq] ### Can I retake an interview? [#can-i-retake-an-interview] Not one you have finished. A screening interview that dropped or that you ended early can be started again from the beginning. ### Are interviews recorded? [#are-interviews-recorded] Yes. We record video of your camera and your shared screen, the audio, and a transcript, along with events such as switching tabs or turning off your camera. See our [data usage policy](https://terac.com/docs/experts/policies/data-usage). ### What language should I use? [#what-language-should-i-use] The language the opportunity is in. If it offers more than one, you choose on the screen before you start. The onboarding interview is in English. ### How long do interviews take? [#how-long-do-interviews-take] The screening interview takes about 3 minutes, and the onboarding interview about 10. The estimate is shown before you start. ### Who reviews my interview? [#who-reviews-my-interview] A person on our team, and on some opportunities our automated review, looks at the recording and transcript. The AI interviewer asks the questions, but it does not make the decision. ## What's Next? [#whats-next] Review [best practices](https://terac.com/docs/experts/working/best-practices) to improve your overall success rate on Terac. # Understanding Submissions (https://terac.com/docs/experts/working/submissions) Track your applications and understand what each status means. A submission is your application and work for one opportunity. It is created when you start an application and follows you through screening, tasks, review, and payment. ## Where to Find Your Submissions [#where-to-find-your-submissions] On **Home**, the **Your opportunities** section groups them in tabs: * **Your turn**: there is something for you to do next. * **Waiting on review**: you have handed something in and it is being reviewed. * **History**: opportunities you have finished. * **Paused**: when more than two of your opportunities are paused, they move here. A paused one says **This opportunity is paused for now.** Your progress is saved for when it resumes. Click **Show all** to open your full history at [terac.com/me/opportunities/history](https://terac.com/me/opportunities/history). You can filter it by **All**, **Approved**, **In review**, **Screened out**, or **Rejected**. ## How a Submission Moves [#how-a-submission-moves] Apply → Screening → Tasks → Review → Approval → Payout Not every opportunity has every step, and review can happen more than once: after your screening, after a task, or both. On a project-based opportunity, you work from the project handbook once you are in, and you are paid on the project's schedule. See [Project-Based Work](https://terac.com/docs/experts/working/projects). ## What Each Status Means [#what-each-status-means] | You see | What it means | | ----------------------------------------- | ------------------------------------------------------------------------------------------------------- | | **In Progress** | You are applying, screening, or working on the tasks. On a project, this lasts for the whole engagement | | **Awaiting Review** or **In review** | You have handed something in and it is being reviewed. There is nothing for you to do | | **Approved**, **Processing**, or **Paid** | Your work was accepted, and the payment is on its way or has landed in your balance | | **Not a match** or **Screened out** | You did not match what the opportunity needed | | **Not Selected** or **Rejected** | Your submitted work was not approved, or you were taken off a project | | **Application closed** | The application ended before it finished, for example because you declined it | | **Study cancelled** | The research team cancelled the opportunity | Once your work on a one-time opportunity is approved, the earnings are added to your balance, and you can [cash out](https://terac.com/docs/experts/how-to/payments) once you reach the **$12.50** minimum. ## Review [#review] Depending on the opportunity, your work is reviewed by our automated review, by our team, or by the research team. The screen you land on after handing something in says who is reviewing it and what happens next, and we email you when there is a decision. * **Automated review** shows **We're reviewing your responses**, and when it finishes you carry on automatically or, on some opportunities, are declined. * **The research team's review** shows **Your application is with the client**, and we email you if you are selected. Review time depends on the opportunity. If yours has been awaiting review for more than **7 days**, [ask Sophia](https://terac.com/docs/experts/support/contact) and we'll chase it up for you. See [Rewards and Fair Pay](https://terac.com/docs/experts/policies/rewards#how-long-review-takes) for more. ## Leaving an Application [#leaving-an-application] There is no button to return a submission you have started. You can close one in two ways: * **Decline an invitation** from Home, using **Not for me** on its row. * Choose **I'm not interested** on an opportunity's agreement step. Both close the application for good, and you cannot take part in that opportunity later. Leaving a study you are not comfortable with does not harm your account standing. > If a study's content is not right for you, see [Your Wellbeing and Sensitive > Content](https://terac.com/docs/experts/policies/wellbeing) for more options. ## FAQ [#faq] ### How long does review take? It depends on the opportunity and on who reviews it. The screen you land on after submitting shows what happens next. If it has been more than 7 days, [ask Sophia](https://terac.com/docs/experts/support/contact) and we'll chase it up. ### Can I edit my submission after submitting? No. Once you submit a task, it cannot be edited. Make sure your responses are complete before submitting. ### What if I started but can't finish? Your application stays open until you finish it, decline it, or the opportunity fills or ends. We may email you a reminder to finish it. ### Why was my submission rejected? The rejection screen does not show a reason. Common reasons include: * Responses didn't meet quality standards * Answers contradicted your profile information * Tasks were incomplete or rushed * Technical issues prevented proper recording ### Can I appeal a rejection? If you believe your submission was rejected in error, [ask Sophia](https://terac.com/docs/experts/support/contact) with the name of the opportunity, and she will pass it to our team. ### Can an approved submission still be rejected? Yes. If an approved one-time submission is later rejected in review, the payment for it is reversed and a **Clawback** appears in your transaction history. ## What's Next? [#whats-next] Learn about [completing screeners](https://terac.com/docs/experts/working/screeners) to improve your pass rate. # Time Tracking with Insightful (https://terac.com/docs/experts/working/time-tracking) Install Insightful and record your hours on an hourly study. Studies that pay by the hour track your time with [Insightful](https://app.insightful.io). The study's **Set up your time tracker** step walks you through three things: create an account, install the app, and run the timer once on the study's project. Each one ticks off on its own when you finish it, and the study moves on once your time has landed on its project. > On your computer the app is called **Workpuls**. Insightful is the company, > Workpuls is the app they ship. Same thing. ## Step 1: Create an account [#step-1-create-an-account] When you reach the step, we create your Insightful account, add you to the study's project, and Insightful sends you an invitation. The step shows the address it went to: the personal email on your Terac account. 1. Open the email **Welcome to Insightful!** from `support@insightful.io`. It can take a minute to arrive, so check spam or promotions too. 2. Click **Start using Insightful**. 3. Create a password and save it. You need it to sign in to the app in the next step. The step ticks off as soon as you open the link in the email. ## Step 2: Install and sign in [#step-2-install-and-sign-in] 1. Download **Workpuls** with the step's **Download** button and open it. The download page only works while you are signed in to Insightful in your browser, which you are after Step 1. 2. Sign in with the address the invitation went to and the password you created. ![The Workpuls sign-in screen, with the Email field marked as the email you signed up to Terac with, and the Forgot Password link marked](https://terac.com/docs/experts/insightful-signin.png) Forgot the password? Click **Forgot Password?** on the sign-in screen and enter that same address. On macOS, allow **Accessibility** and **Screen & System Audio Recording** when the app asks. It cannot record your time properly without them (see below). Windows 10 or later, macOS 11 or later, Linux in beta. The step ticks off as soon as you sign in. ## Screen recording permission [#screen-recording-permission] The app needs permission to capture your screen. Without it the timer still runs and your hours still count, so nothing looks broken, but the screenshots that back those hours up are missing, and most hourly studies expect them. The app takes screenshots only while your timer is running. It does not record video, and it takes no screenshots while you are idle. ### macOS [#macos] This is a Mac permission, not a setting inside the app. 1. Apple menu, then **System Settings** 2. **Privacy & Security** 3. **Screen & System Audio Recording** 4. Turn on **Workpuls**. Insightful ships the app under that name, so that is what you are looking for. 5. If it is not in the list, click **+** and add `Workpuls.app` from Applications 6. Quit the app completely and open it again. The permission does not take effect until you do. Workpuls needs a second permission alongside this one, and Apple changed it in macOS 27. On macOS 26 and earlier, turn Workpuls on under **Accessibility**. On macOS 27 and later, look for **Device Control & Data Access** instead: same Privacy & Security list, same app name. If you granted Accessibility by hand before updating to macOS 27, it carries over. You may see a one-time notification confirming it, and there is nothing for you to do. ### Windows [#windows] Nothing to grant by hand. If screenshots are still missing, your antivirus or a privacy tool is blocking the app, so allow Workpuls there. On a work laptop your employer's software can block screen capture outright, and in that case you will need a personal machine for study work. ### Confirm it worked [#confirm-it-worked] Press play on your task, work for a few minutes, then open **Open Dashboard** at the bottom of the app window. If you can see recent screenshots, it is working. If the timer is counting but nothing appears after a full session, the permission is usually still missing. Some projects switch screenshots off, and on those none appear even with the permission on. > **Warning** > > Turning the permission off mid-study is treated the same way as an inflated > timer: a quality issue rather than a payment dispute. See [Account > Standing](https://terac.com/docs/experts/policies/account-standing). ## Step 3: Start the timer, then stop it [#step-3-start-the-timer-then-stop-it] The step shows which project and task to pick. 1. In the app, open **Assigned Projects** and open the study's project. ![The Workpuls app showing Assigned Projects, with a project card named Terac Showcase marked](https://terac.com/docs/experts/insightful-projects.png) 2. Press **play** on its task to start the timer. ![The task list inside a project, with the play button on a task named Writing tasks marked, labelled press to start press again to stop](https://terac.com/docs/experts/insightful-task-play.png) 3. After a few seconds, press **stop**. It only counts once it is stopped. After you stop, the step checks that the time landed on the study's project. That takes about 20 seconds, then the study moves on by itself. If the time went to another project, the step says so: start the timer on the right project and task, then stop it again. > **Warning** > > Time you record outside the study's project never reaches the study, and it > cannot be moved there afterwards. If the project is not under **Assigned > Projects**, press the sync button at the bottom of the app. From then on, time counts only while the timer runs, and only inside the study's project. Nothing is recorded before you press play or after you stop. ## Already used Insightful? [#already-used-insightful] If you tracked time on an earlier study, you already have an account and the app, so the step shows only **Start the timer**. Open Workpuls, pick the new project and its task, start the timer, then stop it. Using a different computer than before? **Having trouble?** on the step links to the download again. ## How tracked time becomes payment [#how-tracked-time-becomes-payment] Only tracked hours are paid, and they are reviewed before they are paid rather than approved automatically. Each project pays on its own schedule, shown on its **Payout** tab, and some projects pay only for time spent on the apps and sites the work requires. **How payable hours work**, in **Tracked** on the opportunity's **Time** tab, shows which of your time is paid. See [Project-Based Work](https://terac.com/docs/experts/working/projects). We look at tracked time alongside the work you produced. If a session shows hours with no work behind them, we may ask you about it before paying it. Honest mistakes happen, and we would rather hear about them from you than find them ourselves. In practice: * Start the timer when you begin and stop it when you finish. * Do not leave it running while you are away from the work. * If you forget to stop it, tell us the same day and we will correct it. Leaving the timer running while you are not working is treated as a quality issue rather than a payment dispute. See [Account Standing](https://terac.com/docs/experts/policies/account-standing). ## Invitations and signing in [#invitations-and-signing-in] ### I never got an invitation email Check spam and promotions for **Welcome to Insightful!** from `support@insightful.io`. It goes to the personal address on your Terac account, which may not be the one you use for work. The step shows which address it went to. Still nothing after a few minutes? [Contact us](https://terac.com/docs/experts/support/contact). ### My invitation link expired Links expire after 48 hours. Go to [app.insightful.io](https://app.insightful.io), click **Forgot Password?** with the address the invitation went to, and set a password from the email it sends. Then carry on with Step 2: the first two steps tick off together once you sign in to the app. ### Which email should I use? **The one you signed up to Terac with.** Not a work address, and not a second address you own, unless that is the address on your Terac account. If you changed it recently, try the original one. ### Sign-in says 'user not found' You are using a different address than the one your account was created with, or you have not reached the study's time-tracker step yet. Try the address you signed up to Terac with, then [contact us](https://terac.com/docs/experts/support/contact) if neither works. ## Projects and tracking [#projects-and-tracking] ### I signed in but Assigned Projects is empty Press the sync button at the bottom of the app. We add you to the project when you reach the study's time-tracker step, and the app picks it up on its next sync. If it is still empty, [contact us](https://terac.com/docs/experts/support/contact) with the address you signed in with. ### I already used Insightful on another study Then you already have an account and no new invitation is sent. The step shows only **Start the timer**: sign in with the same address as before, pick the new project, start the timer and stop it. ### I stopped the timer but the step has not moved on Checking takes about 20 seconds after you stop. If the step says your time went to another project, start the timer on the project and task the step names, then stop it again. If the timer is still counting in the app, it was not stopped. ### The step says it needs help Something on our side needs fixing first: your account is deactivated, you have not been added to the project yet, or the invitation could not be sent. [Contact us](https://terac.com/docs/experts/support/contact) and we will sort it out. ### I see a project I am no longer working on Press the sync button at the bottom left. When you leave or are removed from a study, we take you off its project, and the app catches up on its next sync. ### Where do I see a breakdown of my hours? On terac.com, open the study from Home, select its **Time** tab, then **Tracked**. It shows your time by week or by day, counted in your own time zone, and new hours appear within about an hour. In the app, **Open Dashboard** at the bottom of the window shows the same time. ### I worked but forgot to start the timer Tell whoever runs your study as soon as you notice. Do not make it up by leaving the timer running while you are not working: untracked time is handled case by case, an inflated timer is a quality problem (see [Account Standing](https://terac.com/docs/experts/policies/account-standing)). ## Devices and connection [#devices-and-connection] ### Which devices are supported? Windows 10 or later, macOS 11 or later, Linux in beta. Chromebooks, tablets and phones are not supported. You can install it on more than one computer, but track on only one at a time. ### Do I need to be online? Yes. The app needs a connection to record your time, and time tracked offline may not sync. # Apply to Opportunities (https://terac.com/docs/experts/how-to/apply) How to find and apply to research opportunities on Terac. Once your phone is verified, you can apply to opportunities from your dashboard at [terac.com/me](https://terac.com/me). ## Where Opportunities Appear [#where-opportunities-appear] ### Invitations, on Home [#invitations-on-home] When you are invited to an opportunity, it appears on **Home** as a row in **Your turn**, marked **Invited**, with what it pays and when the invitation expires. Click **Details** to read about it, **Accept** to start, or **Not for me** to decline. > **Warning** > > Declining an invitation gives up your place on that study, and you will not be > able to take part in it later. ### Everything open to you, on Explore [#everything-open-to-you-on-explore] **Explore** ([terac.com/me/opportunities](https://terac.com/me/opportunities)) is headed **Browse Opportunities**. It has three tabs: * **All opportunities** * **Project-based**: ongoing work over a period of time, usually paid by the hour or per task, with a project handbook. See [Project-Based Work](https://terac.com/docs/experts/working/projects). * **One-time**: a single session such as a survey or an interview You can search by keyword and sort by **Pay rate** or **Spots left**. Most opportunities you are not a fit for are listed separately under **Refer & earn**. You can't apply to those, but you earn a commission if someone you refer to one gets paid. See [Referring Others](https://terac.com/docs/experts/how-to/referrals). ![Browse Opportunities, showing the All opportunities, Project-based and One-time tabs, the search box, the Pay rate sort, and the Refer & earn section](https://terac.com/docs/experts/referrals-opportunities.png) ## The Opportunity Details [#the-opportunity-details] Click any card to open its details: the title and pay, the kind of work (for example a voice interview or a survey), whether it is remote, and then **What You'll Do**, **Who Should Apply**, and **Contract & Payment Terms**. ### The Application Checklist [#the-application-checklist] Above the details, a checklist headed **Application** shows your progress: * **Connect your profiles**: link LinkedIn or Facebook. This step is optional. * **ID verification**: marked **Required**. You need a verified identity before you can cash out anything you earn, so it is best done before you start. See [Verification and Connected Accounts](https://terac.com/docs/experts/how-to/attestations). Steps like these are reused across opportunities: once your ID is verified, it counts for every opportunity. ### The Apply Button [#the-apply-button] The button at the bottom of the details changes with where you are: | Button | What it means | | -------------------------------- | --------------------------------------------------------------- | | **Finish your profile to apply** | Your phone is not verified yet. It takes you back to onboarding | | **Start Application** | You are ready to begin | | **Continue Application** | You have already started. It picks up where you left off | | **Applications opening soon** | The opportunity is not taking applications yet | ## The Application Flow [#the-application-flow] Each opportunity has its own steps, shown in a sidebar as you go. Depending on the opportunity, they can include: 1. **Connect Accounts**: some opportunities need a specific account linked, such as LinkedIn or Google. 2. **Connect Calendar**: for opportunities where Terac books a session for you, connect your Google Calendar so we can find a time you are free. 3. **Basic Info**: any details the opportunity needs that are missing from your profile. If your answers do not match what it is looking for, you will be told straight away. 4. **Screening**: questions to check you are a good fit. This can be a short form, a quick AI voice interview, or a file upload. See [Completing Screeners](https://terac.com/docs/experts/working/screeners). 5. **Your forwarding email**: an email address just for this opportunity, to use when you sign up for any tools it asks you to use. 6. **Tasks**: the work itself, such as a survey, a file upload, a booked session, or signing a contract. See [Completing Tasks](https://terac.com/docs/experts/working/tasks). 7. **Payout**: shows what you earn for the opportunity and, once your work is approved, that the payment is on its way to your Terac balance. You choose how to be paid when you cash out. 8. **Refer & earn**: your own link to share this opportunity with people you know. On a project-based opportunity, the flow ends at **You're in** instead of a payout, and you work from the project's handbook from then on. ## After You Submit [#after-you-submit] Your work is reviewed after screening, after tasks, or both, depending on the opportunity. Some reviews are automatic and some are done by our team or by the research team. We email you when there is a decision, and the screen you are on says what happens next. * If **approved** on a one-time opportunity, your earnings are added to your Terac balance and you can [cash out](https://terac.com/docs/experts/how-to/payments). * If **approved** on a project-based opportunity, you are paid for your hours or tasks on the project's payout schedule. * If you are **screened out**, you will see **Not a fit for this one**. It means you did not match what that opportunity needed. You can still apply to others. > Sometimes an opportunity fills while you are still applying. You may see > **Spot just filled** or **Reserved for other profiles**. Your progress is > saved, and if a spot opens up again you can carry on. ## FAQ [#faq] ### Can I apply to multiple opportunities at once? [#can-i-apply-to-multiple-opportunities-at-once] Yes. Each opportunity is independent, so applying to one doesn't affect others. ### Can I apply to the same opportunity twice? [#can-i-apply-to-the-same-opportunity-twice] No. Each opportunity takes one application per person. If you open one you have already finished, you will see **You already participated**. ### Can I leave an application I have started? [#can-i-leave-an-application-i-have-started] You can decline an invitation from Home, or choose **I'm not interested** on an opportunity's agreement step. Either one closes that application for good. For anything else, [ask Sophia](https://terac.com/docs/experts/support/contact). ### Why is an opportunity not showing up on Explore? [#why-is-an-opportunity-not-showing-up-on-explore] Explore does not show opportunities you are already working on or invited to (those are on Home), ones you have already taken part in, or ones that are full. Opportunities you are not a fit for appear under **Refer & earn** instead, unless they don't take referrals, in which case Explore doesn't show them. ### Where do I see the opportunities I have applied to? [#where-do-i-see-the-opportunities-i-have-applied-to] On **Home**, under **Your opportunities**, in tabs: **Your turn**, **Waiting on review**, and **History**, plus **Paused** when more than two of them are paused. **Show all** opens everything you have taken part in. ## What's Next? [#whats-next] Learn about [understanding submissions](https://terac.com/docs/experts/working/submissions) to track your application status, or [set up payments](https://terac.com/docs/experts/how-to/payments) to cash out your earnings. # Create Your Account (https://terac.com/docs/experts/how-to/create-account) How to sign up for Terac and get started as an expert. Creating a Terac account is free. You sign in with an account you already have, so there is no password to create. ## Sign Up or Log In [#sign-up-or-log-in] Go to [terac.com/login](https://terac.com/login). **Continue with LinkedIn** is the main option and the one we recommend. It lets Terac pre-fill your professional background during onboarding. Under **or continue with**, you can also use: * **Facebook** * **Google** * **Microsoft** * **GitHub** > The provider you used last is marked **Last used**, so you can pick it again > quickly. The marker is remembered by your browser, so it will not show on a > different device. ## What Happens After You Sign In [#what-happens-after-you-sign-in] The first time you sign in, you go through a short onboarding flow before reaching your dashboard: 1. **Verify your phone number.** This is the one required step. See [Setup Your Profile](https://terac.com/docs/experts/how-to/setup-profile) for how it works. 2. **Connect LinkedIn or add your resume** (optional). Terac uses them to pre-fill your experience, education, and certifications. 3. **Review your profile.** Check what Terac filled in, set your preferred hourly rate, and click **All set**. 4. **Take the onboarding interview** (optional). A voice conversation of about 10 minutes that helps us match you with opportunities. You can skip it and take it later from Home. When you are done, you land on your dashboard. **Home** shows what needs your attention, and **Explore** lists the opportunities open to you. ## Signing In Again [#signing-in-again] Return to [terac.com/login](https://terac.com/login) and continue with the same provider you used when you signed up. > **Warning** > > If you sign in with a provider whose email address is different from the one > on your Terac account, Terac treats it as a new account. Use the same > provider, or one that shares the same email address. ## FAQ [#faq] ### Can I have more than one account? [#can-i-have-more-than-one-account] No. Each person should have only one Terac account. Multiple accounts violate our Terms of Service and may result in suspension. ### Can I sign in with more than one provider? [#can-i-sign-in-with-more-than-one-provider] Yes. Go to **Profile → Identity & Security → Linked Accounts** and click **Link Another Account**. Use a provider account with the same email address as your Terac account, so it is added to the account you already have. ### I accidentally created a duplicate account, what do I do? [#i-accidentally-created-a-duplicate-account-what-do-i-do] You cannot merge two accounts yourself. [Ask Sophia](https://terac.com/docs/experts/support/contact) and we will help you sort it out. ### It says my email is associated with a previously deleted account [#it-says-my-email-is-associated-with-a-previously-deleted-account] An email address that belonged to a deleted account cannot be used to sign up again. [Get in touch](https://terac.com/docs/experts/support/contact) if you think this is a mistake. ### It says to use my personal email [#it-says-to-use-my-personal-email] Terac forwarding addresses (ending in `@expert.terac.com`) cannot be used to sign in. Use your own email account instead. ## What's Next? [#whats-next] After signing in, complete onboarding to [set up your profile](https://terac.com/docs/experts/how-to/setup-profile) and start applying to opportunities. # Verification and Connected Accounts (https://terac.com/docs/experts/how-to/attestations) How to verify your phone and identity, and connect the accounts some opportunities ask for. Terac verifies who you are in two ways: your phone number, during onboarding, and your identity, before your first cash out. Your connected accounts, such as LinkedIn, help show your professional background, and some opportunities require a particular one. You manage all of this from **Profile → Identity & Security** at [terac.com/me/settings/identity](https://terac.com/me/settings/identity). ## Phone [#phone] Your phone number is verified during onboarding, by sending us a pre-filled text or by entering a 6-digit code. See [Setup Your Profile](https://terac.com/docs/experts/how-to/setup-profile#step-1-verify-your-phone-number). To add a number, go to **Phone Numbers** under Identity & Security and click **+**. Once it is verified, click **Set as primary** to make it the number we use. ## Identity Verification [#identity-verification] You need to verify your identity before you can cash out, and opportunity pages list it as a required step. It only has to be done once, and it counts for every opportunity. To verify: 1. Go to **Profile → Identity & Security**. 2. Under **Identity Verification**, click **Start Verification**. 3. Follow the steps in the secure form. You will need a valid government-issued photo ID, and it takes a few minutes. The check is run by Whop, our payout and verification partner, so you may see their name in the form. The same check clears you to cash out: if the Earnings page asks you to **Verify identity** first, it is the same form. ### What the status means [#what-the-status-means] | Status | What to do | | -------------------------------------------- | -------------------------------------------------------------------------------------------------- | | **Verification in progress** | Nothing. The page updates on its own when the check finishes | | **Verification failed** | Click **Try Again**, with a clearer photo or a different document | | **Verification Blocked** or **Needs Review** | Retrying will not help. [Ask Sophia](https://terac.com/docs/experts/support/contact) and our team will look into it | | **Verified** | You are done. You can [set up payouts](https://terac.com/docs/experts/how-to/payments) | > If you live in a different country from your citizenship, verify with an ID > issued by your country of residence, such as a driver's license or national > ID. The country on your verified ID decides what is available to you on Terac. ## Connected Accounts [#connected-accounts] Under **Linked Accounts** you see every account you sign in with, grouped by email address. The email marked **Primary** is the one we send email to. To make a different linked email the primary one, click **Set as primary** next to it. To add another way to sign in, click **Link Another Account**. Use a provider account with the same email address as your Terac account, so it is added to the account you already have. ### Connect your profiles [#connect-your-profiles] An opportunity's application checklist includes **Connect your profiles**. A linked LinkedIn or Facebook account completes it. It is optional, but connecting LinkedIn helps show your professional background and can get you put forward for more work. Some opportunities require a specific account, such as LinkedIn, Google, Facebook, Microsoft, or GitHub. If so, the application starts with a **Connect Accounts** step. ## Two-Factor Authentication [#two-factor-authentication] For extra security, turn on **Two-Factor Authentication** under Identity & Security. You will then enter a code from an authenticator app when you sign in. It is also worth turning on two-factor authentication with the provider you sign in with, such as LinkedIn or Google. ## FAQ [#faq] ### Do I have to verify again for each opportunity? [#do-i-have-to-verify-again-for-each-opportunity] No. Your phone and identity verification count for every opportunity. ### Is identity verification required to apply? [#is-identity-verification-required-to-apply] Opportunity pages mark it as required, and you cannot cash out without it. To start an application, you need a verified phone number. Verifying your identity early means nothing holds up your first payout. ### What if my ID verification fails? [#what-if-my-id-verification-fails] Common reasons are a blurry or cut-off photo, an expired ID, a selfie (if you are asked for one) that does not clearly match the photo on the ID, or a type of document that is not accepted. Click **Try Again** with a clearer photo or a different document. If it keeps failing, [ask Sophia](https://terac.com/docs/experts/support/contact). ### Does verification expire? [#does-verification-expire] It can. If yours has expired, the Earnings page asks you to **Re-verify identity** before your next cash out. ### It says my identity is already associated with another account [#it-says-my-identity-is-already-associated-with-another-account] Each identity document can only be used on one Terac account. [Ask Sophia](https://terac.com/docs/experts/support/contact) and our team will look into it. ## What's Next? [#whats-next] Once you are verified, you're ready to [apply to opportunities](https://terac.com/docs/experts/how-to/apply) and [cash out](https://terac.com/docs/experts/how-to/payments). # Setting Up Payments (https://terac.com/docs/experts/how-to/payments) How your earnings reach your Terac balance, and how to cash them out to your bank. Getting paid on Terac has two parts. Approved work adds money to your **Terac balance**, and you cash out from that balance to your bank whenever you like. Both happen on your **Earnings** page at [terac.com/me/earnings](https://terac.com/me/earnings). ## How Money Reaches Your Balance [#how-money-reaches-your-balance] * **One-time opportunities** pay when your work is approved. The payout screen in the application shows the payment's progress, and it lands in your Terac balance shortly after approval. * **Project-based opportunities**, paid by the hour or per task, pay on the project's payout schedule, such as weekly on a set day. The project's **Payout** tab shows your rate, what you have been paid, and when the next payout is. See [Project-Based Work](https://terac.com/docs/experts/working/projects). * **Opportunities you joined through another research panel** are usually paid by that panel, under its own terms. That money never appears in your Terac balance. See [Paid by another panel](https://terac.com/docs/experts/how-to/payments#paid-by-another-panel) below. Referral commissions and bonuses land in the same balance. > **Warning** > > A payment for one-time work can be reversed if the submission is later > rejected in review. You will see a **Payment reversed** notice and a > **Clawback** row in your transaction history. ## Your Earnings Page [#your-earnings-page] * **Available Balance**: what you can cash out now. * **Earnings Analytics**: a chart of your earnings and withdrawals over 7 days, 1 month, or 1 year. * **Transaction History**: your most recent transactions. Filter by **All Transactions**, **Earnings**, **Referrals**, **Bonuses**, or **Withdrawals**, and click any row to see its details. ## The Two Balances [#the-two-balances] Getting paid moves your money through two places, and knowing which is which explains most of what you see on the page: * **Your Terac balance** is where approved earnings accumulate. * **Your payout balance** is held with **Whop**, our payout partner, and is what you withdraw to your bank. Moving money from your Terac balance to your payout balance is free and usually instant. You can move part of your balance or all of it. ## Cashing Out [#cashing-out] Everything happens on your Earnings page. The first time you cash out there is one extra step: verifying your identity. 1. **Verify your identity (first time only)** Until you are verified, the Earnings page shows **Verify identity** where the **Transfer** button would be, with **Required before you can transfer** underneath. Click it and complete the ID check. You will need a government-issued photo ID, and it takes a few minutes. You can also do it ahead of time under [Profile → Identity & Security](https://terac.com/me/settings/identity). It is the same check either way, so you only do it once. While the check is in review, the button reads **In review** and your earnings stay in your Terac balance. Nothing moves until it clears, and the page unlocks **Transfer** on its own when it does. 2. **Move funds to your payout balance** Click **Transfer** between your Terac balance card and the **Payout balance** panel, enter an amount or click **Max**, and click **Move to payout balance**. The move is usually immediate: you will see **Funds moved to your payout balance**, and a Withdrawal row in your transaction history. If you see **Transfer processing** instead, the amount has left your Terac balance and your payout balance updates once Whop confirms the transfer. There is no need to try again. 3. **Withdraw from your payout balance** In the **Payout balance** panel, click **Withdraw**. The first time, you add a payout method such as your bank account. After that, you can withdraw whenever you like. The panel shows the status of each withdrawal. > The identity check is run by Whop, so you will see their branding in the form. > If you verified your identity on Terac before payouts moved to Whop, that > check was with a different provider and you will be asked to verify once more. Make sure the name, date of birth, and address you give match your ID document exactly. Mismatches and typos are the most common reason a verification fails. Money you move to your payout balance cannot be moved back to your Terac balance, but it stays there until you withdraw it. ## Limits and Fees [#limits-and-fees] | Limit | Amount | | ---------------- | ----------------------------- | | Minimum cash out | $12.50 | | Maximum per day | $10,000.00 | There is no limit on a single cash out beyond the daily maximum, so you can move up to $10,000.00 at once. If your cash outs go through Virtual Incentives instead of Whop, each one can be from $5.00 up to $2,000.00. The daily maximum counts everything you move out of your Terac balance in one day and resets at midnight UTC. Your Earnings page shows how much you have left today and when the limit resets, in your own time zone. A cash out that would take you past it is refused with the same details; your balance is untouched and you can move the rest after the reset. Moving funds from your Terac balance to your payout balance is free. When you withdraw from your payout balance, Whop may charge a fee depending on the method you choose: a standard bank transfer has a small flat fee, and instant transfers cost more. The exact fee is always shown before you confirm. Earnings are held in US dollars. ## How Long It Takes [#how-long-it-takes] Moving funds to your payout balance is usually instant. After you withdraw, a standard bank transfer typically arrives within 1 to 3 business days, and some regions and methods offer faster options. The **Payout balance** panel shows the status of each withdrawal. ## Which Countries Are Supported [#which-countries-are-supported] Cash outs are available in most countries. If yours is not supported yet, your Earnings page will say so: [ask Sophia](https://terac.com/docs/experts/support/contact) and we will tell you your options. If the page asks you to set your country first, add it under [Profile → Legal](https://terac.com/me/settings/legal). ## Paid by Another Panel [#paid-by-another-panel] If you joined an opportunity through another research panel, that panel usually pays you directly, under its own terms. That payment never appears in your Terac balance, and Terac cannot see its status, so contact the panel about it. An empty Terac balance for that opportunity does not mean anything went wrong. ## FAQ [#faq] ### I moved money but it's not in my bank [#i-moved-money-but-its-not-in-my-bank] Moving money puts it in your payout balance, not your bank. Withdraw it from the **Payout balance** panel on your Earnings page. The panel shows the status of each withdrawal. ### My payout balance did not update after I moved funds [#my-payout-balance-did-not-update-after-i-moved-funds] If you saw **Transfer processing**, Whop is still confirming the transfer, and your payout balance updates when it does. Otherwise the page refreshes a moment after the transfer; if it still looks wrong, refresh manually. ### My transfer failed [#my-transfer-failed] If a transfer fails, the full amount goes straight back to your Terac balance, and you will see **Failed to create payout. Your balance has been restored.** Your transaction history shows the withdrawal and a matching refund. You can try again. Whop can also fail a transfer after accepting it. When that happens, the amount comes back to your Terac balance automatically, usually within 30 minutes, and your transaction history shows **Withdrawal reversal: Whop transfer failed**. ### Is there a minimum amount to cash out? [#is-there-a-minimum-amount-to-cash-out] Yes, **$12.50**. It is shown under the **Transfer** button and in the transfer window. Earnings from multiple submissions add up toward it. ### I cashed out before and got an email with a reward link. What changed? [#i-cashed-out-before-and-got-an-email-with-a-reward-link-what-changed] We previously paid through Virtual Incentives, which emailed you a link to choose a reward. We have moved to Whop, which lets you withdraw straight to your bank and offers more options. Payouts you already received the old way are not affected. ### Are my earnings taxable? [#are-my-earnings-taxable] Earnings from Terac may be taxable depending on where you live and your total income. You are responsible for reporting your income according to your local tax laws. Whop collects the tax details it needs from you directly during verification; in the US this includes an SSN. See the [Payout Policy](https://terac.com/docs/experts/policies/payout-policy) for more. ## What's Next? [#whats-next] Learn about [referring others](https://terac.com/docs/experts/how-to/referrals) to earn commission, or go back to [applying to opportunities](https://terac.com/docs/experts/how-to/apply). # Referring Others (https://terac.com/docs/experts/how-to/referrals) Earn commission by referring other experts to Terac. Know someone who'd be a great fit for Terac? Refer them, and you earn a commission every time Terac pays them for their work. ## How It Works [#how-it-works] 1. Share your referral link with someone. 2. They sign up through it and take part in opportunities. 3. Every time Terac pays them for approved work, you earn a share of that payment: **20%** by default, up to **$1,000** from each person you refer. Your cap for a person is set when they sign up, and there's no limit on how many people you can refer. > Some opportunities advertise a higher referral rate for that opportunity. When > you share one of those, you earn the higher rate on their payout for that > opportunity, then 20% of everything else they earn on Terac. The rate is shown > wherever you copy the link. ## Getting a Referral Link [#getting-a-referral-link] ![Browse Opportunities, with the Refer & earn section listing opportunities to refer people to, each with a Refer link and what it pays](https://terac.com/docs/experts/referrals-opportunities.png) There are a few ways to get a link: * **For a specific opportunity**: open an opportunity's details and copy the link in its **Refer & Earn** section, or click **Refer** on a card in the **Refer & earn** section of Explore. The person you refer lands on that opportunity. * **Your general link**: on your referrals page at [terac.com/me/referrals](https://terac.com/me/referrals), click **Share your link**. You can copy it, or share it straight to Facebook, X, LinkedIn, or email. * **After an application**: the last step of most applications offers a link to refer that opportunity to someone else. To find more opportunities to share, click **Find more studies to refer** on your referrals page. Links are unique to you. Whoever signs up through your link is counted as your referral. Some organizations don't take referrals for their opportunities. Those opportunities have no referral link, and if an organization stops taking referrals, a link you already shared for one of its opportunities stops working. Your general link keeps working. ## Tracking Your Referrals [#tracking-your-referrals] Your referrals page at [terac.com/me/referrals](https://terac.com/me/referrals) shows what you have earned and how your referrals are doing. It has three tabs: * **Studies**: each opportunity you have referred people to, and what you earn on it. * **People**: everyone you have referred, one row per person and opportunity, with where they are. Filter by stage: **Needs a push**, **Waiting**, **Placed**, **Not applied**, or **Closed**. * **My connections**: upload the connections file you export from LinkedIn, and we'll show you which opportunities fit the people you know. ## When You Get Paid [#when-you-get-paid] Your commission is added to your Terac balance each time Terac pays the person you referred: when their work on a one-time opportunity is approved and paid, or with each payout on an hourly or per-task project. You can cash it out like any other earnings, from your [Earnings page](https://terac.com/docs/experts/how-to/payments). Work that Terac does not pay for, such as an opportunity they joined through another research panel, does not earn a commission. Neither does work on an opportunity that doesn't take referrals, whichever of your links they signed up through. > **Warning** > > If a one-time submission that earned you a commission is later rejected in > review, that commission may be reversed. ## FAQ [#faq] ### Does my referral have to use my link when signing up? [#does-my-referral-have-to-use-my-link-when-signing-up] Yes. The link is how Terac connects them to you. There is no referral code to enter, so if they sign up without your link, you won't get credit. ### What if they clicked someone else's link first? [#what-if-they-clicked-someone-elses-link-first] The first referral link they clicked in that browser, within the last 30 days, is the one that counts. ### What if they already have an account? [#what-if-they-already-have-an-account] Referral credit only applies to new accounts. You can't add someone who has already signed up. ### Is there a minimum they need to earn? [#is-there-a-minimum-they-need-to-earn] No. You earn commission from the first payment Terac makes them. ### Can I refer myself with a second account? [#can-i-refer-myself-with-a-second-account] No. Each person should have one account. Multiple accounts violate our terms and can lead to suspension. ### Are referral earnings taxable? [#are-referral-earnings-taxable] Referral commissions are part of your total Terac earnings and may be taxable depending on where you live. ## What's Next? [#whats-next] Read the full [Referral Program Policy](https://terac.com/docs/experts/policies/referral-policy), or learn about [working on studies](https://terac.com/docs/experts/working). # How-To Guides (https://terac.com/docs/experts/how-to) Step-by-step guides for getting started and making the most of Terac. These guides walk you through every step of using Terac, from creating your account to cashing out your earnings. ## Getting Started [#getting-started] - [Create Your Account](https://terac.com/docs/experts/how-to/create-account): Sign in at terac.com and get started - [Setup Your Profile](https://terac.com/docs/experts/how-to/setup-profile): Complete onboarding and build your expert profile ## Finding Opportunities [#finding-opportunities] - [Apply to Opportunities](https://terac.com/docs/experts/how-to/apply): Browse, apply, and track your applications - [Verification and Connected Accounts](https://terac.com/docs/experts/how-to/attestations): Verify your phone and identity, and connect your profiles ## Earning Money [#earning-money] - [Setting Up Payments](https://terac.com/docs/experts/how-to/payments): Cash out your earnings to your bank through Whop - [Referring Others](https://terac.com/docs/experts/how-to/referrals): Earn commission by referring other experts # Setup Your Profile (https://terac.com/docs/experts/how-to/setup-profile) Complete the onboarding process to build your expert profile and unlock opportunities. After you sign in for the first time, a short onboarding flow builds your expert profile. The progress bar at the top reads **Phone → Sources → Profile → Interview**. Only the phone step is required: once your phone is verified, onboarding counts as complete and you can apply to opportunities. ## Step 1: Verify Your Phone Number [#step-1-verify-your-phone-number] Verifying your phone confirms you are a real person and helps prevent duplicate accounts. Enter your number, then send us a text to prove it is yours: * **On a computer**, scan the QR code with your phone and send the pre-filled text message. There is no code to type: we detect the message as soon as it arrives. * **On a phone**, tap **Open Messages** and send the pre-filled text. If that does not work for you, choose **Verify with a code instead**. We text you a 6-digit code to enter. You can ask for a new code every 60 seconds, even if you change numbers, and for up to 5 codes a day, sent to at most two different numbers. If you reach a limit, choose **Verify through Messages instead**, or email [support@terac.com](mailto:support@terac.com) if you can't use Messages. > Virtual numbers such as Google Voice, and landlines, cannot be used. If you > see a message that we are not available in your country yet, Terac does not > support phone numbers from that country at the moment. ## Step 2: Connect LinkedIn or Add Your Resume [#step-2-connect-linkedin-or-add-your-resume] The next screen asks **Anything else we should read?** Terac pre-fills your experience, education, and certifications from what you give it here: * **Connect LinkedIn**, if you did not sign in with it. * **Upload your resume**: a PDF of up to 10 MB. Both are optional. Choose **I'll do that later** to skip, or **Continue** when you are done. You can upload a resume later from your profile. ## Step 3: Review Your Profile [#step-3-review-your-profile] If you connected LinkedIn or uploaded a resume, Terac builds your profile in the background. This usually takes 15 to 30 seconds. If you gave it neither, you can **Connect LinkedIn** now or choose **I'll fill it in manually**. Then you step through four short parts, using **Next** and **Back**: ### About You [#about-you] Your full name, headline, and a short summary. Edit anything that looks off. ### Experience [#experience] Your work history. Each entry has a role, company, company website, start and end dates, city, country, and an optional description. Add any roles that are missing. ### Education [#education] Each entry has a school, degree, start and end dates, and an optional major and GPA. ### Rate [#rate] Your preferred hourly rate: the rate you would like to work at. You can change it any time in your settings. Click **All set** to save your profile. > You can skip this step, but completed profiles are much more likely to be > approved for opportunities, so it is worth finishing. ## Step 4: The Onboarding Interview [#step-4-the-onboarding-interview] Next comes a short voice conversation with Sophia, our AI interviewer, about your background and the kind of work you want. It takes about 10 minutes and helps us match you with the right opportunities. You will need a working camera and microphone. Before it starts, a quick check asks you to share your entire screen (on a computer), allow your camera and microphone, and center yourself in frame. See [AI Interviews](https://terac.com/docs/experts/working/interviews) for what to expect. The interview is optional. Choose **Skip for now** and you can take it later from Home. ## After Onboarding [#after-onboarding] Your dashboard's **Home** page lists what is left to do, such as uploading a resume, finishing your profile, taking the interview, and verifying your identity. ### Verify Your Identity [#verify-your-identity] Identity verification is not part of onboarding, but you need it before you can cash out, and opportunity pages list it as a required step. Do it early from **Profile → Identity & Security** by clicking **Start Verification**. See [Verification and Connected Accounts](https://terac.com/docs/experts/how-to/attestations). ### Availability [#availability] Go to **Profile → Availability** at [terac.com/me/settings/availability](https://terac.com/me/settings/availability) to connect your Google Calendar, so we can see when you are free, and to save a booking link such as Calendly if you use one. ## What's Next? [#whats-next] Once your profile is set up, you're ready to [apply to opportunities](https://terac.com/docs/experts/how-to/apply).