Skip to content

Dry-run a delta batch and report coherence findings

POST
/v1/branches/{branch_id}/validate
curl --request POST \
--url https://api.hydrate.sh/v1/branches/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/validate \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "deltas": [ { "node": { "data": { "aliases": [ "example" ], "config": [ { "contract_name": null, "description": "", "external": false, "id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "name": "", "type": "" } ], "constraints": [ "example" ], "description": "", "documentation_url": null, "external_kind": null, "inputs": [ { "contract_name": null, "description": "", "external": false, "id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "name": "", "type": "" } ], "is_external": false, "is_test_node": false, "kind": null, "language": null, "name": "", "outputs": [ { "contract_name": null, "description": "", "external": false, "id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "name": "", "type": "" } ], "parent_id": null, "path_prefix": null, "protocol": null, "source_decisions": [ "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0" ], "status": "idle", "user_kind": null, "verifications": [ { "author": "user", "id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "text": "example", "type": null } ] }, "id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "kind": "behavior", "parent_id": null }, "type": "add_node" } ] }'

Dry-run a delta batch and get back the full coherence report over the graph that would result — never mutating the branch. Returns 200 with {valid, findings}: findings is the complete list of coherence problems (an input port with no incoming edge, a wire pinned to a missing port, or an edge whose endpoint types differ), and valid is true only when there are no error-severity findings. The verdict is in the body, so an incoherent graph is still a 200. A 4xx is returned only for a request that can’t be processed at all (a malformed body or an unknown delta type). An empty delta list reports the branch’s current coherence.

branch_id
required
Branch Id

The branch to validate against.

string format: uuid

The branch to validate against.

Media typeapplication/json
V1ValidateBody

Request body for POST /v1/branches/{bid}/validate.

object
deltas
Deltas
Array
One of: discriminator: type
AddNodeDelta

Insert a node.

object
node
required
Node

A behavior or boundary node.

object
data
NodeData

A node’s data payload; its shape varies by kind and whether the node is external.

On update, key-presence semantics apply: a field present with a null value is set to null, while an omitted field is left unchanged. All fields are optional at the type level — cross-field rules (e.g. behavior nodes reject user_kind; external nodes require external_kind) are enforced server-side, so a partial update is not blocked by “missing required field” errors that don’t apply to it.

object
aliases
Aliases
Array<string>
<= 50 items
config
Config
Array<object>
Port

An input, output, or config port on a node.

object
contract_name
Any of:
string
<= 500 characters
description
Description
string
""
external
External
boolean
id
required
Id
string format: uuid
name
Name
string
""
type
Type
string
""
constraints
Constraints
Array<string>
description
Description
string
""
documentation_url
Any of:
string
external_kind
Any of:
string
inputs
Inputs
Array<object>
Port

An input, output, or config port on a node.

object
contract_name
Any of:
string
<= 500 characters
description
Description
string
""
external
External
boolean
id
required
Id
string format: uuid
name
Name
string
""
type
Type
string
""
is_external
Is External
boolean
is_test_node
Is Test Node
boolean
kind
Any of:
string
Allowed values: behavior boundary state io interface
language
Any of:
string
name
Name
string
""
outputs
Outputs
Array<object>
Port

An input, output, or config port on a node.

object
contract_name
Any of:
string
<= 500 characters
description
Description
string
""
external
External
boolean
id
required
Id
string format: uuid
name
Name
string
""
type
Type
string
""
parent_id
Any of:
string format: uuid
path_prefix
Any of:
string
protocol
Any of:
string
source_decisions
Source Decisions
Array<string>
status
Status
string
default: idle
user_kind
Any of:
string
verifications
Verifications
Array<object>
Verification

A test specification attached to a behavior node.

object
author
required
Author
string
Allowed values: user agent
id
required
Id
string format: uuid
text
required
Text
string
type
Any of:
string
id
required
Id
string format: uuid
kind
required
Kind
string
Allowed values: behavior boundary state io interface
parent_id
Any of:
string format: uuid
type
required
Type
string
Allowed value: add_node

Successful Response

Media typeapplication/json
ValidateResponse
object
branch
required
BranchRef
object
id
required
Id
string format: uuid
version
required
Version
integer
findings
Findings
Array<object>
Finding

A single coherence finding over the graph that results from a delta batch. locator is the node / port / edge id (a string) the finding is about.

code is an OPEN set and is published as a plain string on purpose. The codes are owned by the server and new ones are added additively as new coherence rules ship. Publishing the set as a closed enum made every generated client close it too — and a generated enum with no fallback arm fails to deserialize the WHOLE response the first time the server sends a code the client has not heard of. That turns every additive rule into a coordinated release, and turns a client that is merely out of date into one that cannot read a validate response at all.

So the contract is: a consumer must tolerate codes it does not recognize — render them, do not switch exhaustively on them. The known values are documented in the field description rather than enforced by the schema, because enforcing them here bought documentation at the cost of breaking clients.

severity stays closed: it is a two-valued verdict a consumer genuinely must branch on, and adding a third value WOULD be a breaking change that deserves a coordinated release.

object
code
required
Code

Machine-readable finding code. Known values: unsatisfied_input (an input port with no incoming edge), dangling_wire (an edge whose endpoint does not exist), type_mismatch (a wired pair whose port types disagree), orphan_node (a node that traces to no source decision, in a graph that records provenance). This set is open and grows additively — treat an unrecognized code as an opaque string and render it; do not fail on it. Read severity to decide whether a finding is blocking: the first three are error and drive valid, while orphan_node is a warning and never changes it.

string
locator
required
Locator
string
message
required
Message
string
severity
required
Severity
string
Allowed values: error warning
project_id
required
Project Id
string format: uuid
valid
required
Valid
boolean
version
required
Version
string
Example
{
"findings": [
{
"code": "unsatisfied_input",
"severity": "error"
}
]
}

No credentials, malformed credentials, or revoked credentials. The envelope is leak-parity (same shape across all 401 paths) so an attacker cannot distinguish revoked vs. unknown via the body.

Media typeapplication/json
object
detail
required
string
Example
{
"detail": "unauthenticated"
}

Credentials are valid but lack the scope required for this route, or the principal lacks membership in the target project.

Media typeapplication/json
object
detail
object
code
string
message
string
Examplegenerated
{
"detail": {
"code": "example",
"message": "example"
}
}

Branch/project not found or not accessible, OR a delta targets a node that doesn’t exist.

Media typeapplication/json
object
detail
required
One of:
object
code
required
string
message
required
string
Example
{
"detail": {
"error": "not_found"
}
}

Request body failed validation, OR a delta is malformed / unknown / not an object.

Media typeapplication/json
One of:
HTTPValidationError
object
detail
Detail
Array<object>
ValidationError
object
ctx
Context
object
input
Input
loc
required
Location
Array
msg
required
Message
string
type
required
Error Type
string
Example
{
"detail": {
"error": "malformed_delta_field",
"recovery_hint": "regenerate_uuid"
}
}

Per-bucket rate limit exceeded. The response carries Retry-After and the standard X-RateLimit-* headers (Limit / Remaining / Reset).

Media typeapplication/json
object
detail
string
Example
{
"detail": "rate_limited"
}
Retry-After
integer
X-RateLimit-Limit
integer
X-RateLimit-Remaining
integer
X-RateLimit-Reset
integer