Typed JavaScript/TypeScript client for the 2DAI generation API.
Generate images and video, upload references, browse and organise your cloud
drive, and download results — all with an API key. Node 18+ and modern browsers.
Zero runtime dependencies.
Typed JavaScript/TypeScript client for the 2DAI generation API — generate images & video, upload references, and download results with an API key.
2026-11-13
ดาว
—
เครื่องมือ
—
อัปไทม์
—
Not on JavaScript? The same platform is a plain REST API — jump to
Use the REST API.
npm install 2dai-cloud-sdk
New — Gen8 Preview images
client.generate.image({ imageModel: 'next', ... }) runs Gen8 Preview images, the preview of 2DAI's next image engine:
up to 6refCreationIds kept natively (3 on the default model), legible text in the picture, long literal
prompts honoured, the same quality ids as today. The channel opens at Holder — read me().canUseImageNext and
me().qualities.imageNext instead of guessing; a next image is priced on render time (roughly four to seven times the same preset on Gen 7.2),
and every reference adds a surcharge on both models (costUsd on the ticket is the truth).
Gen8 Preview video
client.generate.video({ videoModel: 'next', ... }) runs Gen8 Preview video, the preview of 2DAI's next video engine:
clips come out with sound, from a first frame (inputCreationId) plus up to 6 refCreationIds (characters,
props and places kept consistent), with an optional aspectRatio. Lengths and presets follow the account —
fast quality and 1, 5, 8 or 10 s from Holder, every preset and 12, 15, 17 or 20 s at Founder: read
me().videoDurationsNext and me().qualities.videoNext instead of guessing. Since 2.7.0 the clip can follow an
audio reference too: uploads.audio() an MP3, then pass its id as audioCreationId (audioUse: 'music' for a
soundtrack, 'voice' for a voice sample) — and TIXI writes the full scenario around it.
Get a key
Sign in at 2dai.io (any active account works — no plan
required), then in the dashboard open Integrations → API keys → + New key.
Keys look like 2dai_sk_…, carry scopes (read, generate, …), and can have
an optional spend cap. The secret is shown once — store it somewhere safe. API
generations spend your account credit exactly like the studio. Full walkthrough:
Getting Started.
Quickstart
import { Client } from '2dai-cloud-sdk';
const client = new Client({ apiKey: process.env.TWODAI_API_KEY! });
// Generates, waits for completion, and returns the finished creation.
const gen = await client.generate.image({ prompt: 'a red origami fox', aspectRatio: '1:1' });
console.log(gen.creationId, gen.downloadUrl);
// Save the actual bytes to disk (Node).
await client.download(gen, { savePath: './fox' }); // → ./fox.jpg
Generating
generate.* submits to an async queue. By default it polls to completion and
returns the finished GenerationResult. Pass { wait: false } to get the queue
ticket immediately and poll yourself.
// One-shot (default): resolves when the image is ready.
const img = await client.generate.image({
prompt: 'a neon city street, rain',
aspectRatio: '16:9', // or 'auto' — the server picks the best ratio from the prompt
// style + quality default to 'auto' (the server picks by your tier);
// recommended: quality 'max' for images, 'ultra' at 5 s for video, 'ultimate' = highest resolution
});
// `enhanced` rewrites your prompt with TIXI, the 2DAI prompt agent, before it
// reaches the image model — it fills in camera, lighting, composition and style
// from a short brief. ~2s extra, no extra credit. Your text stays in `.prompt`;
// TIXI's version is in `.raw.finalPrompt`.
const rich = await client.generate.image({ prompt: 'a warrior', enhanced: true });
// Gen8 Preview images, the new image engine (Holder+): up to 6 references kept natively,
// legible text, long literal prompts. Same quality ids; me().qualities.imageNext
// lists what the account may submit.
const poster = await client.generate.image({
imageModel: 'next',
prompt: 'a concert poster titled "NEON TIDE": the singer with the red braids on the neon-lit festival stage, bold white typography',
refCreationIds: [singerId, venueId], // optional, ≤6 (each adds a surcharge)
quality: 'max',
});
// Non-blocking: get a ticket now, poll later.
const { queueId } = await client.generate.image({ prompt: '…' }, { wait: false });
const state = await client.queue.waitFor(queueId, { timeoutMs: 300_000 });
// Reference tools (character-ref / style-transfer / smart-edit), on the Gen8 image engine:
const ref = await client.generate.imageWithRefs({
tool: 'character-ref', // 1–6 shots of one character; face photos lock the exact same face
prompt: 'as an astronaut',
refCreationIds: [gen.creationId],
});
// Smart edit: refs[0] is the image to EDIT, the prompt is the instruction.
const edited = await client.generate.imageWithRefs({
tool: 'smart-edit',
prompt: 'replace the background with a rainy neon street',
refCreationIds: [gen.creationId],
});
// Artist Painter: paint your subject as a new work in a curated
// artistic style. `artisticStyles.list()` gives the ids; omit `artisticStyleId` for 'auto'.
const styles = await client.artisticStyles.list();
const painting = await client.generate.artisticStyle({
prompt: 'a lighthouse on a stormy coast',
artisticStyleId: styles[0]?.artisticStyleId, // or 'auto'
refCreationIds: [gen.creationId], // optional subject images (≤6)
});
// Video from an existing still (silent, 5, 6 or 7 s — 7 s needs Supporter+).
// Every video style except 'raw' runs TIXI on your prompt (the style's guidance
// only exists through that rewrite); 'raw' sends it verbatim unless enhanced: true.
const vid = await client.generate.video({
prompt: 'gentle camera push-in',
inputCreationId: gen.creationId,
duration: 5,
style: 'raw', // verbatim — add `enhanced: true` to let TIXI write the scenario
});
// Gen8 Preview video, the new video engine (clips WITH sound): a first frame plus up to 6
// references. Lengths and presets follow the account (me().videoDurationsNext /
// qualities): Fast and 1-10 s from Holder, up to 20 s at Founder. The SDK waits up to 20 min here.
const next = await client.generate.video({
videoModel: 'next',
prompt: 'the kite surfer lands, waves at the camera, the crowd cheers',
inputCreationId: gen.creationId, // first frame
refCreationIds: [characterId, placeId], // optional, ≤6 (each adds a surcharge)
duration: 10, // one of me().videoDurationsNext
aspectRatio: '16:9', // 'auto' = the first frame's ratio
style: 'raw', enhanced: true, // your idea → TIXI writes the Gen8 Preview video brief (shots, pictures, sound)
});
// Gen8 Preview video with an audio reference: your MP3 as the soundtrack ('music') or
// as a voice sample the speaking character follows ('voice'). Counts as one
// more reference for the price.
const track = await client.uploads.audio({ path: './theme.mp3' });
const scored = await client.generate.video({
videoModel: 'next',
prompt: 'the band walks on stage as the intro swells',
inputCreationId: gen.creationId,
audioCreationId: track.creationId,
audioUse: 'music',
duration: 8,
});
// Wallpaper-resize: expand a creation into a wallpaper dimension.
// Quality is always Ultra; the dimension drives the price.
const wall = await client.generate.wallpaper({
inputCreationId: gen.creationId,
dimension: 'widescreen', // 'standard' | 'photo' | 'widescreen' | 'ultrawide'
});
Each tool caps its references (3 image, 6 on imageModel: 'next' · 6 face/character · 3 style-transfer ·
4 smart-edit · 3 wallpaper · 7 Gen8 Preview video pictures) — exceeding a cap rejects with TOO_MANY_REFS
before anything is charged. A ticket that is still pending can be stopped
with client.queue.cancel(queueId); the charge is refunded. Details:
Generating.
Uploading your own media
Bring an image/gif/mp4 (an .m4v is accepted and stored as mp4) into your library so it can be used as a reference or
animated, or an MP3 that becomes the audio reference of a Gen8 Preview video clip. The
same moderation pass as the studio runs server-side.
const up = await client.uploads.image({ path: './portrait.jpg' }); // Node
const up2 = await client.uploads.image({ data: fileBlob }); // Browser
await client.generate.imageWithRefs({ tool: 'character-ref', prompt: 'in a suit', refCreationIds: [up.creationId] });
// An MP3 (5 min max, Holder tier and up) for Gen8 Preview video — see generate.video above.
const track = await client.uploads.audio({ path: './theme.mp3' });
// File it straight into a folder, and skip re-uploads with the md5 pre-flight:
const probe = await client.uploads.checkDuplicate(md5Hex, folder.folderId);
if (!probe.duplicate) {
await client.uploads.image({ path: './portrait.jpg', targetFolderId: folder.folderId });
}
targetFolderId files the upload into a folder you can write to (403
INVALID_FOLDER otherwise). croppedFromCreationId / erasedFromCreationId
record the upload as a crop or erase edit of a creation you own. Details:
Uploads.
Downloading
client.cdn authenticates with your key, so it works for private creations too.
Accepts a CDN id, a Creation, a QueueState, or a generation result.
await client.cdn.download(gen, { savePath: './out' }); // Node → './out.jpg' (ext inferred)
const asset = await client.cdn.fetch(gen); // { bytes, contentType, ext }
const blob = await client.cdn.toBlob(gen); // Browser
const dataUrl = await client.cdn.toDataUrl(gen); // <img src={dataUrl} />
// The CDN can resize before it sends: `maxSide` scales the longest edge and
// keeps the aspect ratio, so one call covers portrait and landscape.
const preview = await client.cdn.toDataUrl(gen, { maxSide: 512 }); // 1164 kB → 132 kB
await client.cdn.download(gen, { maxSide: 256, savePath: './thumb' }); // composes with savePath
maxSide is what you want for previews and thumbnails; width / height target
one axis specifically and may crop. Details:
Downloading.
Cloud drive
Organise your library from code — folders, moving, trash, publishing (scopes:
manage for organising, publish for the public feed).
creations.list speaks the same lenses and filters as the studio's cloud
drive — one mode per call (a folder, an activity view, a smart collection,
or a folder shared with you), plus filters that compose with any of them.
await client.creations.list({ folderId: folder.folderId }); // or 'root', or { trashed: true }
await client.creations.list({ activity: 'uploaded' }); // all|history|uploaded|likes|public|trash|sdk|mcp|portfolio
await client.creations.list({ smart: 'faces' }); // faces|videos|crop|alpha|favorites
// Filters compose with any lens: whole-word search (trailing * = prefix),
// sort, aspect-ratio buckets, date range, the studio's Likes and Type filters…
await client.creations.list({ search: 'red fox*', sort: 'size-desc', ratioFilter: 'portrait' });
await client.creations.list({ folderId: folder.folderId, likeFilter: 'liked', mediaKind: 'video' });
// ONE uniformly-random creation from any lens (images and videos only), same filters:
const surprise = await client.creations.random({ smart: 'favorites', mediaKind: 'image', blockNsfw: true });
// The cross-user public feed:
const feed = await client.creations.feed({ limit: 48 });
Paginate with nextBeforeDate (limit 1..100) or 1-based page. Rows carry
description, folderId, inTrash, isPublicShared, likes, isLiked
and more. Details:
Cloud Drive.
Deleting a group only detaches its folders — nothing else is deleted.
Account & history
const me = await client.me();
console.log(me.creditUsd, me.tier, me.key.scopes);
console.log(me.promptMaxChars, me.promptMaxCharsNext); // prompt cap for this tier (1,500 → 20,000), and the tier that raises it
const page = await client.creations.list({ limit: 20 });
const one = await client.creations.get(page.creations[0].creationId);
const state = await client.queue.get(queueId); // one-shot status read
const url = client.cdn.url(one); // raw CDN url (public creations)
Stats
Read-only account analytics (scope read):
const o = await client.stats.overview(); // storage, counters, streak, per-tool recents
const g = await client.stats.generations({ days: 30 }); // volume + spend by day / tool / source
const t = await client.stats.top(); // most-used refs (with suggested config), styles, keywords
Wallet data (read-only)
The finance namespace reads your wallet: balance, tier, staking lock,
transactions, balance/credit history. It needs a key minted with the opt-in
finance scope (off by default) — and it is strictly read-only: no deposit,
withdraw, swap or lock operation exists on the API-key surface, so a finance
key can see numbers but can never move funds.
Every failure throws a typed error you can branch on:
import { InsufficientCreditError, SpendLimitError, NsfwRejectedError, ApiError } from '2dai-cloud-sdk';
try {
await client.generate.image({ prompt: '…' });
} catch (err) {
if (err instanceof InsufficientCreditError) console.log('Top up:', err.deficitUsd);
else if (err instanceof SpendLimitError) console.log('Key cap hit at', err.spendLimitUsd);
else if (err instanceof NsfwRejectedError) console.log('Blocked by moderation');
else if (err instanceof ApiError) console.log(err.code, err.httpStatus);
else throw err;
}
Other errors: AuthError, ScopeError, TierError, QueueLimitError,
RateLimitError, ValidationError, NotFoundError, GenerationFailedError,
TimeoutError. All extend ApiError (.code, .httpStatus, .details).
A GenerationFailedError (and a failed QueueState) also carries errorCode to
branch on (CAPACITY_FULL, EXPIRED, GENERATION_TIMEOUT, …) and errorMessage,
a readable sentence you can show as is.
A TierError names the tier that opens what was asked (requiredTier, requiredTierName)
and ships a ready sentence in message ("Gen8 Preview images open at Holder. Upgrade your tier…");
nothing was charged.
A content-rated result is not an error: a creation with nsfwFlagged: true
(nsfwRate 0.8 or 0.99) completed and was charged, but it stays masked in the
owner's drive, is left out of public feeds and cannot be published
(NSFW_NOT_PUBLISHABLE). Hand your user the viewUrl to review it rather than
retrying. Only NSFW_MAX_EXCEEDED (platform ceiling) discards the output — nothing is
charged, reword the prompt. Details: Content rating.
The 2DAI stack
We currently use our Gen 7.2 model, and unlike others, we don't resell
model access. All our models originate from the open-source community or our
own R&D — we then disassemble, modify, fine-tune and optimize them to align
with our legacy and 2DAI❤️ART lines. They also run on our own private
cloud network.
The Gen8 generation is rolling out as two previews behind the next channels:
Gen8 Preview video (videoModel: 'next', clips with sound) and Gen8 Preview images
(imageModel: 'next', 6 references). Same rule, same cloud; me().modelChannels
names the engine behind each channel.
The prompt rewriter (the "TIXI" pass that expands short prompts before
generation) is our own in-house LLM — same rule: not re-sold third-party
model access. Depth:
About the Models.
Not on JavaScript? Use the REST API
Everything below is plain HTTP — the SDK is a convenience layer, not a gate.
Base URL https://dapp.2dai.io:444/v1, bearer auth, JSON in and out.
Generation is asynchronous: submit, then poll.
# 1 · submit → returns a queueId
curl -X POST https://dapp.2dai.io:444/v1/generate/image \
-H "Authorization: Bearer $TWODAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt":"a lighthouse at dusk"}'
# 2 · poll until status is completed, then fetch downloadUrl with the same bearer
curl https://dapp.2dai.io:444/v1/queue/$QUEUE_ID \
-H "Authorization: Bearer $TWODAI_API_KEY"
Full request/response shapes, every error code and the limits are documented in
the REST API reference.
Configuration
new Client({
apiKey: '2dai_sk_…', // required
baseUrl, // default 'https://dapp.2dai.io:444'
timeoutMs, // per-request network timeout, default 60000
maxRetries, // idempotent GET retries on 429/5xx, default 2
fetch, // inject a custom fetch
integration, // 'sdk' (default) | 'mcp' — official 2DAI integrations
integrationVersion, // that integration's own semver, e.g. '1.4.0'
});
integration is a closed set rather than a free-form User-Agent, and you should
leave it alone: the server stamps each creation's source from it, and the
dashboard reads source to fill its SDK / MCP collections and label where a row
came from. 'mcp' is reserved for the official 2DAI MCP server; anything you
build reports as sdk. integrationVersion must be a real 1.2.3; a malformed
value is replaced by 0.0.0 rather than omitted, so the token still attributes.
Generation submits are never auto-retried (they may charge); the server itself
de-duplicates an identical submit within ~5 s (409 IDEMPOTENT_RETRY), and a hex
clientToken you pass sets that window explicitly.
คัดลอกคอนฟิก stdio สำหรับ Cursor, Claude Desktop, Claude Code หรือ VS Code ได้ในหน้านี้ คอนฟิกนี้ใช้การติดตั้งของผู้เผยแพร่ ไม่ใช่พร็อกซีของ PlugMCP
2dai Cloud Sdk ต้องใช้ API key ไหม?
ระเบียนนี้ไม่ได้ระบุตัวแปรสภาพแวดล้อมที่จำเป็น
2dai Cloud Sdk ใช้ทรานสปอร์ตแบบใด?
2dai Cloud Sdk ทำงานแบบโลคัลผ่าน stdio
2dai Cloud Sdk ถูกตรวจสอบล่าสุดเมื่อใด?
ตรวจสอบล่าสุดเมื่อ 2026-11-13 Typed JavaScript/TypeScript client for the 2DAI generation API — generate images & video, upload references, and download results with an API key.