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

# Create build

> Freeze the current draft into an immutable snapshot used for debug runs and publishing.

**`POST`** `https://api-datahub.octoparse.com/v1/data-apps/{app_id}/builds`

Authentication: API key required (`Authorization: Bearer <API Key>`). App author only; others get `404`.

Freeze the current workspace into an immutable snapshot — the object referenced by debug runs and publishing.

Preconditions: a workspace must exist and its current content must be valid (otherwise `422` with `issues`); the body may include `expected_revision` (`409` if the draft changed between save and freeze). If a ready snapshot with the same `content_hash` already exists, it is reused and no duplicate row is created. api-type builds become ready immediately; code-type returns `202` and queues an image build — submitting again while one is already in progress for the same app returns `409`. Deprecated apps can still build; only new Releases are rejected.

## Request

### Path parameters

<ParamField path="app_id" type="string" required>
  App reference.
</ParamField>

### Request body

<ParamField body="expected_revision" type="integer">
  Expected current draft revision. Mismatch returns `409`.
</ParamField>

### Example request

```bash theme={null}
curl -X POST \
  -H "Authorization: Bearer $OCTOPARSE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"expected_revision": 3}' \
  "https://api-datahub.octoparse.com/v1/data-apps/carol/reviews-query/builds"
```

## Response

### 200 success

```json theme={null}
{
  "data": {
    "build_id": "string",
    "build_status": "string",
    "content_hash": "string",
    "source_revision": 0,
    "reused": false,
    "created_at": "string"
  }
}
```

The payload is wrapped in `data`. Fields:

<ResponseField name="build_id" type="string" required>
  Build id.
</ResponseField>

<ResponseField name="build_status" type="string" required>
  `ready`, `building` (code-type), or `failed`.
</ResponseField>

<ResponseField name="content_hash" type="string" required>
  Content hash.
</ResponseField>

<ResponseField name="source_revision" type="integer" required>
  Draft revision at freeze time.
</ResponseField>

<ResponseField name="reused" type="boolean">
  Whether an existing same-content snapshot was reused.
</ResponseField>

<ResponseField name="created_at" type="string">
  Created time.
</ResponseField>

### Errors

| HTTP | `code`                | `category`      | Description                                                                       |
| ---- | --------------------- | --------------- | --------------------------------------------------------------------------------- |
| 401  | `unauthorized`        | `forbidden`     | Missing or invalid API key.                                                       |
| 404  | `workspace-not-found` | `not_found`     | This app has no in-progress draft. That is a normal main-path state, not a fault. |
| 422  | `build-failed`        | `invalid_input` | Workspace content failed validation; `issues[]` lists the problems.               |
| 409  | `revision-conflict`   | `invalid_input` | `expected_revision` does not match the current draft revision — concurrent edit.  |

Error responses use `{"error": {code, category, message, retryable}}`. See <a href="/docs/en/datahub/api/reference/introduction#errors">Errors</a>.

## Client libraries

The Python and JavaScript SDKs do not wrap this endpoint yet. Call REST directly.
