Skip to main content

Gates

A gate is a named validation checkpoint. It defines what data structure you expect, what business rules must hold, and what happens after validation succeeds (rendering, approval, delivery).

Gate Identifiers​

Every gate has three identifiers you can use interchangeably in API calls:

TypeFormatExample
UUID36 characters550e8400-e29b-41d4-a716-446655440000
shortIdfgate_ + 8 charsfgate_a1b2c3d4
sluglowercase + hyphensorder-validation

The slug is auto-generated from the gate name and is unique within your workspace.


Naming Best Practices​

Gate names and descriptions are surfaced to AI agents as MCP tool metadata. Writing them well helps agents pick the correct gate without confusion.

Name​

Keep the name short and scannable — it becomes part of the MCP tool name (submit_{slug}). Good names identify the domain object and action:

GoodAvoid
Order ValidationGate for validating incoming e-commerce orders from the checkout flow
Invoice ReviewInvoiceGate
Content ModerationAI Output Check v2

Description​

Write 1–2 sentences explaining when to use this gate, not what it checks. Agents read the description to decide whether a gate applies; the schema and business rules already document the validation details.

tip

Use the "Submit if…" pattern — it tells agents exactly when to route data to this gate.

Good examples:

Submit if this is a transit-update email envelope with attachment metadata.

Submit if you assembled a complete invoice payload (hub, lines, totals, currency) ready for document generation.

Validates customer orders before they enter the fulfillment pipeline. Use for all checkout-originated orders.

Too verbose — don't repeat schema/rule details:

This gate validates orders by checking that the customer name is a non-empty string, the email matches a valid format, the amount is a positive number greater than zero, the order date is present, and the shipping address contains street, city, and zip fields. It also enforces that quantity * unitPrice equals totalAmount.

Multi-gate pipelines​

When you have multiple gates forming a processing pipeline, the "Submit if…" pattern is especially useful — each description scopes exactly which stage of the pipeline the gate handles:

Gate 1: "Submit if you parsed a transit Excel sheet into rows and
need a structured batch for DATA-1 processing."

Gate 2: "Submit if you normalized DATA-1 sources into shipment
objects and want only clean, delta-aware shipments for merging."

Gate 3: "Submit if you produced the authoritative merged shipment
set for invoicing and reconciliation."

Each gate describes its own input boundary without referencing downstream steps. This lets agents match the right gate based on what they currently have, not what happens next.

Slug versioning​

Adding a version suffix to your slug (e.g., invoice-vmi-payload-v1) lets you introduce breaking schema changes under a new slug while keeping the old gate active for in-flight runs.

Where validation details belong​

DetailWhere to put it
Field types and constraintsSchema properties
Field-level documentationSchema property description field
Cross-field rulesBusiness Rules with descriptive name and errorMessage
When to use this gateGate description

Schema Definition​

Gate schemas use an object format to define expected fields:

{
"type": "object",
"properties": {
"customerName": { "type": "string", "required": true },
"email": { "type": "string", "required": true, "format": "email" },
"amount": { "type": "number", "required": true, "min": 0 },
"isPriority": { "type": "boolean", "required": false },
"orderDate": { "type": "date", "required": true },
"tags": { "type": "array", "itemType": "string" },
"address": {
"type": "object",
"properties": {
"street": { "type": "string", "required": true },
"city": { "type": "string", "required": true },
"zip": { "type": "string", "required": true }
}
}
}
}

Supported Types​

TypeDescriptionConstraints
stringText valueminLength, maxLength, format, allowedValues
numberNumeric valuemin, max
booleanTrue/false—
dateDate string—
arrayList of itemsitemType (required)
objectNested objectproperties (required)

String Formats​

The format constraint validates string patterns:

FormatValidates
emailEmail address
urlURL

Allowed Values​

Restrict a field to a set of valid options:

{
"status": {
"type": "string",
"required": true,
"allowedValues": ["pending", "active", "completed", "cancelled"]
}
}

Import from Template​

If you have an existing Rynko document template, you can import its variables as a gate schema:

curl -X POST https://api.rynko.dev/api/flow/gates/ORDER_GATE_ID/import-schema \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "templateId": "invoice-template" }'

This copies the template's variable definitions into the gate schema and sets the gate's validation mode to variables.

Import from Pydantic or Zod​

If your validation logic already lives in a Pydantic model (Python) or a Zod schema (TypeScript), you can paste its JSON Schema output directly into the gate — no need to rebuild the schema from scratch.

Generate the JSON Schema output​

import json
from pydantic import BaseModel
from typing import Optional
from datetime import date

class OrderModel(BaseModel):
customer_name: str
email: str
amount: float
quantity: int
order_date: date
notes: Optional[str] = None

# Pydantic v2
print(json.dumps(OrderModel.model_json_schema(), indent=2))

# Pydantic v1
# print(json.dumps(OrderModel.schema(), indent=2))

Copy the printed JSON output to your clipboard.

Import in the dashboard​

  1. Open your gate and click Schema & Validate.
  2. Click Import from Pydantic / Zod.
  3. Select the Pydantic or Zod tab.
  4. Paste the JSON output into the text area.
  5. Click Parse Schema to preview what will be imported.
  6. Review any warnings, then click Import.

The import replaces the existing schema and merges any generated business rules into your existing ones (existing rules are preserved).

What gets imported​

Schema featureImported as
Object propertiesGate schema properties with mapped types
required fieldsrequired: true on each property
minimum / maximummin / max constraints
minLength / maxLengthString length constraints
patternRegex pattern constraint
enum / constallowedValues constraint
defaultDefault value constraint
format: date or date-timedate type
format: emailString with email format
integer typenumber type + multipleOf: 1 constraint
Nested $ref objectsNested object properties (resolved recursively)
anyOf / oneOf with a null variantNullable field (required: false)
if / then / elseBusiness rule (where convertible)
uniqueItems: true on an arrayBusiness rule checking array uniqueness
not constraintBusiness rule (where convertible)

Understanding import warnings​

Any part of the schema that could not be converted automatically produces a warning. Warnings are shown immediately after parsing — review each one and resolve it in the Schema Editor or Business Rules section before saving.

Warning typeWhat happenedWhat to do
union typeThe field is a union of multiple incompatible types (e.g. Union[A, B, C]). Only the first type was imported.Open the Schema Editor and correct the field type, or split it into separate fields.
allOf merge conflictTwo allOf schemas defined conflicting values for the same key. The last value was used.Verify the field's settings in the Schema Editor.
depth limitThe model exceeds 15 levels of nesting. Properties beyond that were not imported.Add the missing nested fields manually in the Schema Editor.
notA not constraint is too complex to auto-convert. It was skipped.Add a Business Rule manually using !(expression) syntax.
if/then/else skippedA conditional rule's condition uses unsupported patterns. The rule was skipped entirely.Add a Business Rule manually to replicate the logic.
if/then/else truncatedA generated conditional rule exceeded the 450-character expression limit and was truncated.Open Business Rules, find the affected rule, and shorten or rewrite it.
Fixing warnings before going live

After importing, click Edit Schema to review all properties, then expand Business Rules to check any auto-generated rules. Don't activate the gate until all warnings are resolved.

Limits​

  • $ref references are resolved recursively; circular references are safely treated as plain object.
  • Maximum nesting depth: 15 levels.
  • Maximum business rules per gate: 20. If the import would push you over this limit, remove some existing rules first or reduce the rules in the imported schema.

Business Rules​

Business rules are cross-field expressions that enforce domain logic beyond simple type checking.

Rule Structure​

{
"businessRules": [
{
"id": "rule_date_order",
"name": "End date after start date",
"expression": "endDate > startDate",
"errorMessage": "End date must be after start date",
"enabled": true
},
{
"id": "rule_total_check",
"name": "Line items match total",
"expression": "quantity * unitPrice == totalAmount",
"errorMessage": "Total amount does not match quantity * unit price",
"enabled": true
}
]
}

Expression Syntax​

Expressions have access to all payload fields as variables. Supported operations:

CategoryExamples
Comparison>, <, >=, <=, ==, !=
Logical&&, ||, !
Arithmetic+, -, *, /, %
Math functionsround(), max(), min(), abs(), pow()
String accessField values are available directly by name

A rule passes when its expression evaluates to a truthy value. All enabled rules are evaluated independently (no short-circuit) — you'll see all failures at once.

Custom Error Messages with fail()​

Use the fail() function to return context-specific error messages that include actual payload values:

{
"id": "rule_currency",
"name": "Supported currency",
"expression": "currency == \"USD\" || currency == \"EUR\" ? true : fail(\"Unsupported currency: \" + currency)",
"errorMessage": "Unsupported currency (fallback)",
"enabled": true
}

When fail() is called, its message replaces the static errorMessage. This is useful for:

  • Including the actual invalid value: fail("Expected positive amount, got " + amount)
  • Suggesting fixes: fail("Unknown SKU: " + sku + ". Check the product catalog.")

Limits​

  • Maximum 20 business rules per gate
  • Expression maximum length: 500 characters
  • Expressions are security-validated at save time (no eval, require, or prototype access)

Validation Modes​

Variables Mode (default)​

Validates a JSON object against the schema. This is the standard mode for structured payloads.

Freetext Mode​

Validates unstructured text content (articles, reports, code snippets). Configure with:

{
"validationMode": "freetext",
"freetextConfig": {
"contentFormat": "markdown",
"max_content_length": 50000
}
}
Content FormatDescription
plaintextPlain text
markdownMarkdown formatted
htmlHTML content
codeSource code (set codeLanguage for syntax context)

In freetext mode, business rules are not evaluated.


Gate Configuration​

Approval​

Enable human review before delivery:

{
"approvalMode": "manual",
"approvalConfig": {
"approvers": [
{ "type": "internal", "email": "reviewer@company.com" },
{ "type": "external", "email": "client@partner.com" }
],
"timeout_hours": 48
}
}
  • auto (default) — runs proceed directly after validation
  • manual — runs wait for an approver to approve or reject

See Approvals for details on the review workflow.

Delivery​

Forward validated (and approved) payloads to external systems:

{
"deliveryChannels": [
{
"type": "webhook",
"config": {
"url": "https://api.yourapp.com/webhook",
"headers": { "X-Custom-Header": "value" }
}
}
]
}

Webhook deliveries include an HMAC-SHA256 signature in the X-Rynko-Signature header for verification. Failed deliveries are retried up to 3 times with exponential backoff.

Rendering​

Optionally render a document from the validated payload:

{
"renderMode": "rynko",
"renderTemplateId": "invoice-template",
"renderConfig": { "format": "pdf" }
}
Render ModeDescription
none (default)No rendering — validation only
rynkoRender a Rynko document template using the payload as variables
custom_apiForward to a custom rendering API

Rate Limiting​

Throttle submissions per gate:

{
"maxSubmissionsPerMinute": 60
}

When the limit is exceeded, the API returns HTTP 429 with a retryAfter value in seconds.

Circuit Breaker​

Automatically pause a gate when failures spike:

{
"circuitBreakerEnabled": true,
"maxFailures": 5,
"cooldownSeconds": 300
}

After maxFailures consecutive failures, the circuit opens and rejects new submissions for cooldownSeconds. This prevents cascading failures.

Notifications​

Control how you're notified about gate activity:

{
"notificationMode": "digest",
"notificationDigestMinutes": 5
}
ModeBehavior
immediateNotify on every event
digest (default)Batch notifications every N minutes

Gate Status​

StatusDescription
activeAccepting submissions (default)
pausedRejecting new submissions
archivedSoft-deleted, not visible in listings

Update a gate's status:

curl -X PUT https://api.rynko.dev/api/flow/gates/GATE_ID \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "status": "paused" }'

Schema Versioning​

Every schema update creates a new version. Each run records the schema version it was validated against. List versions:

curl https://api.rynko.dev/api/flow/gates/GATE_ID/versions \
-H "Authorization: Bearer YOUR_API_KEY"

This lets you track how your validation requirements have evolved and correlate runs with the schema version they used.