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

# Submissions

> Understand submission lifecycle and tracking

## What is a submission?

A submission is a container for one or more media assets that you want to process. When you create a submission, the API queues the assets for processing and begins analysis.

Each submission has:

* A unique `submissionId` for tracking and polling
* A workspace context (derived from your API key)
* One or more assets to process
* A lifecycle status (`uploading`, `submitted`, `queued`, `processing`, `complete`, `partial`, `failed`)

## Submission lifecycle

```mermaid theme={null}
graph TD
  A[uploading] --> B[queued]
  B --> C[processing]
  C --> D[complete]
  C --> E[partial]
  C --> F[failed]
```

A live submission is created as `queued` and moves to `processing` as its assets are admitted to review. Sandbox submissions are created as `submitted` instead and skip the queue entirely.

### States explained

| Status       | Meaning                                                    | Next step                                                                   |
| ------------ | ---------------------------------------------------------- | --------------------------------------------------------------------------- |
| `uploading`  | Assets are being uploaded to blob storage                  | Finish uploads, then create the submission                                  |
| `queued`     | Every asset is accepted and waiting for a free review slot | Keep polling — processing starts as slots free up                           |
| `processing` | At least one asset is actively being analyzed              | Poll for completion                                                         |
| `complete`   | All assets successfully analyzed                           | Retrieve results                                                            |
| `partial`    | At least one asset succeeded and at least one failed       | Retrieve the successful results; check failed assets and retry if retryable |
| `failed`     | Every asset failed to process                              | Check error details and retry if retryable                                  |
| `submitted`  | Sandbox submissions only — registered and simulating       | Wait for the simulated result                                               |

Terminal states for polling are `complete`, `partial`, and `failed`. A newly created submission returns `queued`; it is non-terminal, so keep polling.

<Note>
  Assets are admitted to review a few at a time, so a submission reports `processing` as soon as **any** asset starts — even while its remaining assets are still waiting. It stays non-terminal until every asset has settled, so a submission can sit in `processing` with only part of its work actually running. Use `assets[].processingStatus` on the status response to see exactly where each asset is.
</Note>

## Creating a submission

POST `/api/submissions`

Each asset object needs a `blobPath`. The optional `previousSubmissionId` links an asset to a prior submission for versioning. The submission-level `sidekickIds` (a list of UUIDs) is **required** — at least one sidekick must run, or there is nothing to check; an empty or omitted list is rejected with `422`. Each asset may also carry its own `sidekickIds` (a subset of the submission-level set) to run only those sidekicks on that asset; an empty per-asset list inherits the full set. You may also pass a free-form `metadata` object, or an `externalReference` — a structured `{system, ref}` reference to the external work item the submission originates from, which (unlike `metadata`) is returned on the status endpoint and echoed on the `asset.processing.*` webhook so an integration can correlate the completion back (`system` is capped at 64 characters and `ref` must serialize to under 4 KB). To label assets or give the review extra material to work with, attach `tags` and `contextItems` — see [Tags and supporting context](#tags-and-supporting-context).

```json theme={null}
{
  "assets": [
    { "blobPath": "<workspace-id>/campaign-video.mp4" }
  ],
  "sidekickIds": ["<sidekick-id>"],
  "metadata": { "campaign": "spring-2026" }
}
```

Response `201 Created`:

```json theme={null}
{
  "submissionId": "550e8400-e29b-41d4-a716-446655440000",
  "workflowId": "asset-admission-trigger-550e8400-...",
  "ablyChannel": "submission:550e8400-...",
  "status": "queued",
  "assets": [
    { "assetId": "a1b2...", "versionGroupId": "g1...", "versionNumber": 1 }
  ]
}
```

The submission identifier is `submissionId`, not `id`.

## Tags and supporting context

Beyond the assets themselves, a submission can carry two optional, top-level kinds of extra input:

* **Tags** — key/value labels applied to assets (e.g. `Campaign: Summer`). Tags are referenced by `key`/`value`, **not** by id: the matching workspace tag is found-or-created automatically, so you never need to pre-create tags or look up their UUIDs.
* **Context items** — supporting **files** or **links** that give the compliance review more to work with (substantiation documents, brand guidelines, transcripts, legal copy, and so on). A context **file** is uploaded exactly like an asset (see the [File upload guide](/guides/file-upload)) and referenced by its `blobPath`; a context **link** carries an external `url` instead.

### applyTo

Every tag and context item has an `applyTo` field that selects which assets it attaches to:

* `"all"` (the default) — every asset in the submission.
* a list of asset **blobPaths** — only the listed assets.

### Example

```json theme={null}
{
  "assets": [
    { "blobPath": "<workspace-id>/campaign-video.mp4" }
  ],
  "tags": [
    { "key": "Campaign", "value": "Summer", "applyTo": "all" }
  ],
  "contextItems": [
    {
      "kind": "file",
      "contextType": "substantiation",
      "blobPath": "<workspace-id>/evidence.pdf",
      "name": "evidence.pdf",
      "mimeType": "application/pdf",
      "applyTo": "all"
    },
    {
      "kind": "link",
      "contextType": "legal",
      "url": "https://example.com/brand-guidelines",
      "linkName": "Brand guidelines",
      "applyTo": "all"
    }
  ]
}
```

`contextType` must be one of: `substantiation`, `product_info`, `brand_guidelines_evidence`, `legal`, `transcript_script`, `talent_licensing`, `brand_guidelines_publishing`, `platform_metadata`, `other`.

Tags are echoed back on each asset in the [status response](#checking-submission-status); both tags and context items are returned on the [asset result](/submissions/get-asset-result).

## Checking submission status

GET `/api/submissions/{submission_id}`

Returns the current state of the submission and all of its assets. There is no `/status` suffix.

`workflowId` is an internal processing reference and is frequently `null` — track submissions by `submissionId` and read per-asset progress from `assets[].processingStatus`.

```json theme={null}
{
  "submissionId": "550e8400-...",
  "workflowId": null,
  "workspaceId": "660e8400-...",
  "status": "processing",
  "progressPercent": 40,
  "createdAt": "2026-06-03T12:00:00Z",
  "startedAt": "2026-06-03T12:00:05Z",
  "completedAt": null,
  "errorMessage": null,
  "assets": [
    {
      "assetId": "a1b2...",
      "versionGroupId": "g1...",
      "versionNumber": 1,
      "issueCount": null,
      "errorMessage": null,
      "errorType": null,
      "isRetryable": false,
      "retryCount": 0,
      "workflowId": "asset-review-...",
      "startedAt": "2026-06-03T12:00:05Z",
      "completedAt": null
    }
  ],
  "isRetryable": false
}
```

Use `progressPercent` to track progress. A `404` is returned if the submission does not exist in the authenticated workspace. `startedAt` and `completedAt` bracket the work that actually ran: they are `null` while an asset is still `queued`, and the submission's `startedAt` is the earliest asset start (its `completedAt` is when the submission settled). Each asset object also carries a `tags` array (`[{ "key": "...", "value": "..." }]`) reflecting any tags applied at submit time, and a `workflowId` — the processing workflow identifier for that specific asset, useful as a support reference when reporting a stuck or failed asset.

## Polling recommendations

<Tip>
  Poll `GET /api/submissions/{submission_id}` every few seconds and back off over time. Stop polling once the top-level `status` is `complete`, `partial`, or `failed`.
</Tip>

For large-scale operations, prefer [webhooks](/concepts/webhooks) over tight polling — they are more efficient and deliver near real-time updates. For live, in-app progress, subscribe to the submission's channel instead — see the [Realtime updates guide](/guides/realtime-updates).

## Multiple assets per submission

You can submit multiple assets in a single submission. Batching assets into one submission is also the recommended way to stay within rate limits.

```json theme={null}
{
  "assets": [
    { "blobPath": "<workspace-id>/video1.mp4" },
    { "blobPath": "<workspace-id>/video2.mp4" },
    { "blobPath": "<workspace-id>/doc.pdf" }
  ],
  "sidekickIds": ["<sidekick-id>"],
  "metadata": {}
}
```

Each asset is processed independently and has its own per-asset status fields.

## Error handling

If an asset fails, its failure details appear as flat fields on the asset object in the status response — there is no nested `error` object.

```json theme={null}
{
  "submissionId": "550e8400-...",
  "status": "failed",
  "assets": [
    {
      "assetId": "a1b2...",
      "versionGroupId": "g1...",
      "versionNumber": 1,
      "issueCount": null,
      "errorMessage": "Source media could not be decoded.",
      "errorType": "unsupported_codec",
      "isRetryable": true,
      "retryCount": 0,
      "workflowId": "asset-review-...",
      "startedAt": "2026-06-03T12:00:05Z",
      "completedAt": "2026-06-03T12:00:41Z"
    }
  ],
  "isRetryable": true
}
```

On a failed asset, `completedAt` is when it stopped — the failure time, not a success time. Retrying re-stamps `startedAt` and clears `completedAt`, so the pair always describes the most recent attempt.

When `isRetryable` is `true`, you can retry the asset:

POST `/api/submissions/{submission_id}/assets/{asset_id}/retry`

This returns `202 Accepted` with the same shape as creating a submission. A `409` is returned if the asset is not in a failed state, the error is not retryable, the maximum attempts have been reached, or a retry is already in flight.

See [Error codes](/reference/error-codes) for the full list of status codes and error envelopes.

## Versioning and resubmission

To create a new version of an asset, create a new submission whose asset references the original via `previousSubmissionId`. Each version is tracked by `versionGroupId` and `versionNumber`. List all versions with:

GET `/api/submissions/{submission_id}/versions`

See [Assets](/concepts/assets#versioning) for details.
