Reference

Contract stability

What you can build on without it moving, what may change at any time, and how a rename is phased out over one minor version.

Read as Markdown

An agent does not read a form. It reads the JSON Schema a capability publishes, the tool list and the REST paths. This page says which parts of that are a promise and which are free to change, so you know what to hard-code and what to read at run time.

Contract: build on it

A change to any of these can break a working client, so none of them changes without the process below.

ContractWhere you read it
Capability ids (actor_ultra, analyze_media)list_capabilities, GET /capabilities
Config property names, JSON types, required, defaultget_capability_schema, GET /capabilities/{id}
The set of enum values, const, patternthe same schema
Bounds: minimum, maximum, minLength, maxLength, minItems, maxItemsthe same schema
Strictness (additionalProperties: false): an unknown key is refusedevery write body, and the estimate
The deprecated keyword on an old field namethe same schema
output_kind, category and status valuescapability list rows
Error code values, their retryable verdicts, limit.bound_by valuesError codes
MCP tool names and their argumentstools/list on https://mcp.riffads.com
REST paths, methods, request and response field nameshttps://app.riffads.com/api/v1/openapi.json
CLI command names, flags, --json fields and exit codesCLI commands

Detail: may change at any time

Never parse or match on these.

  • Every description, label, help text and unit note in a schema.
  • Display names of capabilities, presets, actors and voices. An operator edits them.
  • The order of properties, of required and of enum values.
  • example_config values. The example always validates, but the values can change.
  • The wording of message, next_action, charge_summary and note. Show them to a person; branch on code, retryable and the typed fields.

Additive changes ship without warning

These can land in any release. A client written to the rules below never notices.

ChangeWhat your client should do
A new capabilityRead the list at run time. Never hard-code the full catalog.
A new optional config fieldNothing. Leave it out and the default applies.
A new enum value or a wider boundNothing. Your old values still validate.
A new MCP tool, REST route or query parameterNothing. Old ones keep their names.
A new field in a responseIgnore fields you do not know. Never fail on an extra key.

The request side is the opposite: RiffAds refuses an unknown key rather than ignoring it, so a typo fails loudly instead of being dropped. An estimate is checked exactly like a submit, so estimating a config is a free way to test it against the live contract.

Breaking changes

ChangeWhat happens
A config field is renamedThe old name keeps working for one minor version, marked deprecated. See below.
A field or enum value is removed, or a bound narrowsOnly when a provider forces it, with a minor version bump. The refusal is invalid_config naming the field.
A capability is removedIt is retired: it leaves the capability list, and a request that names it is refused with capability_unavailable and capability_status: "retired", so the reason is machine readable.
A tool, REST field or error code would be renamedIt is not. A new one is added beside the old one.

The deprecation window

An old field name is accepted for one minor version of the MCP server version (1.1.x accepts a name renamed in 1.1.0, and 1.2.0 stops). While it is accepted:

  • The schema lists the old name as an optional property with "deprecated": true, no default, and a description that names the new field and the version that stops accepting it:

    A deprecated property in config_schema
    "size": {
      "type": "string",
      "deprecated": true,
      "description": "Deprecated: renamed to image_size. Send image_size instead. size is accepted until version 1.2.0."
    }
  • A config that uses the old name is priced, submitted and deduplicated exactly as if it used the new one.

  • A config that sends both names is refused with invalid_config naming both. Send only the new one.

When you see deprecated: true, switch to the new name in the same release of your client. There is no reason to wait for the window to close.

The field names above are an illustration

No capability has a deprecated field today. The shape is what one will look like.

Versions

This page describes version 1.1.0. One number covers the MCP server and the REST document. The REST path also carries a major version of its own (/api/v1).

WhereHow to read it
MCPserverInfo.version in the initialize answer from https://mcp.riffads.com
RESTinfo.version in https://app.riffads.com/api/v1/openapi.json
Server cardversion in https://riffads.com/.well-known/mcp/server-card.json
CLIriffads --version. The CLI has its own version number.

A minor bump means something was added, or a rename started its window.

How to notice a change

  • Watch the version. Compare the number above between runs. A new minor version is the moment to diff.
  • Diff the OpenAPI file. It is built from the same schemas that check requests, so a changed field shows up there first.
  • Scan for deprecated. Walk get_capability_schema for the capabilities you use and look for "deprecated": true.
  • Watch status. A capability you depend on that leaves the list, or answers capability_unavailable with a capability_status, has been retired or paused. requires_plan means the workspace plan changed, not the contract.
  • Treat invalid_config as a signal. A config that worked yesterday and is refused today names the field that moved. Read the schema again rather than retrying.

On this page