Create a draft opportunity
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_idand 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 yourfiltersandscreening_questionsdescribe, and the recruitment window fromexpected_days_to_complete. Changing any of those changes the price, so read the result back frompricingon the response; do not quote a price to anyone before you have. - Pass a
feasibility_request_idand that request's confirmed CPI is honored exactly, with no re-estimate. Submit the brief toPOST /feasibility/requestsfirst and pollGET /feasibility/requests/{requestId}until it readsRESPONDED, 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
apiKey API key as Bearer token: Authorization: Bearer
In: header
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
application/json
application/json
application/json
application/json
curl -X POST "https://terac.com/api/external/v2/opportunities" \ -H "Content-Type: application/json" \ -d '{ "title": "How finance teams choose spend software", "description": "Tell us how your team chose and rolled out its spend management software. Eight written questions, about 30 minutes.", "project_id": "m4q8x2zc7tn1v0bk5r9wh3jd", "num_participants": 12, "business_type": "b2b", "expected_days_to_complete": 10, "feasibility_request_id": "tz4a98xxat96iws9zmbrgj3a", "filters": [ { "multi_select--country": { "$in": [ "US", "CA" ] } }, { "multi_select--seniority": { "$in": [ "manager", "senior-manager", "director" ] } } ], "screening_questions": [ { "key": "chose_tool", "text": "Were you involved in choosing your company\'s current spend management software?", "pick": "one", "answers": [ { "text": "Yes, I led or approved the decision", "qualify_logic": "must" }, { "text": "Yes, I gave input", "qualify_logic": "must" }, { "text": "No", "qualify_logic": "reject" } ] } ], "tasks": [ { "sequence": 1, "task_type": "interview", "review_type": "manual_review", "task_url": "https://research.example.com/studies/spend-software", "title": "Written interview", "description": "Answer the eight questions on the interview page, then submit it.", "duration_minutes": 30 } ] }'{
"id": "h7k2p9w4c1v6n3x8b5m0q2rt",
"title": "How finance teams choose spend software",
"internal_title": null,
"description": "Tell us how your team chose and rolled out its spend management software. Eight written questions, about 30 minutes.",
"status": "draft",
"num_participants": 12,
"estimated_duration_minutes": 30,
"filters": [
{
"multi_select--country": {
"$in": [
"US",
"CA"
]
}
},
{
"multi_select--seniority": {
"$in": [
"manager",
"senior-manager",
"director"
]
}
}
],
"screening_questions": [
{
"key": "chose_tool",
"text": "Were you involved in choosing your company's current spend management software?",
"pick": "one",
"answers": [
{
"text": "Yes, I led or approved the decision",
"qualify_logic": "must"
},
{
"text": "Yes, I gave input",
"qualify_logic": "must"
},
{
"text": "No",
"qualify_logic": "reject"
}
]
}
],
"quotas": [],
"cross_quotas": [],
"tasks": [
{
"sequence": 1,
"task_type": "interview",
"review_type": "manual_review",
"task_url": "https://research.example.com/studies/spend-software",
"participant_url_template": "https://research.example.com/studies/spend-software?submissionId={submissionId}&teracSubmissionId={submissionId}&taskId={taskId}",
"title": "Written interview",
"description": "Answer the eight questions on the interview page, then submit it.",
"duration_minutes": 30
}
],
"pricing": {
"cost_per_participant_cents": 6250,
"total_cost_cents": 75000,
"currency": "usd"
},
"device_types": [
"desktop",
"mobile_ios",
"mobile_android",
"other"
],
"business_type": "b2b",
"candidate_profile": null,
"required_accounts": [],
"video_required": "none",
"screen_share_required": "none",
"customer_screening_review": "auto_invite",
"pay_frequency": "one_time",
"screening_option_randomization": "disabled",
"forwarding_email_enabled": "disabled",
"max_responses_per_expert": null,
"feasibility_request_id": "tz4a98xxat96iws9zmbrgj3a",
"project_id": "m4q8x2zc7tn1v0bk5r9wh3jd",
"created_at": "2026-09-28T15:10:05.903Z",
"funding": {
"affordable": true,
"draws_on_credit": false,
"balance_cents": 240000,
"shortfall_cents": 0,
"finance_url": "https://terac.com/acme-research/settings/finance"
},
"updated_at": "2026-09-28T15:10:06.412Z",
"ending_at": "2026-10-08T15:10:05.903Z",
"launched_at": null,
"links": {
"self": "/api/external/v2/opportunities/h7k2p9w4c1v6n3x8b5m0q2rt",
"launch": "/api/external/v2/opportunities/h7k2p9w4c1v6n3x8b5m0q2rt/launch",
"submissions": "/api/external/v2/opportunities/h7k2p9w4c1v6n3x8b5m0q2rt/submissions",
"dashboard": {
"study": "https://terac.com/acme-research/customer-research-mg4q7x2a/opportunities/h7k2p9w4c1v6n3x8b5m0q2rt",
"draft_editor": "https://terac.com/acme-research/customer-research-mg4q7x2a/opportunities/create?id=h7k2p9w4c1v6n3x8b5m0q2rt",
"submissions": null,
"recruitment": null,
"task": null,
"settings": null
}
}
}{
"code": "BAD_REQUEST",
"message": "Invalid input data",
"issues": []
}{
"error": {
"code": "UNAUTHORIZED",
"message": "API key required. Include Authorization: Bearer <key> header."
}
}{
"error": {
"code": "FORBIDDEN",
"message": "This account is not permitted to use the API."
}
}{
"code": "NOT_FOUND",
"message": "Not found",
"issues": []
}{
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded. Retry after 61 seconds."
}
}{
"code": "INTERNAL_SERVER_ERROR",
"message": "Internal server error",
"issues": []
}Update a project PATCH
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.
List opportunities GET
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}`.