# Import an immutable source bundle

Import up to 10,000 UTF-8 or base64 files, 8 MiB per decoded file and 32 MiB total, with a 64 MiB JSON request limit. Paths must be relative, unique and cannot contain traversal or .git, .context or .hoplite directories. Symlinks are unsupported. Set executable to preserve executable files. Equivalent files in the same app return the same immutable source. No repository is required.

`POST /api/platform/v1/apps/{appId}/sources`

Required permission: `project:update`.

## Parameters

| Name | In | Type | Required | Description |

| --- | --- | --- | --- | --- |

| `appId` | path | `string` | Yes | — |

| `Idempotency-Key` | header | `string` | No | Required for service credentials. Retry the same request with the same key. |

## Request body

| Field | Type | Required | Constraints | Description |
| --- | --- | --- | --- | --- |
| `files` | `object[]` | Yes | max 10000 items | — |
| `files.path` | `string` | Yes | min length 1, max length 1024 | — |
| `files.content` | `string` | Yes | max length 11184812 | — |
| `files.encoding` | `string` | No | utf8 · base64 | — |
| `files.executable` | `boolean` | No | — | — |

```json
{
  "files": [
    {
      "path": "index.html",
      "content": "<h1>Hello</h1>"
    }
  ]
}
```

## Example request

```bash
curl --request POST \
  --url https://api.hoplite.sh/api/platform/v1/apps/appId/sources \
  --header "X-Api-Key: $HOPLITE_API_KEY" \
  --header "Idempotency-Key: $HOPLITE_OPERATION_ID" \
  --header 'Content-Type: application/json' \
  --data '{
  "files": [
    {
      "path": "index.html",
      "content": "<h1>Hello</h1>"
    }
  ]
}'
```

## 200 response

Success

Content type: `application/json`. Response type: `object`.

| Field | Type | Required | Constraints | Description |
| --- | --- | --- | --- | --- |
| `ok` | `boolean` | Yes | true | — |
| `source` | `object` | Yes | — | — |
| `source.id` | `string` | Yes | — | — |
| `source.projectId` | `string` | Yes | — | — |
| `source.sha256` | `string` | Yes | — | — |
| `source.byteSize` | `integer` | Yes | min -9007199254740991, max 9007199254740991 | — |
| `source.fileCount` | `integer` | Yes | min -9007199254740991, max 9007199254740991 | — |
| `source.createdAt` | `string<date-time>` | Yes | — | — |

```json

```

## Status codes

- `200` — Success

- `400` — Invalid request

- `401` — Authentication required

- `403` — Permission, app scope or entitlement denied

- `404` — App, source or thread not found

- `409` — Idempotency conflict or request in progress

- `413` — Request too large

- `503` — Source storage or execution unavailable