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

# Quickstart

> Run your first virtual try-on in under five minutes.

Two API calls and you're done: upload the customer photo, run the try-on. This guide shows the **recommended pattern** — pre-upload the customer image, ship the product inline. You don't need a separate "create product" step.

## Prerequisites

* An API key ( create account on [app.genlook.app](https://app.genlook.app) )
* At least 1 credit on your account
* A product image (URL or local file)
* A customer photo (JPEG, PNG, WebP, or HEIC, max 10 MB)

## Step 1 — Upload the customer photo

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.genlook.app/tryon/v1/images/upload" \
    -H "x-api-key: gk_your_api_key" \
    -F "file=@customer.jpg"
  ```

  ```python Python theme={null}
  import requests

  BASE = "https://api.genlook.app/tryon/v1"
  headers = {"x-api-key": "gk_your_api_key"}

  with open("customer.jpg", "rb") as f:
      r = requests.post(
          f"{BASE}/images/upload",
          headers=headers,
          files={"file": ("customer.jpg", f, "image/jpeg")},
      )
  r.raise_for_status()
  image_id = r.json()["imageId"]
  ```

  ```javascript JavaScript theme={null}
  const form = new FormData();
  form.append("file", customerFile);
  const { imageId } = await fetch("https://api.genlook.app/tryon/v1/images/upload", {
    method: "POST",
    headers: { "x-api-key": API_KEY },
    body: form,
  }).then((r) => r.json());
  ```
</CodeGroup>

Pass `crop=false` in the form data if you want to keep the original framing (default is a 4:5 person-aware crop).

You get back an `imageId`. Reuse it across as many try-ons as you want against the same photo.

## Step 2 — Run the try-on

Reference an existing product by `externalId`, or upsert one inline. The inline form is great for first-time products; the reference form is the cheap repeat-call shape.

<CodeGroup>
  ```bash cURL (inline product) theme={null}
  curl -X POST "https://api.genlook.app/tryon/v1/try-on" \
    -H "x-api-key: gk_your_api_key" \
    -H "Content-Type: application/json" \
    -d '{
      "products": [{
        "externalId": "shirt-42",
        "title": "Red tee",
        "description": "Soft cotton regular fit",
        "images": [{ "source": { "url": "https://cdn.example/red-tee.jpg" } }]
      }],
      "person": { "image": { "source": { "id": "<imageId-from-step-1>" } } }
    }'
  ```

  ```python Python theme={null}
  r = requests.post(
      f"{BASE}/try-on",
      headers={**headers, "Content-Type": "application/json"},
      json={
          "products": [{
              "externalId": "shirt-42",
              "title": "Red tee",
              "description": "Soft cotton regular fit",
              "images": [{"source": {"url": "https://cdn.example/red-tee.jpg"}}],
          }],
          "person": {"image": {"source": {"id": image_id}}},
      },
  )
  r.raise_for_status()
  body = r.json()
  generation_id = body["generationId"]
  print(body["productExternalId"])  # "shirt-42"
  ```

  ```javascript JavaScript theme={null}
  const { generationId } = await fetch("https://api.genlook.app/tryon/v1/try-on", {
    method: "POST",
    headers: { "x-api-key": API_KEY, "Content-Type": "application/json" },
    body: JSON.stringify({
      products: [{
        externalId: "shirt-42",
        title: "Red tee",
        description: "Soft cotton regular fit",
        images: [{ source: { url: "https://cdn.example/red-tee.jpg" } }],
      }],
      person: { image: { source: { id: imageId } } },
    }),
  }).then((r) => r.json());
  ```
</CodeGroup>

The next time you generate against `shirt-42`, just send `{ products: [{ externalId: "shirt-42" }], person: { image: { source: { id } } } }`. The server already has the product cached.

## Step 3 — Poll for the result

```python Python theme={null}
import time

while True:
    r = requests.get(f"{BASE}/generations/{generation_id}", headers=headers)
    data = r.json()
    if data["status"] == "COMPLETED":
        print("Result:", data["resultImageUrl"])
        break
    if data["status"] == "FAILED":
        raise RuntimeError(data.get("errorMessage"))
    time.sleep(2)
```

The `resultImageUrl` is a temporary URL — download or display it promptly. You can re-fetch the generation later for a fresh URL.

## What you skipped

You went straight from "I have an image" to "I have a try-on result" — no `POST /products` call, no sync loop, no per-product housekeeping. Inline upsert is the default path for a reason:

* **No separate "create product" step.** The inline `products[]` field in `/try-on` handles that. The dedicated `POST /products` endpoint is an opt-in alternative for catalog-management cases (see [Upsert Product](/tryon-api/endpoints/create-product)).
* **No catalog sync, no TTL anxiety.** Products created inline keep refreshing for 15 days from their last use — keep using them and they stay alive forever.
* **No image-bytes-on-every-call.** URLs are cached server-side; uploaded person photos live as `imageId` for reuse.

## What's next

* **TypeScript?** — the [`@genlook/api` SDK](/tryon-api/sdk) wraps all three steps in typed methods with automatic retries and a one-line poller, and the [Next.js example app](https://github.com/GenlookLabs/virtual-try-on-api-example) is a clonable working integration.
* **Recommended workflow** — see [Full Example](/tryon-api/full-example) for the ref-first / upsert-on-change pattern, error handling, and what to do when a product expires.
* **All endpoints** — see the API Reference (left nav).
* **Try-On options** — pre-uploaded vs URL vs multipart customer images, crop control, role hints, anonymous one-shots, all on [`POST /try-on`](/tryon-api/endpoints/create-try-on).
