SapixDBSapixDB/Docs
Home
Community · Transactions

Named Procedures

✓ Shipped

Store a named, parameterized transaction template on the server and call it by name. The ops run through the standard atomic transact path — all succeed or none do. Parameters are substituted at call time using $param string placeholders in the ops JSON.

When to use procedures

For one-off writes, direct writes or transact are simpler. Procedures shine when:

  • Multiple callers execute the same business-logic pattern (transfer funds, transition a workflow state, record an event with a side-effect write).
  • You want the ops validated and stored once rather than assembled client-side on every call.
  • You are calling SapixDB from an environment where sending a full ops array is cumbersome (edge workers, mobile clients, scripts).

Defining a procedure

Send a POST to /v1/procedures with a name and an ops array. The ops use the same shape as a transact request, with "$param_name" string values as placeholders for call-time substitution.

Define — fund transfer procedure
POST /v1/procedures
Authorization: Bearer spx_root_YOUR_ROOT_KEY
Content-Type: application/json

{
  "name": "transfer_funds",
  "ops": [
    {
      "op":    "write",
      "agent": "ledger",
      "data":  {
        "type":      "debit",
        "account":   "$from_account",
        "amount":    "$amount",
        "reference": "$reference"
      }
    },
    {
      "op":    "write",
      "agent": "ledger",
      "data":  {
        "type":      "credit",
        "account":   "$to_account",
        "amount":    "$amount",
        "reference": "$reference"
      }
    }
  ]
}
201 Created
{
  "name":           "transfer_funds",
  "ops":            [ ... ],
  "created_at_ms":  1756224000000,
  "updated_at_ms":  1756224000000
}

Re-posting with the same name replaces the procedure (200 OK). The created_at_ms is preserved.

Parameter substitution

At call time, every string value in the ops tree that starts with $ is replaced with the corresponding value from the params map. Substitution is recursive — it works inside nested objects and arrays. Non-string values (numbers, booleans, null) are passed through unchanged; objects and arrays are traversed but not replaced themselves.

Placeholder typeExample ops valueparams valueResult
string param"$status""params": {"status": "active"}"active"
numeric param"$amount""params": {"amount": 49.99}49.99 (number)
boolean param"$enabled""params": {"enabled": true}true (boolean)
missing param"$unknown"(not in params)"$unknown" (unchanged)

Calling a procedure

POST /v1/procedures/:name/call
POST /v1/procedures/transfer_funds/call
Authorization: Bearer spx_root_YOUR_ROOT_KEY
Content-Type: application/json

{
  "params": {
    "from_account": "acc_alice",
    "to_account":   "acc_bob",
    "amount":       250.00,
    "reference":    "inv_9981"
  }
}
200 OK — TransactResponse
{
  "committed": true,
  "results": [
    {
      "index":        0,
      "agent":        "ledger",
      "record_id":    "018f3c2a-4b1d-...",
      "content_hash": "3a7bd3f1c2e9...",
      "chain_head":   "9f1a2b3c4d5e..."
    },
    {
      "index":        1,
      "agent":        "ledger",
      "record_id":    "018f3c2a-4b1e-...",
      "content_hash": "7c8d9e0f1a2b...",
      "chain_head":   "2b3c4d5e6f7a..."
    }
  ]
}
Fully atomic. Procedure calls go through the same transact engine as POST /v1/transact. All ops succeed or none do. If a precondition fails, the response has committed: false and a failed_op_index.

HTTP API Reference

MethodPathDescription
POST/v1/proceduresDefine or replace a procedure. Returns 201 (new) or 200 (updated).
GET/v1/proceduresList all procedures sorted by name.
GET/v1/procedures/:nameGet one procedure definition. 404 if not found.
DELETE/v1/procedures/:nameDelete a procedure. Returns 204.
POST/v1/procedures/:name/callExecute with params. Returns TransactResponse.

Define request body

FieldTypeRequiredDescription
namestringyesUnique procedure name. Must not be empty.
opsarrayyesTransact ops with $param placeholders. Must not be empty.

Call request body

FieldTypeRequiredDescription
paramsobjectnoKey-value map substituted into the ops. Defaults to empty object.

Storage

Procedure definitions are stored in the CF_META column family of the _crons system agent at key proc_def:{name}. They persist across restarts. There is no upper limit on the number of procedures, but each ops array must deserialize into a valid transact request — invalid ops are rejected at definition time.

Authentication and Scopes

Creating, replacing, and deleting procedures requires a root key or a key with write:agents/_crons. Calling a procedure goes through the transact auth check — the caller needs write:agents/:id for each agent the ops touch. Listing and reading procedure definitions requires read:agents/_crons.

← Scheduled Crons→ BlobStore→ SaQL & Transact Reference