# POST /v1/projects/:projectId/media (/docs/api/reference/media/upload-media-inline)



<Endpoint method="POST" path="/v1/projects/:projectId/media" scope="projects:write" phase="1" />

Uploads a file under 256KB as a single base64-encoded request, skipping the presign + finalize dance. Use it for logos, icons, and small reference images where the two-step upload is more ceremony than it's worth.

Returns `201 Created` with the same shape as [`POST /media/finalize`](/docs/api/reference/media/finalize-upload). For anything larger than 256KB, switch to [`POST /media/presign`](/docs/api/reference/media/presign-upload) — the request-size cap is hard.

<Parameters
  title="Path"
  rows="[
  { name: 'projectId', type: 'string (uuid)', required: true, description: 'Project ID.' },
]"
/>

<Parameters
  title="Body"
  rows="[
  { name: 'kind', type: 'string', required: true, description: 'Media class.', enum: ['image', 'video', 'audio', 'logo', 'reference_image'] },
  { name: 'dataBase64', type: 'string', required: true, description: 'File bytes, base64-encoded. Decoded size must be < 256KB.' },
  { name: 'mimeType', type: 'string', required: true, description: 'MIME type. `contentType` also accepted for backwards compat.' },
  { name: 'filename', type: 'string', description: 'Original filename (1-256 chars).' },
]"
/>

## Example request [#example-request]

<Tabs items="['curl', 'TypeScript', 'Python']">
  <Tab value="curl">
    ```bash
    curl -X POST https://api.layers.com/v1/projects/{projectId}/media \
      -H "X-Api-Key: $LAYERS_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "kind": "logo",
        "mimeType": "image/png",
        "dataBase64": "iVBORw0KGgoAAAANSUhEUgAA...",
        "filename": "acme-logo.png"
      }'
    ```
  </Tab>

  <Tab value="TypeScript">
    ```ts
    const res = await fetch(
      `https://api.layers.com/v1/projects/${projectId}/media`,
      {
        method: 'POST',
        headers: {
          'X-Api-Key': process.env.LAYERS_API_KEY!,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          kind: 'logo',
          mimeType: 'image/png',
          dataBase64: await fileToBase64(file),
          filename: file.name,
        }),
      },
    );
    const asset = await res.json();
    ```
  </Tab>

  <Tab value="Python">
    ```python
    import base64, os, httpx

    with open("acme-logo.png", "rb") as f:
        data_b64 = base64.b64encode(f.read()).decode()

    r = httpx.post(
        f"https://api.layers.com/v1/projects/{project_id}/media",
        headers={
            "X-Api-Key": os.environ["LAYERS_API_KEY"],
            "Content-Type": "application/json",
        },
        json={
            "kind": "logo",
            "mimeType": "image/png",
            "dataBase64": data_b64,
            "filename": "acme-logo.png",
        },
    )
    asset = r.json()
    ```
  </Tab>
</Tabs>

## Response [#response]

<Response status="201" description="Created">
  ```json
  {
    "mediaId": "med_01HXA4MNP5RSTUVWXYZABCDEFGH",
    "assetId": "med_01HXA4MNP5RSTUVWXYZABCDEFGH",
    "url": "https://media.layers.com/.../acme-logo.png",
    "kind": "logo",
    "contentType": "image/png",
    "mimeType": "image/png",
    "byteSize": 18472,
    "sizeBytes": 18472,
    "filename": "acme-logo.png",
    "createdAt": "2026-04-18T19:26:02Z"
  }
  ```
</Response>

## Notes [#notes]

* **`contentType` is accepted as an alias for `mimeType`.** Either works; `mimeType` is the canonical name in newer schemas.
* **Response aliases.** Returns both `mediaId`/`assetId`, both `contentType`/`mimeType`, and both `byteSize`/`sizeBytes` for compatibility. Inline uploads do not record a `sha256` checksum — use `/media/presign` + `/media/finalize` if you need to capture and verify a checksum.
* **Hard 256KB cap.** Decoded base64 must be under 256KB or the request is rejected with `413 PAYLOAD_TOO_LARGE`.

## Errors [#errors]

| Status | Code                | When                                                                                                       |
| ------ | ------------------- | ---------------------------------------------------------------------------------------------------------- |
| 401    | `UNAUTHENTICATED`   | Missing or invalid key.                                                                                    |
| 403    | `FORBIDDEN_SCOPE`   | Key lacks `projects:write`.                                                                                |
| 404    | `NOT_FOUND`         | Project does not exist.                                                                                    |
| 413    | `PAYLOAD_TOO_LARGE` | Decoded `dataBase64` exceeds 256KB. Use [`POST /media/presign`](/docs/api/reference/media/presign-upload). |
| 422    | `VALIDATION_FAILED` | Unknown `kind`, malformed base64, missing required field.                                                  |
| 429    | `RATE_LIMITED`      | Write budget exhausted.                                                                                    |

## See also [#see-also]

* [`POST /v1/projects/:projectId/media/presign`](/docs/api/reference/media/presign-upload) — upload files >256KB
* [`POST /v1/projects/:projectId/media/finalize`](/docs/api/reference/media/finalize-upload) — register a presigned upload
