Skip to main content
This reference maps directly to the current implementation in:
  • examples/python-sdk/ara_sdk/modal_like.py
  • examples/python-sdk/ara_sdk/__init__.py
  • examples/python-sdk/ara (script runner wrapper)
Use /sdk/quickstart for first-run commands. Use this page when you need exact behavior and edge-case semantics.

Exported surface (ara_sdk)

from ara_sdk import (
    App,
    AraApp,
    run_cli,
    # workflow helpers
    cron,
    sandbox,
    subagent_hook,
    event_envelope,
    # runtime helpers
    runtime,
    entrypoint,
    file,
    local_file,
    # adapter helpers
    command_adapter,
    langchain_adapter,
    langgraph_adapter,
    agno_adapter,
    # artifact helpers
    git_artifact,
    tarball_artifact,
)

Default constants

The SDK currently hard-codes these defaults:
  • DEFAULT_SUBAGENT_MAX_CONCURRENCY = 4
  • DEFAULT_SUBAGENT_TIMEOUT_SECONDS = 120
  • DEFAULT_SUBAGENT_MAX_RETRIES = 2
  • DEFAULT_SUBAGENT_RETRY_BACKOFF_SECONDS = 5
  • DEFAULT_API_BASE_URL (production Ara API)
These defaults are injected into subagent runtime config and hook command wrappers unless explicitly overridden.

Manifest model

App.manifest resolves to:
{
  "name": "...",
  "slug": "...",
  "description": "",
  "agent": {},
  "workflows": [],
  "interfaces": {},
  "runtime_profile": {}
}
Slug derivation:
  • In App(...): project_name -> slug -> name
  • Normalized with lowercase + non [a-z0-9-] collapsed to -
  • Empty name raises ValueError; slug normalization may fall back to "workflow" when the slug source has no valid characters
Low-level define_app(...) also accepts dict manifests and enforces:
  • dict input
  • non-empty name
  • derivable slug from slug|project_name|project|name

App constructor and state

App(
    name: str,
    *,
    slug: str | None = None,
    project_name: str | None = None,
    description: str = "",
    agent: dict | None = None,
    interfaces: dict | None = None,
    runtime_profile: dict | None = None,
)
Internal tracked collections:
  • _agent_profiles: explicit or generated profile declarations
  • _subagent_specs: subagent metadata emitted into agent.subagents
  • _workflows: task/run/pipeline/hook workflows
  • _local_entrypoint: optional local function for local command

Decorators: detailed behavior

@app.agent(...)

Registers a profile in agent.profiles and supports:
  • id, instructions, handoff_to, always_on
Behavior details:
  • If id omitted, derives from function name
  • instructions falls back to docstring
  • Stores both instructions and backward-compatible persona
  • Upserts by profile id

@app.subagent(...)

High-level macro that does all of this in one call:
  1. Creates/updates an agent profile (@app.agent(...) internally)
  2. Creates a primary workflow (@app.task(...) internally)
  3. Injects shared-sandbox guardrail text into task instructions
  4. Stores subagent metadata in agent.subagents
  5. Expands hooks=[subagent_hook(...)] into additional workflows
Supported args:
  • id, workflow_id
  • instructions, handoff_to, always_on
  • task, trigger, schedule
  • adapter, runtime, sandbox, channels
  • hooks
Generated defaults:
  • workflow_id: defaults to id
  • runtime.working_dir: defaults to subagents/<subagent-id>
  • runtime.timeout_seconds: default 120
  • runtime.max_retries: default 2
  • runtime.retry_backoff_seconds: default 5
  • sandbox.policy: default "shared"
  • sandbox.max_concurrency: default 4
Hook expansion details:
  • Missing hook id becomes <workflow-id>-hook-<n>
  • Missing hook event becomes hook.tick
  • Missing hook task/command gets default task text
  • Hook command mode is wrapped in a shell guardrail launcher

@app.hook(...)

Defines visible hook workflows for schedule/event handling. Args:
  • id, event, agent
  • either task or command (mutually exclusive)
  • trigger, schedule
Execution mode:
  • If command is set: emits a run-mode workflow via @app.run(...)
  • Else: emits a task-mode workflow via @app.task(...)
Also stores metadata on function via __ara_hook__.

@app.task(...)

Alias for @app.workflow(mode="task", ...).

@app.run(...)

Alias for @app.workflow(mode="run", ...). Run-mode constraints:
  • command is required
  • agent= is rejected in run mode with a ValueError

@app.pipeline(...)

Alias for @app.workflow(mode="pipeline", ...).

@app.workflow(...)

Unified primitive behind task/run/pipeline. Key normalization:
  • Workflow ID is slugified
  • Trigger is normalized through _normalize_trigger(...)
  • Cron schedule can come from schedule= or trigger content
  • Empty cron expressions fall back to API trigger; cron syntax itself is not validated by the SDK

@app.local_entrypoint()

Registers one local callable used by python app.py local. call_local_entrypoint(...) invocation rules:
  • 0 args function -> called with no args
  • 1 arg function -> receives full input dict
  • N args function -> kwargs filtered by matching input keys

Guardrail command wrapper behavior

For hook command execution, SDK wraps your command with bash -lc script logic that:
  • creates/chdirs to namespaced workdir
  • sets or derives RUN_ID
  • sets or derives IDEMPOTENCY_KEY
  • exports runtime metadata env vars
  • emits structured JSON logs for start/done phases
Exported env vars in wrapped command:
  • ARA_APP_SLUG
  • ARA_SUBAGENT_ID
  • ARA_WORKFLOW_ID
  • ARA_RUN_ID
  • ARA_IDEMPOTENCY_KEY
  • ARA_TIMEOUT_SECONDS
  • ARA_MAX_RETRIES
  • ARA_RETRY_BACKOFF_SECONDS

Helper functions

cron(expression, timezone="UTC")

  • validates non-empty expression
  • returns {type:"cron", cron, schedule, timezone}

sandbox(...)

sandbox(
  idle_ttl_minutes: int | None = None,
  max_concurrency: int | None = None,
  policy: str | None = None,
) -> dict
Current limitations:
  • Only shared mode is enabled
  • Passing policy other than "shared" raises ValueError

subagent_hook(...)

Builds dict hook specs for @app.subagent(..., hooks=[...]). Validation:
  • event required
  • task and command are mutually exclusive

event_envelope(...)

Builds normalized event payload:
{
  "event": {
    "type": "...",
    "source": "...",
    "channel": "...",
    "message": "...",
    "payload": {},
    "metadata": {
      "run_id": "...",
      "idempotency_key": "..."
    }
  }
}

file(...) / local_file(...)

  • file(...) creates runtime file declarations
  • local_file(...) reads local disk content and maps target path
  • local_file(...) defaults executable=True
  • missing source file raises ValueError

runtime(...)

Runtime-profile builder with pass-through filtering for:
  • image/memory/volume
  • python/node packages
  • files/startup
  • adapter/artifacts

entrypoint(path, shell="bash", args=None)

  • validates non-empty path
  • returns startup descriptor for runtime_profile.startup

Adapter + artifact helpers

command_adapter(...) fields:
  • type: "command"
  • framework, transport, entrypoint, args
  • optional artifact, env
Specialized adapters:
  • langchain_adapter(...) adds AGENT_FRAMEWORK=langchain
  • langgraph_adapter(...) adds AGENT_FRAMEWORK=langgraph
  • agno_adapter(...) adds AGENT_FRAMEWORK=agno
Artifact helpers:
  • git_artifact(repo_url, ref="main", subdir="")
  • tarball_artifact(url, strip_prefix="")

Runtime client (AraApp)

AraApp is the imperative API wrapper used by CLI commands.

Construction

AraApp.from_env(manifest=..., cwd=...)
Behavior:
  • reads .env in cwd
  • only sets env vars that are currently unset
  • defaults API base to production when unset
  • requires ARA_ACCESS_TOKEN

App lookup semantics

_find_app_by_slug() does:
  • GET /apps
  • matches by manifest.slug
  • returns only owner role app rows (member apps ignored)
This avoids ambiguous cross-account collisions.

deploy(...)

deploy(
  activate=True,
  key_name=None,
  key_rpm=60,
  mint_key=True,
  warm=False,
  warm_workflow_id=None,
  on_existing=None,  # update|error
)
Behavior:
  • existing app:
    • on_existing=error -> raises RuntimeError
    • else updates via PATCH /apps/{id}
  • missing app:
    • creates via POST /apps
  • if activate=True: sets status active
  • if mint_key=True: creates app key and writes .runtime-key.local
  • if warm=True: invokes run endpoint with warmup=True
Warmup key resolution priority:
  1. newly minted key value
  2. resolved runtime key fallback source

run(...)

  • resolves app by owner+slug
  • runtime key priority:
    1. explicit arg
    2. resolved runtime key fallback source
  • calls POST /v1/apps/{app_id}/run

events(...)

  • same app and key resolution as run(...)
  • optional idempotency_key forwarded as X-Idempotency-Key
  • calls POST /v1/apps/{app_id}/events

invite(...)

  • calls POST /apps/{app_id}/invites
  • defaults: role="viewer", expires_in_hours=168

setup(...)

  • calls GET /apps/{app_id}/setup

Typical return payloads

deploy(...) returns:
{
  "app_id": "app_...",
  "slug": "investor-meeting-booking",
  "runtime_key_written": true,
  "runtime_key_path": "/abs/path/.runtime-key.local",
  "warmup": {}
}
run(...) and events(...) return raw backend JSON response bodies unchanged.

HTTP client semantics (_Http)

Transport characteristics:
  • JSON request/response
  • 30s timeout per request
  • control-plane auth: Bearer <user_jwt>
  • runtime auth for run/events: Bearer <runtime_key>
Error behavior:
  • Non-2xx raises RuntimeError with method/path/status/body

Internal utilities (non-exported, still relevant)

These live in modal_like.py and shape behavior:
  • _slugify(value) -> workflow/agent/app id normalization
  • _normalize_trigger(trigger, schedule) -> cron/api trigger normalization
  • _new_run_id() -> UTC timestamp + short UUID suffix
  • _parse_input_pairs(...) -> CLI key-value parser
  • _read_dotenv(...) -> non-overwriting .env loader
  • _require_env(...) -> required env var validation
  • _task_guardrail_prefix(...) -> task instruction guardrail preamble
  • _wrap_hook_command_with_guardrails(...) -> command workflow wrapper

CLI behavior (run_cli)

Subcommands:
  • deploy
  • up (alias for deploy)
  • run
  • events
  • local
  • invite
  • setup
Default command:
  • if no subcommand provided, defaults to deploy

deploy / up

python3 app.py deploy \
  [--activate true|false] \
  [--key-name NAME] \
  [--rpm 60] \
  [--warm true|false] \
  [--warm-workflow WORKFLOW_ID] \
  [--on-existing update|error]

run

python3 app.py run [--workflow WF] [--message TEXT] [--input key=value ...]
Auto-injected into input payload when missing:
  • run_id
  • idempotency_key (derived from workflow + run id)

events

python3 app.py events \
  [--workflow WF] \
  [--event-type TYPE] \
  [--channel CHANNEL] \
  [--source SOURCE] \
  [--message TEXT] \
  [--input key=value ...] \
  [--metadata key=value ...] \
  [--idempotency-key KEY]
If --idempotency-key omitted, SDK generates one.

local

python3 app.py local [--input key=value ...]
Requires App object and @app.local_entrypoint().

invite

python3 app.py invite \
  [--email user@example.com] \
  [--role viewer|operator|editor] \
  [--expires-hours 168]
If --email omitted, falls back to ARA_DEFAULT_INVITE_EMAIL.

setup

python3 app.py setup

CLI input parsing rules

--input and --metadata parse only key=value pairs.
  • entries without = are ignored
  • key is trimmed
  • empty key is ignored

Validation and error rules

Common validation errors raised by SDK:
  • App(name=...) with empty name
  • cron("")
  • sandbox(policy="per_run") or any non-shared policy
  • subagent_hook(...) with both task and command
  • @app.hook(...) with both task and command
  • @app.run(...) without command
  • @app.run(..., agent=...) (agent not allowed in run mode)
  • missing runtime key for run/events
  • local command without @app.local_entrypoint()

Script runner (examples/python-sdk/ara)

./ara is a wrapper over run_cli for script-first usage:
./ara deploy my_script.py
./ara run my_script.py --workflow booking-coordinator --message "Need 3 slots"
Behavior:
  • Loads module from script path dynamically
  • Finds app variable or first App instance in module
  • Supports commands: deploy|up|run|events|setup|invite|local

Environment variables

VariableRequiredPurpose
ARA_ACCESS_TOKENyesUser JWT for /apps/* routes
ARA_DEFAULT_INVITE_EMAILnoDefault invite email for CLI invite

Endpoint map used by SDK

SDK operationMethod + pathAuth
List appsGET /appsuser JWT
Create appPOST /appsuser JWT
Update appPATCH /apps/{app_id}user JWT
Get setupGET /apps/{app_id}/setupuser JWT
Create keyPOST /apps/{app_id}/keysuser JWT
Run workflowPOST /v1/apps/{app_id}/runapp runtime key
Send eventPOST /v1/apps/{app_id}/eventsapp runtime key
Create invitePOST /apps/{app_id}/invitesuser JWT

Known current constraints

  • Dedicated sandbox policies are intentionally disabled for now.
  • High-scale queueing/backpressure is not built in; use your own durable queue/store.
  • SDK is Python-first; JS/Go usage is not covered by this module.
  • /sdk/overview for primitives + hello-world cron
  • /sdk/quickstart for end-to-end run sequence
  • /examples/meeting-investor-bot for the investor meeting app walkthrough