Skip to content

Schema & Field

Schema classes define the structure of workflow inputs and outputs. They map directly to JSON Schema objects used by the Tray.io platform.

Subclass Schema and declare fields with Python type annotations:

from riffle.sdk import Schema
class ContactInput(Schema):
name: str
email: str
age: int
active: bool

This corresponds to a JSON Schema with type: "object" and four properties.

When a field contains a nested object with its own properties, Riffle emits a separate Schema class and uses it as the type annotation:

class Address(Schema):
street: str
city: str
zip_code: str
class ContactInput(Schema):
name: str
address: Address

For arrays of objects, use list[ClassName]:

class LineItem(Schema):
product: str
quantity: int
class Order(Schema):
items: list[LineItem]

The inner Meta class holds schema-level properties that don’t map to individual fields:

class ContactInput(Schema):
name: str
email: str
class Meta:
schema = "http://json-schema.org/draft-04/schema#"
required = ["name", "email"]
additional_properties = False
advanced = ["email"]
AttributeTypeDescription
schemastrJSON Schema version URI
requiredlist[str]Field names that must be present
additional_propertiesboolWhether unlisted fields are allowed
advancedlist[str]Fields marked as advanced in the Tray.io UI

All Meta attributes are optional. Omit Meta entirely if you don’t need any of them.

When a field carries metadata beyond its type, use the Field() descriptor:

from riffle.sdk import Field, Schema
class InvoiceInput(Schema):
amount: float = Field(title="Invoice Amount", description="Total in USD")
due_date: str = Field(format="date-time", date_mask="DD/MM/YYYY")
tags: list[str] = Field(items_title="Tag")
content_type: str = Field(name="content-type")

Plain annotations (name: str) work when no extra metadata is needed. Field() is only required when you need to set one or more of the parameters below.

ParameterTypeDescription
titlestrDisplay name shown in the Tray.io workflow builder UI. Different from the Python field name — title is a human-readable label.
descriptionstrHelp text shown in the Tray.io UI for this field.
defaultanyDefault value for the field.
formatstrJSON Schema format hint (e.g. "date-time", "uri", "email").
date_maskstrDate format mask (e.g. "DD/MM/YYYY"). Platform-specific.
typeslist[str]Union type list for polymorphic fields (e.g. ["string", "integer"]). Used when a field accepts multiple JSON Schema types.
additional_itemsboolWhether additional array items beyond the schema are allowed.
items_titlestrDisplay title for items inside an array field. When a field is list[str], title labels the array itself while items_title labels each element within it.
namestrOriginal JSON field name when it differs from the Python identifier. Riffle sets this automatically for fields with characters that aren’t valid in Python identifiers (e.g. hyphens). You generally don’t need to set this manually.
additional_propertiesbool | dictWhether additional properties are allowed on an unstructured object field. Only applies to dict fields without nested schema classes.
requiredlist[str]Required sub-field names on an unstructured object field. Only applies to dict fields without nested schema classes.
advancedlist[str]Advanced sub-field markers on an unstructured object field. Only applies to dict fields without nested schema classes.

These two parameters serve different purposes on array fields:

class Config(Schema):
recipients: list[str] = Field(
title="Email Recipients",
items_title="Email Address",
)
  • title labels the field itself — in the Tray.io UI, the array input as a whole is called “Email Recipients”.
  • items_title labels each individual element within the array — when a user adds an entry, it’s labeled “Email Address”.

For non-array fields, items_title has no effect.

The name parameter preserves JSON field names that can’t be expressed as Python identifiers:

class Headers(Schema):
content_type: str = Field(name="content-type")
x_request_id: str = Field(name="x-request-id")
accept: str # no Field() needed — valid Python identifier matches JSON name

Riffle generates Field(name=...) automatically during riffle pull. You shouldn’t need to set it manually unless you’re writing a workflow from scratch with field names that contain hyphens or other special characters.