# Server-keyed source endpoints

Eight operations on a server's snapshots — the same ones an application has, but keyed by the server identifier. This page covers only the differences: the full request and response contracts live on the pages of the app-scoped family.

Storage overview and the endpoint reference — [Source code storage](/docs/source-storage).

## Differences from the app-scoped family

This family mirrors the app-scoped one (`/v1/apps/:id/sources…`) in full, but it is keyed by a **server** identifier rather than an application one. The request and response contracts are the same as for the app-scoped operations (the same body format, the same headers, the same error codes) — only the path prefix changes.

**Authorization.** Access to server versions is granted to:

- The current managing key of the server.
- The personal key of an application bound to this server, until the server is deleted.
- A Bitrix24 account administrator (ADMIN) — for another user's server.
- A member of the server development team: they may list, download, save and tag versions, but not delete a version or clean up versions.
- The Cowork desktop key — read-only: the version list, metadata and the download link. The key has access while an application owned by the key owner is bound to the server and the server has not been deleted.

Other personal keys of the server owner receive `404`, even when the owner is a Bitrix24 account administrator. This includes a third-party agent key on the Cowork subscription: the exception applies only to the desktop key. No key with the Cowork scope can change versions: saving, tagging, editing, deleting and cleanup return `403 INFRA_FORBIDDEN_FOR_COWORK_KEY`. An OAuth application key is admitted only to a server it owns itself. No explicit scope is required: there is no route-level scope check.

| Method | Path | Action |
|-------|------|--------|
| `POST` | `/v1/infra/servers/:id/sources` | Save a raw-bytes snapshot |
| `GET` | `/v1/infra/servers/:id/sources` | List versions (+ `?sha256=` probe) |
| `GET` | `/v1/infra/servers/:id/sources/:versionId` | Single-version metadata |
| `GET` | `/v1/infra/servers/:id/sources/:versionId/download` | Signed download link |
| `POST` | `/v1/infra/servers/:id/sources/:versionId/tag` | Add or remove a tag |
| `PATCH` | `/v1/infra/servers/:id/sources/:versionId` | Update tags / note |
| `DELETE` | `/v1/infra/servers/:id/sources/:versionId` | Delete a version — soft delete |
| `POST` | `/v1/infra/servers/:id/sources/cleanup` | Bulk cleanup |

- **`POST …/sources`** — the same contract as `POST /v1/apps/:id/sources`: the raw archive bytes in the body, `Content-Type` one of `application/gzip`, `application/x-tar`, `application/zip`, `application/octet-stream`, optional `X-Filename` / `X-Tags` / `X-Note` / `X-AI-Session-Id` headers, a required `Content-Length` (without it — `411 MISSING_CONTENT_LENGTH`), a 500 MB body cap, deduplication by `sha256` within this server.
- **`GET …/sources`** — the server's version list. The optional `?sha256=<64-hex>` parameter is a lightweight probe for whether an archive with the given contents already exists.
- **`GET …/sources/:versionId`** — single-version metadata (the same shape as a `versions[]` item).
- **`GET …/sources/:versionId/download`** — a signed link to the archive; the requested lifetime is 30 minutes, while the actual signature expiry, returned in `expiresAt`, may be shorter. The response also echoes `versionId` (unlike the app-scoped endpoint) — keep it and send it back as `baseVersionId` on the deploy that ships your changes: if a teammate has deployed in the meantime, the platform refuses the deploy instead of overwriting their code. On a server with a development team, `baseVersionId` is required.
- **`POST …/sources/:versionId/tag`** — body `{ "tag": "manual" | "published", "action": "add" | "remove" }`.
- **`PATCH …/sources/:versionId`** — updates the tags and/or note (three-state behavior: a string overwrites, `null` clears, an absent field leaves it unchanged).
- **`DELETE …/sources/:versionId`** — soft delete. A version with the `manual` or `published` tag is protected — the request returns `409 PROTECTED_BY_TAG`. First remove the tag via `PATCH`.
- **`POST …/sources/cleanup`** — bulk cleanup: keeps the `keepLatest` most recent versions plus versions with the `manual` / `published` tags.

**Deleted server.** Reading and cleanup stay available after `DELETE /v1/infra/servers/:id`: the version list, metadata, download, `tag`, `PATCH`, `DELETE`, and `cleanup`. Saving a new version with `POST …/sources` on a deleted server returns `404 SERVER_NOT_FOUND`. After the server is deleted, the key of a bound application and the Cowork desktop key lose access to its versions and receive `404`. How to find such a server and remove its versions — [Versions of a deleted server](/docs/source-storage/retention#versions-of-a-deleted-server).

Every version in this family's responses carries a `serverContext` field — the server-level display context, described in [the version list fields](/docs/source-storage/versions#response-fields).

## See also

- [Source code storage](/docs/source-storage)
- [Save a snapshot](/docs/source-storage/save)
- [List versions](/docs/source-storage/versions)
- [Version tags and notes](/docs/source-storage/metadata)
- [Deleting versions](/docs/source-storage/delete)
- [Version lifetime and cleanup](/docs/source-storage/retention)
