> ## 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.

# ワークスペース保存

> 可変下書き（ワークスペース）を保存。無効内容も保存可。検証結果付き。

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

認証：APIキー必須（`Authorization: Bearer <API Key>`）。App 作者のみ。他者は `404`。

App の可変下書きを保存。**検証失敗でも保存は拒否されない**: 完全検証は走り、結果（`valid` と `issues`）が返る。無効な下書きも保持され、直して再保存できる。

App 識別子は初回保存で暗黙作成 — 独立の「App 作成」エンドポイントはない。ハードゲートで `400` になるのは 3 つだけ: アカウントにユーザー名がある、ペイロードがサイズ内、`app_name` がまだ取られていない。

`expected_revision` は楽観ロック（`409 revision-conflict`）で、複数タブの静かな上書きを防ぐ。添付シークレットはこの保存が有効なときだけ格納。無効な保存で名前だけ残さない。

## リクエスト

### パスパラメータ

<ParamField path="app_id" type="string" required>
  App 参照: `app_<hex>` または `<namespace>/<app_name>`。 初回保存では後者を使う。
</ParamField>

### リクエスト body

<ParamField body="manifest" type="string" required>
  生マニフェストテキスト。
</ParamField>

<ParamField body="files" type="object">
  添付ファイル: キーは相対パス、値はテキスト。
</ParamField>

<ParamField body="secrets" type="object">
  シークレット名→平文。この保存が有効なときのみ格納。
</ParamField>

<ParamField body="expected_revision" type="integer">
  期待する現下書きリビジョン。不一致は `409`。
</ParamField>

### リクエスト例

```bash theme={null}
curl -X PUT \
  -H "Authorization: Bearer $OCTOPARSE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"manifest": "spec_version: \"0.2\"\n…", "files": {}, "expected_revision": 3}' \
  "https://api-datahub.octoparse.com/v1/data-apps/carol/reviews-query/workspace"
```

## レスポンス

### 200 成功

```json theme={null}
{
  "data": {
    "app_name": "string",
    "app_id": "string",
    "namespace": "string",
    "revision": 0,
    "valid": false,
    "issues": [
      {
        "path": "string",
        "message": "string"
      }
    ],
    "missing_secrets": [
      "string"
    ],
    "secrets_deferred": [
      "string"
    ],
    "name_reused_from": "string"
  }
}
```

ペイロードは `data` に包まれます。フィールド:

<ResponseField name="app_name" type="string" required>
  —
</ResponseField>

<ResponseField name="app_id" type="string" required>
  Stable app id.
</ResponseField>

<ResponseField name="namespace" type="string">
  —
</ResponseField>

<ResponseField name="revision" type="integer" required>
  保存後の下書きリビジョン。
</ResponseField>

<ResponseField name="valid" type="boolean" required>
  現内容が検証を通ったか。
</ResponseField>

<ResponseField name="issues" type="object[]">
  Issue list.

  <Expandable title="fields">
    <ResponseField name="path" type="string" required>
      —
    </ResponseField>

    <ResponseField name="message" type="string" required>
      —
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="missing_secrets" type="string[]">
  —
</ResponseField>

<ResponseField name="secrets_deferred" type="string[]">
  この保存が無効なため格納されなかったシークレット名。
</ResponseField>

<ResponseField name="name_reused_from" type="string">
  —
</ResponseField>

### エラー

| HTTP | `code`              | `category`      | Description                                       |
| ---- | ------------------- | --------------- | ------------------------------------------------- |
| 401  | `unauthorized`      | `forbidden`     | API キー欠落または無効。                                    |
| 400  | `username-required` | `invalid_input` | アカウントにまだユーザー名がなく `<username>/<app_name>` を構成できない。 |
| 409  | `revision-conflict` | `invalid_input` | `expected_revision` が現下書きリビジョンと一致しない — 同時編集。      |
| 400  | `invalid-app-name`  | `invalid_input` | Name fails naming rules or is a reserved word.    |
| 409  | `app-name-reserved` | `forbidden`     | 他者の履歴バインドが名前を保持（ユーザー名移管後 30 日凍結）。                 |
| 413  | `payload-too-large` | `invalid_input` | リクエスト body がサイズ上限超過。                              |

エラー応答は `{"error": {code, category, message, retryable}}` です。<a href="/docs/jp/datahub/api/reference/introduction#errors">エラー</a> を参照。

## クライアントライブラリ

Python / JavaScript SDK はまだこのエンドポイントをラップしていません。REST を直接呼び出してください。
