> ## Documentation Index
> Fetch the complete documentation index at: https://docs.whop.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Bounty Submission

A Bounty Submission is one worker's attempt on a bounty. It starts as an in-progress attempt, enters the review queue when proof is submitted, and ends approved (paid from the bounty's escrowed pool) or denied.

Use the Bounty Submissions API to submit proof of completed work to a bounty, list the submissions you authored, and review the submissions on your bounties — across every bounty or narrowed to one.

## Endpoints

| Endpoint | Request |
| - | - |
| [List Bounty Submissions](/api-reference/beta/bounty-submissions/list-bounty-submissions) | <Badge color="blue" size="sm" stroke>GET</Badge> `/bounty_submissions` |
| [Retrieve Bounty Submission](/api-reference/beta/bounty-submissions/retrieve-bounty-submission) | <Badge color="blue" size="sm" stroke>GET</Badge> `/bounty_submissions/{id}` |
| [Create Bounty Submission](/api-reference/beta/bounty-submissions/create-bounty-submission) | <Badge color="green" size="sm" stroke>POST</Badge> `/bounty_submissions` |
| [Submit Bounty Submission](/api-reference/beta/bounty-submissions/submit-bounty-submission) | <Badge color="green" size="sm" stroke>POST</Badge> `/bounty_submissions/{id}/submit` |
| [Cancel Bounty Submission](/api-reference/beta/bounty-submissions/cancel-bounty-submission) | <Badge color="red" size="sm" stroke>DELETE</Badge> `/bounty_submissions/{id}` |

## Attributes

<Columns cols={2}>
  <Column>
    <ResponseField name="id" type="string" required>
      Submission ID, prefixed `btys_`.
    </ResponseField>

    <ResponseField name="bounty_id" type="string" required>
      The bounty the work was submitted to, prefixed `bnty_`.
    </ResponseField>

    <ResponseField name="capture_clips" type="object[] | null" required>
      The submission's capture clips in recording order, each carrying its own review `status` and temporary signed artifact URLs. The full deliverable when `deliverable_type` is `data_capture`, populated only on single-submission reads; `null` on list responses (use `captured_clip_count` and `captured_duration_seconds` for the summary) and for other deliverable types. An attempt still in progress lists every clip it has recorded, including failed ones; once submitted, only clips that passed validation are listed, since only those are evidence.

      <Accordion title="Properties" defaultOpen={true}>
        <ResponseField name="id" type="string" required>
          Capture clip ID, prefixed `bclip_`.
        </ResponseField>

        <ResponseField name="bounty_submission_id" type="string" required>
          The bounty submission (attempt) this clip belongs to, prefixed `btys_`.
        </ResponseField>

        <ResponseField name="created_at" type="string" required>
          When the clip was created, as an ISO 8601 timestamp.
        </ResponseField>

        <ResponseField name="duration_seconds" type="integer | null" required>
          Server-validated clip duration in whole seconds. `null` until validation
          completes.
        </ResponseField>

        <ResponseField name="failure_code" type="string | null" required>
          Stable validation failure code. `null` unless `status` is `failed`.
        </ResponseField>

        <ResponseField name="failure_message" type="string | null" required>
          Human-readable validation failure reason. `null` unless `status` is `failed`.
        </ResponseField>

        <ResponseField name="frames_url" type="string | null" required>
          Temporary signed URL for the video frame timestamp log. Returned only on
          single-clip reads for an authorized viewer; `null` on list responses or until
          the artifact is attached.
        </ResponseField>

        <ResponseField name="imu_url" type="string | null" required>
          Temporary signed URL for the IMU (accelerometer + gyroscope) log. Returned
          only on single-clip reads for an authorized viewer; `null` on list responses
          or until the artifact is attached.
        </ResponseField>

        <ResponseField name="manifest_url" type="string | null" required>
          Temporary signed URL for the capture manifest. Returned only on single-clip
          reads for an authorized viewer; `null` on list responses or until the artifact
          is attached.
        </ResponseField>

        <ResponseField name="ready_at" type="string | null" required>
          When server-side validation completed successfully, as an ISO 8601 timestamp.
          `null` until then.
        </ResponseField>

        <ResponseField name="sequence" type="integer" required>
          The clip's stable order within the attempt, starting at 1.
        </ResponseField>

        <ResponseField name="status" type="string" required>
          Recording and validation state. `recording` is still capturing; `verifying` is running server-side validation; `ready` passed validation and counts toward the verified-duration payout gate; `failed` did not validate.

          Available options: `recording`, `verifying`, `ready`, `failed`
        </ResponseField>

        <ResponseField name="updated_at" type="string" required>
          When the clip was last updated, as an ISO 8601 timestamp.
        </ResponseField>

        <ResponseField name="video_url" type="string | null" required>
          Temporary signed URL for the synchronized MP4 video. Returned only on single-clip reads for an authorized viewer; `null` on list responses or until the artifact is attached.
        </ResponseField>
      </Accordion>
    </ResponseField>

    <ResponseField name="capture_filename" type="string | null" required>
      The vendor filename stem `Country_City_Site_Station_Operator`, derived from
      the capture metadata. `null` until every component is present.
    </ResponseField>

    <ResponseField name="captured_clip_count" type="integer" required>
      Number of verified capture clips accepted for this submission so far. `0` for
      submissions whose deliverable doesn't accumulate clips.
    </ResponseField>

    <ResponseField name="captured_duration_seconds" type="integer" required>
      Total verified duration of accepted capture clips, in whole seconds. `0` for
      submissions whose deliverable doesn't accumulate clips.
    </ResponseField>

    <ResponseField name="city" type="string | null" required>
      Capture metadata: city the footage was recorded in. `null` unless capture
      metadata was provided.
    </ResponseField>

    <ResponseField name="claimed_at" type="string | null" required>
      When the worker claimed the submission, as an ISO 8601 timestamp.
    </ResponseField>

    <ResponseField name="content" type="string | null" required>
      Written proof the worker submitted with their work.
    </ResponseField>

    <ResponseField name="country" type="string | null" required>
      Capture metadata: country the footage was recorded in. `null` unless capture
      metadata was provided.
    </ResponseField>

    <ResponseField name="created_at" type="string" required>
      When the submission was created, as an ISO 8601 timestamp.
    </ResponseField>

    <ResponseField name="deliverable_type" type="string | null" required>
      How the work arrived when it came in through the API in one shot, informational only — read the work from `deliverable_urls`, `files`, and `capture_clips` directly. `null` for submissions whose proof is a livestream recording, including ones that attached links or files on submit.

      Available options: `content_url`, `media`, `data_capture`
    </ResponseField>

    <ResponseField name="deliverable_urls" type="string[] | null" required>
      Links submitted as work, followed by temporary download URLs for the uploaded
      files. `null` when the submission carries neither.
    </ResponseField>

    <ResponseField name="denial_reason" type="string | null" required>
      Why the submission was denied, when a presentable reason exists. Always `null`
      unless `status` is `denied`.
    </ResponseField>

    <ResponseField name="device" type="string | null" required>
      Capture metadata: device the footage was recorded on. `null` unless capture
      metadata was provided.
    </ResponseField>

    <ResponseField name="files" type="object[]" required>
      Files uploaded as part of the work, in upload order. Empty when the submission has none.

      <Accordion title="Properties" defaultOpen={true}>
        <ResponseField name="id" type="string" required>
          File ID, prefixed `file_`.
        </ResponseField>

        <ResponseField name="attachment_type" type="string | null" required>
          Broad kind of file.

          Available options: `image`, `video`, `audio`, `other`
        </ResponseField>

        <ResponseField name="content_type" type="string | null" required>
          MIME type of the file.
        </ResponseField>

        <ResponseField name="filename" type="string | null" required>
          Name the file was uploaded with.
        </ResponseField>

        <ResponseField name="url" type="string | null" required>
          Temporary download URL for the file.
        </ResponseField>
      </Accordion>
    </ResponseField>

    <ResponseField name="fov" type="integer | null" required>
      Capture metadata: horizontal field of view in degrees. `null` when not
      reported.
    </ResponseField>

    <ResponseField name="latest_proof_livestream_feed" type="object | null" required>
      Latest public proof livestream attached to the submission.

      <Accordion title="Properties" defaultOpen={true}>
        <ResponseField name="id" type="string" required>
          Livestream feed ID.
        </ResponseField>

        <ResponseField name="ended_at" type="string | null" required>
          When the proof livestream ended, as an ISO 8601 timestamp. `null` while it is
          still live — a feed with a `started_at` and no `ended_at` is streaming right
          now.
        </ResponseField>

        <ResponseField name="recording_status" type="string | null" required>
          Recording lifecycle state.

          Available options: `recording`, `processing`, `completed`, `failed`
        </ResponseField>

        <ResponseField name="recording_url" type="string | null" required>
          Playback URL for a completed proof recording, when available.
        </ResponseField>

        <ResponseField name="started_at" type="string | null" required>
          When the proof livestream went live, as an ISO 8601 timestamp. `null` before
          it starts.
        </ResponseField>

        <ResponseField name="thumbnail_url" type="string | null" required>
          Current proof thumbnail URL, when available.
        </ResponseField>

        <ResponseField name="title" type="string" required>
          Display title for the proof livestream.
        </ResponseField>
      </Accordion>
    </ResponseField>

    <ResponseField name="operator" type="string | null" required>
      Capture metadata: identifier of the person who recorded the footage. `null`
      unless capture metadata was provided.
    </ResponseField>

    <ResponseField name="resolved_at" type="string | null" required>
      When the submission was approved or denied, as an ISO 8601 timestamp. `null`
      until then.
    </ResponseField>

    <ResponseField name="site" type="string | null" required>
      Capture metadata: site or venue the footage was recorded at. `null` unless
      capture metadata was provided.
    </ResponseField>

    <ResponseField name="station" type="string | null" required>
      Capture metadata: station or position within the site. `null` unless capture
      metadata was provided.
    </ResponseField>

    <ResponseField name="status" type="string | null" required>
      Lifecycle state. `in_progress` submissions are active attempts that have not submitted proof yet; `submitted` submissions await review; `approved` submissions were accepted and paid; `denied` submissions were rejected. `null` when the attempt ended without proof, taking it out of the public lifecycle — those attempts are absent from every public list and read.

      Available options: `in_progress`, `submitted`, `approved`, `denied`
    </ResponseField>

    <ResponseField name="submitted_at" type="string | null" required>
      When proof was submitted for review, as an ISO 8601 timestamp. `null` while
      the attempt is in progress.
    </ResponseField>

    <ResponseField name="updated_at" type="string" required>
      When the submission was last updated, as an ISO 8601 timestamp.
    </ResponseField>

    <ResponseField name="worker" type="object" required>
      User who submitted the work.

      <Accordion title="Properties" defaultOpen={true}>
        <ResponseField name="id" type="string" required>
          User ID, prefixed `user_`.
        </ResponseField>

        <ResponseField name="name" type="string | null" required>
          Display name.
        </ResponseField>

        <ResponseField name="profile_picture" type="object" required>
          Avatar wrapper; its `url` is always present, using a generated placeholder when the user set no picture.

          <Accordion title="Properties" defaultOpen={true}>
            <ResponseField name="url" type="string" required>
              Avatar image URL. Always present — a generated placeholder when the user set no picture.
            </ResponseField>
          </Accordion>
        </ResponseField>

        <ResponseField name="username" type="string" required>
          Public username.
        </ResponseField>
      </Accordion>
    </ResponseField>
  </Column>

  <Column>
    <div className="api-resource-sticky-example">
      ```json BountySubmission theme={null}
      {
      	"id": "btys_xxxxxxxxxxxxxx",
      	"bounty_id": "bnty_xxxxxxxxxxxxxx",
      	"capture_clips": null,
      	"capture_filename": null,
      	"captured_clip_count": 0,
      	"captured_duration_seconds": 0,
      	"city": null,
      	"claimed_at": "2026-09-10T09:00:00.000Z",
      	"content": "Ceramic coating reveal, filmed at the Burnet Rd bay.",
      	"country": null,
      	"created_at": "2026-09-10T09:00:00.000Z",
      	"deliverable_type": "content_url",
      	"deliverable_urls": [
      		"https://www.tiktok.com/@shinetime/video/7412345678901234567"
      	],
      	"denial_reason": null,
      	"device": null,
      	"files": [
      		{
      			"id": "file_xxxxxxxxxxxxxx",
      			"attachment_type": "image",
      			"content_type": "image/png",
      			"filename": "ceramic-coating-reveal.png",
      			"url": "https://whop-assets-example.s3.amazonaws.com/uploads/image/2026-09-12/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
      		}
      	],
      	"fov": null,
      	"latest_proof_livestream_feed": null,
      	"operator": null,
      	"resolved_at": "2026-09-13T11:20:00.000Z",
      	"site": null,
      	"station": null,
      	"status": "approved",
      	"submitted_at": "2026-09-12T18:45:00.000Z",
      	"updated_at": "2026-09-13T11:20:00.000Z",
      	"worker": {
      		"id": "user_xxxxxxxxxxxxxx",
      		"name": "Dana Whitfield",
      		"profile_picture": {
      			"url": "https://ui-avatars.com/api/"
      		},
      		"username": "danawhitfield"
      	}
      }
      ```
    </div>
  </Column>
</Columns>


## Related topics

- [CLI Commands](/cli/commands.md)
- [Bounties](/developer/bounties/overview.md)
- [List Bounty Submissions](/api-reference/beta/bounty-submissions/list-bounty-submissions.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.