Catalog npm
PACKAGE EXPLORERView in registry

2dai-cloud-sdk

npm

Typed JavaScript/TypeScript client for the 2DAI generation API — generate images & video, upload references, and download results with an API key.

2daiagentsaiapi-clientclaudegenerative-aiimage-generationimage-to-videomcpsdktext-to-imagetypescriptvideo-generationagenticautonomousllm
All keywords (18)
multimodalvision
Versions collected43
Direct dependencies3From release 3.0.0
Last observed20 Oct, 19:11 UTC

README

Current package documentation

2dai-cloud-sdk

npm

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.

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 6 refCreationIds 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).

const folder = await client.folders.create({ title: 'Brand assets' });
await client.creations.move(gen, folder.folderId);   // or move(gen, 'root')
await client.creations.publish(gen);                 // generated work only
await client.creations.trash(gen);                   // reversible
await client.creations.restore(gen);
await client.creations.trash(gen); await client.creations.delete(gen); // permanent (trash first)
await client.folders.delete(folder.folderId, { trashContents: true });

Browsing your library

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…
await client.creations.list({ search: 'red fox*', sort: 'size-desc', ratioFilter: 'portrait' });

// ONE uniformly-random creation from any lens:
const surprise = await client.creations.random({ smart: 'favorites' });

// 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.

Organising

await client.creations.like(gen, true);              // idempotent SET, retry-safe
await client.creations.batch('trash', ids);          // bulk trash/untrash/delete, ≤1000 ids
await client.creations.reorder([id1, id2, id3]);     // stamp drag-reorder order

// Sidebar folder groups + folder metadata:
const group = await client.folders.groups.create('Client work');
await client.folders.update(folder.folderId, {
  groupId: group.groupId,            // null detaches
  posterCreationId: gen.creationId,  // null clears
  isFavorite: true,
});

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.

const w = await client.finance.wallet();          // tokens, creditUsd, tier, pending flags
const tx = await client.finance.transactions({ limit: 20 });
const price = await client.finance.tokenPrice();  // cached $2DAI quote — only needs `read`

Depth: Stats & Wallet.

Error handling

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"
MethodEndpointScopeWhat
GET/v1/mereadAccount, credit, key scopes
POST/v1/generate/imagegenerateText → image
POST/v1/generate/image-with-refsgenerateFace / character ref, style transfer, smart edit
POST/v1/generate/wallpapergenerateWallpaper-resize (priced by dimension)
POST/v1/generate/videogenerateStill → video
POST/v1/uploadsgenerateUpload media (multipart)
GET/v1/uploads/check-duplicatereadmd5 pre-flight dedup probe
GET/v1/queue/:queueIdreadPoll a generation
POST/v1/queue/:queueId/cancelgenerateCancel a pending generation (refunds)
GET/v1/creationsreadList / search your drive (lenses + filters)
GET/v1/creations/:idreadOne creation, full detail
GET/v1/feedreadThe cross-user public feed
POST/v1/creations/:id/move | /trash | /restore | /likemanageOrganise
POST/v1/creations/reorder | /batchmanageReorder / bulk actions
DELETE/v1/creations/:idmanagePermanent delete (trash first)
POST/v1/creations/:id/publish | /unpublishpublishPublic feed
GET/POST/PATCH/DELETE/v1/foldersread / manageFolders
GET/POST/PATCH/DELETE/v1/folder-groupsread / manageSidebar folder groups
GET/v1/stats/overview | /generations | /topreadAccount analytics
GET/v1/wallet*, /v1/account/tierfinanceRead-only wallet data
GET/v1/tiers, /v1/token/pricereadTier catalogue, cached token quote
GET/cdn/file/:cdnIdreadDownload bytes (?s=512 for a preview)

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.

License

MIT

Downloads & popularity

Awaiting observations

No metric observations yet

Download counts and popularity will appear here as the collector receives them.

Versions 43

Collected releases
VersionPublishedDirect dependenciesOpen version
3.0.0Registry latest14 Oct 20263
2.8.313 Oct 20263
2.8.212 Oct 20263
2.8.112 Oct 20263
2.8.011 Oct 20263
2.7.07 Oct 20263
2.6.16 Oct 20263
2.6.06 Oct 20263
2.5.927 Sept 20263
2.5.827 Sept 20263
2.5.723 Sept 20263
2.5.619 Sept 20263
2.5.510 Sept 20263
2.5.410 Sept 20263
2.5.32 Sept 20263
2.5.22 Sept 20263
2.5.12 Sept 20263
2.5.02 Sept 20263
2.4.323 Aug 20263
2.4.222 Aug 20263

Package advisories

0
No matching advisories collected

This describes our collected data; it is not a security assessment.

Repository

PENDING
2daicommunity/2dai-cloud-sdk

Shared repository data · GitHub

License detected in repository
Not collected

Metadata history

Meaningful changes
Current collected state Current

Observed since 20 Oct, 19:10 UTC

No superseded metadata states collected. Unchanged observations do not create new revisions.

Record identity

package:b82cb59d4b25d6f628a0715a67495c4321bd174aed38cee555695a5a45205476