# Download a submission

```http
GET /api/public/v1/competitions/lean-kernel-challenge/submissions/{submissionId}/download
```

**Scope**: `competition.read`.

Downloads the exact immutable `Submission.lean` bytes for one current-visible formal candidate. An individual entrant may download their own candidate. Any active member of a team entrant may download a candidate currently owned by that team.

Use an ID returned by [List my submissions](./list-my-submissions.md). Replaced, withdrawn, or invalidated submissions are no longer available through this endpoint.

The endpoint remains readable for current-visible candidates after the competition closes. It does not expose formal evaluation inputs, evaluator output, auxiliary Playground files, or any source other than the stored root `Submission.lean`.

## Path parameter

| Parameter | Type, requirement, and meaning |
| :--- | :--- |
| `submissionId` | **Required positive integer string.** Stable ID returned by [Submit a Lean file](./submit-package.md) or [List my submissions](./list-my-submissions.md). |

## Example request

```bash
export SAIR_API_BASE="https://api.sair.foundation/api/public/v1"
export SAIR_SUBMISSION_ID="42"

curl \
  "$SAIR_API_BASE/competitions/lean-kernel-challenge/submissions/$SAIR_SUBMISSION_ID/download" \
  -H "Authorization: Bearer $SAIR_API_KEY" \
  --output "lean-kernel-submission-$SAIR_SUBMISSION_ID.lean"
```

## Successful response

This endpoint returns file bytes directly, not the standard JSON success envelope.

```http
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Disposition: attachment; filename="lean-kernel-submission-42.lean"
Content-Length: 1842
X-Content-Type-Options: nosniff
```

| Header | Meaning |
| :--- | :--- |
| `Content-Type` | Always `text/plain; charset=utf-8` for the current one-file formal source contract. |
| `Content-Disposition` | Supplies a deterministic filename composed of `lean-kernel-submission-`, the submission ID, and `.lean`. |
| `Content-Length` | Exact number of downloaded source bytes. |
| `X-Content-Type-Options` | `nosniff` prevents content-type guessing. |

`Content-Length` above is illustrative. The response body is the exact stored `Submission.lean`; it is not normalized, reformatted, or wrapped in a ZIP archive.

## Verify the snapshot

[List my submissions](./list-my-submissions.md) returns `rawUploadSha256` for every current-visible candidate. Compute SHA-256 over the downloaded file bytes and compare the lowercase hexadecimal digest with that value:

```bash
shasum -a 256 "lean-kernel-submission-$SAIR_SUBMISSION_ID.lean"
```

Use `rawUploadSha256` for this check. `canonicalSourceTreeSha256` includes the canonical path-and-content framing, while `manifestSha256` identifies the canonical manifest; neither is the plain file digest.

The service verifies the stored snapshot and artifact before returning any bytes. If those records do not match, the download fails closed with `409 FORMAL_ARTIFACT_INTEGRITY_FAILED`.

## Visibility and retries

- The submission must belong to the caller's current effective entrant and remain current-visible.
- Team owner and member roles have the same download access while their membership is active.
- A caller who leaves a team immediately loses access to that team's candidates. Joining a team does not transfer or restore earlier individual submissions.
- Nonexistent, unowned, replaced, withdrawn, and otherwise non-visible submissions all return the same `404 NOT_FOUND`, so the endpoint does not reveal whether another entrant's ID exists.

Retry a transient `502` with bounded backoff. Do not retry `400`, `404`, or `409` without first changing the underlying condition. A `409 FORMAL_ARTIFACT_INTEGRITY_FAILED` is a server-side integrity incident, not an invalid participant source.

## Errors

- `400 INVALID_ID` — `submissionId` is not a positive integer.
- `404 NOT_FOUND` — The competition is not public, the current entrant has no access to this submission, the candidate is no longer participant-visible, or the artifact does not exist.
- `409 FORMAL_ARTIFACT_INTEGRITY_FAILED` — The immutable snapshot or stored artifact failed integrity verification; no source bytes are returned.
- `502 PLATFORM_UNAVAILABLE` — Competition visibility or the current entrant could not be verified.
- `502 PLATFORM_RESPONSE_INVALID` — The platform returned an invalid participant context.
- `502 SERVICE_UNAVAILABLE` — The authoritative formal-submission service is temporarily unavailable.
- `502 SERVICE_RESPONSE_INVALID` — The formal service returned an empty, oversized, or non-`Submission.lean` artifact response.

See [Errors](../../../errors.md) for shared authentication, scope, and account-rate-limit errors.
