Challenges
Challenges are sponsor-run prizes or tasks attached to an event — for example, "Best use of a sponsor's API." A challenge is linked to the event it runs at, and participants compete to win it.
Challenge submissions are the entries participants send in for a challenge. Each submission ties a project to a challenge and moves through a review workflow (submitted, approved, rejected, or withdrawn) until an organizer decides whether it won.
This page covers both resources. For conventions shared across the API — authentication, pagination, filtering, expanding related data, and errors — see Making requests and Authentication.
Challenges
A challenge record links an event to a challenge (the sponsor prize). It tells you which event a challenge belongs to and which challenge it points at.
Endpoints
| Method | Path | Does |
|---|---|---|
GET | /v4/challenges | List challenges |
GET | /v4/challenges/{id} | Get one challenge |
POST | /v4/challenges | Create a challenge |
PATCH / PUT | /v4/challenges/{id} | Update a challenge |
GET | /v4/challenges/identify | Look up a challenge entry by a user's email or phone |
GET /v4/challenges supports pagination and filtering. You can filter on the
challenge record's own fields (id) and on the underlying challenge's fields
(name, description, short_description, prize_description, challenge_type,
featured, active, mlh_sponsorship), plus created_at and updated_at. See
Filtering.
Fields
These are the fields on a challenge record.
| Field | Required | Read-only | Description |
|---|---|---|---|
id | — | Yes | Unique identifier for the challenge record. Example: "a1b2c3d4-...". |
event_id | Optional | No | ID of the event this challenge belongs to. Example: "e5f6...". |
challenge_id | Optional | No | ID of the challenge (the sponsor prize) this record points at. Example: "c7d8...". |
event | Optional | No | The related event. Expand with expand[]=event to include it. |
challenge | Optional | No | The related challenge. Expand with expand[]=challenge to include it. |
created_at | — | Yes | When the record was created, as a Unix timestamp (seconds). Example: 1755561600. |
updated_at | — | Yes | When the record was last updated, as a Unix timestamp (seconds). Example: 1755561600. |
id, created_at, and updated_at are set by the API and cannot be written. In
responses, created_at and updated_at are Unix timestamps; when you send them in
a request body they are ISO 8601 datetime strings.
Use expand[] to pull in related records — event and challenge are the
expandable associations on both GET /v4/challenges and GET /v4/challenges/{id}.
See Expanding related data.
Scopes
These endpoints use OAuth 2.0. Request only the scopes your integration needs (see Authentication). Reading challenges needs read access; creating or updating them needs write access.
Identify a challenge
GET /v4/challenges/identify
Looks up a challenge entry for a user by an identifier. Use this when you have a participant's email or phone number and need to find their challenge record.
| Parameter | In | Required | Description |
|---|---|---|---|
identifier | query | Yes | The email address or phone number to identify the user by. Example: jane@example.com. |
auto_create | query | Optional | If true, create the user when no match is found. Defaults to false. |
GET /v4/challenges/identify?identifier=jane@example.com
The response is a challenge record with the same fields listed above.
Request examples
List challenges for one event, with the related event and challenge expanded:
GET /v4/challenges?event_id[eq]=e5f6...&expand[]=event&expand[]=challenge
Create a challenge record linking an event to a challenge:
POST /v4/challenges
Content-Type: application/json
{
"event_id": "e5f6...",
"challenge_id": "c7d8..."
}
Challenge Submissions
A challenge submission is a participant's entry for a challenge. It records the project, who submitted it, which event and challenge it's for, and where it is in the review workflow.
Endpoints
| Method | Path | Does |
|---|---|---|
GET | /v4/challenge_submissions | List submissions |
GET | /v4/challenge_submissions/{id} | Get one submission |
POST | /v4/challenge_submissions | Create a submission |
PATCH / PUT | /v4/challenge_submissions/{id} | Update a submission |
DELETE | /v4/challenge_submissions/{id} | Delete a submission |
POST | /v4/challenge_submissions/{id}/submit | Submit a draft for review |
POST | /v4/challenge_submissions/{id}/manual_approve | Approve one submission by hand |
POST | /v4/challenge_submissions/{id}/auto_approve | Auto-approve one submission |
POST | /v4/challenge_submissions/{id}/bulk_approve | Approve as part of a bulk action |
POST | /v4/challenge_submissions/{id}/reject | Reject a submission |
POST | /v4/challenge_submissions/{id}/withdraw | Withdraw a submission |
POST | /v4/submissions/export | Export submissions |
GET /v4/challenge_submissions supports pagination and filtering on project_id,
event_id, challenge_id, submitter_id, status, winner, submitted_at,
created_at, and updated_at. Expandable associations are project, submitter,
event, and challenge.
Fields
| Field | Required | Read-only | Description |
|---|---|---|---|
id | — | Yes | Unique identifier for the submission. Example: "9f0a...". |
project_id | Optional | No | ID of the project being submitted. Example: "p1q2...". |
submitter_id | Optional | No | ID of the user who submitted it. Example: "u3v4...". |
event_id | Optional | No | ID of the event the submission is for. |
challenge_id | Optional | No | ID of the challenge the submission is for. |
status | Optional | No | Where the submission is in the workflow. One of draft, submitted, manual_approved, bulk_approved, auto_approved, rejected, withdrawn. Usually driven by the workflow actions below rather than set directly. |
winner | Optional | No | Whether this submission won the challenge. true or false. |
rejection_reason | Optional | No | Why the submission was rejected. Set when you reject. Example: "Out of scope". |
withdraw_reason | Optional | No | Why the submission was withdrawn. Set when you withdraw. Example: "Duplicate entry". |
submitted_at | Optional | No | When it was submitted. Unix timestamp in responses; ISO 8601 datetime string in requests. |
accepted_at | Optional | No | When it was accepted. Unix timestamp in responses; ISO 8601 in requests. |
rejected_at | Optional | No | When it was rejected. Unix timestamp in responses; ISO 8601 in requests. |
withdrawn_at | Optional | No | When it was withdrawn. Unix timestamp in responses; ISO 8601 in requests. |
verified_at | Optional | No | When it was verified. Unix timestamp in responses; ISO 8601 in requests. |
custom_fields | Optional | No | An object for extra event-specific data. Example: { "demo_url": "https://..." }. |
project | Optional | No | The related project. Expand with expand[]=project. |
submitter | Optional | No | The related user. Expand with expand[]=submitter. |
event | Optional | No | The related event. Expand with expand[]=event. |
challenge | Optional | No | The related challenge. Expand with expand[]=challenge. |
created_at | — | Yes | When the submission was created, as a Unix timestamp. |
updated_at | — | Yes | When the submission was last updated, as a Unix timestamp. |
id, created_at, and updated_at are set by the API and cannot be written. In
responses, all timestamp fields (created_at, updated_at, submitted_at,
accepted_at, rejected_at, withdrawn_at, verified_at) are Unix timestamps;
in request bodies the datetime fields are ISO 8601 strings.
Scopes
These endpoints use OAuth 2.0. Request only the scopes your integration needs (see Authentication). Reading submissions needs read access; creating, updating, deleting, or running a workflow action needs write access.
Workflow actions
A submission moves through its lifecycle with the action endpoints below. Each is a
POST to /v4/challenge_submissions/{id}/<action>, where {id} is the submission
you're acting on. Each returns the updated submission.
submit— moves adraftsubmission into review. Call this once a participant has finished their entry. After this the status becomessubmitted.manual_approve— approves a single submission by hand, typically after an organizer reviews it. Sets the status tomanual_approved.auto_approve— approves a single submission through an automated rule rather than manual review. Sets the status toauto_approved.bulk_approve— approves a submission as part of a bulk approval. Use this when approving many entries at once. Sets the status tobulk_approved. This still targets one submission per call (via its{id}); the "bulk" refers to the approval mode, so to approve several entries you call it once per submission.reject— rejects a submission. Setrejection_reasonin the body to record why. Sets the status torejected.withdraw— withdraws a submission (for example, a participant pulls their entry). Setwithdraw_reasonin the body to record why. Sets the status towithdrawn.
Gotchas:
- Actions depend on the current status.
submitapplies to adraft; the approve,reject, andwithdrawactions apply to a submission that has beensubmitted. Acting on a submission that isn't in the right state returns a validation error. - There are three separate approve actions —
manual_approve,auto_approve, andbulk_approve— and they set different final statuses (manual_approved,auto_approved,bulk_approved). Pick the one that matches how the decision was made. All three approve a single submission per call; none of them approves a batch in one request. - Prefer the workflow actions over writing
statusdirectly withPATCH/PUT, so the related timestamp and reason fields are set consistently.
Export
POST /v4/submissions/export
Runs an export over challenge submissions. Send a request body describing the
submissions to export using the submission fields above (for example, filter by
event_id, challenge_id, or status).
Request examples
Submit a draft for review:
POST /v4/challenge_submissions/9f0a.../submit
Reject a submission with a reason:
POST /v4/challenge_submissions/9f0a.../reject
Content-Type: application/json
{
"rejection_reason": "Out of scope for this challenge"
}
Withdraw a submission with a reason:
POST /v4/challenge_submissions/9f0a.../withdraw
Content-Type: application/json
{
"withdraw_reason": "Duplicate entry"
}
List a challenge's winning submissions, with the project and submitter expanded:
GET /v4/challenge_submissions?challenge_id[eq]=c7d8...&winner[eq]=true&expand[]=project&expand[]=submitter