Skip to main content
Install the npm package:
npm install @asciidev/box-sdk
Import BoxApi, configure auth, and call methods like box.create, box.prompt, and box.events.

Configure

import { BoxApi, Configuration, waitUntilReady, waitForPrompt } from "@asciidev/box-sdk";

const box = new BoxApi(new Configuration({
  basePath: process.env.BOX_BASE_URL ?? "https://ascii.dev/api/box/v1",
  accessToken: process.env.BOX_API_KEY ?? (() => {
    throw new Error("Set BOX_API_KEY from the Box dashboard API keys tab.");
  })(),
}));
For TypeScript projects, use modern Node resolution and DOM fetch types:
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "lib": ["ES2022", "DOM"],
    "types": ["node"],
    "strict": true
  }
}
Node 18+ provides fetch. Older runtimes need a fetch polyfill. For ESM examples, set "type": "module" in package.json; CommonJS projects can use the require example below.

Create, prompt, and clean up

import { BoxApi, Configuration } from "@asciidev/box-sdk";

const box = new BoxApi(new Configuration({
  basePath: process.env.BOX_BASE_URL ?? "https://ascii.dev/api/box/v1",
  accessToken: process.env.BOX_API_KEY ?? (() => {
    throw new Error("Set BOX_API_KEY from the Box dashboard API keys tab.");
  })(),
}));

async function main() {
  let boxId: string | undefined;

  try {
    const created = await box.create({
      createBoxRequest: { ttlSeconds: 1800 },
    });
    boxId = created.box.id;

    await box.update({
      boxId,
      updateBoxRequest: { name: "sdk-demo" },
    });

    await waitUntilReady(box, boxId);

    const queued = await box.prompt({
      boxId,
      promptRequest: {
        provider: "codex",
        prompt: "Inspect the repository and summarize the test command.",
      },
    });

    const run = await waitForPrompt(box, boxId, queued.promptId);
    console.log(run.status);

    const events = await box.events({ boxId, limit: 50, type: "prompt,response,git_checkpoint" });
    console.log(events.events);
  } finally {
    if (boxId) await box.stop({ boxId });
  }
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});
Use ttlSeconds when creating a Box. Set a friendly name afterward with box.update.

CommonJS

const { BoxApi, Configuration } = require("@asciidev/box-sdk");

const box = new BoxApi(new Configuration({
  basePath: process.env.BOX_BASE_URL || "https://ascii.dev/api/box/v1",
  accessToken: process.env.BOX_API_KEY,
}));

Methods

All methods are called on BoxApi. Request bodies are plain objects typed by the exported model interfaces.
MethodArgumentsReturnsUse
me()nonePromise<MeResponse>Get the authenticated Box account user.
limits()nonePromise<LimitsResponse>Check whether the account can create or operate Boxes before starting work.
repos({ sync, limit, cursor, sort, q, selected }?)optional sync, pagination, search, and selected-only filtersPromise<ReposResponse>List GitHub installations, repositories, and selected repositories.
selectRepo({ repoSelectionRequest })repositoryId, optional baseBranchPromise<RepoSelectionResponse>Select a repository for future Boxes. Use databaseId from repos() as repositoryId.
apiKeys()nonePromise<ApiKeysResponse>List API key metadata. Raw secrets are not returned.
createApiKey({ apiKeyCreateRequest }?)optional namePromise<ApiKeysResponse>Create an API key. Store response.secret immediately; it is returned once.
rotateApiKey({ apiKeyId })API key idPromise<ApiKeysResponse>Rotate an API key secret. Store response.secret immediately; it is returned once.
deleteApiKey({ apiKeyId })API key idPromise<ApiKeyDeleteResponse>Delete an API key.
secrets()nonePromise<SecretsResponse>Read the current environment variables and secret files configured for Boxes.
updateSecrets({ secretsUpdateRequest })envContents, secretFilesPromise<SecretsResponse>Replace the complete secret setup. Send every env var and file that should remain.
boxes({ limit, cursor, sort, state }?)optional pagination and state filterPromise<BoxListResponse>List Boxes for the account.
create({ createBoxRequest }?)optional ttlSecondsPromise<CreateBoxResponse>Create a Box. Use ttlSeconds: null to disable auto-stop.
get({ boxId })Box idPromise<BoxInfoResponse>Fetch the latest Box state and connection fields.
update({ boxId, updateBoxRequest })Box id plus name and/or ttlSecondsPromise<BoxInfoResponse>Rename a Box or change its auto-stop TTL.
stop({ boxId })Box idPromise<BoxActionResponse>Stop/archive a Box.
resume({ boxId })Box idPromise<BoxActionResponse>Resume an archived Box. Poll get() until it is ready.
fork({ boxId })Box idPromise<BoxActionResponse>Create a new Box from the source Box snapshot.
remove({ boxId })Box idPromise<DeleteBoxResponse>Delete a Box record and associated machine resources when allowed.
prompt({ boxId, promptRequest })Box id plus provider, prompt, optional model, optional reasoningEffortPromise<PromptResponse>Queue work inside a Box. Returns promptRun.status and promptId.
promptRunStatus({ boxId, promptId })Box id and prompt idPromise<PromptRunResponse>Read first-class prompt run status.
events({ boxId, limit, cursor, sort, type })Box id plus optional pagination/filteringPromise<EventsResponse>Read typed event history for a Box.
readFile({ boxId, path, encoding })Box id and relative pathPromise<FileReadResponse>Deterministically read a text/base64 file from the Box work directory.
writeFile({ boxId, fileWriteRequest })Box id plus relative path/contentPromise<FileWriteResponse>Deterministically write a text/base64 file.
command({ boxId, commandRequest })Box id plus command/cwd/timeoutPromise<CommandResponse>Execute a bounded command in the Box work directory.
artifact({ boxId, path })Box id and relative pathPromise<Blob>Download an artifact as bytes.
interrupt({ boxId })Box idPromise<BoxActionResponse>Interrupt current work in a running Box.
desktop({ boxId, vnc, theme, requestBody })Box id plus optional desktop parameters. For VNC, send requestBody: { publicAccess: true } to return a URL without _token.Promise<DesktopResponse>Create or fetch a desktop streaming URL. Treat returned URLs as secrets. If provisioning is true, poll again.
sshKey({ boxId, sshKeyRequest })Box id plus public SSH keyPromise<SshKeyResponse>Add a public SSH key for Box SSH access.

Waiters and helpers

The package exports first-class waiters and deterministic file/command helper functions:
import { waitUntilReady, waitUntilIdle, waitForDesktop, waitForPrompt, waitForPromptDone, streamEvents, streamPrompt, stopAndRemove, readText, writeText, execCommand } from "@asciidev/box-sdk";

await waitUntilReady(box, boxId);
const queued = await box.prompt({ boxId, promptRequest: { provider: "codex", prompt: "Run tests" } });
await waitForPrompt(box, boxId, queued.promptId);
const publicVnc = await waitForDesktop(box, boxId, { publicAccess: true });
await writeText(box, boxId, "notes/result.txt", "done\n");
const result = await execCommand(box, boxId, "cat notes/result.txt");
await stopAndRemove(box, boxId);
Use waitForPrompt/waitForPromptDone instead of inferring completion from box.state plus event polling. Use streamPrompt or streamEvents when you need incremental response/tool-call events as work runs.

Streaming responses and tool calls

The SDK now exports streamEvents and streamPrompt for response streaming. They use the Box v1 events cursor API under the hood, so no separate SSE or WebSocket endpoint is required. response events carry text in event.data.content; streaming partials set event.data.isStreaming; tool-call events are response events with event.data.tools.
import { BoxApi, Configuration, streamPrompt } from "@asciidev/box-sdk";

const box = new BoxApi(new Configuration({
  basePath: "https://ascii.dev/api/box/v1",
  accessToken: process.env.BOX_API_KEY!,
}));

const stream = streamPrompt(box, boxId, {
  provider: "codex",
  prompt: "Run pwd and ls, then summarize the result.",
});

for await (const event of stream) {
  if (event.type !== "response") continue;
  const data = event.data;
  if (data.tools?.length) console.log("tools", data.tools);
  if (data.content) process.stdout.write(data.content);
  if (data.isStreaming) process.stdout.write("\n[partial]\n");
}
For a dashboard-style feed not tied to one prompt, use streamEvents(box, boxId, { type: "prompt,response,git_checkpoint" }) and stop it with an AbortController.

Operation request types

These exported interfaces wrap method parameters for BoxApi methods.
TypeFieldsUsed by
ArtifactRequestboxId, pathartifact()
BoxesRequestlimit, cursor, sort, stateboxes()
CommandOperationRequestboxId, commandRequestcommand()
CreateRequestcreateBoxRequestcreate()
CreateApiKeyRequestapiKeyCreateRequestcreateApiKey()
DeleteApiKeyRequestapiKeyIddeleteApiKey()
DesktopRequestboxId, vnc, theme, requestBodydesktop(); set requestBody.publicAccess for an ungated VNC URL.
EventsRequestboxId, limit, cursor, sort, typeevents()
ForkRequestboxIdfork()
GetRequestboxIdget()
InterruptRequestboxIdinterrupt()
PromptOperationRequestboxId, promptRequestprompt()
PromptRunStatusRequestboxId, promptIdpromptRunStatus()
ReadFileRequestboxId, path, encodingreadFile()
RemoveRequestboxIdremove()
ReposRequestsync, limit, cursor, sort, q, selectedrepos()
ResumeRequestboxIdresume()
RotateApiKeyRequestapiKeyIdrotateApiKey()
SelectRepoRequestrepoSelectionRequestselectRepo()
SshKeyOperationRequestboxId, sshKeyRequestsshKey()
StopRequestboxIdstop()
UpdateRequestboxId, updateBoxRequestupdate()
UpdateSecretsRequestsecretsUpdateRequestupdateSecrets()
WriteFileRequestboxId, fileWriteRequestwriteFile()
BoxesSortEnum"asc", "desc"boxes({ sort })
DesktopVncEnum1desktop({ vnc })
DesktopThemeEnum"light", "dark"desktop({ theme })
EventsSortEnum"asc", "desc"events({ sort })
ReadFileEncodingEnum"utf8", "base64"readFile({ encoding })
ReposSortEnum"asc", "desc"repos({ sort })

Model types

TypeScript models use camelCase fields.
TypeFieldsNotes
ApiKeyid, name, keyPrefix, keyLastFour, createdAt, lastUsedAtMetadata only; not the raw secret.
ApiKeyCreateRequestnameOptional display name.
ApiKeyDeleteResponseok, type, apiKeysReturned after deletion with the remaining key metadata.
ApiKeysResponseok, type, apiKeys, apiKey, secretsecret is present after create/rotate only.
Boxid, name, state, url, ip, createdAt, updatedAt, archiveAfter, desktopAvailable, desktopUrl, snapshotAvailable, snapshotCompletedAtdesktopUrl can contain a token; redact it.
BoxActionResponseok, type, id, status, boxReturned by lifecycle actions such as stop, resume, fork, and interrupt.
BoxInfoResponseok, type, boxReturned by get() and update().
BoxEventid, type, timestamp, taskId, data, plus additional fieldsExtensible event object returned inside EventsResponse.events. Branch on each event type.
BoxListResponseok, type, boxes, pageInfoReturned by boxes().
CommandRequestcommand, cwd, timeoutSecondsBounded command execution request.
CommandResponseok, type, success, exitCode, signal, stdout, stderr, stdoutTruncated, stderrTruncated, timedOut, cwd, startedAt, finishedAtReturned by command() and execCommand().
CompletionEventid, type, timestamp, taskId, dataEvent subtype for task_notification and compaction_complete.
CreateBoxRequestttlSecondsSeconds before auto-stop; null disables auto-stop.
CreateBoxResponseok, type, status, ttlSeconds, boxReturned immediately after creation starts.
DeleteBoxResponseok, type, id, statusReturned by remove().
DesktopResponseok, type, success, desktopUrl, ip, mode, provisioning, messageIf provisioning is true, poll desktop() again.
ErrorEnvelopeok, type, status, code, message, requestId, errorNon-2xx response body. Include requestId in support logs.
ErrorEnvelopeErrorcode, message, status, detailsStructured error details.
ErrorEventid, type, timestamp, taskId, dataEvent subtype for usage_limit and shield.
EventsResponseok, type, id, events, pageInfoevents contains Box event objects.
FileReadResponseok, type, success, path, encoding, size, contentReturned by readFile() and readText().
FileWriteRequestpath, content, encodingWrite a UTF-8 string or base64 payload.
FileWriteResponseok, type, success, path, encoding, sizeReturned by writeFile() and writeText().
GitCheckpointEventid, type, timestamp, taskId, dataEvent subtype for git_checkpoint.
GitCheckpointEventDatacommitSha, commitMessage, commitUrl, branch, filesChanged, additions, deletions, pushedGit checkpoint event payload.
LimitsFieldsaccessTier, blockedReason, currentLimits, standardLimits, trialLimits, upgradeEffects, canStart, checkoutRequired, startBlockedReason, contactMessage, activeBoxes, activeStates, maxActiveBoxes, maxCreationRequestsPerMinute, maxCreationRequestsPerDay, hasPaymentHistory, _package, subscriptionQuotaSeconds, subscriptionRemainingSeconds, packBalanceSeconds, creditPurchasedSeconds, creditUsedSeconds, liveUsageSeconds, creditSecondsPerDollar, billingStatus, subscriptionStatus, subscriptionCancelAtPeriodEnd, hasSubscription, subscriptionTrialEndsAt, subscriptionCurrentPeriodEnd, creditBalanceSecondsShared limit and billing-access fields. Use canStart and startBlockedReason before creating Boxes.
LimitsFieldsCurrentLimitsactiveBoxes, creationRatePerMinute, creationRequestsPerDayNumeric quota limits.
LimitsResponseok, type, plus all LimitsFields fieldsReturned by limits().
MeResponseok, type, userAuthenticated account response.
MeResponseAllOfUserlogin, emailUser identity fields.
PageInfonextCursor, hasMore, limitOptional pagination metadata on list responses.
PromptEventid, type, timestamp, taskId, dataEvent subtype for prompt.
PromptEventDataprompt, status, isRevertedPrompt event payload.
PromptRequestprovider, model, reasoningEffort, promptprovider is codex or claude-code. Omit model to use the saved default.
PromptResponseok, type, id, promptId, promptRun, status, provider, model, reasoningEffortReturned after work is queued.
PromptRunid, promptId, boxId, status, done, createdAt, model, reasoningEffortFirst-class prompt run state.
PromptRunResponseok, type, id, promptRunReturned by promptRunStatus().
ResponseEventid, type, timestamp, taskId, dataEvent subtype for response.
ResponseEventDatacontent, model, tools, isStreamingResponse event payload.
RepoSelectionRequestrepositoryId, baseBranchrepositoryId is a databaseId from repos().
RepoSelectionResponseok, type, success, environmentId, selectedRepositoriesReturned by selectRepo().
ReposResponseok, type, installations, environmentId, selectedRepositories, pageInfoRepository inventory and current selections.
Repositoryid, databaseId, name, fullName, _private, permissions, pushedAtUse databaseId when selecting a repository.
RepositoryInstallationtype, accountLogin, accountAvatarUrl, repositoriesGroup of repositories available through one installation/account.
SecretFilepath, contentsTreat contents as sensitive.
SecretsResponseok, type, success, environmentId, envContents, secretFiles, pushedCurrent secret setup. Treat envContents and secretFiles as sensitive.
SecretsUpdateRequestenvContents, secretFilesFull replacement request for secrets.
SelectedRepositoryid, databaseId, name, fullName, _private, permissions, pushedAt, baseBranch, setupRoutineId, setupScript, setupBlocking, preCommitHooksRepository selected for future Boxes.
SelectedRepositoryAllOfPreCommitHooksid, script, blockingPre-commit hook configured for a selected repository.
SshKeyRequestkeyPublic SSH key in OpenSSH format.
SshKeyResponseok, type, success, machineIp, sshUserReturned after adding an SSH key.
SuccessBaseok, typeBase success-envelope fields.
UnknownEventtype, plus additional propertiesForward-compatible fallback for event types the SDK does not model yet.
UpdateBoxRequestname, ttlSecondsSend only fields you want to change. ttlSeconds: null disables auto-stop.

Errors

Non-2xx responses reject with a ResponseError. Read the status and parse the JSON body for the structured Box error envelope. Redact API keys, Box secrets, SSH keys, and desktop URLs.
import { ResponseError } from "@asciidev/box-sdk";

try {
  await box.get({ boxId: "bx_missing" });
} catch (error) {
  if (error instanceof ResponseError) {
    console.error(error.response.status);
    console.error(await error.response.json());
  }
}