Documentation
OverviewQuickstartConceptsDecisions and errorsApprovalsVersioningSecurityExtensionsFAQPartner integration profileExtensions
Status: Draft 0.4, pre-release. Sources: Draft 0.4 section 4.5, binding section 10a,
AGENTS.md,CONTRIBUTING.md,GOVERNANCE.md.
Partners sometimes need something the current draft does not have. RCTP has two ways to add it without forking the specification and without confusing anyone else:
- Open values under your own reverse domain. Draft 0.4 makes some values open [4.5(2)]: method types, event types,
adjustment types, attestation types and term names. A party may add one under a domain it controls, such as
example.carrier:picked_up_sealed. Anyone who does not recognise it treats it as informative, and it never changes a decision. x-experiments for anything else not in Draft 0.4: a verb, a reason or a field. The prefix tells every other party "this is an experiment; if you don't know it, it grants nothing".
Reverse-domain open values in v0.2
v0.2 narrows the open values so that closed schemas can carry them (binding 10a):
| Kind | v0.2 behaviour |
|---|---|
| Deal event | Accepted: an event may be an RCTP checkpoint, a UCP fulfillment event or adjustment, or <reverse.domain>:<name> (common.schema.json, ExtensionValue). Extension events are written to the deal record as informative and never satisfy or block a money rule. |
| Fulfillment method type | Accepted: a UCP method type such as shipping or meetup, or <reverse.domain>:<name>. The gateway checks an offer's selected_option_id against the listing's fulfillment_options either way. |
| Term name | Not accepted. Only the eleven terms of Draft 0.4 6.2 are allowed; a term set that needs another waits for a registry addition. |
The pattern is ^[a-z0-9-]+(\.[a-z0-9-]+)+:[a-z][a-z0-9_]{0,63}$: a domain, a colon and a snake_case name. Use a
domain you control.
x- naming
x-<partner>.<name>, where <partner> is a short lowercase label for your organisation and <name> says what it
is. Use lowercase letters, digits, - in the partner label, and _ or . in the name.
| Kind | Example | Where it would appear |
|---|---|---|
| Verb | x-examplepartner.bulk_relist |
An action in a decision or deal record entry |
| Reason | x-examplepartner.relist_quota_reached |
The reason of a refusal of an x- verb |
| Field | x-examplepartner.campaign_ref |
A property in an RCTP object |
The schema pattern for verb names is ^x-[a-z0-9-]+\.[a-z0-9_.]+$
(common.schema.json, Action).
Rules for every extension
- Your own name. It carries
x-and a partner label you control, or a reverse domain you control. Never reuse a registered name or another partner's label. - Tracking issue. An
x-extension has a tracking issue in this repository before anything ships. The issue names the owner, the purpose and the end date. - End date. An
x-extension lives for at most two drafts or six months, whichever comes first. By then it is promoted into the spec or removed. - No weakened invariant. It must not weaken INV-1 to INV-9. For example, an extension cannot carry a floor or personal data, cannot run below the floor, cannot sign a contract, and cannot be approved by anyone but the seller.
- Registered rules keep registered reasons. An
x-reason may only refuse anx-verb. A refusal under any registered rule still carries its registered reason [13]. - Unknown means nothing. A gateway never lets an unrecognised value change a decision [4.5(2)]. An
x-verb the gateway does not support is never run. - Agents fail safe. An agent that receives an
x-reason it does not know treats the call as refused and does not retry it unchanged. - Declared. List every extension you use, with its tracking issue, in your partner integration profile.
What v0.2 does with x- today
The reference gateway is stricter than "ignore" in most places. Stricter never weakens an invariant.
| Kind | v0.2 behaviour |
|---|---|
| Verb | The Action schema accepts x- names, but scopes are only dev.rctp.listing:* and dev.rctp.deal:* and the gateway has no route for any x- verb, so no grant can allow one and it is never run. |
| Reason | Not accepted yet. Reason in common.schema.json is a closed enum generated from the registry, and the SDKs' Reason types are closed too. An x- reason needs a schema change first. |
| Field | Not accepted yet. Every object schema is closed (additionalProperties: false). An x- field in a request body is a 400 invalid_request. An x- field needs a schema change first. |
Proposing an extension
- Open an issue describing the problem, not just the fix. Give the name, the spec sections it touches, an invariant-by-invariant note on why none is weakened, and the end date.
- Change the schema first if the extension adds a reason or a field: edit
schemas/, add a valid and an invalid example underschemas/examples/, runpnpm codegen, then write code (CONTRIBUTING.md). - Test it. Add tests for its rules. If an invariant is involved, the test name contains the invariant's ID.
- Record it in
CHANGELOG.md, with the tracking issue and the end date. - Keep the pull request small. Editors review it under lazy consensus: an editor's approval and no unresolved
objection after 5 working days (
GOVERNANCE.md).
Promoting an extension into the spec
An extension that proves useful should become part of the next draft, without the prefix.
- Open a promotion issue that links the tracking issue and shows evidence: who used it, where, and what broke.
- Write one pull request that changes the spec text, the schemas and the conformance vectors together, as every spec change does. A new verb, reason, checkpoint, term or level is a registry addition and needs editor review [16].
- Add a conformance vector for any behaviour another implementation must also follow, with its plausible wrong answer [17.3].
- Plan the move. Keep the
x-name working until its end date so integrators can switch, then remove it and note both in the changelog.
An extension that is not promoted by its end date is removed. Removing an x- name does not need a new major version:
it was never registered.
Example tracking record
| Field | Example |
|---|---|
| Name | x-examplepartner.bulk_relist |
| Kind | Verb |
| Owner | Example Partner Agents (https://agents.examplepartner.example/.well-known/ucp) |
| Purpose | Relist several expired listings in one call, each keeping its approved terms |
| Intended tier | ask |
| Invariants touched | INV-5 (only the seller changes terms), INV-9 (no personal data) |
| Tracking issue | #<number> |
| Started | Draft 0.4 |
| End date | Two drafts or six months after start, whichever comes first |
| Status | Proposed |
Source: docs/extensions.md