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 MarkdownAn 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.
| Contract | Where you read it |
|---|---|
Capability ids (actor_ultra, analyze_media) | list_capabilities, GET /capabilities |
Config property names, JSON types, required, default | get_capability_schema, GET /capabilities/{id} |
The set of enum values, const, pattern | the same schema |
Bounds: minimum, maximum, minLength, maxLength, minItems, maxItems | the same schema |
Strictness (additionalProperties: false): an unknown key is refused | every write body, and the estimate |
The deprecated keyword on an old field name | the same schema |
output_kind, category and status values | capability list rows |
Error code values, their retryable verdicts, limit.bound_by values | Error codes |
| MCP tool names and their arguments | tools/list on https://mcp.riffads.com |
| REST paths, methods, request and response field names | https://app.riffads.com/api/v1/openapi.json |
CLI command names, flags, --json fields and exit codes | CLI 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
requiredand ofenumvalues. example_configvalues. The example always validates, but the values can change.- The wording of
message,next_action,charge_summaryandnote. Show them to a person; branch oncode,retryableand the typed fields.
Additive changes ship without warning
These can land in any release. A client written to the rules below never notices.
| Change | What your client should do |
|---|---|
| A new capability | Read the list at run time. Never hard-code the full catalog. |
| A new optional config field | Nothing. Leave it out and the default applies. |
A new enum value or a wider bound | Nothing. Your old values still validate. |
| A new MCP tool, REST route or query parameter | Nothing. Old ones keep their names. |
| A new field in a response | Ignore 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
| Change | What happens |
|---|---|
| A config field is renamed | The old name keeps working for one minor version, marked deprecated. See below. |
A field or enum value is removed, or a bound narrows | Only when a provider forces it, with a minor version bump. The refusal is invalid_config naming the field. |
| A capability is removed | It 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 renamed | It 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_confignaming 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).
| Where | How to read it |
|---|---|
| MCP | serverInfo.version in the initialize answer from https://mcp.riffads.com |
| REST | info.version in https://app.riffads.com/api/v1/openapi.json |
| Server card | version in https://riffads.com/.well-known/mcp/server-card.json |
| CLI | riffads --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. Walkget_capability_schemafor the capabilities you use and look for"deprecated": true. - Watch
status. A capability you depend on that leaves the list, or answerscapability_unavailablewith acapability_status, has been retired or paused.requires_planmeans the workspace plan changed, not the contract. - Treat
invalid_configas a signal. A config that worked yesterday and is refused today names the field that moved. Read the schema again rather than retrying.