## Reserve a file upload to Drive

`POST /v1/disk/uploads/reserve`

Registers an upcoming file upload to Bitrix24 Drive. This is the first step of a write: the file content is sent in the next call, by operation ID.

The body is flat, with no `fields` wrapper.

## Request fields (body)

| Field | Type | Required | Description |
|------|-----|:-----:|----------|
| `reserveRequestId` | string | yes | Your reserve request ID, 1 to 512 characters. A repeat with the same value and the same body returns the operation already created |
| `intent` | array | yes | Upload intent — an array of exactly eight elements in the order of the rows below. An extra, missing or reordered element returns `400 INVALID_PARAMS` |
| `intent[0]` | number | yes | Format version, always `1` |
| `intent[1]` | string | yes | Mode: `create` — a new file in a folder, `version` — a new version of an existing file |
| `intent[2]` | string | yes | With `create` — the ID of the folder the file goes into. List: [`GET /v1/folders`](/docs/entities/folders/list), the storage root folder is the `rootFolderId` field in [`GET /v1/storages/:id`](/docs/entities/storages/get). With `version` — the file ID: `objectId` from [`POST /v1/disk/search`](/docs/disk/search) or `fileId` from the response of a previous upload. A decimal number as a string, without leading zeros |
| `intent[3]` | string | yes | SHA-256 of the file's source bytes — 64 lowercase characters |
| `intent[4]` | string | yes | File size in bytes, a decimal number as a string. At most `26214400` (25 MiB) |
| `intent[5]` | null \| array | yes | With `create` — `null`. With `version` — the expected current state of the file: an array of three strings `[contentFileId, contentGeneration, size]`. Values come from the file card in [`POST /v1/disk/search`](/docs/disk/search) or from the response of a previous upload |
| `intent[6]` | string | yes | File name on Drive. Not empty and not whitespace-only, with no control characters and none of `<` `>` `:` `"` `\|` `?` `*` `\` `/` |
| `intent[7]` | string | yes | Write policy: `createFail` for `create` mode, `versionPreflightNoCAS` for `version` mode. Any other mode and policy pair returns `400 INVALID_PARAMS` |
| `lineage` | object | yes | Link between the operation and previous ones |
| `lineage.kind` | string | yes | `initial` — the first upload with this `intent`. `successor` — the next file version after a successful operation: the previous operation is in the `application: "applied"` state, the new `intent` is in `version` mode, and `intent[2]` equals the `fileId` of the previous operation. `retry` — a repeat of the same `intent` after an operation that ended with `application: "not_applied"` |
| `lineage.priorOperationId` | string | no | Required with `kind: "successor"` and `kind: "retry"`. The `operationId` of the previous operation. List of operations: [`GET /v1/disk/uploads`](/docs/disk/uploads-list). Not sent with `kind: "initial"` |

The body accepts no other fields: an unknown field returns `400 INVALID_PARAMS`.

## Examples

### curl — personal key

```bash
curl -X POST https://vibecode.bitrix24.com/v1/disk/uploads/reserve \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "reserveRequestId": "docs-check-reserve-2",
    "intent": [1, "create", "1", "6be5cdb4cdbf136301c74adf2fd5a380fe5ceda1e998ce4b62f01cf7d20589a5", "29", null, "vibe-disk-docs-check-2.txt", "createFail"],
    "lineage": { "kind": "initial" }
  }'
```

### curl — OAuth application

```bash
curl -X POST https://vibecode.bitrix24.com/v1/disk/uploads/reserve \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "reserveRequestId": "docs-check-reserve-2",
    "intent": [1, "create", "1", "6be5cdb4cdbf136301c74adf2fd5a380fe5ceda1e998ce4b62f01cf7d20589a5", "29", null, "vibe-disk-docs-check-2.txt", "createFail"],
    "lineage": { "kind": "initial" }
  }'
```

### JavaScript — personal key

```javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/disk/uploads/reserve', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    reserveRequestId: 'docs-check-reserve-2',
    intent: [1, 'create', '1', '6be5cdb4cdbf136301c74adf2fd5a380fe5ceda1e998ce4b62f01cf7d20589a5', '29', null, 'vibe-disk-docs-check-2.txt', 'createFail'],
    lineage: { kind: 'initial' },
  }),
})

const { success, data, error } = await res.json()
if (!success) throw new Error(error.code)
// Store operationId and intentHash before uploading the content
console.log(data.operationId, data.intentHash)
```

### JavaScript — OAuth application

```javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/disk/uploads/reserve', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    reserveRequestId: 'docs-check-reserve-2',
    intent: [1, 'create', '1', '6be5cdb4cdbf136301c74adf2fd5a380fe5ceda1e998ce4b62f01cf7d20589a5', '29', null, 'vibe-disk-docs-check-2.txt', 'createFail'],
    lineage: { kind: 'initial' },
  }),
})

const { success, data, error } = await res.json()
```

## Response fields

| Field | Type | Description |
|------|-----|----------|
| `success` | boolean | `true` on a successful reserve |
| `data.operationId` | string | Operation ID. Passed to [`POST /v1/disk/uploads`](/docs/disk/uploads) and [`GET /v1/disk/uploads/:operationId`](/docs/disk/uploads-get) |
| `data.phase` | string | Operation stage: `prepared` — reserved, `submitting` — the write has been sent to Bitrix24, `settled` — finished |
| `data.application` | string | Write result: `applied` — the file is written, `not_applied` — no write was performed, `unknown` — the result is unknown |
| `data.verification` | string | Check against the current state of the file: `matched` — the file matches what was written, `superseded` — the file has changed since, `unavailable` — the check was not performed |
| `data.intentHash` | string | SHA-256 of `intent` serialized as compact JSON in UTF-8. Passed to [`POST /v1/disk/uploads`](/docs/disk/uploads) |
| `data.reservationExpiresAt` | string | Date and time in ISO 8601 until which the operation accepts content. Set to 30 days after the operation is created |
| `data.recovered` | boolean | `true` — an operation created earlier was returned, `false` — a new one was created |

## Response example

New reserve:

```json
{
  "success": true,
  "data": {
    "operationId": "xykPPgOlNBiKMTlAV3SfBoGJhG6Hi6AX57Ede4rAFoU",
    "phase": "prepared",
    "application": "not_applied",
    "verification": "unavailable",
    "intentHash": "3e10ae1152a0e4b4e89d3b312bfacafc1f3e0cf22d8a5eae3e231fa8bc274498",
    "reservationExpiresAt": "2026-11-22T10:10:36.591Z",
    "recovered": false
  }
}
```

A repeat of the same request after the file is uploaded returns the same operation in its current state:

```json
{
  "success": true,
  "data": {
    "operationId": "xykPPgOlNBiKMTlAV3SfBoGJhG6Hi6AX57Ede4rAFoU",
    "phase": "settled",
    "application": "applied",
    "verification": "matched",
    "intentHash": "3e10ae1152a0e4b4e89d3b312bfacafc1f3e0cf22d8a5eae3e231fa8bc274498",
    "reservationExpiresAt": "2026-11-22T10:10:36.591Z",
    "recovered": true
  }
}
```

## Error response example

409 — the same `reserveRequestId` arrived with a different `intent`:

```json
{
  "success": false,
  "error": {
    "code": "DISK_OPERATION_CONFLICT",
    "message": "Disk request unavailable",
    "retrySafe": false
  }
}
```

## Errors

| HTTP | Code | Description |
|------|-----|----------|
| 400 | `INVALID_PARAMS` | The body failed validation: wrong `intent` composition, invalid file name, `lineage` without `priorOperationId` for `successor` or `retry`, an unknown field |
| 401 | `MISSING_API_KEY` | The `X-Api-Key` header is missing |
| 401 | `INVALID_API_KEY` | The key is not recognized |
| 401 | `TOKEN_MISSING` | The key has no connection to Bitrix24 |
| 401 | `DISK_AUTH_UNAVAILABLE` | The Bitrix24 user could not be confirmed: an application key was sent without `Authorization: Bearer`, the session does not belong to the key's application, or the key is not supported by the Drive agent |
| 401 | `TOKEN_REFRESH_FAILED` | The portal authorization for the key could not be refreshed. A retry does not help until the key is authorized again |
| 403 | `SCOPE_DENIED` | The key has no `disk` scope |
| 403 | `WRITE_BLOCKED_READONLY` | The key is in read-only mode |
| 403 | `DISK_ACCESS_DENIED` | Bitrix24 denied access while checking the user |
| 409 | `DISK_OPERATION_CONFLICT` | `reserveRequestId` was already used with a different `intent` or `lineage`. Or the `priorOperationId` operation already has a continuation with a different `intent` or a different `lineage.kind` |
| 409 | `DISK_PRIOR_REQUIRED` | The `priorOperationId` operation was not found or has not finished yet, or the `lineage.kind` condition is not met: for `successor` — the previous record is not `applied`, the mode of the new `intent` is not `version`, or `intent[2]` does not equal its `fileId`; for `retry` — the previous record is not `not_applied` or its `intent` differs |
| 409 | `DISK_OUTCOME_UNKNOWN` | The result of the `priorOperationId` operation is unknown: `application` equals `unknown`. Continuations and retries from it are not accepted |
| 409 | `DISK_GENERATION_STALE` | The key's connection to Bitrix24 changed during the request. Repeat the reserve |
| 413 | `DISK_UPLOAD_TOO_LARGE` | The `intent[4]` size exceeds `26214400` bytes |
| 502 | `DISK_READ_UNAVAILABLE` | Bitrix24 returned an error that Vibecode did not recognize |
| 503 | `QUERY_LIMIT_EXCEEDED` | Bitrix24 limited the request rate. Retry later |
| 503 | `BITRIX_TIMEOUT` | Bitrix24 did not respond in time |
| 503 | `QUEUE_OVERFLOW`, `QUEUE_TIMEOUT` | The portal call queue is full. The `Retry-After` header gives the pause before a retry |
| 503 | `RATE_LIMITED` | The portal responded with HTTP 429. The `Retry-After` header gives the pause before a retry |
| 503 | `ERROR_LOOP_DETECTED` | Repeated errors of this key on this method have temporarily stopped calls. The `Retry-After` header gives the pause |
| 503 | `user_self_deletion_pending` | The key owner requested deletion of their account — Drive calls are stopped |

Full list of common API errors — [Errors](/docs/errors).

## Known specifics

- **A reserve sends no bytes and does not access the folder or file.** Folder existence, write permissions and name availability are checked only when the content is uploaded in [`POST /v1/disk/uploads`](/docs/disk/uploads).
- **The same `intent` with `lineage.kind: "initial"` yields one operation.** A new `reserveRequestId` with the same `intent` returns the existing operation with `recovered: true`, including a finished one.
- **An operation has one continuation.** Only one `successor` or `retry` continuation is accepted from a previous operation. A second request with the same `intent` and `lineage.kind` returns the continuation already created.
- **Operations are bound to the key's context.** Reserve, upload and operation reads work within one Bitrix24 account, one Bitrix24 user and one authorization source: the personal key's inbound webhook or the OAuth application. An operation is not visible from another context.

## See also

- [Upload file content to Drive](/docs/disk/uploads)
- [Get an upload operation](/docs/disk/uploads-get)
- [List upload operations](/docs/disk/uploads-list)
- [Search Drive](/docs/disk/search)
- [Get Drive capabilities](/docs/disk/capabilities)
- [Errors](/docs/errors)
