Przykłady

Kompletne przykłady integracji z użyciem curl, JavaScript i Python.

Ta strona przedstawia kompletny przepływ pracy generowania sesji zdjęciowej w trzech językach: curl, JavaScript (fetch) i Python (requests). Każdy przykład realizuje te same kroki:

  1. Wylistuj produkty, aby wybrać jeden do sesji.
  2. Wylistuj zatwierdzone modele AI i scenerie.
  3. Zarejestruj generowanie sesji zdjęciowej, łącząc produkt, model i scenerię.
  4. Odpytuj endpoint image-picker, aż wyniki będą gotowe.
  5. Przejrzyj i zatwierdź wygenerowane zdjęcia.

Wspólna konfiguracja

Każde zapytanie wymaga dwóch rzeczy:

  • Bazowy URLhttps://app.qamera.ai (zastąp własnym URL w przypadku wdrożeń self-hosted).
  • Uwierzytelnianie — Przekaż API key w nagłówku X-Api-Key.
X-Api-Key: mk_live_abc123.secret456

API key możesz wygenerować w Ustawienia > API Keys w zespołowym workspace. Klucz jest wyświetlany tylko raz, więc skopiuj go natychmiast.

Endpointy GET są bezpłatne i nigdy nie zużywają kredytów. Endpointy POST uruchamiające generowanie treści rezerwują kredyty z góry i zużywają je po zakończeniu przetwarzania.


Przykłady curl

Krok 1 — Lista produktów

Pobierz wszystkie produkty przesłane na Twoje konto.

curl -s -X GET "https://app.qamera.ai/api/external/products" \
  -H "X-Api-Key: mk_live_abc123.secret456"

Odpowiedź:

{
  "products": [
    {
      "id": "rec_product_001",
      "name": "White T-Shirt Front",
      "thumbnail": "https://cdn.example.com/products/thumb_001.jpg",
      "status": "UPLOADED",
      "createdAt": "2026-03-20T14:30:00.000Z"
    },
    {
      "id": "rec_product_002",
      "name": "Leather Handbag",
      "thumbnail": "https://cdn.example.com/products/thumb_002.jpg",
      "status": "UPLOADED",
      "createdAt": "2026-03-21T09:15:00.000Z"
    }
  ],
  "count": 2
}

Wybierz ID produktu do użycia w sesji zdjęciowej. W tych przykładach użyjemy rec_product_001.

Krok 2 — Lista zatwierdzonych modeli

Pobierz tylko modele, które zostały zatwierdzone do użycia.

curl -s -X GET "https://app.qamera.ai/api/external/models?statusFilter=approved" \
  -H "X-Api-Key: mk_live_abc123.secret456"

Odpowiedź:

{
  "models": [
    {
      "id": "rec_model_101",
      "name": "Sophie — Casual",
      "thumbnail": "https://cdn.example.com/models/thumb_101.jpg",
      "status": "DONE",
      "voting": "APPROVED",
      "createdAt": "2026-03-18T10:00:00.000Z"
    }
  ],
  "count": 1
}

Krok 3 — Lista zatwierdzonych scenerii

curl -s -X GET "https://app.qamera.ai/api/external/sceneries?statusFilter=approved" \
  -H "X-Api-Key: mk_live_abc123.secret456"

Odpowiedź:

{
  "sceneries": [
    {
      "id": "rec_scenery_201",
      "name": "Bright Studio",
      "thumbnail": "https://cdn.example.com/sceneries/thumb_201.jpg",
      "status": "DONE",
      "voting": "APPROVED",
      "createdAt": "2026-03-19T11:45:00.000Z"
    }
  ],
  "count": 1
}

Krok 4 — Rejestracja sesji zdjęciowej

Połącz produkt, model i scenerię, aby rozpocząć generowanie. Ten endpoint rezerwuje kredyty.

curl -s -X POST "https://app.qamera.ai/api/external/image-picker/register-ideas" \
  -H "X-Api-Key: mk_live_abc123.secret456" \
  -H "Content-Type: application/json" \
  -d '{
    "config": {
      "product": { "id": "rec_product_001" },
      "model": { "id": "rec_model_101" },
      "scenery": { "id": "rec_scenery_201" },
      "industry": "fashion",
      "suggestions": "bright lighting, clean composition"
    },
    "count": 4
  }'

Odpowiedź:

{
  "createdRecordIds": ["rec_gen_301", "rec_gen_302", "rec_gen_303", "rec_gen_304"],
  "batchId": "batch_abc123",
  "orderId": "ord_xyz789",
  "reservationId": "res_def456",
  "creditsReserved": 120
}

Zapisz createdRecordIds — użyjesz ich do sprawdzania postępu.

Krok 5 — Odpytywanie o wyniki

Generowanie jest asynchroniczne. Odpytuj endpoint image-picker, filtrując po produkcie, aż zdjęcia osiągną status DONE.

curl -s -X GET "https://app.qamera.ai/api/external/image-picker?productId=rec_product_001" \
  -H "X-Api-Key: mk_live_abc123.secret456"

Odpowiedź (w trakcie przetwarzania):

{
  "images": [
    {
      "id": "rec_gen_301",
      "name": "White T-Shirt — Variant 1",
      "thumbnail": "",
      "status": "INPROGRESS",
      "voting": null,
      "createdAt": "2026-03-27T12:00:00.000Z"
    }
  ],
  "count": 1
}

Odczekaj minutę i wywołaj ten sam endpoint ponownie. Gdy status zmieni się na DONE, pole thumbnail będzie zawierało URL zdjęcia.

Krok 6 — Zatwierdzanie lub odrzucanie wyników

Możesz zatwierdzać zdjęcia za pomocą endpointu aktualizacji packshotu. Zdjęcia z sesji, które chcesz zachować, zatwierdź, aby pojawiały się w filtrowanych widokach.

# Zatwierdź zdjęcie
curl -s -X POST "https://app.qamera.ai/api/external/packshots/update-packshot" \
  -H "X-Api-Key: mk_live_abc123.secret456" \
  -H "Content-Type: application/json" \
  -d '{
    "recordId": "rec_gen_301",
    "Voting": "APPROVED"
  }'

Odpowiedź:

{
  "success": true,
  "message": "Packshot voting updated successfully",
  "recordId": "rec_gen_301"
}

Aby odrzucić zdjęcie (co je archiwizuje):

curl -s -X POST "https://app.qamera.ai/api/external/packshots/update-packshot" \
  -H "X-Api-Key: mk_live_abc123.secret456" \
  -H "Content-Type: application/json" \
  -d '{
    "recordId": "rec_gen_302",
    "Voting": "REJECTED"
  }'

Przykład JavaScript (fetch)

Poniższy przykład wykorzystuje API fetch dostępne w przeglądarce/Node.js do realizacji tego samego przepływu pracy.

Funkcja pomocnicza

const BASE_URL = "https://app.qamera.ai";
const API_KEY = "mk_live_abc123.secret456";

async function qameraFetch(path, options = {}) {
  const url = new URL(path, BASE_URL);

  if (options.params) {
    Object.entries(options.params).forEach(([key, value]) =>
      url.searchParams.set(key, value)
    );
  }

  const response = await fetch(url.toString(), {
    method: options.method || "GET",
    headers: {
      "X-Api-Key": API_KEY,
      ...(options.body ? { "Content-Type": "application/json" } : {}),
    },
    ...(options.body ? { body: JSON.stringify(options.body) } : {}),
  });

  if (!response.ok) {
    const error = await response.json().catch(() => ({}));
    throw new Error(
      `API error ${response.status}: ${error.error || response.statusText}`
    );
  }

  return response.json();
}

Pełny przepływ pracy

async function runPhotoShootWorkflow() {
  // 1. Lista produktów
  const { products } = await qameraFetch("/api/external/products");
  console.log(`Found ${products.length} products`);

  const product = products[0];
  if (!product) throw new Error("No products found. Upload one first.");
  console.log(`Using product: ${product.name} (${product.id})`);

  // 2. Lista zatwierdzonych modeli
  const { models } = await qameraFetch("/api/external/models", {
    params: { statusFilter: "approved" },
  });
  console.log(`Found ${models.length} approved models`);

  // 3. Lista zatwierdzonych scenerii
  const { sceneries } = await qameraFetch("/api/external/sceneries", {
    params: { statusFilter: "approved" },
  });
  console.log(`Found ${sceneries.length} approved sceneries`);

  // 4. Rejestracja generowania sesji zdjęciowej
  const registration = await qameraFetch(
    "/api/external/image-picker/register-ideas",
    {
      method: "POST",
      body: {
        config: {
          product: { id: product.id },
          model: models[0] ? { id: models[0].id } : undefined,
          scenery: sceneries[0] ? { id: sceneries[0].id } : undefined,
          industry: "fashion",
          suggestions: "bright lighting, clean composition",
        },
        count: 4,
      },
    }
  );

  console.log(`Generation started — order ${registration.orderId}`);
  console.log(`Credits reserved: ${registration.creditsReserved}`);

  // 5. Odpytywanie aż wyniki będą gotowe
  const generatedIds = new Set(registration.createdRecordIds);
  let attempts = 0;
  const maxAttempts = 30;

  while (attempts < maxAttempts) {
    await new Promise((resolve) => setTimeout(resolve, 10_000));
    attempts++;

    const { images } = await qameraFetch("/api/external/image-picker", {
      params: { productId: product.id },
    });

    const ours = images.filter((img) => generatedIds.has(img.id));
    const done = ours.filter((img) => img.status === "DONE");

    console.log(`Poll ${attempts}: ${done.length}/${ours.length} done`);

    if (done.length === ours.length && done.length > 0) {
      console.log("All images ready!");

      // 6. Zatwierdzenie pierwszego wyniku
      await qameraFetch("/api/external/packshots/update-packshot", {
        method: "POST",
        body: {
          recordId: done[0].id,
          Voting: "APPROVED",
        },
      });
      console.log(`Approved image ${done[0].id}`);

      return done;
    }
  }

  throw new Error("Generation timed out after 5 minutes");
}

runPhotoShootWorkflow()
  .then((images) => console.log("Completed:", images))
  .catch((err) => console.error("Failed:", err.message));

Przykład Python (requests)

Poniższy przykład wykorzystuje bibliotekę requests (pip install requests).

Konfiguracja sesji

import time
import requests

BASE_URL = "https://app.qamera.ai"
API_KEY = "mk_live_abc123.secret456"

session = requests.Session()
session.headers.update({"X-Api-Key": API_KEY})

Pełny przepływ pracy

def run_photo_shoot_workflow():
    # 1. Lista produktów
    resp = session.get(f"{BASE_URL}/api/external/products")
    resp.raise_for_status()
    products = resp.json()["products"]
    print(f"Found {len(products)} products")

    if not products:
        raise RuntimeError("No products found. Upload one first.")

    product = products[0]
    print(f"Using product: {product['name']} ({product['id']})")

    # 2. Lista zatwierdzonych modeli
    resp = session.get(
        f"{BASE_URL}/api/external/models",
        params={"statusFilter": "approved"},
    )
    resp.raise_for_status()
    models = resp.json()["models"]
    print(f"Found {len(models)} approved models")

    # 3. Lista zatwierdzonych scenerii
    resp = session.get(
        f"{BASE_URL}/api/external/sceneries",
        params={"statusFilter": "approved"},
    )
    resp.raise_for_status()
    sceneries = resp.json()["sceneries"]
    print(f"Found {len(sceneries)} approved sceneries")

    # 4. Rejestracja generowania sesji zdjęciowej
    config = {
        "product": {"id": product["id"]},
        "industry": "fashion",
        "suggestions": "bright lighting, clean composition",
    }
    if models:
        config["model"] = {"id": models[0]["id"]}
    if sceneries:
        config["scenery"] = {"id": sceneries[0]["id"]}

    resp = session.post(
        f"{BASE_URL}/api/external/image-picker/register-ideas",
        json={"config": config, "count": 4},
    )
    resp.raise_for_status()
    registration = resp.json()

    print(f"Generation started — order {registration['orderId']}")
    print(f"Credits reserved: {registration['creditsReserved']}")

    # 5. Odpytywanie aż wyniki będą gotowe
    generated_ids = set(registration["createdRecordIds"])
    max_attempts = 30

    for attempt in range(1, max_attempts + 1):
        time.sleep(10)

        resp = session.get(
            f"{BASE_URL}/api/external/image-picker",
            params={"productId": product["id"]},
        )
        resp.raise_for_status()
        images = resp.json()["images"]

        ours = [img for img in images if img["id"] in generated_ids]
        done = [img for img in ours if img["status"] == "DONE"]

        print(f"Poll {attempt}: {len(done)}/{len(ours)} done")

        if len(done) == len(ours) and done:
            print("All images ready!")

            # 6. Zatwierdzenie pierwszego wyniku
            resp = session.post(
                f"{BASE_URL}/api/external/packshots/update-packshot",
                json={
                    "recordId": done[0]["id"],
                    "Voting": "APPROVED",
                },
            )
            resp.raise_for_status()
            print(f"Approved image {done[0]['id']}")

            return done

    raise TimeoutError("Generation timed out after 5 minutes")


if __name__ == "__main__":
    try:
        results = run_photo_shoot_workflow()
        for img in results:
            print(f"  {img['name']}: {img['thumbnail']}")
    except Exception as e:
        print(f"Error: {e}")