Every operation has one reference entry: its arguments, its result and its
errors. HTTP, MCP, the CLI and the SDK all call that same operation, so what you
learn on one transport holds on the others. Here is function.invoke three ways:
curl -s -X POST "$SUBSTRIO_URL/v1/functions/hello/_invoke" \
-H "Authorization: Bearer $SUBSTRIO_KEY" -d '{"name":"world"}'
curl -s "$SUBSTRIO_URL/mcp" -H "Authorization: Bearer $SUBSTRIO_KEY" \
-H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"substrio_function_invoke","arguments":{"name":"hello","event":{"name":"world"}}}}'
substrio invoke hello name=world
All three run the same checks: the key, the arguments against the operation's
schema, then the call. A refused call answers the same code on every
transport.
Retries and races
- Send
Idempotency-Keyon a write you may retry: a retry with the same key answers the first result again (HTTP API). - A write guarded by an expected version (
if: { _version }) that lost a race answers a conflict; read again before the next attempt. DUPLICATE_RUN,SCHEDULER_FULL,DEADLINE_EXPIRED,CANCELLEDandRATE_LIMITEDare distinct outcomes, not variations on one error: branch on the code (see Errors).
Error model
Errors are stable, typed codes grouped into families: lifecycle, storage, memory, transaction and concurrency, key-value, cursor, graph, dimensional, schema, API, quota and rate, auth, query, app and resource. Every operation can add its own error-specific guidance for what to call next. Branch on the stable code, and use the message text only for display: see Errors for the envelope and the full code table, and HTTP API for how it is carried over HTTP.
Versioning
The registry version is 1. A new field is added so older clients ignore it safely, and an operation name or tool name is never reused for something else.