Submissions
Submissions represent files uploaded by workers for a job. Each submission is automatically scored by AI.
Submission Lifecycle
┌───────────────┐ AI scores ┌──────────┐
│ pending_score │ ──── 3-5★ ──────▶│ approved │
└───────────────┘ └──────────┘
│
│ AI scores 1-2★
▼
┌──────────┐ worker retries ┌───────────────┐
│ rejected │ ◀───────────────────── │ retry_pending │
└──────────┘ └───────────────┘| Status | Description |
|---|---|
pending_score | Uploaded by worker, waiting for AI scoring (typically 5-15 seconds) |
approved | Scored 3+ stars — file delivered to buyer’s job folder, worker earns payout |
rejected | Scored 1-2 stars — worker receives AI feedback and can retry |
retry_pending | Worker has re-accepted the job to submit a new attempt |
Scoring threshold: Submissions scoring 3+ stars are auto-approved. Submissions scoring 1-2 stars are auto-rejected with AI-generated feedback explaining why.
Pre-scoring rejections: Submissions that fail resolution checks (min_width/min_height) are auto-rejected with 1 star before AI scoring runs. These rejections do not consume credits.
Job slot recovery: When a submission is rejected, the job’s slot reopens. If the job was in filled status (all slots taken by pending submissions), it reverts to open so other workers can accept it. This prevents jobs from getting stuck when submissions are rejected.
Timing: AI scoring typically completes within 5-15 seconds of upload. Use the submission.scored webhook event to get notified immediately.
List Submissions
GET /v1/submissionsList submissions across all jobs for your organization.
Query Parameters:
| Param | Type | Description |
|---|---|---|
job_id | string | Filter by job ID |
status | string | Filter: pending_score, approved, rejected, retry_pending |
min_score | integer | Minimum AI star rating (1-5) |
start_date | string | Start date (ISO 8601, default: 30 days ago) |
end_date | string | End date (ISO 8601, default: now) |
limit | integer | Max results (1-100, default 20) |
cursor | string | Pagination cursor |
Response:
{
"object": "list",
"data": [
{
"object": "submission",
"id": "sub_01JQ...",
"job_id": "job_01JQ...",
"worker_id": "wkr_01JQ...",
"status": "approved",
"ai_star_rating": 4,
"ai_reasoning": "Clear, well-lit photo that matches the job description. Subject is centered and in focus.",
"file_url": "https://files.firsthandapi.com/...",
"content_type": "image/jpeg",
"size_bytes": 2450000,
"submitted_at": "2026-03-16T14:30:00Z",
"scored_at": "2026-03-16T14:30:05Z"
}
],
"has_more": true,
"next_cursor": "..."
}Get Submission
GET /v1/submissions/{submission_id}Retrieve a single submission by ID, including its AI star rating and reasoning.
Response:
{
"object": "submission",
"id": "sub_01JQ...",
"job_id": "job_01JQ...",
"worker_id": "wkr_01JQ...",
"status": "approved",
"ai_star_rating": 4,
"ai_reasoning": "Clear, well-lit photo that matches the job description. Subject is centered and in focus with good composition.",
"file_url": "https://files.firsthandapi.com/...",
"content_type": "image/jpeg",
"size_bytes": 2450000,
"submitted_at": "2026-03-16T14:30:00Z",
"scored_at": "2026-03-16T14:30:05Z"
}Submission Summary
GET /v1/submissions/summaryReturns aggregate submission metrics for your organization.
Query Parameters:
| Param | Type | Description |
|---|---|---|
job_id | string | Filter by job ID |
start_date | string | Start date (ISO 8601, default: 30 days ago) |
end_date | string | End date (ISO 8601, default: now) |
Response:
{
"object": "submission_summary",
"total_submissions": 1250,
"approved": 1050,
"rejected": 200,
"approval_rate": 0.84,
"avg_ai_star_rating": 3.8,
"by_type": [
{
"type": "data_collection",
"total": 800,
"approved": 700,
"avg_ai_star_rating": 4.0
}
]
}