---
title: "Create a photo upload"
url: https://docs.light-ai.app/api-reference/mockups/create-photo-upload.md
canonical_url: https://docs.light-ai.app/api-reference/mockups/create-photo-upload
last_updated: 2026-10-13
summary: "POST /v1/projects/{project_id}/photo_uploads: A single-use slot for a front photo larger than a JSON body allows (up to 10 MB, JPEG, PNG or WebP): PUT the bytes to `upload_url` with their Content-Type within 2 hours, then name the upload in a generation (`source.type` `photo_upload`)."
api_status: beta
related:
  - https://docs.light-ai.app/api-reference/mockups
---

# Create a photo upload

> POST /v1/projects/{project_id}/photo_uploads: A single-use slot for a front photo larger than a JSON body allows (up to 10 MB, JPEG, PNG or WebP): PUT the bytes to `upload_url` with their Content-Type within 2 hours, then name the upload in a generation (`source.type` `photo_upload`).

`POST /v1/projects/{project_id}/photo_uploads`

A single-use slot for a front photo larger than a JSON body allows (up to 10 MB, JPEG, PNG or WebP): PUT the bytes to `upload_url` with their Content-Type within 2 hours, then name the upload in a generation (`source.type` `photo_upload`). At most 20 open slots and 100 slots a day per organization (429 `photo_uploads_daily`). Uploaded originals are working copies: deleted once used or a few hours after an unused slot expires; the generated mockups stay in the project. Test keys get a simulated slot (no upload URL).

|   |   |
| --- | --- |
| Scope | `mockups:write` |
| Credential | API key: `Authorization: Bearer lai_live_…` (or `lai_test_…`). Partner keys add `Light-AI-Organization`. |
| Test mode | Works with test keys against simulated test objects. |

## Path parameters

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `project_id` | string | yes | The project id (`prj_…`). Id prefix `prj_`. |

## Headers

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `Light-AI-Organization` | string |  | Partner keys: the connected organization to act for (`org_…`). Organization keys may omit it. Id prefix `org_`. |
| `Light-AI-Version` | string |  | Pin a dated API version (defaults to the key's version). One of: `2026-11-01`. |

## Response 201

The upload slot.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | Id prefix `upl_`. |
| `object` | string | yes | Always `photo_upload`. |
| `livemode` | boolean | yes | false for objects created with a test key. |
| `project_id` | string | yes | Id prefix `prj_`. |
| `upload_url` | string \| null | yes | PUT the photo bytes here (Content-Type image/jpeg, image/png or image/webp, at most 10 MB). null in test mode. |
| `method` | string | yes | Always `PUT`. |
| `max_bytes` | integer | yes |  |
| `content_types` | string[] | yes |  |
| `expires_at` | timestamp | yes | RFC 3339 timestamp (UTC, milliseconds). |

## Errors

| Status | Meaning |
| --- | --- |
| `400` | Bad request. [`bad_request`](https://docs.light-ai.app/errors#bad_request), [`test_mode_mismatch`](https://docs.light-ai.app/errors#test_mode_mismatch), [`test_mode_unsupported`](https://docs.light-ai.app/errors#test_mode_unsupported), [`unsupported_api_version`](https://docs.light-ai.app/errors#unsupported_api_version) |
| `401` | Missing or invalid credential. [`authentication_required`](https://docs.light-ai.app/errors#authentication_required), [`invalid_api_key`](https://docs.light-ai.app/errors#invalid_api_key), [`api_key_revoked`](https://docs.light-ai.app/errors#api_key_revoked), [`api_key_expired`](https://docs.light-ai.app/errors#api_key_expired), [`api_key_suspended`](https://docs.light-ai.app/errors#api_key_suspended) |
| `403` | Not allowed. [`insufficient_scope`](https://docs.light-ai.app/errors#insufficient_scope), [`plan_upgrade_required`](https://docs.light-ai.app/errors#plan_upgrade_required), [`organization_paused`](https://docs.light-ai.app/errors#organization_paused), [`organization_off_season`](https://docs.light-ai.app/errors#organization_off_season), [`api_disabled`](https://docs.light-ai.app/errors#api_disabled), [`api_access_not_enabled`](https://docs.light-ai.app/errors#api_access_not_enabled), [`ip_not_allowed`](https://docs.light-ai.app/errors#ip_not_allowed), [`forbidden`](https://docs.light-ai.app/errors#forbidden) |
| `404` | Not found (also for objects of other organizations and for organizations without an active grant). |
| `409` | Conflict. [`stale_state`](https://docs.light-ai.app/errors#stale_state), [`conflict`](https://docs.light-ai.app/errors#conflict), [`idempotency_in_progress`](https://docs.light-ai.app/errors#idempotency_in_progress), [`idempotency_scope_mismatch`](https://docs.light-ai.app/errors#idempotency_scope_mismatch), [`quote_already_decided`](https://docs.light-ai.app/errors#quote_already_decided), [`quote_not_current`](https://docs.light-ai.app/errors#quote_not_current), [`billing_review_required`](https://docs.light-ai.app/errors#billing_review_required) |
| `413` | Request bodies are limited to 256 KB. |
| `422` | Validation failed. [`idempotency_key_reused`](https://docs.light-ai.app/errors#idempotency_key_reused) |
| `429` | Rate limited or daily cap reached. See Retry-After and the RateLimit headers. [`rate_limited`](https://docs.light-ai.app/errors#rate_limited), [`daily_cap_reached`](https://docs.light-ai.app/errors#daily_cap_reached) |
| `500` | Unexpected server error. Safe to retry with exponential backoff; quote the Request-Id to support. [`internal_error`](https://docs.light-ai.app/errors#internal_error) |
| `503` | Temporarily unavailable. Retry with exponential backoff. [`service_unavailable`](https://docs.light-ai.app/errors#service_unavailable) |

## Examples

**Request**

curl:

```bash
curl -X POST "https://api.light-ai.app/v1/projects/{project_id}/photo_uploads" \
  -H "Authorization: Bearer $LIGHT_AI_API_KEY"
```

JavaScript:

```javascript
const response = await fetch('https://api.light-ai.app/v1/projects/{project_id}/photo_uploads', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.LIGHT_AI_API_KEY}`,
  },
});
const data = await response.json();
```

Python:

```python
import os, uuid, requests

response = requests.post(
    "https://api.light-ai.app/v1/projects/{project_id}/photo_uploads",
    headers={
        "Authorization": f"Bearer {os.environ['LIGHT_AI_API_KEY']}",
    },
    timeout=30,
)
data = response.json()
```

**Response 201**

```json
{
  "id": "upl_3f9c2a7be41d…",
  "object": "photo_upload",
  "livemode": false,
  "project_id": "prj_3f9c2a7be41d…",
  "upload_url": "https://example.com/…",
  "method": "PUT",
  "max_bytes": 0,
  "content_types": [
    "string"
  ],
  "expires_at": "2026-10-12T14:03:11.000Z"
}
```
