Runtime API

The Runtime API evaluates a workbook that xplo has already prepared. Send input values to a stable action ID and receive recalculated outputs in the same HTTP request, without running Excel or deploying a workbook runtime yourself.

For supported Go, TypeScript, Python, and Rust wrappers, see the Hosted HTTP SDKs. Their explicit http names distinguish network execution from the separately distributed Local Runtime Clients that execute a downloaded model on your infrastructure.

The Runtime API and Workbook API are separate parts of one workflow:

API Use it to Typical frequency
Workbook API Upload a workbook, start and monitor preparation, manage labels, and download artifacts When a workbook or its interface changes
Runtime API Inspect the prepared model's contract and execute it on xplo-managed infrastructure Once per scenario or calculation

They use the same base URL, bearer tokens, action IDs, organization access rules, and JSON error format. Preparing a workbook is asynchronous; executing a prepared workbook is synchronous.

How remote execution works

Workbook preparation produces an immutable plan.xvm model for an action. A remote run:

  1. Authenticates the caller and checks access to the action's file group.
  2. Loads the prepared model identified by the action ID.
  3. Starts from the workbook's captured baseline values and applies the input overrides in the request.
  4. Evaluates the requested dependency graph with xplo's shared vmrun interpreter.
  5. Returns the selected cell values and Excel errors as JSON.

The original spreadsheet is not uploaded again or modified by a run. The same prepared action can be evaluated repeatedly with different inputs. The downloadable runtime bundle uses the same model and interpreter contract as hosted execution, so you can move execution to your own infrastructure later without changing the workbook contract.

Each POST /run request is one server-side execution. Runs are not idempotent for usage metering: retrying a request performs and counts another run.

Prerequisites

You need:

  • An API token beginning with xplo_. Create or manage credentials in API Tokens.
  • The action ID of a successfully prepared workbook.
  • curl for the examples below; jq is useful for inspecting and constructing JSON.

See the Workbook API to upload a workbook and wait for its action to reach COMPLETED. A normal completed action is ready for remote execution; you do not need to request the optional native or WebAssembly compilation first.

Set the API base URL and token once:

export XPLO_API_URL='https://xplo.pythia.software/api/v2'
export XPLO_API_URL="${XPLO_API_URL%/}"
export XPLO_TOKEN='xplo_...'
export ACTION_ID='act-...'

Every Runtime API request uses the token as a bearer credential. API tokens are scoped to one organization, so the token must have access to the action.

Inspect the runtime contract

Do not guess which cells are valid inputs or outputs. Fetch the model manifest after preparation completes. No Hosted HTTP SDK wraps this endpoint, so fetch it over plain HTTP even when you drive runs through an SDK:

curl --fail-with-body -sS \
  "$XPLO_API_URL/actions/$ACTION_ID/vm/manifest" \
  -H "Authorization: Bearer $XPLO_TOKEN" \
  -o manifest.json

jq . manifest.json

A manifest looks like this:

{
  "schema_version": 2,
  "blob_hash": "19a687c0f74d8d4c",
  "input_count": 2,
  "output_count": 1,
  "intermediate_count": 1,
  "inputs": [
    { "address": "Loan!B1", "type": "number", "baseline": 300000 },
    { "address": "Loan!B2", "type": "number", "baseline": 0.065 }
  ],
  "outputs": [
    { "address": "Loan!B8", "type": "number" }
  ],
  "intermediates": [
    { "address": "Loan!B4" }
  ],
  "example": {
    "inputs": [
      { "address": "Loan!B1", "value": 300000 },
      { "address": "Loan!B2", "value": 0.065 }
    ],
    "outputs": ["Loan!B8"]
  }
}

The manifest is the source of truth for the prepared action:

  • inputs lists overridable cells, their runtime type, and baseline value.
  • outputs lists terminal cells that can be returned.
  • intermediates lists supported non-terminal cells that can also be selected as outputs.
  • example provides a connected input/output request when xplo can derive one; otherwise it is null.

An action captures a specific workbook version. Fetch its manifest again when you prepare a new action rather than assuming addresses and types have stayed the same.

Run by cell address

The smallest valid request uses no overrides. It evaluates the captured baseline and returns every terminal output:

curl --fail-with-body -sS -X POST \
  "$XPLO_API_URL/actions/$ACTION_ID/run" \
  -H "Authorization: Bearer $XPLO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"inputs":{}}' | jq .

For a scenario, key each input by its exact manifest address and select only the outputs you need:

curl --fail-with-body -sS -X POST \
  "$XPLO_API_URL/actions/$ACTION_ID/run" \
  -H "Authorization: Bearer $XPLO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "inputs": {
      "Loan!B1": 325000,
      "Loan!B2": 0.06
    },
    "outputs": ["Loan!B8"]
  }' | jq .

The response has one entry per selected address:

{
  "outputs": {
    "Loan!B8": {
      "value": 382633.4653724008,
      "error": null
    }
  }
}

Omit outputs, or send an empty array, to return every terminal output. Selecting only the values your application consumes keeps responses smaller. A selected output may be a manifest output or a supported intermediate.

Value types

Input overrides are currently numeric:

  • Send numbers as JSON numbers.
  • Send booleans as 1 or 0.
  • Send dates as Excel serial values.
  • Text input overrides are not currently supported.

An output value can be a number, boolean, string, or null. If the evaluated cell contains an Excel error, value is null and error contains a value such as #DIV/0!.

Unknown cells, cells that cannot be overridden, and unsupported output addresses reject the entire request with HTTP 400. A run never returns a partial success response.

Run with semantic names

Naming + Labelling is optional. You can use the address-based request above for every run and never create a label. For application-facing integrations, labels let you use keys such as loan_amount and monthly_payment; raw cell selectors remain available and can be mixed with names in the same request.

Labels are created and managed through the Workbook API. Fetch the current interface and its revision before constructing a named request:

curl --fail-with-body -sS \
  "$XPLO_API_URL/actions/$ACTION_ID/cell-interface" \
  -H "Authorization: Bearer $XPLO_TOKEN" \
  -o interface.json

jq '{revision, workbooks, labels}' interface.json

A semantic request may combine names with explicit workbook-qualified cells:

{
  "expected_interface_revision": 3,
  "inputs": {
    "named": {
      "loan_amount": 325000
    },
    "cells": [
      {
        "cell": {
          "workbook_id": "awb-...",
          "sheet": "Loan",
          "address": "B2"
        },
        "value": 0.06
      }
    ]
  },
  "outputs": {
    "named": ["monthly_payment"],
    "all_named_outputs": false,
    "all_named_inputs": false,
    "cells": []
  }
}

Save that body as run.json, then execute it:

curl --fail-with-body -sS -X POST \
  "$XPLO_API_URL/actions/$ACTION_ID/run" \
  -H "Authorization: Bearer $XPLO_TOKEN" \
  -H "Content-Type: application/json" \
  --data-binary @run.json | jq .

Semantic responses keep named and raw projections separate:

{
  "interface_revision": 3,
  "outputs": {
    "named": {
      "monthly_payment": {
        "canonical_key": "monthly_payment",
        "address": {
          "workbook_id": "awb-...",
          "sheet": "Loan",
          "address": "B12"
        },
        "long_label": "Monthly payment",
        "units": "USD/month",
        "value": 1199.1,
        "error": null
      }
    },
    "cells": []
  },
  "warnings": []
}

Semantic keys are matched case-insensitively. Renamed deprecated aliases continue to resolve and produce a warning that identifies the canonical key. Provisional imported labels and retired names do not resolve.

expected_interface_revision is optional but recommended for generated clients and production integrations. If labels changed since your client fetched the interface, the run fails with 409 CONFLICT instead of silently resolving against a different contract.

When outputs is an object:

  • named selects explicit semantic keys.
  • cells selects explicit workbook-qualified raw cells.
  • all_named_outputs: true selects every active label whose direction is output or both.
  • all_named_inputs: true returns the evaluated values of labels whose direction is input or both.

If you omit outputs entirely, the Runtime API preserves the address-based default and returns all terminal outputs as raw cells.

Build a request from the manifest example

The optional manifest example is convenient for a smoke test because it contains real connected inputs and outputs:

RUN_BODY=$(jq -c '
  if .example then
    {
      inputs: (.example.inputs | map({key: .address, value: .value}) | from_entries),
      outputs: .example.outputs
    }
  else
    {inputs: {}}
  end
' manifest.json)

curl --fail-with-body -sS -X POST \
  "$XPLO_API_URL/actions/$ACTION_ID/run" \
  -H "Authorization: Bearer $XPLO_TOKEN" \
  -H "Content-Type: application/json" \
  -d "$RUN_BODY" | jq .

Endpoint reference

Paths are relative to /api/v2 and require Authorization: Bearer <token>.

Method Path Description
GET /actions/{id}/vm/manifest Return the prepared model's typed input/output contract.
POST /actions/{id}/run Execute the model remotely with address-based or semantic selectors.
GET /actions/{id}/cell-interface Return semantic labels and the current interface revision.
GET /actions/{id}/vm/bundle Download the model and interpreter for execution on your own infrastructure. Add ?runtime=none for the slim variant.
GET /clients/{lang} Download a Local Runtime Client's source as a zip (python, typescript, go, java) — the registry-free install path.

The Workbook API owns action creation, polling, label mutation, and artifact management; those operations are documented on the Workbook API page.

Errors, limits, and retries

Errors use the standard JSON envelope:

{ "error": "action not found", "code": "NOT_FOUND" }
HTTP Meaning
400 Invalid JSON, invalid selector, unknown address, or unsupported override.
401 Missing, invalid, expired, or revoked credentials.
403 The token is valid but cannot access the action.
404 The action does not exist.
409 The model is not ready or expected_interface_revision is stale.
429 The organization's server-side run quota is exhausted; honor Retry-After.
500 Model execution or another internal operation failed.
503 The hosted VM runtime is unavailable on this server.

Runtime constraints:

  • Request bodies are capped at 1 MiB.
  • Each hosted evaluation has a 30-second server timeout.
  • Every request consumes a server-side run, including a retry.
  • Responses use Cache-Control: private, no-store; do not expect run results to be cached by xplo.

For transient 500 or 503 responses, use bounded exponential backoff. Only retry a run when your application accepts that another server-side execution will be counted. Fix 400 and 409 responses before retrying unchanged requests.

Hosted versus local execution

Use the Runtime API when you want xplo to manage execution infrastructure and return results over HTTP. Use the runtime bundle when execution must happen in your own environment, when the data path must remain local after preparation, or when you need direct process-level integration.

In client-library terminology, Runtime API wrappers are always called Hosted HTTP SDKs and include http in their package and main client names. Libraries that load or execute the downloaded bundle are Local Runtime Clients (Python, TypeScript, Go, Java) and never include http. Neither family silently falls back to the other execution mode.

Download the bundle:

curl --fail-with-body -sS \
  "$XPLO_API_URL/actions/$ACTION_ID/vm/bundle" \
  -H "Authorization: Bearer $XPLO_TOKEN" \
  -o model-bundle.zip

The bundle contains the prepared plan.xvm, a matching vmrun interpreter and model.wasm, a snapshot of the semantic interface, and an examples/ directory holding one ready-to-run program per language with the client library vendored beside it — so unzip then one command prints your model's own numbers, with no package install. Add ?runtime=none for a slim bundle (~20 KB) when the interpreter is already cached on the target machine.

If your language has no packaged client, or your build environment has no access to pip / npm / the Go proxy / Maven, download the client source instead:

curl --fail-with-body -sS \
  "$XPLO_API_URL/clients/python" \
  -H "Authorization: Bearer $XPLO_TOKEN" \
  -o xplo-client-python.zip

Both downloads are part of the Local Runtime tier (Team plan and up) and are metered as egress. Hosted execution avoids platform compatibility and deployment concerns; the bundle gives you control over where evaluation happens.

Getting help

File bugs and feature requests through the bug form or email contact@pythia.software.