Crowd Jobs
Creating and managing crowdsourcing jobs — define tasks, publish to workers, and control the job lifecycle.
A crowd job is a self-contained crowdsourcing campaign scoped to a project. It ties a published survey template version to an optional input data set, then makes structured tasks available to Crowdee's worker pool. Workers claim individual task slots, complete the structured questionnaire, and submit answers. You review each submission and accept or reject it — accepted answers release the reserved credits to the worker.
Crowd jobs are independent of verification pipelines. While crowd stages in pipelines are triggered automatically by the pipeline runner, crowd jobs give you direct control: you define the task, set the availability window and reward, and manage the lifecycle through the API or the platform UI.
Job Fields
When creating or updating a crowd job, the following fields are accepted.
| Field | Type | Default | Description |
|---|---|---|---|
name | string | — | Internal label visible only to organisation members. |
title | string | — | Headline displayed to workers when they browse available jobs. |
description | string | — | Full job description shown to workers before they accept a task. |
category | "survey" | "qualification_test" | "gathering" | "survey" | Survey jobs collect data; qualification tests screen workers before they access higher-value campaigns; gathering jobs ask workers to find and submit new content — see Content Gathering. |
surveyTemplateVersionId | CUID | — | Required. Links a specific published survey template version. The version is frozen at creation time — updating the underlying template does not affect running jobs. |
inputDataSetId | CUID | null | Attaches an input data set. Each record in the set becomes a separate task variant, so workers see different content while answering the same questionnaire. When omitted, all workers answer a single identical task. |
maxAssignments | integer | 1 | Reserved field for capping total task slot assignments across all variants. |
maxRepetitionsPerVariant | integer | 1 | How many independent workers can complete each variant. Combined with the variant count this determines maxSlots — the total number of task completions the job accepts. |
rewardCredits | integer | 10 | Credits paid to a worker per accepted answer. Credits are blocked from your organisation balance at job creation and released to the worker when you accept their answer. |
validFrom | ISO 8601 timestamp | now | When the job becomes visible and assignable to workers. Defaults to the creation timestamp. |
validUntil | ISO 8601 timestamp | now + 30 days | When the job stops accepting new assignments. After this point the computed status transitions to not_assignable. |
qualificationRequirements | JSONB | null | Reserved for future worker-filtering capabilities. Stored but not currently enforced by the matching engine. |
forceUniqueTasks | boolean | true | When true, the same worker cannot be assigned the same input data variant twice. Recommended for most jobs to avoid contaminating results with repeat exposures. |
isDryRun | boolean | false | Test mode. No credits are blocked, and access is restricted to workers in dryRunAllowedEmails or those holding the magic token generated at creation. |
dryRunAllowedEmails | string[] | [] | Email allowlist for dry-run jobs. Workers whose registered email appears here can access the job without the magic token. |
verificationCode | string | null | Optional password workers must enter before claiming a task. Useful for invite-only or gated campaigns. |
creditsBlocked is a read-only field returned by the API. It records the credits currently reserved against your organisation balance and is managed automatically — do not include it in create or update requests.
Job Status Lifecycle
A job moves through the following statuses as conditions change. The computedStatus field returned by GET /v2/crowd-jobs/:jobId reflects the live state recalculated on every fetch; the stored status field is updated lazily to match.
assignable — The job is live. validFrom has passed, validUntil has not yet been reached, and available task slots remain. Workers can discover and claim tasks.
not_assignable — The job is paused. Either validUntil has passed or you have manually set the status to not_assignable via a PUT request. No new assignments are accepted, but in-progress tasks can still be submitted.
no_tasks — All task slots are occupied by workers who have claimed but not yet submitted, or by answers awaiting your review. The slot ceiling has been reached. The job returns to assignable if any slots are freed — for example, when you reject an answer the variant slot is returned to the pool.
finished — The total number of accepted (and pending-review) answers has reached maxSlots. The job is complete and no further assignments are accepted. You can still review and act on any pending answers.
archived — The job has been permanently closed via a DELETE request. All outstanding assigned tasks are cancelled, remaining blocked credits are returned to your organisation balance, and the job cannot be reactivated. Archived jobs are excluded from list responses unless you pass includeArchived=true.
maxSlots is derived at runtime as variantCount × maxRepetitionsPerVariant, where variantCount is the number of records in the attached input data set (or 1 when no input data set is attached). It is returned alongside computedStatus in the GET /v2/crowd-jobs/:jobId response.
Creating a Job
Jobs are created under a project. The minimum required fields are name, title, description, and surveyTemplateVersionId.
curl -X POST https://api.crowdee.ai/v2/projects/{projectId}/crowd-jobs \
-H "X-API-Key: crw_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Q3 Image Authenticity Review",
"title": "Assess whether this image has been digitally manipulated",
"description": "You will be shown an image and asked to provide your expert assessment. Please review all visible evidence carefully before submitting.",
"category": "survey",
"surveyTemplateVersionId": "tmplv_abc123def456ghi789jkl01",
"inputDataSetId": "idata_xyz987stu654rqp321onm09",
"maxRepetitionsPerVariant": 3,
"rewardCredits": 25,
"validFrom": "2026-07-01T08:00:00Z",
"validUntil": "2026-07-31T18:00:00Z",
"forceUniqueTasks": true
}'A 201 Created response returns the full job object including the system-assigned id, the computed creditsBlocked, and a dryRunMagicToken if isDryRun was true.
The organisation must have sufficient balance to cover creditsBlocked at the time of job creation. If the balance is insufficient the API returns 403 Forbidden and the job is not created. Top up credits via the billing section of the platform before publishing large campaigns.
Dry Runs
A dry run lets you test the full worker experience — task rendering, survey display, answer submission — without blocking credits or exposing the job to the full worker pool.
Set isDryRun: true when creating the job. The platform will:
- Block zero credits from your organisation balance.
- Generate a unique
dryRunMagicToken(a short random string returned in the job response). - Restrict task access to workers whose registered email is in
dryRunAllowedEmails, or to anyone who accesses the job via the magic token link in the worker UI.
A magic token link takes the form https://workers.crowdee.ai/jobs/{jobId}?token={dryRunMagicToken}. Share it directly with internal testers who need to preview the worker experience without a Crowdee account.
{
"name": "Q3 Image Authenticity Review — Test",
"title": "Assess whether this image has been digitally manipulated",
"description": "Internal preview only. Please complete as if this were a real task.",
"category": "survey",
"surveyTemplateVersionId": "tmplv_abc123def456ghi789jkl01",
"isDryRun": true,
"dryRunAllowedEmails": ["tester@yourorg.com", "qa@yourorg.com"]
}Dry-run answers follow the same accept/reject flow as live answers, but no credits change hands at any point — not even on acceptance. Use dry runs to validate template rendering, input data mapping, and your review workflow before committing credits to a live campaign. Once you are satisfied, clone the dry-run job without isDryRun to publish the live version.
Credits and Billing
When a non-dry-run job is created, Crowdee immediately reserves credits from your organisation balance. The amount blocked is:
creditsBlocked = variantCount × maxRepetitionsPerVariant × rewardCreditsWhere variantCount is the number of records in the attached input data set, or 1 if no input data set is used.
Credits move through the following states across the job lifecycle:
| State | Event |
|---|---|
| Blocked | Reserved on your organisation balance when the job is created. Not yet paid to anyone. |
| Earned (pending) | When a worker submits an answer, rewardCredits are moved into the worker's pending balance. The credits remain held until you review the answer. |
| Released | When you accept an answer, the credits move from the worker's pending balance into their available balance. The job's creditsBlocked decreases by rewardCredits. |
| Returned to job | When you reject an answer, the credits are returned to the job's blocked pool and the variant slot is reopened so another worker can attempt it. |
| Refunded to org | When you archive a job (DELETE), all remaining creditsBlocked are returned to your organisation balance in full. |
If you update a job in a way that increases the required reservation — for example by raising rewardCredits or maxRepetitionsPerVariant — the additional credits are deducted from your balance immediately. Decreases release the surplus back to your balance immediately.
Your organisation must have sufficient balance to cover creditsBlocked at the time of job creation. If the balance is insufficient, the API returns 403 Forbidden and the job is not created. Top up your balance via the billing section of the platform before retrying.
Cloning a Job
Cloning creates a new job that inherits all configuration from the source — template version, input data set, reward settings, availability window, and access controls — while resetting task and answer history. The clone starts with status: assignable.
curl -X POST https://api.crowdee.ai/v2/crowd-jobs/{jobId}/clone \
-H "X-API-Key: crw_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"isDryRun": false,
"name": "Q4 Image Authenticity Review"
}'Both isDryRun and name are optional. When isDryRun is omitted it defaults to false. When name is omitted the clone is named {original name} (copy).
Common use cases for cloning:
- Test before launch — Clone an existing production job with
isDryRun: trueto verify template or input data changes before committing credits. - Repeat a campaign — Clone a
finishedjob to rerun it against the same template and input data set, optionally adjusting the availability window or reward. - Staging mirrors — Maintain a dry-run copy of each production job to onboard new internal reviewers without touching live data.
Cloning a job as a non-dry-run immediately blocks credits for the new job. Ensure your organisation balance is sufficient before calling the clone endpoint without isDryRun: true.
Managing Jobs
List jobs for a project — returns all non-archived jobs by default; pass includeArchived=true to include archived ones, or status to filter by lifecycle state:
GET https://api.crowdee.ai/v2/projects/{projectId}/crowd-jobs?status=assignable
X-API-Key: crw_YOUR_API_KEYGet a single job — includes the live-computed computedStatus and the derived maxSlots:
GET https://api.crowdee.ai/v2/crowd-jobs/{jobId}
X-API-Key: crw_YOUR_API_KEYcomputedStatus reflects the actual current state — for example not_assignable if validUntil has already passed even though the stored status field still says assignable. maxSlots is variantCount × maxRepetitionsPerVariant.
Update a job — any writable field can be changed while the job is not archived. Updating rewardCredits, maxRepetitionsPerVariant, or inputDataSetId triggers an immediate credit recalculation and balance adjustment:
curl -X PUT https://api.crowdee.ai/v2/crowd-jobs/{jobId} \
-H "X-API-Key: crw_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"validUntil": "2026-08-15T18:00:00Z",
"maxRepetitionsPerVariant": 5
}'Archive a job — equivalent to a soft delete. All currently assigned tasks are cancelled, remaining blocked credits are returned to your organisation balance, and the job transitions to archived. This action is irreversible:
curl -X DELETE https://api.crowdee.ai/v2/crowd-jobs/{jobId} \
-H "X-API-Key: crw_YOUR_API_KEY"Archiving a job cancels all in-progress tasks immediately. Workers who were mid-task will lose their work. If you want to stop new assignments without disrupting workers already in progress, set status to not_assignable via a PUT request first, then archive only after all in-flight tasks have been submitted or expired.
Job Statistics
The stats endpoint gives you a real-time breakdown of all task and answer activity for a job.
GET https://api.crowdee.ai/v2/crowd-jobs/{jobId}/stats
X-API-Key: crw_YOUR_API_KEYThe response contains aggregate counts and per-worker detail arrays:
| Field | Description |
|---|---|
maxSlots | Total task completions this job will accept (variantCount × maxRepetitionsPerVariant). |
currentlyWorking | Workers with an assigned task that has not yet been submitted or expired. |
submitted | Total answers submitted across all workers, regardless of review status. |
accepted | Answers you have accepted; credits have been released to those workers. |
rejected | Answers you have rejected; those variant slots have been returned to the pool. |
pending | Submitted answers not yet reviewed. |
timedOut | Tasks that were assigned but expired before the worker submitted. |
cancelled | Tasks cancelled by the worker or by a job archive operation. |
returned | Task slots reopened after answer rejection — available for reassignment. |
The workers object contains four arrays, each identifying individual workers by workerId (authenticated workers) or guestWorkerId (guest / magic-token workers):
| Array | Contents |
|---|---|
workers.started | Currently assigned workers: workerId, guestWorkerId, taskId, assignedAt. |
workers.submitted | All workers who submitted: workerId, guestWorkerId, answerId, createdAt. |
workers.cancelled | Workers whose tasks were cancelled: workerId, guestWorkerId, taskId, updatedAt. |
workers.timedOut | Workers whose tasks expired: workerId, guestWorkerId, taskId, expiresAt. |
An example response for a job with 60 total slots partway through a campaign:
{
"maxSlots": 60,
"currentlyWorking": 4,
"submitted": 38,
"accepted": 31,
"rejected": 3,
"pending": 4,
"timedOut": 2,
"cancelled": 1,
"returned": 0,
"workers": {
"started": [
{ "workerId": "usr_abc", "guestWorkerId": null, "taskId": "tsk_001", "assignedAt": "2026-07-14T09:12:00Z" }
],
"submitted": [...],
"cancelled": [...],
"timedOut": [...]
}
}How is this guide?
Crowdsourcing Overview
How Crowdee's crowdsourcing system works — both as automated crowd stages in verification pipelines and as directly published crowd jobs.
Content Gathering
Using the crowd to search for and collect content matching your criteria, rather than reviewing content you already have.