Node.js Quickstart
This guide shows a complete integration: submit a try-on job, receive the result via webhook, and verify the signature.Submit a try-on job
Copy
Ask AI
const apiKey = process.env.ZUPERTRY_API_KEY; // zt_live_sk_... or zt_test_sk_...
async function createTryOnJob(modelImageUrl, garmentImageUrl, webhookUrl) {
const res = await fetch('https://app.zupertry.com/v1/tryon', {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model_image_url: modelImageUrl,
garment_image_url: garmentImageUrl,
webhook_url: webhookUrl, // optional
}),
});
if (!res.ok) {
const err = await res.json();
throw new Error(`Zupertry error: ${err.error.code} — ${err.error.message}`);
}
return res.json(); // { job_id, status: "pending", created_at, mode }
}
// Usage
const job = await createTryOnJob(
'https://your-cdn.com/model-photo.jpg',
'https://your-cdn.com/product-image.jpg',
'https://your-app.com/webhooks/zupertry'
);
console.log('Job created:', job.job_id);
Poll for completion (without webhook)
Copy
Ask AI
async function waitForResult(jobId) {
while (true) {
const res = await fetch(`https://app.zupertry.com/v1/jobs/${jobId}`, {
headers: { Authorization: `Bearer ${apiKey}` },
});
const job = await res.json();
if (job.status === 'completed') {
return job.output_url;
}
if (job.status === 'failed') {
throw new Error(`Job failed: ${job.error}`);
}
await new Promise((r) => setTimeout(r, 2000)); // poll every 2s
}
}
const outputUrl = await waitForResult(job.job_id);
console.log('Result:', outputUrl);
Receive results via webhook (recommended)
Set up an HTTP endpoint to receive real-time job completion events. Zupertry signs every request with HMAC-SHA256.Copy
Ask AI
const express = require('express');
const crypto = require('crypto');
const app = express();
// Parse raw body BEFORE json middleware for signature verification
app.use('/webhooks/zupertry', express.raw({ type: 'application/json' }));
app.post('/webhooks/zupertry', (req, res) => {
// 1. Verify the signature
const signature = req.headers['x-zupertry-signature'];
const secret = process.env.ZUPERTRY_WEBHOOK_SECRET;
const expectedSig = crypto
.createHmac('sha256', secret)
.update(req.body) // use raw Buffer, not parsed JSON
.digest('hex');
if (signature !== expectedSig) {
return res.status(401).json({ error: 'Invalid signature' });
}
// 2. Handle the event
const event = JSON.parse(req.body.toString());
switch (event.type) {
case 'job.completed': {
const { job_id, output_url, credits_consumed } = event.data;
console.log(`Job ${job_id} done. URL: ${output_url}. Credits: ${credits_consumed}`);
// Save output_url to your database, notify user, update product listing, etc.
break;
}
case 'job.failed': {
const { job_id, error } = event.data;
console.error(`Job ${job_id} failed: ${error}`);
break;
}
}
// Always respond 2xx quickly — Zupertry retries on any non-2xx response
res.status(200).json({ received: true });
});
app.listen(3000);
Full working example
Copy
Ask AI
// server.js — try this locally with: node server.js
require('dotenv').config();
const express = require('express');
const crypto = require('crypto');
const app = express();
const API_KEY = process.env.ZUPERTRY_API_KEY;
const WEBHOOK_SECRET = process.env.ZUPERTRY_WEBHOOK_SECRET;
// Submit a job
app.get('/submit', async (req, res) => {
const job = await fetch('https://app.zupertry.com/v1/tryon', {
method: 'POST',
headers: {
Authorization: `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model_image_url: 'https://picsum.photos/800/1200',
garment_image_url: 'https://picsum.photos/600/800',
webhook_url: 'https://your-ngrok-url.ngrok.io/webhooks/zupertry',
}),
}).then((r) => r.json());
res.json(job);
});
// Receive webhook
app.use('/webhooks/zupertry', express.raw({ type: 'application/json' }));
app.post('/webhooks/zupertry', (req, res) => {
const sig = req.headers['x-zupertry-signature'];
const expected = crypto.createHmac('sha256', WEBHOOK_SECRET).update(req.body).digest('hex');
if (sig !== expected) return res.status(401).end();
const event = JSON.parse(req.body.toString());
console.log('Webhook received:', event.type, event.data);
res.json({ ok: true });
});
app.listen(3000, () => console.log('http://localhost:3000'));
Copy
Ask AI
# .env
ZUPERTRY_API_KEY=zt_test_sk_your_test_key
ZUPERTRY_WEBHOOK_SECRET=your_webhook_signing_secret
Use ngrok to expose your local webhook endpoint during development:
ngrok http 3000. Use the generated URL as your webhook URL.Next steps
API Reference
Every endpoint, parameter, and error schema
Webhooks Guide
Event types, retry logic, signature verification in depth