Node.js · Virtual Try-On API

Virtual Try-On API w Node.js

Nie ma jeszcze pakietu @photta/node — i nie jest on potrzebny. Node 18+ zawiera natywny `fetch`, a 20-liniowy helper pozwoli Ci uzyskać pierwsze zdjęcie na modelu w około pięć minut.

W jednym zdaniu

Ustaw `PHOTTA_API_KEY` w env, napisz mały helper `phottaFetch()` wywołujący `https://ai.photta.app/api/v1` z nagłówkiem `Authorization: Bearer photta_live_xxx`, wyślij POST do `/tryon/apparel` z URL zdjęcia produktu, mannequin ID i pose ID, a następnie odpytuj `/tryon/apparel/:id` co 3 sekundy, aż `status === 'completed'` — zazwyczaj w ciągu 1.5–4 minut.

Zaktualizowano · 2026-04-19

Twoje pierwsze zapytanie

Node.jsPythoncURLcURL
import { phottaFetch } from "./photta.js";

// 1. Submit the job — returns a generation ID immediately.
const created = await phottaFetch("/tryon/apparel", {
  method: "POST",
  body: JSON.stringify({
    product_type: "dress",
    product_images: ["https://example.com/dress.jpg"],
    mannequin_id: "mnq_athena_ts",
    pose_id: "pose_standing_front",
    resolution: "2K",
    aspect_ratio: "3:4",
  }),
});
const generationId = created.data.id;

// 2. Poll every 3 seconds until processing completes. Typical
// completion is 1.5–4 minutes; put an upper bound so a stuck
// job can't hang your request forever.
const pollInterval = 3000;
const maxAttempts = 120;     // 120 × 3s = 6 minutes
let result;

for (let i = 0; i < maxAttempts; i++) {
  result = await phottaFetch(`/tryon/apparel/${generationId}`);
  if (result.data.status === "completed") break;
  if (result.data.status === "failed") {
    throw new Error(result.data.error_message);
  }
  await new Promise((r) => setTimeout(r, pollInterval));
}

console.log("Result:", result.data.output_url);

Czego się spodziewać

Typical completion

1.5–4min

2K / 4K credits

5 / 7

Jewelry types

4

Close-up mannequins

built-in

Jak to działa

Virtual Try-On API w Node.js

Pięć kroków, około pięć minut od rejestracji do pierwszego zdjęcia.

  1. 01

    Krok 1

    Zarejestruj się i wygeneruj klucz

    Utwórz konto na ai.photta.app. Otwórz sekcję Developers w panelu, kliknij Generate API key i skopiuj klucz live. Zaczyna się on od `photta_live_`, po którym następuje 32 znaki hex.

  2. 02

    Krok 2

    Przechowuj klucz w env, nie w kodzie

    Dodaj `PHOTTA_API_KEY=...` do `.env` (lub magazynu sekretów Twojej platformy) i ładuj go przez `process.env`. Nigdy nie commituj klucza; nigdy nie importuj go do pliku oznaczonego `'use client'` — bundler wciągnie go do przeglądarki.

  3. 03

    Krok 3

    Napisz prosty helper fetch

    Jeden 20-liniowy wrapper wokół `fetch()` obsługuje bazowy URL, nagłówek Authorization, body JSON i normalizację błędów. To jedyna abstrakcja, której potrzebujesz do czasu wydania oficjalnego SDK.

  4. 04

    Krok 4

    Prześlij przymierzalnię i odpytuj

    Wyślij POST do `/tryon/apparel` z `product_type`, `product_images`, `mannequin_id`, `pose_id`, `resolution` i `aspect_ratio`. API natychmiast zwróci identyfikator generacji. Odpytuj `/tryon/apparel/:id` co 3 sekundy, aż `status === 'completed'`.

  5. 05

    Krok 5

    Zapisz wynik

    Zakończone zadanie zawiera `output_url` i `thumbnail_url`. Pobierz bajty raz i zapisz je we własnym magazynie obiektowym — adresy URL są stabilne, ale Twój produkt nie powinien polegać na CDN Photta przy renderowaniu.

Kod, od początku do końca

Copy, paste, done.

Four snippets — install prerequisites, wrap the REST call, submit + poll, then handle the errors that actually happen in production.

01No SDK required — use native fetch in Node 18+
bash
# Node 18+ ships fetch natively. No install step needed.
node --version   # ensure v18 or later

# Optional: keep your API key out of source control
echo "PHOTTA_API_KEY=photta_live_xxxxx" >> .env
02Wrap the REST call in a tiny client helper
javascript
// photta.js — a 20-line wrapper you can reuse across your app.
const PHOTTA_BASE_URL = "https://ai.photta.app/api/v1";

export async function phottaFetch(path, init = {}) {
  const res = await fetch(`${PHOTTA_BASE_URL}${path}`, {
    ...init,
    headers: {
      "Authorization": `Bearer ${process.env.PHOTTA_API_KEY}`,
      "Content-Type": "application/json",
      ...init.headers,
    },
  });
  const body = await res.json();
  if (!res.ok) {
    const err = new Error(body?.error?.message ?? res.statusText);
    err.status = res.status;
    err.code = body?.error?.code;
    throw err;
  }
  return body;
}
03Submit a try-on job and poll until it lands
javascript
import { phottaFetch } from "./photta.js";

// 1. Submit the job — returns a generation ID immediately.
const created = await phottaFetch("/tryon/apparel", {
  method: "POST",
  body: JSON.stringify({
    product_type: "dress",
    product_images: ["https://example.com/dress.jpg"],
    mannequin_id: "mnq_athena_ts",
    pose_id: "pose_standing_front",
    resolution: "2K",
    aspect_ratio: "3:4",
  }),
});
const generationId = created.data.id;

// 2. Poll every 3 seconds until processing completes. Typical
// completion is 1.5–4 minutes; put an upper bound so a stuck
// job can't hang your request forever.
const pollInterval = 3000;
const maxAttempts = 120;     // 120 × 3s = 6 minutes
let result;

for (let i = 0; i < maxAttempts; i++) {
  result = await phottaFetch(`/tryon/apparel/${generationId}`);
  if (result.data.status === "completed") break;
  if (result.data.status === "failed") {
    throw new Error(result.data.error_message);
  }
  await new Promise((r) => setTimeout(r, pollInterval));
}

console.log("Result:", result.data.output_url);
04Handle 402 credit exhaustion and 429 rate limits
javascript
try {
  await phottaFetch("/tryon/apparel", {
    method: "POST",
    body: JSON.stringify({ /* … */ }),
  });
} catch (err) {
  if (err.status === 402) {
    // Out of credits — surface a top-up CTA to the user.
    // err.code === "insufficient_credits"
  } else if (err.status === 429) {
    // Rate-limited. Honour the Retry-After header.
    const retryAfter = err.retryAfter ?? 30;
    await new Promise((r) => setTimeout(r, retryAfter * 1000));
    // Then retry…
  } else if (err.status >= 500) {
    // Server-side issue — backoff + retry.
  }
  throw err;
}

Dlaczego taki format

Dlaczego helper jest lepszy od biblioteki klienta — na razie

  • Node 18+ ships native fetch — no runtime deps required
  • Same helper works inside Next.js route handlers, API routes, server actions and edge functions
  • Keep API keys in env; never import the helper from a 'use client' module or the bundler will leak them
  • Works with any queue/cron: BullMQ, Inngest, Trigger.dev, Vercel Cron, Cloudflare Queues

Czego to nie robi

Honest caveats

  • No official @photta/node SDK yet — REST is the only path today
  • No built-in webhook delivery; polling is the documented pattern (3–5s interval)
  • Bearer token lives in env; the API doesn't support OAuth client credentials yet

Pytania innych programistów

Questions other developers ask

Czy istnieje oficjalne Photta Node.js SDK?+

Jeszcze nie. Dokumentacja Photta przewiduje SDK po osiągnięciu progów adopcji: Python przy dziesięciu użytkownikach API, Node.js przy dwudziestu. Do tego czasu zalecaną ścieżką jest czysty REST przez cURL, natywny fetch w Node lub bibliotekę Python requests. 20-liniowy helper na tej stronie odpowiada klientowi dla najczęściej używanych endpointów.

Jakie wersje Node.js są obsługiwane?+

Wszystkie wspierające natywny `fetch` — czyli Node 18 i nowsze. W Node 16 należy użyć polyfilla `node-fetch` lub podbić wersję. Reszta kodu to czysty ECMAScript 2020 (async/await, template literals, dynamiczne importy), więc nie ma innych wymagań runtime.

Jak uwierzytelnić się z Node?+

Wysyłaj `Authorization: Bearer photta_live_xxx` w każdym zapytaniu. Klucz zaczyna się od `photta_live_` w produkcji lub `photta_test_` po wprowadzeniu trybu sandbox. Umieść go w zmiennej środowiskowej i ładuj przez `process.env.PHOTTA_API_KEY`. API nie obsługuje jeszcze OAuth client credentials ani autoryzacji opartej na sesji.

Jak obsłużyć 1.5–4 minutowe okno generacji?+

Endpoint przymierzalni jest w pełni asynchroniczny. POST natychmiast zwraca identyfikator generacji ze statusem `status: 'processing'`. Odpytuj `GET /tryon/apparel/:id` co 3–5 sekund do momentu zmiany statusu na `completed` lub `failed`. Ustal limit prób, aby zawieszone zadanie nie blokowało zapytania w nieskończoność — 120 prób co 3 sekundy pokrywa udokumentowane okno z marginesem.

Czy mogę wywołać API wewnątrz Next.js?+

Tak — z route handlera (`app/**/route.ts`), server action (`'use server'`) lub middleware. Trzymaj klucz API na serwerze; nigdy nie importuj helpera w pliku `'use client'`. W przypadku zadań przekraczających timeout funkcji Vercel, przenieś odpytywanie do kolejki (Inngest, Trigger.dev, BullMQ) i zwróć identyfikator generacji do przeglądarki, aby mogła odpytywać webhook lub Twój własny endpoint statusu.

Jak API sygnalizuje błędy?+

Odpowiedzi inne niż 2xx zawierają body JSON z obiektem `error`: `type`, `code`, `message` oraz ID zapytania dla wsparcia. Typowe przypadki: 400 invalid_request_error z `param` wskazującym na błędne pole; 402 insufficient_credits z liczbami `required` i `available`; 429 rate_limit_exceeded z `retry_after` w sekundach i nagłówkiem Retry-After. Błędy 5xx można bezpiecznie powtarzać z wykładniczym czasem oczekiwania (exponential backoff).

Node.js · Virtual Try-On API

Utwórz konto i pobierz klucz API

Ustaw `PHOTTA_API_KEY` w env, napisz mały helper `phottaFetch()` wywołujący `https://ai.photta.app/api/v1` z nagłówkiem `Authorization: Bearer photta_live_xxx`, wyślij POST do `/tryon/apparel` z URL zdjęcia produktu, mannequin ID i pose ID, a następnie odpytuj `/tryon/apparel/:id` co 3 sekundy, aż `status === 'completed'` — zazwyczaj w ciągu 1.5–4 minut.

Virtual Try-On API dla Node.js — Photta | Photta