Node.js · Virtual Try-On API

Virtual Try-On API in Node.js

Er is nog geen @photta/node package — en je hebt er ook geen nodig. Node 18+ bevat native `fetch`, en een helper van 20 regels brengt je in ongeveer vijf minuten naar je eerste afbeelding op een model.

In één zin

Stel `PHOTTA_API_KEY` in in env, schrijf een kleine `phottaFetch()` helper die `https://ai.photta.app/api/v1` aanroept met `Authorization: Bearer photta_live_xxx`, doe een POST naar `/tryon/apparel` met een product image-URL, mannequin_id en pose_id, poll vervolgens `/tryon/apparel/:id` elke 3 seconden totdat `status === 'completed'` — meestal binnen 1.5–4 minuten.

Bijgewerkt · 2026-04-19

Je eerste verzoek

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);

Wat je kunt verwachten

Typical completion

1–3min

2K / 4K credits

4 / 6

Styles

2

Batch-ready

yes

Hoe het werkt

Virtual Try-On API in Node.js

Vijf stappen, ongeveer vijf minuten van aanmelding tot eerste afbeelding.

  1. 01

    Stap 1

    Meld je aan en genereer een sleutel

    Maak een account aan op ai.photta.app. Open de Developers-sectie van het dashboard, klik op Generate API key en kopieer de live sleutel. Deze begint met `photta_live_` gevolgd door 32 hex-tekens.

  2. 02

    Stap 2

    Bewaar de sleutel in env, niet in code

    Voeg `PHOTTA_API_KEY=...` toe aan `.env` (of de secret store van je platform) en laad deze via `process.env`. Voeg de sleutel nooit toe aan je code; importeer hem nooit in een bestand gemarkeerd als `'use client'` — de bundler zal hem dan naar de browser trekken.

  3. 03

    Stap 3

    Schrijf een kleine fetch-helper

    Eén wrapper van 20 regels rond `fetch()` handelt de basis-URL, Authorization header, JSON-body en fout-normalisatie af. Dit is de enige abstractie die je nodig hebt totdat er een officiële SDK verschijnt.

  4. 04

    Stap 4

    Dien een try-on in en poll

    POST naar `/tryon/apparel` met `product_type`, `product_images`, `mannequin_id`, `pose_id`, `resolution` en `aspect_ratio`. De API retourneert direct een generatie-ID. Poll `/tryon/apparel/:id` elke 3 seconden totdat `status === 'completed'`.

  5. 05

    Stap 5

    Sla het resultaat op

    De voltooid-payload bevat `output_url` en `thumbnail_url`. Haal de bytes eenmalig op en sla ze op in je eigen object storage — de URL's zijn stabiel, maar je product moet voor weergave niet afhankelijk zijn van Photta's CDN.

Code, van begin tot eind

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;
}

Waarom deze vorm

Waarom een helper beter is dan een client library — voor nu

  • 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

Wat het niet doet

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

Vragen die andere developers stellen

Questions other developers ask

Is er een officiële Photta Node.js SDK?+

Nog niet. De Photta-documentatie plant SDK's op basis van adoptiedrempels: Python zodra tien API-gebruikers aan boord zijn, Node.js bij twintig. Tot die tijd is het gedocumenteerde pad raw REST via cURL, Node's native fetch of de Python requests-bibliotheek. De helper van 20 regels op deze pagina staat gelijk aan een client voor de endpoints die je het meest zult gebruiken.

Welke Node.js versies worden ondersteund?+

Alles wat native `fetch` ondersteunt — dus Node 18 en later. Op Node 16 gebruik je een polyfill zoals `node-fetch` of voer je een upgrade uit. De rest van de code is gewone ECMAScript 2020 (async/await, template literals, dynamic imports), dus er zijn geen andere runtime-vereisten.

Hoe authenticeer ik vanuit Node?+

Stuur `Authorization: Bearer photta_live_xxx` mee bij elk verzoek. De sleutel begint met `photta_live_` in productie, `photta_test_` wanneer de sandbox-modus verschijnt. Zet deze in een omgevingsvariabele en laad hem via `process.env.PHOTTA_API_KEY`. De API ondersteunt nog geen OAuth client credentials of sessie-gebaseerde auth.

Hoe ga ik om met het generatievenster van 1.5–4 minuten?+

Het try-on endpoint is volledig asynchroon. De POST retourneert direct een generatie-ID met `status: 'processing'`. Poll `GET /tryon/apparel/:id` elke 3–5 seconden totdat `status` verspringt naar `completed` of `failed`. Stel een maximum aantal pogingen in zodat een vastgelopen taak je verzoek niet eeuwig blokkeert — 120 pogingen met een interval van 3 seconden dekt het gedocumenteerde venster met marge.

Kan ik de API aanroepen binnen Next.js?+

Ja — vanuit een route handler (`app/**/route.ts`), een server action (`'use server'`) of middleware. Houd de API-sleutel op de server; importeer de helper nooit vanuit een `'use client'` bestand. Voor langlopende taken die de timeout van Vercel-functies overschrijden, verplaats je het pollen naar een wachtrij (Inngest, Trigger.dev, BullMQ) en retourneer je het generatie-ID naar de browser zodat deze een webhook of je eigen status-endpoint kan pollen.

Hoe signaleert de API fouten?+

Niet-2xx responses bevatten een JSON-body met een `error`-object: `type`, `code`, `message` en een verzoek-ID voor ondersteuning. Veelvoorkomende gevallen: 400 invalid_request_error waarbij `param` wijst naar het foute veld; 402 insufficient_credits met `required` en `available` aantallen; 429 rate_limit_exceeded met `retry_after` in seconden en een bijbehorende Retry-After header. 5xx-fouten kunnen veilig opnieuw worden geprobeerd met exponentiële back-off.

Node.js · Virtual Try-On API

Maak een account aan en ontvang een API-sleutel

Stel `PHOTTA_API_KEY` in in env, schrijf een kleine `phottaFetch()` helper die `https://ai.photta.app/api/v1` aanroept met `Authorization: Bearer photta_live_xxx`, doe een POST naar `/tryon/apparel` met een product image-URL, mannequin_id en pose_id, poll vervolgens `/tryon/apparel/:id` elke 3 seconden totdat `status === 'completed'` — meestal binnen 1.5–4 minuten.

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