Skip to main content
Install the Python package:
python -m pip install ascii-box-sdk
Import BoxApi, configure auth, and call snake_case methods like box.create, box.prompt, and box.events.

Configure

import os

from ascii_box_sdk import ApiClient, Configuration
from ascii_box_sdk.api.box_api import BoxApi

config = Configuration(
    host=os.getenv("BOX_BASE_URL", "https://ascii.dev/api/box/v1"),
    access_token=os.environ["BOX_API_KEY"],
)

with ApiClient(config) as client:
    box = BoxApi(client)
Request models live under ascii_box_sdk.models. Prompting requires the selected provider to be configured for the authenticated account. If Codex or Claude Code credentials are missing, box.prompt(...) raises ApiException with code provider_not_configured.

Create, prompt, and clean up

import os
import time

from ascii_box_sdk import ApiClient, Configuration
from ascii_box_sdk.api.box_api import BoxApi
from ascii_box_sdk.models.create_box_request import CreateBoxRequest
from ascii_box_sdk.models.prompt_request import PromptRequest
from ascii_box_sdk.models.update_box_request import UpdateBoxRequest
from ascii_box_sdk import wait_until_ready, wait_for_prompt

config = Configuration(
    host=os.getenv("BOX_BASE_URL", "https://ascii.dev/api/box/v1"),
    access_token=os.environ["BOX_API_KEY"],
)

box_id = None
with ApiClient(config) as client:
    box = BoxApi(client)

    try:
        created = box.create(CreateBoxRequest(ttl_seconds=1800))
        box_id = created.box.id

        box.update(box_id, UpdateBoxRequest(name="sdk-demo"))

        wait_until_ready(box, box_id)

        queued = box.prompt(
            box_id,
            PromptRequest(
                provider="codex",
                prompt="Inspect the repository and summarize the test command.",
            ),
        )

        run = wait_for_prompt(box, box_id, queued.prompt_id)
        print(run.status)

        events = box.events(box_id, limit=50, type="prompt,response,git_checkpoint")
        print(events.events)
    finally:
        if box_id:
            box.stop(box_id)

Methods

All methods are called on BoxApi. Request bodies use model classes from ascii_box_sdk.models.
MethodArgumentsReturnsUse
me()noneMeResponseGet the authenticated Box account user.
limits()noneLimitsResponseCheck whether the account can create or operate Boxes before starting work.
repos(sync=None, limit=None, cursor=None, sort=None, q=None, selected=None)optional sync, pagination, search, and selected-only filtersReposResponseList GitHub installations, repositories, and selected repositories.
select_repo(RepoSelectionRequest)repository_id, optional base_branchRepoSelectionResponseSelect a repository for future Boxes. Use database_id from repos() as repository_id.
api_keys()noneApiKeysResponseList API key metadata. Raw secrets are not returned.
create_api_key(ApiKeyCreateRequest)optional nameApiKeysResponseCreate an API key. Store response.secret immediately; it is returned once.
rotate_api_key(api_key_id)API key idApiKeysResponseRotate an API key secret. Store response.secret immediately; it is returned once.
delete_api_key(api_key_id)API key idApiKeyDeleteResponseDelete an API key.
secrets()noneSecretsResponseRead the current environment variables and secret files configured for Boxes.
update_secrets(SecretsUpdateRequest)env_contents, secret_filesSecretsResponseReplace the complete secret setup. Send every env var and file that should remain.
boxes(limit=None, cursor=None, sort=None, state=None)optional pagination and state filterBoxListResponseList Boxes for the account.
create(CreateBoxRequest)optional ttl_secondsCreateBoxResponseCreate a Box. Use ttl_seconds=None to disable auto-stop.
get(box_id)Box idBoxInfoResponseFetch the latest Box state and connection fields.
update(box_id, UpdateBoxRequest)Box id plus name and/or ttl_secondsBoxInfoResponseRename a Box or change its auto-stop TTL.
stop(box_id)Box idBoxActionResponseStop/archive a Box.
resume(box_id)Box idBoxActionResponseResume an archived Box. Poll get() until it is ready.
fork(box_id)Box idBoxActionResponseCreate a new Box from the source Box snapshot.
remove(box_id)Box idDeleteBoxResponseDelete a Box record and associated machine resources when allowed.
prompt(box_id, PromptRequest)Box id plus provider, prompt, optional model, optional reasoning_effortPromptResponseQueue work inside a Box. Returns prompt_run.status and prompt_id.
prompt_run_status(box_id, prompt_id)Box id and prompt idPromptRunResponseRead first-class prompt run status.
events(box_id, limit=None, cursor=None, sort=None, type=None)Box id plus optional pagination/filteringEventsResponseRead typed event history for a Box.
read_file(box_id, path, encoding="utf8")Box id and relative pathFileReadResponseDeterministically read a text/base64 file from the Box work directory.
write_file(box_id, FileWriteRequest)Box id plus relative path/contentFileWriteResponseDeterministically write a text/base64 file.
command(box_id, CommandRequest)Box id plus command/cwd/timeoutCommandResponseExecute a bounded command in the Box work directory.
artifact(box_id, path)Box id and relative pathbytes responseDownload an artifact as bytes.
interrupt(box_id)Box idBoxActionResponseInterrupt current work in a running Box.
desktop(box_id, vnc=None, theme=None, request_body=None)Box id plus optional desktop parameters. For VNC, send request_body={"publicAccess": True} to return a URL without _token.DesktopResponseCreate or fetch a desktop streaming URL. Treat returned URLs as secrets. If provisioning is true, poll again.
ssh_key(box_id, SshKeyRequest)Box id plus public SSH keySshKeyResponseAdd a public SSH key for Box SSH access.

Waiters and helpers

The package exports first-class waiters and deterministic file/command helper functions:
from ascii_box_sdk import wait_until_ready, wait_until_idle, wait_for_desktop, wait_for_prompt, wait_for_prompt_done, stop_and_remove, read_text, write_text, exec_command

wait_until_ready(box, box_id)
queued = box.prompt(box_id, PromptRequest(provider="codex", prompt="Run tests"))
wait_for_prompt(box, box_id, queued.prompt_id)
public_vnc = wait_for_desktop(box, box_id, public_access=True)
write_text(box, box_id, "notes/result.txt", "done\n")
result = exec_command(box, box_id, "cat notes/result.txt")
stop_and_remove(box, box_id)
Use wait_for_prompt/wait_for_prompt_done instead of inferring completion from box.state plus event polling. Use stream_prompt or stream_events when you need incremental response/tool-call events as work runs.

Types

Python models use snake_case attributes. JSON serialization uses the API’s camelCase field names.
TypeFieldsNotes
ApiKeyid, name, key_prefix, key_last_four, created_at, last_used_atMetadata only; not the raw secret.
ApiKeyCreateRequestnameOptional display name.
ApiKeyDeleteResponseok, type, api_keysReturned after deletion with the remaining key metadata.
ApiKeysResponseok, type, api_keys, api_key, secretsecret is present after create/rotate only.
Boxid, name, state, url, ip, created_at, updated_at, archive_after, desktop_available, desktop_url, snapshot_available, snapshot_completed_atdesktop_url 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().
BoxListResponseok, type, boxes, page_infoReturned by boxes(). Use page_info.next_cursor when page_info.has_more is true.
BoxEventid, type, timestamp, task_id, data, plus additional fieldsExtensible event object returned inside EventsResponse.events. Branch on each event type.
CommandRequestcommand, cwd, timeout_secondsBounded command execution request. timeout_seconds defaults to 30 and is capped by the API.
CommandResponseok, type, success, exit_code, signal, stdout, stderr, stdout_truncated, stderr_truncated, timed_out, cwd, started_at, finished_atReturned by command() and exec_command().
CompletionEventid, type, timestamp, task_id, dataCompletion-style event such as task_notification or compaction_complete. data is extensible.
CreateBoxRequestttl_secondsSeconds before auto-stop; None disables auto-stop.
CreateBoxResponseok, type, status, ttl_seconds, boxReturned immediately after creation starts.
DeleteBoxResponseok, type, id, statusReturned by remove().
DesktopResponseok, type, success, desktop_url, ip, mode, provisioning, messageIf provisioning is true, poll desktop() again.
ErrorEnvelopeok, type, status, code, message, request_id, errorNon-2xx response body. Include request_id in support logs.
ErrorEnvelopeErrorcode, message, status, detailsStructured error details.
ErrorEventid, type, timestamp, task_id, dataError/protection event such as usage_limit or shield. data is extensible.
EventsResponseok, type, id, events, page_infoevents contains Box event objects. Use page_info.next_cursor when page_info.has_more is true.
FileReadResponseok, type, success, path, encoding, size, contentReturned by read_file() and read_text(). encoding is utf8 or base64.
FileWriteRequestpath, content, encodingWrite a UTF-8 string or base64 payload to a relative Box work-directory path.
FileWriteResponseok, type, success, path, encoding, sizeReturned by write_file() and write_text().
GitCheckpointEventid, type, timestamp, task_id, dataGit checkpoint event. data includes commit_sha, commit_message, commit_url, branch, files_changed, additions, deletions, and pushed.
LimitsFieldsaccess_tier, blocked_reason, current_limits, standard_limits, trial_limits, upgrade_effects, can_start, checkout_required, start_blocked_reason, contact_message, active_boxes, active_states, max_active_boxes, max_creation_requests_per_minute, max_creation_requests_per_day, has_payment_history, package, subscription_quota_seconds, subscription_remaining_seconds, pack_balance_seconds, credit_purchased_seconds, credit_used_seconds, live_usage_seconds, credit_seconds_per_dollar, billing_status, subscription_status, subscription_cancel_at_period_end, has_subscription, subscription_trial_ends_at, subscription_current_period_end, credit_balance_secondsShared limit and billing-access fields. Use can_start and start_blocked_reason before creating Boxes.
LimitsFieldsCurrentLimitsactive_boxes, creation_rate_per_minute, creation_requests_per_dayNumeric quota limits.
LimitsResponseok, type, plus all LimitsFields fieldsReturned by limits().
MeResponseok, type, userAuthenticated account response.
MeResponseAllOfUserlogin, emailUser identity fields.
PageInfonext_cursor, has_more, limitPagination metadata on list responses.
PromptEventid, type, timestamp, task_id, dataPrompt lifecycle event. data includes prompt, status, and optionally is_reverted.
PromptRequestprovider, model, reasoning_effort, promptprovider is codex or claude-code. Omit model to use the saved default.
PromptResponseok, type, id, prompt_id, prompt_run, status, provider, model, reasoning_effortReturned after work is queued.
PromptRunid, prompt_id, box_id, status, done, created_at, model, reasoning_effortFirst-class prompt run state. status is sending, queued, running, finished, or failed.
PromptRunResponseok, type, id, prompt_runReturned by prompt_run_status().
ResponseEventid, type, timestamp, task_id, dataAgent response event. data includes content, optional model, optional tools, and optional is_streaming.
RepoSelectionRequestrepository_id, base_branchrepository_id is a database_id from repos().
RepoSelectionResponseok, type, success, environment_id, selected_repositoriesReturned by select_repo().
ReposResponseok, type, installations, environment_id, selected_repositories, page_infoRepository inventory and current selections. Use page_info.next_cursor when page_info.has_more is true.
Repositoryid, database_id, name, full_name, private, permissions, pushed_atUse database_id when selecting a repository.
RepositoryInstallationtype, account_login, account_avatar_url, repositoriesGroup of repositories available through one installation/account.
SecretFilepath, contentsTreat contents as sensitive.
SecretsResponseok, type, success, environment_id, env_contents, secret_files, pushedCurrent secret setup. Treat env_contents and secret_files as sensitive.
SecretsUpdateRequestenv_contents, secret_filesFull replacement request for secrets.
SelectedRepositoryid, database_id, name, full_name, private, permissions, pushed_at, base_branch, setup_routine_id, setup_script, setup_blocking, pre_commit_hooksRepository selected for future Boxes.
SelectedRepositoryAllOfPreCommitHooksid, script, blockingPre-commit hook configured for a selected repository.
SshKeyRequestkeyPublic SSH key in OpenSSH format.
SshKeyResponseok, type, success, machine_ip, ssh_userReturned after adding an SSH key.
SuccessBaseok, typeBase success-envelope fields.
UnknownEventtype plus additional fieldsForward-compatible event shape for event types not modeled by the current SDK.
UpdateBoxRequestname, ttl_secondsSend only fields you want to change. ttl_seconds=None disables auto-stop.

Errors

Methods raise ApiException for non-2xx responses. Log the status, reason, and response body when debugging, but redact API keys, Box secrets, SSH keys, and desktop URLs.
from ascii_box_sdk.exceptions import ApiException

try:
    box.get("bx_missing")
except ApiException as exc:
    print(exc.status)
    print(exc.reason)
    print(exc.body)

Streaming responses and tool calls

The Python helper module exports stream_events and stream_prompt for response streaming. They long-poll the Box v1 events cursor API; no separate SSE or WebSocket endpoint is required. response events carry text in event.data.content; streaming partials set event.data.is_streaming; tool-call events are response events with event.data.tools.
from ascii_box_sdk import stream_prompt
from ascii_box_sdk.models.prompt_request import PromptRequest

for event in stream_prompt(
    box,
    box_id,
    PromptRequest(provider="codex", prompt="Run pwd and ls, then summarize the result."),
):
    if event.type != "response":
        continue
    data = event.data
    tools = data.get("tools") if isinstance(data, dict) else getattr(data, "tools", None)
    content = data.get("content") if isinstance(data, dict) else getattr(data, "content", "")
    if tools:
        print("tools", tools)
    if content:
        print(content, end="")
For a dashboard-style feed not tied to one prompt, use stream_events(box, box_id, type="prompt,response,git_checkpoint").