Skip to main content

Tool Spec Schema

This page defines the complete JSON Schema for Tool specifications in Fascia. A Tool is the fundamental building block for backend operations -- equivalent to an API endpoint, webhook handler, or cron job. Each Tool is defined by a spec that includes its trigger, flow graph, input/output schemas, and policies.

JSON Schema

{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["name", "version", "description", "trigger", "input", "output", "flow"],
"properties": {
"name": {
"type": "string",
"pattern": "^[a-z][a-zA-Z0-9]*(\\.[a-z][a-zA-Z0-9]*)*$",
"description": "camelCase tool name (e.g., createReservation, auth.login)"
},
"version": {
"type": "integer",
"minimum": 1
},
"description": { "type": "string" },
"trigger": { "$ref": "#/$defs/Trigger" },
"input": {
"type": "object",
"description": "JSON Schema for input validation"
},
"output": {
"type": "object",
"description": "JSON Schema for output format"
},
"flow": { "$ref": "#/$defs/FlowGraph" },
"policies": {
"type": "array",
"items": { "type": "string" },
"description": "References to Policy spec names"
},
"auth": { "$ref": "#/$defs/AuthConfig" },
"riskLevel": {
"enum": ["green", "yellow", "red"],
"description": "Assigned by Risk Engine (not user-defined)"
},
"concurrencyStrategy": {
"enum": ["none", "optimistic", "pessimistic"],
"description": "Auto-selected based on flow analysis"
},
"idempotencyKey": {
"type": "string",
"description": "Value DSL expression to derive idempotency key from input"
}
}
}

Top-Level Properties

PropertyTypeRequiredDescription
namestringYescamelCase tool name. Dot notation allowed for namespacing (e.g., auth.login). Must match ^[a-z][a-zA-Z0-9]*(\.[a-z][a-zA-Z0-9]*)*$.
versionintegerYesImmutable version number, starting at 1.
descriptionstringYesHuman-readable description of what the tool does.
triggerTriggerYesHow the tool is invoked. See Trigger.
inputobjectYesJSON Schema defining the expected input payload. Validated at step 1 of the Execution Contract.
outputobjectYesJSON Schema defining the response format.
flowFlowGraphYesDirected acyclic graph defining the execution logic. See FlowGraph.
policiesstring[]NoArray of Policy spec names to enforce before execution.
authAuthConfigNoAuthentication and authorization configuration. See AuthConfig.
riskLevelenumNoAssigned by the Risk Engine after analysis. One of green, yellow, red. Not user-defined. See Risk Rules.
concurrencyStrategyenumNoAuto-selected based on flow analysis. One of none, optimistic, pessimistic.
idempotencyKeystringNoA Value DSL expression used to derive a unique key from the input, preventing duplicate execution.

Definitions

Trigger

Defines how the tool is invoked.

{
"type": "object",
"required": ["type"],
"properties": {
"type": { "enum": ["http", "webhook", "cron", "queue"] },
"method": {
"enum": ["GET", "POST", "PUT", "DELETE"],
"description": "For HTTP triggers"
},
"path": {
"type": "string",
"description": "Custom path for HTTP triggers"
},
"schedule": {
"type": "string",
"description": "Cron expression for cron triggers"
},
"webhookSecret": {
"type": "string",
"description": "Secret name in Secret Manager"
}
}
}
PropertyTypeRequiredDescription
typeenumYesTrigger type: http, webhook, cron, or queue.
methodenumNoHTTP method. Applicable only when type is "http".
pathstringNoCustom URL path. Applicable only when type is "http".
schedulestringNoCron expression (e.g., 0 */6 * * *). Required when type is "cron".
webhookSecretstringNoName of the secret in GCP Secret Manager used to verify webhook signatures. Applicable when type is "webhook".

Trigger Types

TypeInvocationUse Case
httptool.call(name, payload) via HTTPExternal clients calling the Tool API
webhookExternal service callbackPayment provider notifications, third-party integrations
cronScheduled executionDaily reports, cleanup jobs, recurring tasks
queueMessage queue event (future)Asynchronous processing, event-driven workflows

FlowGraph

The directed acyclic graph (DAG) that defines the tool's execution logic.

{
"type": "object",
"required": ["nodes", "edges", "startNode"],
"properties": {
"nodes": {
"type": "object",
"additionalProperties": { "$ref": "#/$defs/FlowNode" }
},
"edges": {
"type": "array",
"items": { "$ref": "#/$defs/FlowEdge" }
},
"startNode": { "type": "string" }
}
}
PropertyTypeRequiredDescription
nodesobjectYesMap of node ID to FlowNode definition. Keys are unique string identifiers.
edgesFlowEdge[]YesList of connections between nodes. See FlowEdge.
startNodestringYesThe node ID where execution begins. Must reference a key in nodes.

FlowNode

A single operation within the flow graph.

{
"type": "object",
"required": ["type"],
"properties": {
"type": {
"enum": [
"read", "write", "transform", "if", "switch",
"retry", "timeout", "payment", "email", "sms",
"httpRequest", "transaction", "policyCheck", "assert"
]
},
"config": {
"type": "object",
"description": "Node-type-specific configuration"
},
"position": {
"type": "object",
"properties": {
"x": { "type": "number" },
"y": { "type": "number" }
},
"description": "Visual position in Flow Studio canvas"
}
}
}
PropertyTypeRequiredDescription
typeenumYesThe node type. Determines the operation performed. See Node Types.
configobjectNoConfiguration specific to the node type (e.g., entity name for read, expression for transform).
positionobjectNoVisual coordinates (x, y) for rendering in Flow Studio. Does not affect execution.

Node Types

CategoryNodeDescription
DatareadFetch data from an Entity or Composite
DatawriteCreate, update, transition status, or soft-delete an Entity
LogictransformTransform data using a Value DSL expression
LogicifConditional branching (two outputs: true/false)
LogicswitchMulti-way branching based on a value
LogicretryRetry a downstream node on failure (configurable count and backoff)
LogictimeoutEnforce an execution time limit on downstream nodes
ExternalpaymentCall a payment service (e.g., Stripe, Toss)
ExternalemailSend an email
ExternalsmsSend an SMS
ExternalhttpRequestCall an external HTTP API
SafetytransactionDefine an explicit transaction boundary around child nodes
SafetypolicyCheckEvaluate a Policy at runtime
SafetyassertEnforce an invariant; abort execution if the assertion fails

FlowEdge

A directed connection between two nodes, defining data flow and execution order.

{
"type": "object",
"required": ["from", "to"],
"properties": {
"from": { "type": "string", "description": "Source node ID" },
"to": { "type": "string", "description": "Target node ID" },
"label": {
"type": "string",
"description": "Branch label (for if/switch nodes)"
},
"dataMapping": {
"type": "object",
"description": "Maps output fields from source to input fields of target"
}
}
}
PropertyTypeRequiredDescription
fromstringYesSource node ID. Must reference a key in nodes.
tostringYesTarget node ID. Must reference a key in nodes.
labelstringNoBranch label for conditional nodes. For if nodes, use "true" or "false". For switch nodes, use the matching case value.
dataMappingobjectNoDefines how output fields from the source node are mapped to input fields of the target node.

AuthConfig

Authentication and authorization settings for the tool.

{
"type": "object",
"properties": {
"required": {
"type": "boolean",
"default": true
},
"allowedRoles": {
"type": "array",
"items": { "type": "string" },
"description": "Roles that can execute this tool. Empty = all roles."
}
}
}
PropertyTypeRequiredDescription
requiredbooleanNoWhether authentication is required to invoke this tool. Defaults to true.
allowedRolesstring[]NoList of roles permitted to execute this tool (e.g., ["admin", "staff"]). An empty array or omission means all authenticated roles are allowed.

Flow Graph Validation Rules

The Risk Engine validates every flow graph against these rules before deployment:

  1. DAG requirement -- The graph must be a directed acyclic graph. Cycles are not permitted (triggers a Red risk level).
  2. Single start node -- There must be exactly one startNode, and it must reference a valid node in the nodes map.
  3. Terminal reachability -- All execution paths must reach at least one terminal node (a node with no outgoing edges).
  4. Write nodes in transactions -- All write nodes must be enclosed within a transaction boundary. Missing transaction boundaries trigger a Red risk level.
  5. External node retry -- External nodes (payment, email, sms, httpRequest) should have a retry node configured. Missing retry triggers a Yellow risk level.
  6. No orphan nodes -- Every node must be reachable from the startNode. Unreachable nodes indicate a malformed flow.

Execution Contract

Every Tool execution follows this exact 9-step sequence. No exceptions.

StepOperation
1Validate input against the input JSON Schema
2Authorize (JWT verification + RBAC + row-level policy)
3Policy check (evaluate all referenced policies)
4Start database transaction
5Execute flow graph (node by node, following edges)
6Enforce invariants on all modified entities
7Commit transaction (or rollback on failure)
8Write audit log entry
9Return normalized output

No LLM is involved at any step. All behavior is deterministic.

Example

A createReservation tool spec:

{
"name": "createReservation",
"version": 1,
"description": "Create a new vehicle reservation",
"trigger": {
"type": "http",
"method": "POST",
"path": "/reservations"
},
"input": {
"type": "object",
"required": ["customerId", "vehicleId", "startDate", "endDate"],
"properties": {
"customerId": { "type": "string", "format": "uuid" },
"vehicleId": { "type": "string", "format": "uuid" },
"startDate": { "type": "string", "format": "date-time" },
"endDate": { "type": "string", "format": "date-time" },
"notes": { "type": "string" }
}
},
"output": {
"type": "object",
"properties": {
"id": { "type": "string", "format": "uuid" },
"status": { "type": "string" },
"totalPrice": { "type": "number" }
}
},
"flow": {
"startNode": "calcPrice",
"nodes": {
"calcPrice": {
"type": "transform",
"config": {
"expression": "diffDays(input.startDate, input.endDate) * vehicle.dailyRate"
}
},
"txn": {
"type": "transaction"
},
"createRecord": {
"type": "write",
"config": {
"entity": "Reservation",
"operation": "create",
"fields": {
"customerId": "input.customerId",
"vehicleId": "input.vehicleId",
"startDate": "input.startDate",
"endDate": "input.endDate",
"totalPrice": "calcPrice.result",
"notes": "input.notes"
}
}
},
"checkInvariants": {
"type": "assert",
"config": {
"expression": "createRecord.result.totalPrice > 0",
"message": "Total price must be positive"
}
}
},
"edges": [
{ "from": "calcPrice", "to": "txn" },
{ "from": "txn", "to": "createRecord" },
{ "from": "createRecord", "to": "checkInvariants" }
]
},
"policies": ["maxRowLimit"],
"auth": {
"required": true,
"allowedRoles": ["admin", "staff", "customer"]
},
"idempotencyKey": "concat(input.customerId, '-', input.vehicleId, '-', input.startDate)"
}

See Also