Bản lưu ngày / Snapshot of 2026-10-14 · Xem bản mới nhất / See the latest version
WanduraDocsBeta
BetaDocs đang ở bản beta và còn thay đổi. Thấy chỗ sai, khó hiểu hoặc còn thiếu? Hãy cho chúng tôi biết.
API tạo ảnh và video (v2)

Tạo ảnh và video: Jobs API

Markdown
Trên trang này

TL;DR — POST /v2/jobs với một model, một style và dữ liệu đầu vào của bạn sẽ trả về job_id sau khoảng một giây. Wandura tạo kết quả ở nền rồi POST job cuối cùng (có ký) tới webhook_url của bạn; GET /v2/jobs/{job_id} là đường dự phòng. Key test (wdr_test_…) là sandbox miễn phí. Body sai bị từ chối ngay bằng 422 kèm lý do; job failed, timeout, bị huỷ hoặc bị moderation chặn thì không bị tính tiền.

Cơ bản

  • Base URL: https://api.wandura.ai (vùng Singapore).
  • OpenAPI 3.1: api.wandura.ai/v2/openapi.json, không cần key. Jobs API là các operation gắn tag Jobs.
  • Luồng: submit kèm webhook_url và Idempotency-Key → 202 {job_id, status: "queued"} → Wandura POST job cuối cùng (có ký) tới webhook_url, bạn trả 200 → nếu webhook không tới, poll GET /v2/jobs/{job_id} → tải output[0].url.
  • Mọi request là JSON qua HTTPS.
Endpoint Làm gì
POST /v2/jobs Gửi một job.
GET /v2/jobs/{job_id} Trạng thái và output. Cùng body với webhook.
POST /v2/jobs/{job_id}/cancel Huỷ job còn queued.
GET /v2/models Danh sách model.
GET /v2/models/{model_id}/schema JSON Schema của body submit cho một model.
GET /v2/styles?model= Các style đang chạy, kèm bản xem trước.

Xác thực và key

Mọi lời gọi gửi Authorization: Bearer <key>. Tạo key ở console; một product có thể có nhiều key và xoay vòng được. Giữ key ở máy chủ của bạn.

Key Làm gì Chi phí
wdr_test_… Sandbox. Cùng API, cùng validation, status và webhook; job trả về một file mẫu thay vì tạo thật $0
wdr_live_… Tạo thật Giá của style, chỉ khi job thành công

Job thuộc về product của key đã gửi nó: key của product khác nhận 404.

Model và schema

Hai model, mỗi loại đầu ra một model.

model Đầu ra
wandura-image-v1 ảnh
wandura-video-v1 video

v1 trong model id là version của model, không phải version của API. Id cố định: thay đổi phá vỡ tương thích sẽ có id mới (…-v2), không bao giờ đổi âm thầm. Style được thêm và gỡ dưới một model mà không đổi model.

curl https://api.wandura.ai/v2/models -H "Authorization: Bearer $WANDURA_KEY"

# JSON Schema (2020-12) của body submit cho một model: kiểu, field bắt buộc, min/max,
# độ dài, danh sách style id, và với mỗi style: field nào bắt buộc, field nào không nhận
curl https://api.wandura.ai/v2/models/wandura-video-v1/schema -H "Authorization: Bearer $WANDURA_KEY"

Đọc schema lúc build hoặc theo lịch để dựng form nhập; nó đổi khi style được thêm hoặc gỡ.

Style

Style là một "kiểu" đã đóng gói sẵn: prompt, các bước tạo và cài đặt của nó nằm ở phía Wandura. GET /v2/styles?model=wandura-video-v1 liệt kê các style đang chạy mà key của bạn dùng được:

{
  "id": "sty_motion_clip_test", "object": "style", "model": "wandura-video-v1", "name": "Motion Clip",
  "description": "Bring a still image to life with gentle camera motion.",
  "input_hint": "One clear image of the subject you want to bring to life.",
  "preview_url": "https://<wandura-storage-host>/samples/v1/motion-clip.mp4",
  "inputs": { "image_url": "required", "prompt": "optional" },
  "max_job_seconds": 300, "price": { "credits": 23.4, "usd": 0.234 }
}
Field Ý nghĩa
preview_url Link https tới ảnh hoặc video mẫu làm bằng style này. Hiển thị trong màn chọn style của bạn. null nếu style chưa có mẫu.
inputs Các field của POST /v2/jobs mà style này nhận, mỗi field là required hoặc optional. Field không có trong danh sách sẽ bị từ chối.
max_job_seconds Thời gian tối đa của style. Quá mức này job kết thúc timeout, không tính tiền.
price Giá của một job live thành công (1 credit = $0.01).

Gửi, poll, huỷ

curl -X POST https://api.wandura.ai/v2/jobs \
  -H "Authorization: Bearer $WANDURA_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: your-job-123" \
  -d '{
    "model": "wandura-video-v1",
    "style": "sty_motion_clip_test",
    "image_url": "https://your-bucket.example.com/u/42/photo.jpg?X-Amz-Signature=...",
    "prompt": "a girl smiling at the camera",
    "webhook_url": "https://your-backend.example.com/hooks/wandura",
    "end_user_id": "user_42"
  }'
# HTTP 202
# X-RateLimit-Limit: 60
# X-RateLimit-Remaining: 59
# X-RateLimit-Reset: 1791897360
# {"job_id":"run_QVqsXwr7VGSP","status":"queued","model":"wandura-video-v1","created_at":"2026-10-13T13:15:40.825Z"}

curl https://api.wandura.ai/v2/jobs/run_QVqsXwr7VGSP -H "Authorization: Bearer $WANDURA_KEY"

curl -X POST https://api.wandura.ai/v2/jobs/run_QVqsXwr7VGSP/cancel -H "Authorization: Bearer $WANDURA_KEY"
Field Ghi chú
model Bắt buộc. wandura-image-v1 hoặc wandura-video-v1.
style Bắt buộc. Id style của model đó (GET /v2/styles).
prompt Đoạn chữ mà style đưa vào kết quả. Tuỳ chọn, trừ khi style liệt kê nó là bắt buộc; "" được coi như không gửi.
image_url, image_urls, video_url URL https công khai (URL presigned GET dùng được, tối đa 8192 ký tự). Ảnh JPEG, PNG hoặc WEBP tới 20 MB; video MP4, MOV hoặc WEBM tới 50 MB.
negative_prompt, aspect_ratio, resolution, duration, seed Chỉ cho style có liệt kê trong inputs. Xem Validation.
num_outputs Tuỳ chọn, phải là 1: mỗi job một output.
webhook_url Tuỳ chọn. URL https công khai nhận job cuối cùng, có ký. Cần có Jobs API signing secret của product trước (xem Webhook).
end_user_id Tuỳ chọn. Id của người dùng trong hệ thống của bạn; khi gửi, giới hạn theo ngày cho từng người sẽ áp dụng (xem Giới hạn).
  • URL đầu vào được tải khi job bắt đầu chạy, không phải lúc submit. URL không tải được sẽ làm job failed, không tính tiền. Giữ URL presigned còn hạn ít nhất bằng max_job_seconds của style cộng 10 phút.
  • Huỷ được khi job còn queued: 200, status cancelled, không tính tiền, và webhook vẫn được gửi. Khi job đã processing nó chạy tới cùng, và huỷ trả 409 JOB_NOT_CANCELLABLE.

Validation chặt và đồng bộ

Body sai không bao giờ trở thành job. Wandura kiểm tra cả body ngay lúc submit và liệt kê mọi lỗi:

HTTP 422
{"error":{"code":"INVALID_INPUT","message":"Validation failed.","retryable":false,"details":[
  {"field":"num_outputs","reason":"must be 1 (one output per job)"},
  {"field":"aspect_ratio","reason":"style sty_motion_clip_test does not take this field"},
  {"field":"image_url","reason":"required by style sty_motion_clip_test"}]}}
  • Style tự cố định tỉ lệ khung hình, độ phân giải và thời lượng. Các style hiện tại không nhận aspect_ratio, resolution, duration hay seed, nên gửi một trong số đó sẽ trả 422 kèm lý do. Chỉ gửi những gì inputs của style liệt kê, và dùng schema của model để biết đó là gì.
  • Field lạ, thiếu field bắt buộc, URL sai định dạng và style không tồn tại đều là 422.

Idempotency

Gửi Idempotency-Key (1 đến 255 ký tự) ở mọi submit để retry không bao giờ chạy hai lần.

  • Cùng key và cùng body: cùng một job (200, header Idempotent-Replayed: true, status hiện tại của job).
  • Cùng key nhưng body khác: 409 IDEMPOTENCY_KEY_REUSED. Dùng key mới cho job mới.
  • Không gửi header: Wandura tự sinh key từ API key và body, nên cùng body trong 10 phút là cùng một job.

Status

queued → processing → succeeded | failed | cancelled | timeout

Bốn status cuối là status cuối cùng và không bao giờ đổi nữa.

status Cuối Tính tiền (key live) error
queued không chưa null
processing không chưa null
succeeded có có, giá của style null
failed có không GENERATION_FAILED, INPUT_UNUSABLE hoặc CONTENT_MODERATION_BLOCKED
timeout có không TIMEOUT
cancelled có không null

Lỗi nằm trong một job đã kết thúc (error.code):

status code retryable Ý nghĩa
failed CONTENT_MODERATION_BLOCKED false Đầu vào hoặc kết quả bị content moderation chặn. Không tính tiền.
failed INPUT_UNUSABLE false Một file đầu vào không dùng được (không tải được, định dạng không hỗ trợ, hoặc quá dài). Không tính tiền.
failed GENERATION_FAILED true Tạo kết quả thất bại ở phía Wandura. Không tính tiền; gửi lại thường là được.
timeout TIMEOUT true Job không xong trong thời gian tối đa của style. Không tính tiền.

Bị moderation chặn là một job cuối cùng bình thường, không phải lỗi HTTP:

{"job_id":"run_4kT9wQx2LmPa","status":"failed","model":"wandura-video-v1","style":"sty_motion_clip_test","output":null,
 "error":{"code":"CONTENT_MODERATION_BLOCKED","message":"The input or the result was blocked by content moderation. Not charged.","retryable":false},
 "created_at":"2026-10-13T13:12:02.410Z","completed_at":"2026-10-13T13:12:03.902Z"}

Output

output là danh sách có một file, và là null cho tới khi job succeeded.

{"job_id":"run_…","status":"succeeded","model":"wandura-image-v1","style":"sty_…",
 "output":[{"url":"https://<wandura-storage-host>/outputs/2026-10-10/<random name>.png","type":"image/png",
   "width":48,"height":32,"duration":null,"fps":null,"size_bytes":136,"seed":null,"expires_at":"2026-10-13T00:00:00.000Z"}],
 "error":null,"created_at":"2026-10-10T00:00:00.000Z","completed_at":"2026-10-10T00:00:00.000Z"}
  • url là link HTTPS trên storage của Wandura (Wandura CDN), tải thẳng bằng GET thường. Đó không phải link của bên thứ ba nào.
  • Lưu 72 giờ. expires_at cho biết chính xác khi nào hết hạn. Hãy chép file về storage của bạn trước đó.
  • Metadata đọc từ chính file: type, width, height, duration và fps (video), size_bytes, seed. Giá trị không đọc được là null.
  • File mẫu của key test có metadata null và không hết hạn.
  • Bản ghi job được giữ lâu hơn 72 giờ nhiều: GET /v2/jobs/{job_id} vẫn trả lời cho job cũ.

Webhook

Mỗi job có webhook_url nhận một POST khi đạt status cuối cùng (không có webhook trung gian). Body đúng bằng thứ GET /v2/jobs/{job_id} trả về tại thời điểm đó.

  1. Tạo Jobs API signing secret của product một lần: ở console, mục "Jobs API signing secret" → Create. Secret chỉ hiện một lần; Roll thay ngay lập tức. Khi chưa có secret, submit kèm webhook_url bị từ chối bằng 422.
  2. Verify mọi request trên raw body: X-Signature là HMAC-SHA256 dạng hex của X-Timestamp + "." + raw body bằng secret của bạn, và X-Timestamp (unix giây) lệch hiện tại không quá 5 phút.
  3. Trả 2xx trong 10 giây, rồi mới xử lý việc của bạn. Dedupe theo job_id (hoặc header Wandura-Event-Id, giống nhau ở mọi lần retry): giao tin ít nhất một lần.
POST /hooks/wandura HTTP/1.1
Content-Type: application/json
User-Agent: Wandura-Webhooks/1
X-Timestamp: 1791897341
X-Signature: 6f1c…(64 hex characters)
Wandura-Event-Id: evt_…
Wandura-Event-Type: job.ended

{"job_id":"run_QVqsXwr7VGSP","status":"succeeded","model":"wandura-video-v1", …}

Chính sách retry

Trả non-2xx (kể cả redirect), timeout hoặc lỗi mạng sẽ được retry sau 30 giây, 2 phút, 10 phút, 1 giờ, 6 giờ và 24 giờ: 7 lần thử trong khoảng 31 giờ, cùng body và job_id. URL có host trỏ về địa chỉ riêng hoặc dành riêng sẽ không bao giờ được gọi. Nếu mọi lần thử đều thất bại, job vẫn còn đó: hãy poll.

Verify chữ ký

Node.js (Express):

import express from 'express';
import { createHmac, timingSafeEqual } from 'node:crypto';

// raw = the body exactly as received (before JSON.parse)
export function verifyWandura(raw, headers, secret, toleranceS = 300) {
  const ts = headers['x-timestamp'], sig = String(headers['x-signature'] || '');
  if (!/^\d+$/.test(ts || '') || Math.abs(Date.now() / 1000 - Number(ts)) > toleranceS) return false;
  const want = createHmac('sha256', secret).update(`${ts}.${raw}`).digest('hex');
  return sig.length === want.length && timingSafeEqual(Buffer.from(sig), Buffer.from(want));
}

const app = express();
app.post('/hooks/wandura', express.raw({ type: 'application/json' }), (req, res) => {
  const raw = req.body.toString('utf8');
  if (!verifyWandura(raw, req.headers, process.env.WANDURA_JOBS_SECRET)) return res.sendStatus(401);
  const job = JSON.parse(raw); // { job_id, status, output, error, ... }
  // store it, dedupe on job.job_id, then:
  res.sendStatus(200);
});

Python (Flask):

import hashlib, hmac, os, time
from flask import Flask, request

def verify_wandura(raw: bytes, headers, secret: str, tolerance_s: int = 300) -> bool:
    ts, sig = headers.get("X-Timestamp", ""), headers.get("X-Signature", "")
    if not ts.isdigit() or abs(time.time() - int(ts)) > tolerance_s:
        return False
    want = hmac.new(secret.encode(), ts.encode() + b"." + raw, hashlib.sha256).hexdigest()
    return hmac.compare_digest(want, sig)

app = Flask(__name__)

@app.post("/hooks/wandura")
def wandura_hook():
    raw = request.get_data()  # raw bytes, before any JSON parsing
    if not verify_wandura(raw, request.headers, os.environ["WANDURA_JOBS_SECRET"]):
        return "", 401
    job = request.get_json()  # dedupe on job["job_id"]
    return "", 200

Dùng sandbox để thử endpoint của bạn: job wdr_test_… cũng bắn webhook có ký y như thật.

Lỗi

Mọi câu trả lời lỗi có dạng {"error": {"code", "message", "retryable"}}, thêm details cho INVALID_INPUT. Rẽ nhánh theo code; khi nhờ hỗ trợ, đưa kèm header X-Request-Id.

HTTP code retryable Ý nghĩa
422 INVALID_INPUT false Body hoặc header sai; details[] liệt kê từng {field, reason}.
401 UNAUTHORIZED false Thiếu key, key lạ, đã thu hồi hoặc hết hạn.
402 INSUFFICIENT_CREDITS false Ví của product không đủ trả cho job. Nạp thêm rồi thử lại.
403 FORBIDDEN false Key hợp lệ nhưng không được làm việc này. Dự phòng: hiện chưa trả.
404 NOT_FOUND false Không có job, model hoặc endpoint đó cho key này.
409 IDEMPOTENCY_KEY_REUSED false Idempotency-Key này đã dùng với body khác.
409 JOB_NOT_CANCELLABLE false Chỉ huỷ được job đang queued.
429 RATE_LIMITED true Product gửi quá nhiều job trong phút này. Thử lại sau Retry-After.
429 END_USER_LIMIT_REACHED true End user này đã chạm giới hạn job trong ngày. Thử lại sau Retry-After (reset 00:00 UTC).
500 INTERNAL_ERROR true Phía Wandura có sự cố. Thử lại với cùng Idempotency-Key.
503 SERVICE_UNAVAILABLE true Style này tạm thời không chạy được trên key live. Key test vẫn trả file mẫu.

429 luôn kèm header Retry-After (giây). Chờ đúng khoảng đó, cộng thêm một chút jitter ngẫu nhiên, rồi retry với cùng Idempotency-Key. Chỉ retry những lỗi có retryable.

HTTP/1.1 429
Retry-After: 12
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1791897360

{"error":{"code":"RATE_LIMITED","message":"Too many jobs submitted this minute for this product. Retry after the Retry-After header.","retryable":true}}

Giới hạn

Đây là mức mặc định hiện tại, và được nâng theo từng khách hàng khi có yêu cầu: hãy báo cho Wandura đỉnh tải dự kiến của bạn (số job mỗi phút, tỉ lệ video) và giới hạn cho product của bạn sẽ được nâng.

Gì Hiện tại Ghi chú
Tốc độ submit 60 job mỗi phút mỗi product Key live và key test tính riêng. Vượt mức: 429 RATE_LIMITED kèm Retry-After. Mỗi submit trả X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset.
Job chạy cùng lúc 20 mỗi product Job thừa chờ ở queued. Job còn chờ tới hết thời gian tối đa của nó sẽ kết thúc timeout, không tính tiền.
Mỗi end user 20 job mỗi ngày Chỉ khi bạn gửi end_user_id. Reset 00:00 UTC; 429 END_USER_LIMIT_REACHED.
Thời gian job tối đa ảnh 60 s, video 300 s Theo style (max_job_seconds). Quá mức này job kết thúc timeout, không tính tiền.
Thời gian trả lời submit dưới 3 giây Submit chỉ kiểm tra, giữ chỗ giá và xếp job vào hàng.
File output 72 giờ Xem Output.

Trạng thái và thông báo bảo trì: wandura.instatus.com. Chưa cam kết SLA uptime.

Tính tiền thế nào

  • Tính tiền: job succeeded trên key live, theo giá của style.
  • Không tính tiền: failed (lỗi tạo, đầu vào không dùng được, bị moderation chặn), timeout, cancelled, và mọi thứ bị từ chối ngay lúc submit (4xx). Giá được giữ khi job bắt đầu và được trả lại khi job kết thúc mà không có kết quả.
  • Key test không bao giờ tính tiền.

Phiên bản

  • Đường dẫn có version (/v2/…). Model id mang version riêng (wandura-image-v1 là version của model, không phải version của API).
  • Thay đổi phá vỡ tương thích đến dưới dạng version đường dẫn mới hoặc model id mới, báo trước ít nhất 30 ngày. Bản cũ vẫn chạy trong thời gian đó.
  • Thay đổi cộng thêm có thể ra bất cứ lúc nào: style mới, field tuỳ chọn mới, mã lỗi mới, field mới trong câu trả lời. Bỏ qua field bạn không biết, và coi error.code lạ là không retry được trừ khi retryable nói khác.

Liên hệ

  • Trạng thái và bảo trì: wandura.instatus.com.
  • Câu hỏi, nâng giới hạn, sự cố: [đầu mối tích hợp của Wandura: sẽ bổ sung].
  • Gửi kèm job_id và X-Request-Id của lời gọi.

Cập nhật 14/10/2026 · Lịch sử thay đổi (1)

↑↓ chọnEnter mởEsc đóng

Gửi góp ý

Góp ý được gửi thẳng tới đội Wandura, kèm địa chỉ trang này.

0 / 4000