AutomationGuides

Write a script

Define input and output, call platform capabilities and inspect results.

A script exports a default function that receives input. Its return value becomes the run's output.

Before running this page, complete quickstart steps 1–3 in the same Bash session: set PRAXIS_API_URL and PRAXIS_API_KEY, define SOURCE and run it, then define and run read_result. The helper checks for answer 42, as expected by the arithmetic examples here.

Entry point and schemas

Save this as script.ts:

export default async (input: { n: number }): Promise<{ answer: number }> => {
  console.log("Adding one", input.n);
  return { answer: input.n + 1 };
};

The input schema comes from parameter types. The output schema comes from an explicit return type, with Promise unwrapped. Supported forms include primitives, objects, arrays, unions and resolvable local type aliases. Unsupported types produce an empty schema and a warning. An empty schema does not validate your fields. Do not rely on a return expression to infer output constraints when the return annotation is missing.

Validate the definition to inspect the generated schemas before publishing:

curl --fail-with-body -sS "$PRAXIS_API_URL/automation/v1/validate" \
  -H "Authorization: Bearer $PRAXIS_API_KEY" \
  -H 'Content-Type: application/json' \
  --data "$(jq -n --rawfile source script.ts '{kind:"script",source:$source}')" \
  | jq -e '.valid == true and .input_schema.properties.n.type == "number" and .output_schema.properties.answer.type == "number"'

Expected: true and exit code 0. Inspect errors and warnings in the full validation response when a type cannot be inferred.

Execution and logs

RUN=$(curl --fail-with-body -sS "$PRAXIS_API_URL/automation/v1/runs" \
  -H "Authorization: Bearer $PRAXIS_API_KEY" \
  -H 'Content-Type: application/json' \
  --data "$(jq -n --rawfile source script.ts \
    '{source:$source,language:"ts",input:{n:41},wait:60}')")
RUN_ID=$(jq -er '.id' <<< "$RUN")
read_result "$RUN_ID"
curl --fail-with-body -sS "$PRAXIS_API_URL/automation/v1/runs/$RUN_ID/logs" \
  -H "Authorization: Bearer $PRAXIS_API_KEY"

Expected: the result assertion prints true; the log response includes Adding one 41. console.log/info/debug produce info entries; console.warn and console.error retain their levels. Logs are separate from output: use return for data the caller needs. Read logs, or subscribe with Accept: text/event-stream.

The global praxis object

The runtime supplies praxis. No import, API Key or package installation is required inside a script. Its SDK entry points are listed below. Methods use the reference pages' path, query and body options, for example praxis.automation.runs.create({body:{target,input}}). The initiator's permissions still govern access.

Entry pointReference
praxis.agentAgent
praxis.modelModel
praxis.fileFile
praxis.sandboxSandbox
praxis.searchSearch
praxis.databaseDatabase
praxis.automationAutomation
praxis.organizationOrganization
praxis.metaMeta

Scripts cannot access external networks directly. Use platform capabilities and await calls to receive their results and handle failures.

Call a built-in action

call("namespace/name", input) takes an Action reference and an input object and returns the Action's output. It is one step of the current run: it has its own input and output and takes one row in the call ledger; it does not create a child run. praxis is the platform namespace, and a bare name resolves only to platform Actions. A call always uses the Action's current revision; @version is not supported yet, and the namespace::name form is no longer accepted.

A failed call throws an error carrying code, message, hint, the call-ledger position seq and request_id. Several calls can run concurrently with Promise.all. praxis.* is the platform SDK only: call(...) and praxis.ocr(...) were removed and fail at runtime with a hint to use call.

Save this as chat.ts. It calls praxis/chat. The model is built into the action and maintained by its author; callers do not select a model. The same applies to praxis/ocr, praxis/image and praxis/embed. The legacy model field is deprecated and ignored. This call incurs model usage charges:

export default async (input: { prompt: string }) => {
  return await call("praxis/chat", {
    messages: [{ role: "user", content: input.prompt }],
    stream: false,
  });
};
RUN=$(curl --fail-with-body -sS "$PRAXIS_API_URL/automation/v1/runs" \
  -H "Authorization: Bearer $PRAXIS_API_KEY" \
  -H 'Content-Type: application/json' \
  --data "$(jq -n --rawfile source chat.ts \
    '{source:$source,language:"ts",input:{prompt:"Say hello in one sentence."},wait:60}')")
RUN_ID=$(jq -er '.id' <<< "$RUN")
curl --fail-with-body -sS "$PRAXIS_API_URL/automation/v1/runs/$RUN_ID" \
  -H "Authorization: Bearer $PRAXIS_API_KEY" | jq '{id,status,output,error}'

Expected: terminal status completed, model output in output, and null error. If still pending or running, repeat the final GET until terminal. For faulted, inspect the error; HTTP success alone does not prove a successful model call. Retrieve the action at praxis/chat for its complete input and output contract.

Call your own action

This example creates an action, then calls its full name. The generated script contains a literal reference so it can be checked before publication.

ACTION_NAME="add-one-$(date +%s)-$RANDOM"
curl --fail-with-body -sS "$PRAXIS_API_URL/automation/v1/actions" \
  -H "Authorization: Bearer $PRAXIS_API_KEY" \
  -H 'Content-Type: application/json' \
  --data "$(jq -n --arg name "$ACTION_NAME" '{
    namespace:"docs-samples",name:$name,
    input_schema:{type:"object",properties:{n:{type:"number"}},required:["n"],additionalProperties:false},
    output_schema:{type:"object",properties:{answer:{type:"number"}},required:["answer"],additionalProperties:false},
    implementation:{kind:"code",language:"ts",source:"export default async (input: { n: number }) => ({ answer: input.n + 1 });"}
  }')" | jq -e '.id | startswith("act_")'
SOURCE="export default async (input: { n: number }) => await call(\"docs-samples/$ACTION_NAME\", input);"
RUN=$(curl --fail-with-body -sS "$PRAXIS_API_URL/automation/v1/runs" \
  -H "Authorization: Bearer $PRAXIS_API_KEY" \
  -H 'Content-Type: application/json' \
  --data "$(jq -n --arg source "$SOURCE" \
    '{source:$source,language:"ts",input:{n:41},wait:60}')")
RUN_ID=$(jq -er '.id' <<< "$RUN")
read_result "$RUN_ID"

Expected: both creation and result checks print true, with answer 42. Archive the action when finished. Actions are reusable capabilities, but cannot orchestrate other actions, scripts or workflows from inside their implementation.

Call another script or workflow

invoke("namespace/name@version", input) starts another published script or workflow as a child run, waits for it to finish and returns the child's output; leave out @version for the latest. Unlike call, which is one step inside the current run and creates no child run, invoke creates a child run with its own ID, status and logs, and the call record points to it through child_run_id.

export default async (input: { urls: string[] }) => {
  // Use Promise.all to run several invokes at once; child runs start through a queue.
  const pages = await Promise.all(input.urls.map(url => invoke("team/fetch-page", { url })));
  return { pages };
};
  • The wait is capped: invoke waits synchronously, bounded by the parent run's deadline. At the cap it throws INVOKE_TIMEOUT with the child run ID in the message; the child is not cancelled, so you can read its result separately. For long waits use a workflow run: step, which consumes no compute while it waits.
  • Failures: a child that fails, is cancelled or cannot be created makes invoke throw; code is the child's error code and message carries the child run ID. A missing or archived target fails immediately and nothing is queued.
  • Cancel and timeout: when the parent run is cancelled or fails, children it is still waiting on are cancelled with it.
  • Finding child runs: list runs filtered by parent_run_id for each child's ID, target, status and duration.
  • Limits: at most 100 child runs per run, 20 executing at once (the rest queue), nesting up to 10 levels, and a run cannot invoke itself.

A script cannot start a detached child with invoke; to start a run without waiting, call create run.

Output and time budgets

Return JSON-serializable data. Do not return undefined, BigInt or circular references. UTF-8 encoded JSON output must fit within 1 MiB; larger output faults the run. Store large results in a file or other storage and return a reference.

The default script wall-clock limit is five minutes. CPU time is capped at 30 seconds and further bounded by the remaining deadline. Waiting for a platform call differs from executing pure computation; increasing wait time does not fix an infinite loop. See limits and errors for the full limits.

Diagnosing errors

CodeOriginal messageAction
SCRIPT_ERROR脚本执行出错Inspect the original exception and user-code locations in detail, fix the script and start a new run.
CPU_LIMIT_EXCEEDED脚本 CPU 时间超出上限Remove infinite loops and reduce computation per batch.
PAYLOAD_TOO_LARGEPayload exceeds 1 MiBReduce JSON output and return a storage reference.
INPUT_INVALIDValue must be JSON serializableReturn an explicit JSON value such as an object or null.

Execution failures appear in Run.error. Rejected create requests return an HTTP problem response. Handle these separately.

API reference → · Cookbook →

Export

On this page