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 point | Reference |
|---|---|
praxis.agent | Agent |
praxis.model | Model |
praxis.file | File |
praxis.sandbox | Sandbox |
praxis.search | Search |
praxis.database | Database |
praxis.automation | Automation |
praxis.organization | Organization |
praxis.meta | Meta |
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:
invokewaits synchronously, bounded by the parent run'sdeadline. At the cap it throwsINVOKE_TIMEOUTwith the child run ID in the message; the child is not cancelled, so you can read its result separately. For long waits use a workflowrun:step, which consumes no compute while it waits. - Failures: a child that fails, is cancelled or cannot be created makes
invokethrow;codeis the child's error code andmessagecarries 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_idfor 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
| Code | Original message | Action |
|---|---|---|
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_LARGE | Payload exceeds 1 MiB | Reduce JSON output and return a storage reference. |
INPUT_INVALID | Value must be JSON serializable | Return 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.