Proto: RAMP v1
Source: proto/ramp/v1/ramp.proto
Services
Section titled “Services”ExchangeService
Section titled “ExchangeService”The core protocol. Both AI agents and Brokers are valid clients.
| RPC | Request | Response | Description |
|---|---|---|---|
DiscoverResources | ResourceQuery | ResourceResponse | Discover available resource offers matching the query. Steps 2-3 in the RAMP flow. |
ExecuteTransaction | TransactionRequest | TransactionResponse | Commit to an offer and receive delivery information. Steps 4-5 in the RAMP flow. |
ReportUsage | UsageReport | UsageReportResponse | Submit a post-usage report for a completed transaction. Step 7 in the RAMP flow. |
DisputeTransaction | DisputeRequest | DisputeResponse | Signal a resource dispute for a completed transaction. Filed by the agent when delivered resource does not match what was promised (hash mismatch, resource unavailable, wrong resource). The Exchange records the dispute and initiates resolution. Resolution mechanics (refund, credit, re-delivery) are implementation- specific — this RPC standardizes the dispute signal, not the outcome. |
RequestDomainVerification | DomainVerificationRequest | DomainVerificationChallenge | Request a domain verification challenge for provider onboarding. Used by ramp-cli to prove domain control before pushing signing keys. Follows the ACME HTTP-01 pattern (Let's Encrypt). |
ConfirmDomainVerification | DomainVerificationConfirmation | DomainVerificationResult | Confirm domain verification and register a signing key. Called after the challenge token is placed at the provider's domain. |
Register | RegisterRequest | RegisterResponse | Create the calling agent's account with the Exchange. The caller's identity is proven by the request signature — the Exchange derives who is registering from the verified signature, never from the request body. Registering again for the same agent returns the same billing_ref (idempotent by design), which is why this RPC carries no idempotency_key. A refused registration travels as a non-OK transport error carrying ErrorDetail.registration_failure. |
GetAccountStatus | GetAccountStatusRequest | GetAccountStatusResponse | Read-only check of whether the calling agent's account is active. Identity comes from the request signature, so the request carries no identifying field. |
CatalogService
Section titled “CatalogService”Optional RPC for providers/CMS/third-party intelligence providers to push content metadata.
| RPC | Request | Response | Description |
|---|---|---|---|
PushResources | PushResourcesRequest | PushResourcesResponse | Push or update resource entries in the Exchange catalog. |
RemoveResources | RemoveResourcesRequest | RemoveResourcesResponse | Remove resource entries. |
RefreshCatalog | RefreshCatalogRequest | RefreshCatalogResponse | Trigger a full catalog refresh from configured sources. |
BrokerService
Section titled “BrokerService”The Broker entry point. Resolve(DiscoveryRequest) → DiscoveryResponse is discovery-only: a client sends the URIs/query it wants resolved and receives offer_groups (one OfferGroup per URI, each carrying the full signed Offer) or a typed absence_reason with empty offer_groups when nothing licensable was found. “No result” is a successful answer; malformed requests, auth failures, and internal faults are non-OK transport errors carrying an ErrorDetail. Resolve does not deliver inline — the per-transaction result (transaction id, billing id, retrieval endpoint, …) is returned on TransactionResponse via the separate execute path, routed by Offer.exchange.
| RPC | Request | Response | Description |
|---|---|---|---|
Resolve | DiscoveryRequest | DiscoveryResponse | Resolve runs the broker discovery flow for the requested URIs/query: it fans out to one or more Exchanges and returns the merged offers. It is pure discovery — it selects and returns offers, never executes a transaction, so it neither charges nor produces transaction denials. A denial is raised only when the agent later calls ExchangeService.ExecuteTransaction on a selected offer, and rides there on TransactionResponse.DenialReason. A result returns OK with offers populated on DiscoveryResponse.offer_groups (one OfferGroup per requested URI). A request that ran but yielded nothing licensable (not in catalog, no offers, entitlement/budget absence, upstream temporarily unavailable) returns OK with DiscoveryResponse.absence_reason set and empty offer_groups — "no result" is a successful answer, mirroring DiscoverResources (ADR-019 §2). Here "authz" means resource entitlement (→ OK + absence); transport authentication failures are a different axis and, like malformed requests and internal faults, are non-OK transport errors carrying an ErrorDetail. |
Messages — Supply Discovery
Section titled “Messages — Supply Discovery”ResourceQuery
Section titled “ResourceQuery”| Field | Type | Number | Description |
|---|---|---|---|
ver | string | 1 | RAMP protocol version — "1.0". Stamped by the sender from a single constant; advisory on receive. See "Protocol version" in the file header. |
requester | Requester | 3 | Requester identity — who is making this request, what scopes they have, and optional delegation chain. |
uris | repeated string | 8 | Resource URIs being queried. |
acceptable_restrictions | repeated AcceptableRestriction | 9 | The limits this query operates within, per restriction axis (function, geography, user-type, …) — see AcceptableRestriction. Advisory selection inputs the Exchange/Broker MAY pre-select offers against (convenience, not enforcement); the agent self-selects and bears compliance. |
deadline | optional Duration | 6 | Maximum time the caller will wait for a response. Exchange SHOULD prioritize speed over completeness when tight. Absent = "0.5s" default (proto-JSON encodes Duration as seconds). |
supported_profiles | repeated string | 7 | Domain extension profiles the caller understands. Declares which ext field vocabularies the caller can parse and act on. The Exchange SHOULD include profile-specific ext fields in Offers when the caller declares support. The Exchange MAY skip expensive metadata computation (e.g., retraction checking, consolidation verification) when the caller does not declare the relevant profile. Absence means "send all available metadata" — Exchange MUST NOT withhold ext fields solely because the caller omitted this field. Values match the Exchange's WellKnownManifest.supported_profiles entries. Examples: ["ramp-news-v1", "ramp-academic-v1", "ramp-legal-v1"] |
ext | Struct | 15 | Extension point |
ext_critical | repeated string | 90 | Critical extension keys (COSE crit pattern, RFC 9052). Lists keys within ext that the consumer MUST understand. Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION. Empty (default) → all ext keys are safe to ignore. |
AcceptableRestriction
Section titled “AcceptableRestriction”The limits a query operates within on one restriction axis, in the same RestrictionKind vocabulary the terms use. The Exchange/Broker MAY pre-select offers whose term restrictions fall within these (convenience, not enforcement). Used in ResourceQuery and DiscoveryRequest.
| Field | Type | Number | Description |
|---|---|---|---|
axis | RestrictionKind | 1 | Which axis (same enum as Restriction.kind): FUNCTION / GEOGRAPHY / USER_TYPE / OTHER. |
values | repeated string | 2 | The values the query operates within on this axis — same token vocabulary as the terms (e.g. FUNCTION ["ai-train"], GEOGRAPHY ["US", "EU"]). |
ResourceResponse
Section titled “ResourceResponse”| Field | Type | Number | Description |
|---|---|---|---|
ver | string | 1 | RAMP protocol version — "1.0". Stamped by the sender from a single constant; advisory on receive. See "Protocol version" in the file header. |
exchange | string | 3 | Canonical domain of the responding Exchange. |
offers | repeated Offer | 4 | Flat list of offers (for single-URI queries). |
offer_groups | repeated OfferGroup | 5 | Offers grouped by requested URI (for multi-URI batch queries). When populated, offers SHOULD be empty to avoid ambiguity. |
rate_limit | optional RateLimitInfo | 6 | Rate limit status for this caller. Present when the Exchange enforces per-caller rate limits on discovery. Enables agents/Brokers to throttle proactively rather than hitting hard limits. Particularly important when a Broker fans out the same batch query to multiple Exchanges — mid-batch rate limiting can cause partial results if not signaled early. |
ext | Struct | 15 | Extension point |
ext_critical | repeated string | 90 | Critical extension keys (COSE crit pattern, RFC 9052). Lists keys within ext that the consumer MUST understand. Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION. Empty (default) → all ext keys are safe to ignore. |
OfferGroup
Section titled “OfferGroup”| Field | Type | Number | Description |
|---|---|---|---|
uri | string | 1 | The URI this group of offers is for (echoed from ResourceQuery.uris). |
offers | repeated Offer | 2 | Zero or more offers for this URI. Empty = resource not available. |
discovery_method | optional DiscoveryMethod | 3 | How this URI was discovered by the Broker (v2 extension point). v1: always DISCOVERY_METHOD_EXCHANGE (Broker queried an Exchange). v2: may include DISCOVERY_METHOD_SEARCH (URI found via search engine like Exa), DISCOVERY_METHOD_RECOMMENDATION, etc. The Broker discovers URIs through any source, then routes through Exchange for pricing/transaction. The discovery method does not affect the transaction flow — it's metadata for the agent to understand how the resource was found. |
absence_reason | optional OfferAbsenceReason | 4 | Why no offers are available for this URI. Present when offers is empty. Enables agents/Brokers to distinguish "resource not in catalog" from "resource blocked for your use case" without trial-and-error transactions. Analogous to OpenRTB nbr codes and Shutterstock per-item error metadata in batch responses. |
restriction_filters | repeated RestrictionKind | 5 | When absence_reason = RESTRICTION_FILTERED, the restriction axes that drove the convenience pre-filter, in the same RestrictionKind vocabulary the terms use (e.g. [GEOGRAPHY] when the requester's stated geography matched no term). Advisory diagnostics, not an enforcement verdict. |
RateLimitInfo
Section titled “RateLimitInfo”Rate limit status modeled after IETF RateLimit header fields.
| Field | Type | Number | Description |
|---|---|---|---|
limit | int32 | 1 | Maximum requests allowed in the current window. |
remaining | int32 | 2 | Requests remaining in the current window. |
reset_at | Timestamp | 3 | When the current window resets (UTC). After this time, remaining resets to limit. |
window | optional Duration | 4 | Duration of the rate limit window (e.g. 60s = per-minute limit). |
Messages — Requester Identity
Section titled “Messages — Requester Identity”Requester
Section titled “Requester”Identity and entitlements only — who is asking and what they’re entitled to. What they’re asking for (uris) and the limits they’ll accept (acceptable_restrictions) live on the ask (ResourceQuery / DiscoveryRequest), not here. Used in ResourceQuery and DiscoveryRequest.
| Field | Type | Number | Description |
|---|---|---|---|
id | string | 1 | Unique requester identifier (e.g., "agent-research-bot-001"). |
domain | string | 2 | Domain the requester belongs to — used for public key lookup. Keys published at {domain}/.well-known/ramp.json (WellKnownManifest, role=ROLE_AGENT). |
type | RequesterType | 3 | What kind of entity is making this request. |
name | optional string | 4 | Human-readable name (e.g., "Acme Research Assistant"). |
scopes | repeated string | 6 | Entitlement scopes. Declare what the requester can access. The Exchange filters its catalog to resources matching these scopes. Resources outside the scopes are not returned — the requester never learns they exist. This is the enforcement mechanism for both enterprise RBAC and open-market subscription entitlements. Scope format: colon-separated segments, "{domain}:{permission}" or "{profile}:{permission}", optionally multi-segment ("dist:US:CA"); matching is segment-wise per the rule below (no implicit hierarchy). Examples: "credit:read" — can access credit reports "subscription:marketdata-2026" — has active MarketData subscription "academic:" — full access to academic resources "internal:reports" — can access internal reports "" — unrestricted (public Exchange default) Matching is SEGMENT-WISE (":" separated). A granted scope G covers a required scope R iff, segment by segment, each G segment equals the corresponding R segment or is ""; a terminal "" matches all remaining segments. There is NO implicit prefix match, and a grant NARROWER than the requirement does not cover it (G must be equal-to-or-broader than R). Examples: "dist:" covers "dist:US" and "dist:US:CA"; "dist:US:" covers "dist:US:CA" but not "dist:EU"; bare "dist" covers only "dist"; granted "dist:US:CA" does NOT cover required "dist:US"; "*" covers everything. This same rule governs LicenseTerm.scopes — one algorithm protocol-wide. When empty, Exchange applies its default access policy (typically returns all publicly available resources). |
delegation | optional Delegation | 7 | Optional delegation — present when the requester acts on behalf of another entity (user, organization, upstream agent). |
ext | Struct | 15 | Extension point |
ext_critical | repeated string | 90 | Critical extension keys (COSE crit pattern, RFC 9052). Lists keys within ext that the consumer MUST understand. Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION. Empty (default) → all ext keys are safe to ignore. |
Delegation
Section titled “Delegation”Scoped, time-limited, spend-capped credential. The credential itself is opaque bytes carried in token, and token_format selects how to interpret them: "jwt" (the default and sole format; the field stays open for a future one). The format is never encoded as a prefix inside the token bytes. The token is a holder-bound JWT: a chain of cnf-linked JWTs (RFC 7800 cnf.jkt = RFC 7638 thumbprint) where each child is signed by the key its parent named and narrows scope, and the holder proves possession by signing the request (RFC 9421).
| Field | Type | Number | Description |
|---|---|---|---|
principal_domain | string | 1 | Who granted this delegation (domain for public key lookup). |
principal_id | string | 2 | Principal's identifier (e.g., "user@acme.com", "marketdata.example.com"). |
scopes | repeated string | 3 | Scopes granted by this delegation. MUST be a subset of the principal's own scopes (attenuation — can only narrow, not widen). |
expires_at | Timestamp | 4 | When this delegation expires. Exchange MUST reject expired tokens. |
max_spend_cents | optional int64 | 5 | Maximum spend in currency minor units (e.g., cents for USD). Exchange tracks cumulative spend against this cap. |
max_accesses | optional int32 | 9 | Maximum number of accesses allowed under this delegation. Exchange tracks cumulative access count against this cap. Deny with DENIAL_REASON_QUOTA_EXCEEDED when count >= limit. For subscriptions with "10,000 accesses/month", this carries the ceiling. |
quota_period | optional Duration | 10 | Quota reset period. How often the access/spend counters reset. Example: 30 days for monthly subscriptions — "2592000s" on the wire (proto-JSON encodes Duration as seconds; "720h" is not accepted). When absent, the quota is lifetime (bounded only by expires_at). |
token | bytes | 6 | Token bytes. A JWT (base64url-encoded JWS). |
token_format | string | 7 | Token format: "jwt" (default). Empty is treated as "jwt". The field stays open for a future format. |
revocation_uri | optional string | 8 | Optional: URI for real-time revocation checking. Exchange MAY check this for high-value transactions. Not checked for routine low-value access (performance tradeoff). |
issuer | optional string | 11 | Token issuer. OIDC issuer URL or GNAP grant server URL. Exchange uses this for JWT validation (OIDC discovery → JWKS) or GNAP token introspection. |
ext | Struct | 15 | Extension point |
ext_critical | repeated string | 90 | Critical extension keys (COSE crit pattern, RFC 9052). Lists keys within ext that the consumer MUST understand. Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION. Empty (default) → all ext keys are safe to ignore. |
Delegation claims. The claim vocabulary carried inside the token is not re-listed here — the authoritative registry is the Delegation-Claims Profile table in authentication.mdx. The one claim worth restating: every delegation MUST carry a holder binding (the cnf claim, cnf.jkt = the RFC 7638 thumbprint of the holder key) — the request-signing key MUST hash to cnf.jkt, or the token is rejected. Without it the token is bearer-usable. Everything else (scope, spend cap, expiry, etc.) is optional and defined in that single source of truth.
Narrowing example: An agent can further restrict (but never widen) a delegation by issuing a child JWT:
- Original:
scope: credit:*, max_spend_cents: 100000 - Attenuated:
scope: credit:read, max_spend_cents: 20000, uris: duns:123*
Multi-hop forwarding (no message — HTTP layer)
Section titled “Multi-hop forwarding (no message — HTTP layer)”There is no in-message hop chain. Multi-hop forwarding (Agent → Broker → … → Exchange) is a stack of RFC 9421 HTTP Message Signatures: each forwarding party adds one labeled signature, and each signature covers the request plus the prior hop’s signature, so the ordered set of signatures is the chain (tamper-evident, order-bound). The Exchange resolves each keyid (an RFC 7638 thumbprint) in the signer’s WBA directory at {domain}/.well-known/http-message-signatures-directory, verifies every signature, and enforces RequestConstraints.max_hops / WellKnownManifest.max_intermediary_hops by counting them. Responses do not retrace the chain — the terminal Exchange returns directly to the originating agent (bound by agent_identity_hash).
Messages — Offers and Pricing
Section titled “Messages — Offers and Pricing”| Field | Type | Number | Description |
|---|---|---|---|
offer_id | string | 1 | Unique identifier for this offer, assigned by the Exchange. |
title | optional string | 2 | Resource title (human-readable, for display/logging). |
pricing | Pricing | 3 | Pricing for this offer. An offer represents a single licensing arrangement: each projected LicenseTerm yields its own offer, so this is that term's pricing (the authoritative copy lives in terms[].pricing). Used for cross-exchange comparison and Broker ranking. A resource with multiple alternative terms (e.g. dual-licensed) produces multiple separate offers, one per term — never one offer with a "headline" picked among them. |
delivery_method | DeliveryMethod | 4 | How resource will be delivered. |
reporting | optional ReportingObligation | 5 | Post-usage reporting requirements for this offer. |
expires_at | optional Timestamp | 6 | When this offer expires (ISO 8601). |
identity | optional ResourceIdentity | 7 | Resource identity for cross-exchange deduplication. Enables Brokers to recognize the same resource offered by different Exchanges and compare pricing. |
exchange | string | 8 | Canonical domain of the Exchange that issued this offer (e.g. "exchange.example.com"). This is the execute-routing target: the agent (or a relaying Broker) sends the ExecuteTransaction call for this offer to this Exchange. Because it is an ordinary Offer field it falls inside the signed bytes (see signature below — the signature covers every field except signature / signature_algorithm), so an intermediary cannot redirect the execute call to a different Exchange without invalidating the offer. This enables multi-Exchange fan-out routing from the offer itself, retiring the X-RAMP-Exchange-Endpoint transport header. |
signature | string | 9 | REQUIRED. JWS (alg=EdDSA) over the canonical serialization of the ENTIRE Offer — every field, including pricing, terms (the full licensing payload), expires_at, and exchange. Only signature and signature_algorithm are excluded from the signed bytes. expires_at is signed so the offer's validity window is integrity-protected: a relaying Broker cannot extend (or shorten) the TTL of a signed offer to replay it outside the window the Exchange intended. CANONICAL SIGNING (RFC 8785 JCS over canonical proto-JSON). The signed bytes are: signed_payload = JCS( protojson(msg with signature + signature_algorithm cleared) ) i.e. render the message to canonical proto-JSON with the PINNED option set below, then apply RFC 8785 (JSON Canonicalization Scheme). Deterministic protobuf BINARY marshaling is explicitly NOT canonical across languages and versions (protobuf's own caveat), so it cannot be a cross-language signing primitive; JCS over proto-JSON can be reproduced by ANY language (Go, TS, Python) without a protobuf binary codec, so a broker/exchange/client in any language signs and verifies byte-identically. This same definition applies to the agent offer-acceptance signature (AgentAcceptance.signature). PINNED proto-JSON option set (the arbiter is the Go-emitted golden vector — whatever these options render MUST be byte-identical across all languages): - enum values as NAME strings (not numbers); - int64 / uint64 / fixed64 as decimal STRINGS; - bytes as standard (padded) base64; - google.protobuf.Timestamp / Duration per the proto-JSON WKT rules (RFC 3339 string for Timestamp); - unpopulated fields are OMITTED (never emitted as defaults); - field naming is snake_case (the proto field name, UseProtoNames=true), the naming every SDK target shares — wire, corpus, and signed form are all snake_case; - google.protobuf.Struct (ext) → a plain JSON object; JCS then sorts its keys recursively, so the Struct case needs no special handling. UNKNOWN FIELDS. A canonicalizer either OMITS content it has no schema for or PRESERVES it, and the rule follows from which: - OMITTING (e.g. proto-JSON, which emits only schema-defined fields): such a canonicalizer CANNOT reproduce the signed bytes of a message carrying unknown fields — what it renders silently drops part of what the signer covered. It MUST refuse the message rather than emit the reduced bytes, and a verifier built on it MUST reject rather than verify over them. The refusal binds at EVERY depth: a nested message and each element of a repeated or map field carries its own unknown-field set. - PRESERVING (a canonicalizer that carries unrecognized members through): it reproduces the signed bytes faithfully, so there is nothing to refuse. Either way an APPENDED field cannot pass: an omitting canonicalizer refuses the message, and a preserving one renders the appended member into bytes the signer never covered, so the signature fails. Without the refusal the omitting case would fail OPEN — an intermediary could add unknown fields to an already-signed message and leave its signature verifying, smuggling unauthenticated content through a message the recipient treats as verified. Extensions therefore ride in ext / ext_critical, which are defined fields and inside the signed bytes — never as undeclared field numbers. Because the signature covers terms, pricing, expires_at, and exchange, an intermediary (Broker) cannot tamper with price, restrictions, quotas, obligations, the expiry, the execute-routing target, or any licensing term without invalidating it. Agent SHOULD verify the signature (RFC 2119) against the Exchange's public key, and MUST reject an offer whose expires_at is in the past. |
signature_algorithm | string | 10 | JWS algorithm. Always 'EdDSA' for Ed25519 via JWS Compact Serialization. |
subscription_id | optional string | 11 | If set, this offer is available under an existing subscription/deal. No per-request billing — usage tracked against subscription quota. Pricing.rate = "0" for subscription offers (zero marginal cost). The Broker SHOULD prefer subscription offers when available. |
iab_categories | repeated string | 13 | IAB Content Taxonomy category codes. Enables agents to filter offers by topic (e.g., "only finance resources"). Uses IAB Content Taxonomy 3.1 codes. |
attestations | repeated ResourceAttestation | 14 | Signed attestations about the resource at this URI. Attestations provide cryptographic proof of resource properties from trusted parties (providers or verification vendors). Three verification levels determine what is independently verifiable: Level 0 (no attestations): Resource may carry identifiers (DOI, IPTC GUID) for identification, but nothing is cryptographically verifiable. Only CDN delivery failure is auto-disputable. Level 1 (self-attested): Provider signs own claims with Ed25519 key. Agent can independently verify content hash and token count. CDN delivery failure + content hash mismatch are auto-disputable. Level 2 (third-party attested): Independent verification vendor crawled the resource and attested to its properties. Agent trusts the attestation (does not re-verify hash). Token count discrepancy is auto-disputable when corroborated by CDN response size. Multiple attestations may be present (e.g., provider self-attestation plus a third-party verification). Agents choose which to trust. |
data_as_of | optional Timestamp | 16 | When the offered data was current. For dynamic resources (resource_mutability = DYNAMIC), this is the snapshot timestamp. Enables the Broker to evaluate freshness: "this credit report reflects data as of March 18" or "this drug database was updated today." Not set for STATIC resources (content doesn't change) or LIVE resources (content doesn't exist yet). The Broker compares this against RequestConstraints.max_data_age to filter stale offers. Example: agent requests max_data_age = 7 days, Broker drops offers where now() - data_as_of > 7 days. |
subscription_quota | repeated SubscriptionQuotaInfo | 17 | Subscription quota state, when this offer is under a subscription. Enables the agent to see remaining quota before committing. Multiple entries when the subscription has independent quotas (e.g., access count + spend cap). |
previews | repeated Preview | 18 | Lightweight previews for offer evaluation. The Exchange holds URLs (50–200 bytes each); the provider's CDN serves the actual bytes. Agents fetch previews only when evaluating offers — not on every discovery query. Multiple previews at different sizes allow agents to pick the cheapest fetch for their evaluation needs. Per content type: Image: watermarked thumbnail (150–450px JPEG) Video: short clip (10–30s MP4, watermarked) Audio: short clip (15–30s MP3, low-bitrate or watermarked) Text: snippet or abstract (first 200 words as text/plain) Data: sample records (1–3 rows as application/json) Stream: optional frame capture or none (streams are priced by time) Modeled after Shutterstock (multi-size thumbnail URLs), Spotify (preview_url to 30s clip), IIIF (parameterized image URLs), and OpenRTB native (img.url + dimensions). |
terms | repeated LicenseTerm | 19 | Licensing terms for this offer, sourced from the publisher's ResourceEntry. Multiple terms when the resource has different arrangements by use case. See: Universal Licensing Core section. |
ext | Struct | 15 | Extension point |
ext_critical | repeated string | 90 | Critical extension keys (COSE crit pattern, RFC 9052). Lists keys within ext that the consumer MUST understand. Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION. Empty (default) → all ext keys are safe to ignore. |
ResourceAttestation
Section titled “ResourceAttestation”Signed envelope of claims from a trusted party (provider or verification vendor) about content at a specific URI.
| Field | Type | Number | Description |
|---|---|---|---|
verifier | string | 1 | Canonical domain of the attesting party (e.g., "nytimes.com" for self-attestation, "doubleverify.com" for third-party attestation). Used to look up the verifier's attestation-signing keys in its WBA directory (WBAFile.keys) at https://{verifier}/.well-known/http-message-signatures-directory |
keyid | string | 2 | RFC 7638 JWK Thumbprint (the RFC 9421 keyid) of the verifier's attestation-signing key, resolved against the verifier's WBA directory (WBAFile.keys). Identifies which Ed25519 key signed this attestation. Enables key rotation: new keys are published with overlapping validity, new attestations use the new key's thumbprint, old attestations remain verifiable while the old key is still published. |
attested_at | Timestamp | 3 | When this attestation was created. Agents use this to assess freshness (e.g., "I accept attestations up to N hours old for breaking news"). |
uri | string | 4 | The resource URI this attestation covers. Must match the URI in the Offer or ResourceEntry this attestation is attached to. |
claims | Struct | 5 | Signed claims about the resource (max 4KB). A JSON object containing whatever properties the attesting party can determine about the resource. Recommended claim names for interoperability: estimated_quantity (integer): estimated consumption quantity (e.g., token count for text) word_count (integer): word count (estimated_quantity ~ word_count * 1.32 for text) language (string): ISO 639-1 language code iab_categories (string[]): IAB Content Taxonomy 3.1 codes content_hash (string): hash of content in "method:hexdigest" format hash_method (string): algorithm used for content_hash Vendors MAY add vendor-specific claims (e.g., brand_safety, sentiment). The protocol does NOT define "quality score" — it is inherently subjective. If a vendor provides a proprietary score, the vendor defines what it means via their WellKnownManifest ext["ramp.attestation.claims_schema"]. |
signature | string | 6 | Ed25519 signature over JCS-canonicalized (RFC 8785) representation of {verifier, keyid, attested_at, uri, claims}. JCS (JSON Canonicalization Scheme) produces deterministic UTF-8 bytes: lexicographic key sorting, ECMAScript number serialization, strict string escaping, no whitespace. Each attestation is self-contained — new claim fields do not invalidate old attestations because the signature covers the specific claims instance. |
Three verification levels:
| Level | Condition | What’s Verifiable |
|---|---|---|
| 0 — None | attestations empty | CDN delivery failure only |
| 1 — Self-attested | verifier matches provider domain | Content hash + token count |
| 2 — Third-party | verifier is a verification vendor | Token count (with CDN corroboration) |
Pricing
Section titled “Pricing”| Field | Type | Number | Description |
|---|---|---|---|
model | PricingModel | 1 | Provider's pricing model. |
rate | string | 2 | Price in the provider's model, as an exact decimal string — e.g. "0.05" = $0.05 per article. NOT a float: money is decimal to avoid binary rounding and to allow arbitrary sub-cent precision (e.g. "0.0001234"). Denominated in currency. |
currency | string | 3 | ISO 4217 currency code (e.g. "USD", "EUR"). |
unit_cost | optional string | 4 | Normalized cost per unit — the universal comparison metric, exact decimal string. For text: cost per token. For video: cost per second. For data: cost per record. For APIs: cost per call. Denominated in the Exchange's base_currency (from its WellKnownManifest). |
estimated_quantity | optional int32 | 5 | Estimated quantity in the metering unit. For text: token count. For video: duration in seconds. For documents: page count. For data: record count. |
license_duration_months | optional int32 | 7 | License duration in months. How long the granted access remains valid. |
unit | optional string | 8 | Metering basis — the "per what" of PER_UNIT pricing. REQUIRED when model = PER_UNIT. Custom units namespace as "vendor:unit". Ignored for FREE / FLAT. The (ramp.v1.vocab) entries below are the SOLE authored source of the registered bare tokens. A buf plugin reads them structurally and emits the pricingunits constants + IsRegistered; ingest enforces membership from those. The CEL is STRUCTURE ONLY (empty / bare-form / vendor:namespaced) — it never lists the tokens, so it cannot drift from the registry. |
metering | optional PricingMetering | 9 | How usage is tracked for billing reconciliation. Absent = PRICING_METERING_ONLINE (default real-time tracking). NONE = one-time perpetual sale; no ReportUsage required after ExecuteTransaction. OFFLINE_SELF_REPORTED = agent self-reports physical-world consumption. |
Pricing has no ext / ext_critical fields — the licensing core is closed.
ResourceIdentity
Section titled “ResourceIdentity”Layered content identification for cross-exchange dedup and integrity verification.
| Field | Type | Number | Description |
|---|---|---|---|
canonical_url | optional string | 1 | Provider's authoritative URL for this resource (rel="canonical"). Always available. Different per provider for syndicated content. |
doi | optional string | 2 | Digital Object Identifier — persistent, never changes. |
iptc_guid | optional string | 3 | IPTC NewsML-G2 globally unique identifier. Present when resource flows through news wire syndication (AP, Reuters). |
isni | optional string | 4 | International Standard Name Identifier for the creator. |
content_hash | optional string | 5 | Hash of the content. Interpretation depends on hash_method: "simhash-v1" → locality-sensitive hash, for fuzzy dedup (Level 1) "sha256" → exact-match integrity hash (Level 2) Level 1 (SimHash): computed by Exchange from extracted text. Agent verifies that fetched content is "substantially similar." Tolerates dynamic page elements. Level 2 (SHA-256): computed by provider from deterministic payload. Agent verifies exact match. Requires provider to serve consistent content (e.g., API endpoint, static HTML, structured JSON). Mismatch = dispute. Commands premium pricing. |
hash_method | optional string | 6 | Hash algorithm and verification level. Examples: "simhash-v1", "minhash-v1", "sha256", "sha384" |
resource_mutability | ResourceMutability | 8 | Signals whether this resource's content is stable, changes over time, or does not exist at offer time (live streaming). Drives hash verification behavior: STATIC: content_hash is stable. Agent SHOULD verify delivered content matches. DYNAMIC: content changes between offer and fetch (credit reports, drug databases). content_hash reflects state at offer generation time. Hash mismatch is expected and MUST NOT trigger automatic dispute. LIVE: content does not exist at offer time (streaming feeds, live broadcasts). content_hash is not applicable. The "resource" is the stream endpoint. Validated across 18 use cases: static content (articles, patents, legislation), dynamic data (credit reports, drug interactions, stock snapshots), and live streams (MarketData quotes, NPR broadcast, news monitoring feeds). |
c2pa_manifest | optional string | 7 | C2PA content credentials manifest URI. Points to a sidecar or embedded C2PA manifest for this resource. C2PA-aware agents MAY follow this URI to validate the full provenance chain (creator identity, transformation history, ingredient composition) using C2PA libraries (JUMBF/COSE Sign1). C2PA-unaware agents can rely on c2pa_status and c2pa-bridged attestation claims instead. Formats: Sidecar: HTTPS URI to a .c2pa manifest file Embedded: same URI as canonical_url (manifest is inside the asset) Content Credentials Cloud: https://contentcredentials.org/verify?uri=... |
c2pa_status | optional C2PAStatus | 9 | Summary validation status of the C2PA manifest. Populated by the Exchange or a verification vendor after validating the C2PA manifest. Enables agents to filter for provenance-verified content without parsing JUMBF/COSE themselves. The full C2PA validation details (signer identity, trust list, action history, training/mining status) are carried in a ResourceAttestation with c2pa.* claims — see ramp-c2pa-v1 profile. |
soft_binding | optional string | 10 | Soft binding hash — content-derived identifier that survives format transcoding (resolution changes, compression, PDF-to-text extraction). Extracted from C2PA soft binding assertion when present. Enables post-delivery verification when the hard binding hash breaks due to legitimate format conversion. Algorithm specified in soft_binding_method. Values are algorithm-specific (e.g., perceptual hash hex string, watermark identifier). |
soft_binding_method | optional string | 11 | Algorithm used for soft_binding. Examples: "phash-v1" (perceptual hash), "c2pa-watermark" (C2PA invisible watermark), "chromaprint" (audio fingerprint). |
ext | Struct | 15 | Extension point |
ext_critical | repeated string | 90 | Critical extension keys (COSE crit pattern, RFC 9052). Lists keys within ext that the consumer MUST understand. Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION. Empty (default) → all ext keys are safe to ignore. |
SubscriptionQuotaInfo
Section titled “SubscriptionQuotaInfo”Quota status for a subscription within the current billing period. Appears on Offer (field 17) as a pre-commit snapshot and on TransactionResponse (field 17) as the post-transaction remaining quota. The field is repeated to support multi-dimensional quotas (e.g. access count + spend cap).
| Field | Type | Number | Description |
|---|---|---|---|
subscription_id | string | 1 | Subscription this quota applies to. |
quota_limit | int32 | 2 | Total allowed in the current period. |
quota_used | int32 | 3 | Used so far in the current period. |
quota_remaining | int32 | 4 | Remaining in the current period. |
resets_at | optional Timestamp | 5 | When the quota counter resets (UTC). |
unit | optional string | 6 | What is being metered. Distinguishes access count quotas from spend quotas from burst limits. Standard values: "accesses", "tokens", "spend_cents", "burst" |
Preview
Section titled “Preview”Lightweight resource preview for offer evaluation. The Exchange populates preview URLs during catalog ingestion; assets are served by the provider’s CDN, not by the Exchange. Carried on Offer (field 18, repeated).
| Field | Type | Number | Description |
|---|---|---|---|
url | string | 1 | URL to a preview asset (thumbnail, clip, snippet, sample). Served by the provider's CDN, not by the Exchange. |
media_type | string | 2 | MIME type of the preview. Examples: "image/jpeg", "image/webp", "audio/mpeg", "video/mp4", "text/plain", "application/json" |
width | optional int32 | 3 | Dimensions in pixels (for images and video). |
height | optional int32 | 4 | Height in pixels (images and video) |
duration | optional int32 | 5 | Duration in seconds (for audio and video clips). |
size | optional string | 6 | Size category hint. Agents use this to select the right preview without fetching all of them. Standard values: "thumbnail" — smallest useful preview (100–150px or 5–10s) "preview" — mid-size for evaluation (300–500px or 15–30s) "sample" — larger / more detailed (for data: 1–3 sample records) |
Messages — Transaction
Section titled “Messages — Transaction”TransactionRequest
Section titled “TransactionRequest”| Field | Type | Number | Description |
|---|---|---|---|
ver | string | 1 | RAMP protocol version — "1.0". Stamped by the sender from a single constant; advisory on receive. See "Protocol version" in the file header. |
idempotency_key | string | 2 | Idempotency key (REQUIRED). The server MUST dedupe on this: a replay returns the original result rather than re-executing. The transaction's durable identity is the Exchange-assigned transaction_id in the response. Uniqueness is scoped to the verified RFC 9421 signer: the server dedupes per (authenticated caller, key), never globally, so a key chosen by one caller cannot collide with another's cached result. |
requester | Requester | 4 | Requester identity — forwarded for authorization and audit. |
items | repeated TransactionItem | 7 | The offers committed in this request (REQUIRED, min 1), each carrying its own reflected signed Offer + detached acceptance. A single offer is the degenerate 1-element list. The Exchange verifies each item's offer.signature (which covers pricing, terms, and expires_at) over the presented bytes against its own key — stateless, self-contained bearer tokens, with no reconstruct-from-catalog. |
ext | Struct | 15 | Extension point |
ext_critical | repeated string | 90 | Critical extension keys (COSE crit pattern, RFC 9052). Lists keys within ext that the consumer MUST understand. Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION. Empty (default) → all ext keys are safe to ignore. |
TransactionResponse
Section titled “TransactionResponse”| Field | Type | Number | Description |
|---|---|---|---|
ver | string | 1 | RAMP protocol version — "1.0". Stamped by the sender from a single constant; advisory on receive. See "Protocol version" in the file header. |
agent_identity_hash | string | 10 | Identity that a delivered retrieval_endpoint is bound to: the RFC 7638 JWK Thumbprint of the agent's Ed25519 request-signing key (see "Retrieval-URL identity binding" above). Shared across the request; set once. |
items | repeated TransactionResultItem | 13 | Per-offer results (one entry per committed item, in original order). |
total_cost | optional Cost | 14 | Aggregate cost across all items. |
subscription_quota | repeated SubscriptionQuotaInfo | 17 | Post-transaction quota state. Tells the agent how much quota remains after this transaction. Enables proactive throttling ("1 access left"). Multiple entries for multi-dimensional quotas. |
ext | Struct | 15 | Extension point |
ext_critical | repeated string | 90 | Critical extension keys (COSE crit pattern, RFC 9052). Lists keys within ext that the consumer MUST understand. Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION. Empty (default) → all ext keys are safe to ignore. |
TransactionItem
Section titled “TransactionItem”A single offer commitment within a batch transaction.
| Field | Type | Number | Description |
|---|---|---|---|
offer | Offer | 3 | The FULL signed Offer for this batch entry, reflected back exactly as received at discovery. The Exchange verifies offer.signature over these presented bytes — stateless, no reconstruct-from-catalog. REQUIRED: every batch item carries its offer. |
agent_acceptance | optional AgentAcceptance | 4 | The agent's detached acceptance signature over this item's offer. Optional on the wire; the Exchange enforces presence per item at the service layer for relayed batches. Signed bytes = the canonical AgentAcceptancePayload form, with requester_* and idempotency_key taken from the ENCLOSING TransactionRequest and offer_sig = offer.signature. |
AgentAcceptance
Section titled “AgentAcceptance”The agent’s detached acceptance signature over an accepted Offer. It travels in the execute body alongside the reflected Offer and is independent of the transport (RFC 9421) request signature, so it survives any number of broker relays. The Exchange verifies it and binds the delivery URL to the agent’s key (RFC 7638 thumbprint). signature is a hex-encoded detached Ed25519 signature over the JCS-canonicalized (RFC 8785) proto-JSON of AgentAcceptancePayload — the same canonical signing form Offer.signature defines, reduced here to JCS(protojson(payload)) because the payload carries no signature fields to clear; signature_algorithm is EdDSA.
| Field | Type | Number | Description |
|---|---|---|---|
signature | string | 1 | Hex-encoded detached Ed25519 signature over the canonical AgentAcceptancePayload bytes (see the canonical-signing definition on Offer.signature). |
signature_algorithm | string | 2 | Signature algorithm; "EdDSA" for Ed25519. |
AgentAcceptancePayload
Section titled “AgentAcceptancePayload”The canonical signing structure for AgentAcceptance. It is never sent on the wire — this message fixes the field set, and the byte layout is the canonical signing form defined on Offer.signature: RFC 8785 JCS over canonical proto-JSON with a pinned option set. Both halves are normative, so signer and verifier derive byte-identical signed bytes in any language without a protobuf binary codec, and the contract cannot drift between implementations. offer_sig is the accepted Offer.signature (which transitively binds pricing, terms, and expiry); requester_id, requester_domain, and idempotency_key come from the enclosing TransactionRequest.
| Field | Type | Number | Description |
|---|---|---|---|
offer_sig | string | 1 | The accepted Offer's signature (Offer.signature). Anchors the whole signed offer without re-serializing its terms/pricing/expiry. |
requester_id | string | 2 | Requester identity (Requester.id) the acceptance is bound to. |
requester_domain | string | 3 | Requester domain (Requester.domain) the acceptance is bound to. |
idempotency_key | string | 4 | The transaction's idempotency key — binds the acceptance to a single execute so it cannot be replayed under a different transaction. |
TransactionResultItem
Section titled “TransactionResultItem”Result for a single offer in a batch transaction.
| Field | Type | Number | Description |
|---|---|---|---|
offer_id | string | 1 | The offer_id this result is for. |
transaction_id | string | 2 | Exchange-assigned transaction identifier. |
billing_id | string | 3 | Billing record identifier minted by the Exchange's billing adapter for this transaction (not the account handle — see RegisterResponse.billing_ref). |
resource_title | optional string | 4 | Resource title echoed from the Offer. |
cost | Cost | 5 | Cost for this item. |
subscription_id | optional string | 6 | If under subscription, no per-request charge. |
subscription_unit_value | optional Cost | 11 | Computed per-unit cost for financial attribution on subscription transactions. Even when cost.amount="0" (subscription), this field carries the value of the access for accounting purposes (e.g., ASC 606 prepaid drawdown). |
denial_reason | optional DenialReason | 7 | Set if this specific item was denied (others may succeed). |
restriction_mismatches | repeated RestrictionKind | 13 | When denial_reason = RESTRICTION_NOT_SATISFIED, the restriction axes the request failed, in the same RestrictionKind vocabulary the terms use. |
expires_at | optional Timestamp | 8 | When retrieval_endpoint expires. |
retrieval_endpoint | optional string | 12 | Signed retrieval URL for this item. Bound to the requesting agent's identity via the parent TransactionResponse.agent_identity_hash (shared across all batch items); expires at expires_at. Absent if this item was denied or its delivery_method is not signed-URL-based. |
delivery_method | DeliveryMethod | 9 | How resource is delivered for this item. |
reporting_obligation | optional ReportingObligation | 10 | Reporting requirements for this item. |
Actual transaction cost.
| Field | Type | Number | Description |
|---|---|---|---|
amount | string | 1 | Exact decimal string (not a float), e.g. "19.99". Denominated in currency. |
currency | string | 2 | ISO 4217 |
unit_cost | optional string | 3 | Effective cost per unit (decimal string) |
Messages — Reporting
Section titled “Messages — Reporting”UsageReport
Section titled “UsageReport”| Field | Type | Number | Description |
|---|---|---|---|
ver | string | 1 | RAMP protocol version — "1.0". Stamped by the sender from a single constant; advisory on receive. See "Protocol version" in the file header. |
idempotency_key | string | 2 | Idempotency key (REQUIRED). The server MUST dedupe on this so a replayed report does not double-count usage. The report's durable identity is the Exchange-assigned report_id in UsageReportResponse. Uniqueness is scoped to the verified RFC 9421 signer: the server dedupes per (authenticated caller, key), never globally, so a key chosen by one caller cannot collide with another's cached result. |
transaction_id | string | 3 | Transaction ID from the delivery. |
billing_id | string | 4 | Billing record identifier from the delivery (TransactionResultItem.billing_id). |
usage | Usage | 5 | How the resource was actually used. |
timestamp | Timestamp | 6 | When the resource was used (ISO 8601). |
exchange | optional string | 8 | Exchange this report is for. |
assets | repeated UsageAsset | 9 | Assets that were delivered and used. |
ext | Struct | 15 | Extension point |
ext_critical | repeated string | 90 | Critical extension keys (COSE crit pattern, RFC 9052). Lists keys within ext that the consumer MUST understand. Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION. Empty (default) → all ext keys are safe to ignore. |
ReportingObligation
Section titled “ReportingObligation”Requirements attached to a delivery.
| Field | Type | Number | Description |
|---|---|---|---|
required | bool | 1 | Whether post-usage reporting is required. |
window | optional Duration | 2 | Duration within which the report must be submitted (e.g. "86400s" = 24 hours; proto-JSON encodes Duration as seconds). |
endpoint | optional string | 3 | URL to submit the usage report to (if different from Exchange). |
required_fields | repeated string | 4 | Field names that must be present in the report. |
ext | Struct | 15 | Extension point |
ext_critical | repeated string | 90 | Critical extension keys (COSE crit pattern, RFC 9052). Lists keys within ext that the consumer MUST understand. Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION. Empty (default) → all ext keys are safe to ignore. |
| Field | Type | Number | Description |
|---|---|---|---|
function | repeated string | 1 | How the resource was used. Standard values: "ai-train", "ai-input", "ai-index", "search", "display". Multiple allowed. CoMP-specific values available via ramp-comp-v1 extension profile. |
subfn | repeated string | 2 | Sub-function detail. Standard values: "training", "rag", "grounding", "agent_view", "agent_actions". |
consumed_quantity | int32 | 3 | REQUIRED. Actual quantity consumed, in the metering unit from the Offer's Pricing. For text: tokens consumed. For video: seconds watched. For data: records accessed. Exchange cross-references against Offer.pricing.estimated_quantity. |
displayed_to_user | optional bool | 4 | Whether resource/output was displayed to a human. |
citation_included | optional bool | 5 | Whether citation was included as required by the offer terms. |
attribution | repeated AttributionDetail | 6 | Structured attribution details for each citation provided. |
consumed_unit | optional string | 8 | Metering unit for consumed_quantity. Must match the Offer's Pricing.unit. If omitted, defaults to "tokens". Same token format as Pricing.unit: a bare registered token or a vendor:namespaced token. |
AttributionDetail
Section titled “AttributionDetail”Structured attribution metadata for usage reporting.
| Field | Type | Number | Description |
|---|---|---|---|
displayed_url | optional string | 1 | URL displayed to the user as the attribution link. |
format | optional CitationFormat | 2 | How the citation was presented. |
visible_to_user | optional bool | 3 | Whether the attribution was visible to the end user. |
UsageAsset
Section titled “UsageAsset”A single asset included in the usage report.
| Field | Type | Number | Description |
|---|---|---|---|
uri | string | 1 | Asset URI |
title | optional string | 2 | Asset title |
package_id | optional string | 3 | Package identifier |
UsageReportResponse
Section titled “UsageReportResponse”Acknowledgment of a usage report. A successful response means the report was accepted; a rejection travels as a non-OK transport error carrying ErrorDetail.usage_report_rejection.
| Field | Type | Number | Description |
|---|---|---|---|
ver | string | 1 | RAMP protocol version — "1.0". Stamped by the sender from a single constant; advisory on receive. See "Protocol version" in the file header. |
report_id | string | 3 | Exchange-assigned report identifier. Required for the dispute chain — the agent must reference this report_id in DisputeRequest to prove that a usage report was filed before disputing. The complete evidence chain: Offer → Transaction (transaction_id, billing_id) → UsageReport → UsageReportResponse (report_id) → DisputeRequest (transaction_id + report_id) |
ext | Struct | 15 | Extension point |
ext_critical | repeated string | 90 | Critical extension keys (COSE crit pattern, RFC 9052). Lists keys within ext that the consumer MUST understand. Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION. Empty (default) → all ext keys are safe to ignore. |
Messages — Dispute Resolution
Section titled “Messages — Dispute Resolution”DisputeRequest
Section titled “DisputeRequest”Agent signals a content delivery problem for a completed transaction.
| Field | Type | Number | Description |
|---|---|---|---|
ver | string | 1 | RAMP protocol version — "1.0". Stamped by the sender from a single constant; advisory on receive. See "Protocol version" in the file header. |
idempotency_key | string | 2 | Idempotency key (REQUIRED). The server MUST dedupe on this so a replayed filing does not open a duplicate case. The dispute's durable identity is the Exchange-assigned dispute_id in DisputeResponse. Uniqueness is scoped to the verified RFC 9421 signer: the server dedupes per (authenticated caller, key), never globally, so a key chosen by one caller cannot collide with another's cached result. |
transaction_id | string | 3 | Transaction being disputed. |
billing_id | string | 4 | Billing record identifier from the disputed transaction (TransactionResultItem.billing_id). |
reason | DisputeReason | 5 | Reason for the dispute. |
description | optional string | 6 | Human-readable description of the issue. |
received_content_hash | optional string | 7 | Evidence: content hash of what was actually received. Exchange compares against the hash promised in ResourceIdentity. |
received_hash_method | optional string | 8 | Hash algorithm the agent used |
report_id | string | 9 | Must reference a filed UsageReport. The agent MUST file a UsageReport (via ReportUsage RPC) and receive a report_id BEFORE filing a dispute. This prevents fire-and-forget disputes and ensures the Exchange has the complete evidence chain: what was offered, what was transacted, what the agent reported using, and what the agent disputes. The dispute chain: Transaction → UsageReport → Dispute. |
ext | Struct | 15 | Extension point |
ext_critical | repeated string | 90 | Critical extension keys (COSE crit pattern, RFC 9052). Lists keys within ext that the consumer MUST understand. Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION. Empty (default) → all ext keys are safe to ignore. |
DisputeResponse
Section titled “DisputeResponse”Exchange acknowledges and processes the dispute. A successful response means the dispute was accepted for processing; a refusal to file travels as a non-OK transport error carrying ErrorDetail.dispute_failure.
| Field | Type | Number | Description |
|---|---|---|---|
ver | string | 1 | RAMP protocol version — "1.0". Stamped by the sender from a single constant; advisory on receive. See "Protocol version" in the file header. |
dispute_id | optional string | 2 | Exchange-assigned dispute case identifier. |
estimated_resolution | optional Duration | 4 | Expected resolution timeline. |
status | DisputeStatus | 5 | Current lifecycle status of the dispute. Tracks progression through the three-tier resolution process: Tier 1 (automated, <1s): FILED → AUTO_RESOLVED or EVIDENCE_NEEDED Tier 2 (rule-based, <24h): UNDER_REVIEW → RESOLVED Tier 3 (pattern investigation, async): ESCALATED → SETTLED → FINAL Losing party may appeal: RESOLVED → APPEALED → back to UNDER_REVIEW. |
resolution | optional ResolutionType | 6 | Resolution outcome, populated when the dispute reaches a terminal state (RESOLVED, SETTLED, or FINAL). Absent while dispute is in progress (FILED, UNDER_REVIEW, ESCALATED, etc.). |
ext | Struct | 15 | Extension point |
ext_critical | repeated string | 90 | Critical extension keys (COSE crit pattern, RFC 9052). Lists keys within ext that the consumer MUST understand. Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION. Empty (default) → all ext keys are safe to ignore. |
Messages — Domain Verification
Section titled “Messages — Domain Verification”DomainVerificationRequest
Section titled “DomainVerificationRequest”Request an ACME-style domain verification challenge.
| Field | Type | Number | Description |
|---|---|---|---|
ver | string | 1 | RAMP protocol version — "1.0". Stamped by the sender from a single constant; advisory on receive. See "Protocol version" in the file header. |
domain | string | 2 | The provider domain to verify (e.g., "techcrunch.com"). |
caller_id | optional string | 3 | Caller identity (registered with the Exchange). |
ext | Struct | 15 | Extension point |
ext_critical | repeated string | 90 | Critical extension keys (COSE crit pattern, RFC 9052). Lists keys within ext that the consumer MUST understand. Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION. Empty (default) → all ext keys are safe to ignore. |
DomainVerificationChallenge
Section titled “DomainVerificationChallenge”Exchange returns a challenge token to be placed at the provider’s domain.
| Field | Type | Number | Description |
|---|---|---|---|
ver | string | 1 | RAMP protocol version — "1.0". Stamped by the sender from a single constant; advisory on receive. See "Protocol version" in the file header. |
token | string | 2 | Opaque challenge token. Provider must serve this at: https://{domain}/.well-known/ramp-verify/{token} |
expires_at | Timestamp | 3 | When this challenge expires. Provider must confirm before this time. |
verification_url | string | 4 | The exact URL the Exchange will fetch to verify. |
ext | Struct | 15 | Extension point |
ext_critical | repeated string | 90 | Critical extension keys (COSE crit pattern, RFC 9052). Lists keys within ext that the consumer MUST understand. Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION. Empty (default) → all ext keys are safe to ignore. |
DomainVerificationConfirmation
Section titled “DomainVerificationConfirmation”Confirm domain verification and register a signing key.
| Field | Type | Number | Description |
|---|---|---|---|
ver | string | 1 | RAMP protocol version — "1.0". Stamped by the sender from a single constant; advisory on receive. See "Protocol version" in the file header. |
domain | string | 2 | The domain being verified. |
token | string | 3 | The challenge token (echoed from DomainVerificationChallenge). |
signing_key | optional string | 4 | Optional: signing key to register upon successful verification. If present, the key is registered atomically with verification. Key format depends on CDN type (PEM for CloudFront, hex for HMAC). |
cdn_type | optional string | 5 | CDN type this key is for. |
ext | Struct | 15 | Extension point |
ext_critical | repeated string | 90 | Critical extension keys (COSE crit pattern, RFC 9052). Lists keys within ext that the consumer MUST understand. Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION. Empty (default) → all ext keys are safe to ignore. |
DomainVerificationResult
Section titled “DomainVerificationResult”A successful response means verification succeeded; a failure travels as a non-OK transport error carrying ErrorDetail.domain_verification_failure.
| Field | Type | Number | Description |
|---|---|---|---|
ver | string | 1 | RAMP protocol version — "1.0". Stamped by the sender from a single constant; advisory on receive. See "Protocol version" in the file header. |
key_id | optional string | 2 | If signing_key was provided: confirmation of key registration. |
valid_until | optional Timestamp | 4 | Verification is valid until this time. Provider must re-verify periodically. |
ext | Struct | 15 | Extension point |
ext_critical | repeated string | 90 | Critical extension keys (COSE crit pattern, RFC 9052). Lists keys within ext that the consumer MUST understand. Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION. Empty (default) → all ext keys are safe to ignore. |
Messages — Agent Account Registration
Section titled “Messages — Agent Account Registration”RegisterRequest
Section titled “RegisterRequest”Agent asks the Exchange to create its account. The caller’s identity is proven by the verified request signature, never asserted in the body; the operator-defined business payload rides in registration_data. Whether the Exchange inspects that payload follows its manifest: one publishing WellKnownManifest.registration_schema validates against that schema and refuses a non-conforming payload, one publishing none passes it through to its system of record uninspected.
| Field | Type | Number | Description |
|---|---|---|---|
ver | string | 1 | RAMP protocol version — "1.0". Stamped by the sender from a single constant; advisory on receive. See "Protocol version" in the file header. |
registration_data | Struct | 2 | Operator-defined registration payload; the business fields are not fixed in the wire contract. Whether the Exchange inspects it follows its manifest — see WellKnownManifest.registration_schema. The caller's identity is taken from the verified request signature, never from this payload. |
ext | Struct | 15 | Extension point |
ext_critical | repeated string | 90 | Critical extension keys (COSE crit pattern, RFC 9052). Lists keys within ext that the consumer MUST understand. Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION. Empty (default) → all ext keys are safe to ignore. |
RegisterResponse
Section titled “RegisterResponse”Exchange returns the minted billing_ref — the opaque, long-lived, per-Exchange account handle — and the account’s current active state. A repeat Register for the same agent returns the same billing_ref; a refused registration travels as a non-OK transport error carrying ErrorDetail.registration_failure.
| Field | Type | Number | Description |
|---|---|---|---|
ver | string | 1 | RAMP protocol version — "1.0". Stamped by the sender from a single constant; advisory on receive. See "Protocol version" in the file header. |
billing_ref | string | 2 | Opaque, long-lived, per-Exchange account handle minted by the Exchange. Means nothing on its own and is never accepted as caller input. A repeat Register for the same agent returns the same value. |
active | bool | 3 | Whether the account is currently active. Accounts may start inactive and be activated out-of-band by the Exchange operator. |
ext | Struct | 15 | Extension point |
ext_critical | repeated string | 90 | Critical extension keys (COSE crit pattern, RFC 9052). Lists keys within ext that the consumer MUST understand. Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION. Empty (default) → all ext keys are safe to ignore. |
GetAccountStatusRequest
Section titled “GetAccountStatusRequest”Read-only check of whether the calling agent’s account is active. Deliberately carries no identifying field — the Exchange resolves the account from the verified request signature.
| Field | Type | Number | Description |
|---|---|---|---|
ver | string | 1 | RAMP protocol version — "1.0". Stamped by the sender from a single constant; advisory on receive. See "Protocol version" in the file header. |
ext | Struct | 15 | Extension point |
ext_critical | repeated string | 90 | Critical extension keys (COSE crit pattern, RFC 9052). Lists keys within ext that the consumer MUST understand. Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION. Empty (default) → all ext keys are safe to ignore. |
GetAccountStatusResponse
Section titled “GetAccountStatusResponse”The account’s current state. billing_ref is empty when the calling agent has no account yet.
| Field | Type | Number | Description |
|---|---|---|---|
ver | string | 1 | RAMP protocol version — "1.0". Stamped by the sender from a single constant; advisory on receive. See "Protocol version" in the file header. |
billing_ref | string | 2 | The account handle minted at registration (see RegisterResponse.billing_ref). Empty when the calling agent has no account yet. |
active | bool | 3 | Whether the account is currently active. |
ext | Struct | 15 | Extension point |
ext_critical | repeated string | 90 | Critical extension keys (COSE crit pattern, RFC 9052). Lists keys within ext that the consumer MUST understand. Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION. Empty (default) → all ext keys are safe to ignore. |
Messages — Discovery
Section titled “Messages — Discovery”WellKnownManifest
Section titled “WellKnownManifest”Served at /.well-known/ramp.json by every RAMP participant (agent, exchange, broker, publisher). Single canonical document, role-tagged via role. Signing keys are not carried here — each participant publishes them in its WBA directory (the JWK Set at /.well-known/http-message-signatures-directory, modeled by WBAFile). Per-role fields are populated only when that role applies; consumers MUST ignore non-applicable fields based on role.
| Field | Type | Number | Description |
|---|---|---|---|
ver | string | 1 | RAMP protocol version of THIS MANIFEST DOCUMENT's schema — a namespace separate from the RPC envelope ver, deliberately not coupled to it. MUST equal "1.0"; consumers REJECT unrecognised major versions. |
role | Role | 2 | Role this manifest describes. |
domain | string | 3 | Canonical domain serving this manifest. |
contact | optional string | 4 | Contact email (licensing, integration, security). |
exchanges | repeated AuthorizedExchange | 7 | Publisher-only. Authorized exchanges for this publisher's resources. Like ads.txt — declares who may sell. MUST be empty for non-publisher roles. |
catalog_contributors | repeated CatalogContributor | 8 | Publisher-only. Authorized third-party catalog contributors. MUST be empty for non-publisher roles. |
name | optional string | 9 | Exchange-only. Human-readable Exchange name. |
operator | optional string | 10 | Exchange-only. Organization operating this Exchange. |
operator_domain | optional string | 11 | Exchange-only. Operator's corporate domain (may differ from domain). |
endpoint | optional string | 12 | Exchange-only. ExchangeService endpoint URL. MUST be on the same host AND PORT that serve this manifest, or on a subdomain of that host on that port, and MUST NOT carry userinfo. A consumer refuses an endpoint anywhere else: this document is only as trustworthy as the host that served it, so an endpoint naming an unrelated host would let whoever answers for the manifest redirect a signed call to a party the signature never covered, and another port is another service the publisher of the manifest need not control. The host match is on a full dot-delimited label boundary, so evil-a.com is not a subdomain of a.com. A port equal to the scheme's default and an omitted port are the SAME port, so https://x, https://x:443 and x all match. An Exchange reachable on a non-default port names that port on both sides. (One paragraph deliberately: a blank line here routes the first paragraph into the generated types' JSON-Schema title, which the Pydantic/Zod export drops.) |
health_endpoint | optional string | 13 | Exchange-only. Health check endpoint URL. |
catalog_endpoint | optional string | 14 | Exchange-only. CatalogService endpoint URL (if exposed). |
protocol_versions_supported | repeated string | 16 | Exchange-only. Supported RAMP protocol versions (e.g. ["1.0"]). |
pricing_models_supported | repeated PricingModel | 17 | Exchange-only. Supported pricing models. |
delivery_methods_supported | repeated DeliveryMethod | 18 | Exchange-only. Supported delivery methods. |
hash_methods_supported | repeated string | 19 | Exchange-only. Accepted resource hash methods for attestation verification. |
accepted_verifiers | repeated string | 20 | Exchange-only. Trusted attestation verification vendors (domains). |
terms_uri | optional string | 21 | Exchange-only. Terms of service URL. |
privacy_uri | optional string | 22 | Exchange-only. Privacy policy URL. |
supported_profiles | repeated string | 23 | Exchange-only. Domain extension profiles this Exchange conforms to. See standards-layering docs. |
supported_auth_methods | repeated AuthMethod | 24 | Exchange-only. Authorization methods this Exchange supports (ordered by preference). |
oidc_issuer | optional string | 25 | Exchange-only. OIDC Discovery URL when OAuth methods are supported. |
gnap_grant_endpoint | optional string | 26 | Exchange-only. GNAP grant endpoint when GNAP is supported. |
base_currency | optional string | 27 | Exchange-only. Base currency for pricing (ISO 4217). All unit_cost values from this Exchange are denominated in this currency. |
max_intermediary_hops | optional int32 | 28 | Exchange-only. Maximum forwarding hops this Exchange tolerates on an inbound request (Agent → Broker → … → Exchange), counted as RFC 9421 HTTP Message Signatures. A request carrying more SHOULD be rejected. Lets Exchanges publish their chain-depth tolerance so Brokers prune before forwarding. Absent = no published limit (Exchange applies its own default policy). |
registration_schema | Struct | 29 | Exchange-only. JSON Schema (draft 2020-12) describing the RegisterRequest.registration_data object this Exchange expects. This field is the single home of the enforce/pass-through contract, and publishing it IS the enforcement switch. Present: this Exchange validates registration_data against the schema and refuses a non-conforming payload with REGISTRATION_FAILURE_REASON_INVALID_REGISTRATION_DATA, naming the offending members in RegistrationFailure.field_errors. Absent: registration_data is passed through to the system of record uninspected, so an Exchange that publishes no schema needs no change to stay conformant. Safety rules, because a consumer reads this schema out of a third party's manifest: it MUST be self-contained, and a consumer MUST NOT resolve a remote $ref out of it — doing so turns every reader into an SSRF vector aimed at a URL the schema's author chose. A consumer SHOULD bound validation time and recursion depth; draft 2020-12 pattern admits regexes with catastrophic backtracking. Size is capped at 16KB, measured as the UTF-8 bytes of this member as served in ramp.json; a consumer SHOULD reject an oversized schema and skip its local pre-check rather than truncate it, which leaves the Exchange's own enforcement the deciding check exactly as when no schema is published. |
ext | Struct | 15 | Extension point |
ext_critical | repeated string | 90 | Critical extension keys (COSE crit pattern, RFC 9052). Lists keys within ext that the consumer MUST understand. Unknown values reject with UNKNOWN_CRITICAL_EXTENSION. Empty (default) → ignore-unknown. |
WBAFile
Section titled “WBAFile”The pure WBA directory, served at /.well-known/http-message-signatures-directory (Content-Type: application/jwk-set+json). Holds each participant’s inline signing keys and an optional emergency-revocation pointer. Keys are identified by their RFC 7638 thumbprint (the RFC 9421 keyid).
| Field | Type | Number | Description |
|---|---|---|---|
keys | repeated JsonWebKey | 1 | Signature-verification keys; ≥1 valid at serve time |
revocation_url | optional string | 2 | Emergency key-revocation list URL (KeyRevocationList) |
JsonWebKey
Section titled “JsonWebKey”Inline RFC 7517 JWK. RAMP v1.0 supports Ed25519 only. Time bounds are RFC3339 strings; the validity window is half-open [not_before, not_after). Keys carry no kid label — a key is identified by its RFC 7638 thumbprint (the RFC 9421 keyid).
| Field | Type | Number | Description |
|---|---|---|---|
kty | string | 2 | Key type. RAMP v1.0: MUST be "OKP". |
crv | string | 3 | Curve. RAMP v1.0: MUST be "Ed25519". |
use | string | 4 | Intended key use. RAMP v1.0: MUST be "sig". |
alg | string | 5 | Signing algorithm. RAMP v1.0: MUST be "EdDSA". |
x | string | 6 | base64url-encoded 32-byte Ed25519 public key. |
not_before | string | 7 | RFC3339 timestamp. Key is invalid before this instant. |
not_after | string | 8 | RFC3339 timestamp. Key is invalid at and after this instant (strict upper bound). |
KeyRevocationList
Section titled “KeyRevocationList”Body served at WBAFile.revocation_url. Snapshot semantics: revoked is the complete set of revoked key thumbprints at as_of; consumers replace their local revocation set on each successful poll.
| Field | Type | Number | Description |
|---|---|---|---|
as_of | Timestamp | 1 | Server's response time (RFC3339, UTC). Consumers use this to detect clock skew. |
revoked | repeated string | 2 | Complete list of revoked key thumbprints (RFC 7638, base64url-no-pad) at as_of. |
CatalogContributor
Section titled “CatalogContributor”Authorizes a third party to push catalog metadata on the provider’s behalf.
| Field | Type | Number | Description |
|---|---|---|---|
domain | string | 1 | Canonical domain of the authorized contributor (e.g., "doubleverify.com"). |
relationship | string | 2 | Relationship of this contributor to the provider. Examples: "verifier" (resource intelligence vendor that attests to resource properties), "exchange" (an Exchange that enriches catalog entries). |
AuthorizedExchange
Section titled “AuthorizedExchange”A Exchange authorized to sell this provider’s content.
| Field | Type | Number | Description |
|---|---|---|---|
domain | string | 1 | Canonical domain of the Exchange. |
endpoint | string | 2 | RAMP ExchangeService endpoint URL. |
relationship | ProviderRelationship | 3 | Relationship type (mirrors ads.txt DIRECT/RESELLER). |
ext | Struct | 15 | Extension point |
ext_critical | repeated string | 90 | Critical extension keys (COSE crit pattern, RFC 9052). Lists keys within ext that the consumer MUST understand. Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION. Empty (default) → all ext keys are safe to ignore. |
Messages — Broker Protocol
Section titled “Messages — Broker Protocol”Messages for the Agent-to-Broker path (Steps 1 and 6). When an agent talks directly to an Exchange, it uses ResourceQuery/TransactionRequest instead.
DiscoveryRequest
Section titled “DiscoveryRequest”Agent sends to Broker (Step 1), carried by BrokerService.Resolve. Pure discovery: it returns offers, executes no transaction, and so carries no idempotency_key (retrying is naturally safe). Correlation rides on the X-Request-ID header, not the body.
| Field | Type | Number | Description |
|---|---|---|---|
ver | string | 1 | RAMP protocol version — "1.0". Stamped by the sender from a single constant; advisory on receive. See "Protocol version" in the file header. |
requester | Requester | 3 | Requester identity — who is making this request, what scopes they have. The Broker forwards this to Exchanges in ResourceQuery.requester. |
uris | repeated string | 8 | Resource URIs the agent wants. The Broker forwards these to Exchanges in ResourceQuery.uris. Optional when query / search_filters drive Broker-side discovery instead. |
acceptable_restrictions | repeated AcceptableRestriction | 9 | The limits the agent will operate within, per restriction axis — see AcceptableRestriction. The Broker forwards these to Exchanges in ResourceQuery.acceptable_restrictions. Advisory selection inputs, not enforcement. |
constraints | optional RequestConstraints | 4 | Constraints for exchange filtering and offer selection. |
supported_profiles | repeated string | 5 | Domain extension profiles the agent understands. The Broker uses this to: 1. Route queries to Exchanges that support these profiles 2. Forward the profiles in ResourceQuery.supported_profiles 3. Include profile-specific ext fields when returning results Examples: ["ramp-academic-v1"] — agent working on literature review |
query | optional string | 6 | Search query for Broker-side resource discovery. Used when the agent doesn't know specific URIs but wants the Broker to find matching resources across Exchanges. When present, the Broker interprets the query and discovers resources across Exchanges on the agent's behalf. Results returned as Offers in DiscoveryResponse, same as for specific URI requests. Can be used alongside uris (specific URIs + search in one request). |
search_filters | optional Struct | 7 | Structured search filters (optional, alongside or instead of query). Keys are profile-specific: "academic.topic", "news.category", "legal.jurisdiction", etc. The Broker maps these to Exchange-specific query parameters. |
ext | Struct | 15 | Extension point |
ext_critical | repeated string | 90 | Critical extension keys (COSE crit pattern, RFC 9052). Lists keys within ext that the consumer MUST understand. Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION. Empty (default) → all ext keys are safe to ignore. |
RequestConstraints
Section titled “RequestConstraints”Budget and preference constraints for exchange filtering and offer selection.
| Field | Type | Number | Description |
|---|---|---|---|
exchanges | repeated string | 1 | Authorized Exchange domains. Broker queries only these. |
max_price | optional Cost | 2 | Maximum price the agent is willing to pay. |
max_unit_cost | optional string | 3 | Maximum effective cost per unit, as an exact decimal string (not a float). |
delivery_preference | repeated DeliveryMethod | 4 | Preferred delivery methods, in order of preference. |
reporting_capable | optional bool | 5 | Whether the agent supports post-usage reporting. |
preferred_exchanges | repeated string | 6 | Exchanges the agent has existing relationships with (subscriptions, contracts). The Broker SHOULD prefer these when resource is available — subscription resource has zero marginal cost. |
budget_scope | optional string | 7 | Budget scope identifier for per-period tracking. E.g. "user:u-12345" for per-user budgets, "team:eng" for per-team. The Broker tracks cumulative spend per scope across sessions. |
period_budget | optional Cost | 8 | Per-period budget limit. The Broker tracks spend against this for the budget_scope. Transactions that would exceed are denied. |
budget_period | optional Duration | 9 | Budget period (e.g. "2592000s" = 30 days; proto-JSON encodes Duration as seconds). Resets at period boundary. |
max_data_age | optional Duration | 10 | Maximum acceptable age of resource data. The Broker SHOULD exclude offers where (now - Offer.data_as_of) exceeds this duration. Only relevant for DYNAMIC resources. Ignored for STATIC (content is immutable) and LIVE (content doesn't exist yet). Examples: 7 days — "credit report updated within the last week" 1 hour — "stock snapshot from the last hour" 30 days — "drug interaction database updated this month" |
max_hops | optional int32 | 11 | Maximum forwarding hops the agent will allow (Agent → Broker → … → Exchange), counted as the number of RFC 9421 HTTP Message Signatures on the request. Caps chain depth so a request is not relayed through more brokers than the agent is willing to trust or pay. A Broker MUST NOT forward a request whose signature count would exceed this. Absent = agent imposes no cap (the Exchange's max_intermediary_hops still applies). |
DiscoveryResponse
Section titled “DiscoveryResponse”Broker returns to Agent (Step 6). Discovery-only: offer_groups (field 4, one OfferGroup per requested URI, each carrying the full signed Offer with Offer.exchange as the execute-routing target) or absence_reason (field 16) with empty offer_groups. There are no inline delivery fields — the per-transaction result rides on TransactionResponse via the separate execute path.
| Field | Type | Number | Description |
|---|---|---|---|
ver | string | 1 | RAMP protocol version — "1.0". Stamped by the sender from a single constant; advisory on receive. See "Protocol version" in the file header. |
offer_groups | repeated OfferGroup | 4 | Offers grouped by requested URI — the sole offer representation in this response. One OfferGroup per URI the agent asked for (echoed in OfferGroup.uri); a group with no offers carries OfferGroup.absence_reason explaining why. Each contained Offer is the full signed Offer the Exchange issued (including Offer.exchange, the execute-routing target), forwarded by the Broker unchanged so the agent can verify the signature end to end. |
absence_reason | optional OfferAbsenceReason | 16 | Why the resolve produced no offers at all. Set (and offer_groups empty) on a successful "no result" answer; unset when offer_groups is non-empty. Same vocabulary DiscoverResources uses for OfferGroup.absence_reason. RESTRICTION_FILTERED may appear here, but Resolve does not surface the per-axis detail: DiscoveryResponse has no restriction_filters companion (unlike OfferGroup). A consumer needing the filtered axes calls DiscoverResources. Existence-oracle note: an authorization-flavored reason (SCOPE_INSUFFICIENT, NOT_AUTHORIZED, NOT_IN_CATALOG, CONTENT_BLOCKED) confirms a resource exists and why access was refused. Resolve surfaces the same oracle at the broker that OfferGroup.absence_reason does at the Exchange, so the same mitigation applies: where existence itself must stay hidden, the Broker MAY omit the reason (leave this unset) rather than reveal it. See the threat model. |
ext | Struct | 15 | Extension point |
ext_critical | repeated string | 90 | Critical extension keys (COSE crit pattern, RFC 9052). Lists keys within ext that the consumer MUST understand. Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION. Empty (default) → all ext keys are safe to ignore. |
Messages — CatalogService
Section titled “Messages — CatalogService”Messages for the optional CatalogService RPC.
PushResourcesRequest
Section titled “PushResourcesRequest”Push or update content entries in the Exchange catalog.
| Field | Type | Number | Description |
|---|---|---|---|
ver | string | 1 | RAMP protocol version — "1.0". Stamped by the sender from a single constant; advisory on receive. See "Protocol version" in the file header. |
tenant_id | string | 2 | Tenant identifier |
entries | repeated ResourceEntry | 3 | Content entries to push |
caller_id | string | 4 | Identity of the caller (who is pushing this data). The Exchange verifies this matches a registered CatalogService client. |
ext | Struct | 15 | Extension point |
ext_critical | repeated string | 90 | Critical extension keys (COSE crit pattern, RFC 9052). Lists keys within ext that the consumer MUST understand. Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION. Empty (default) → all ext keys are safe to ignore. |
ResourceEntry
Section titled “ResourceEntry”A single resource catalog entry.
| Field | Type | Number | Description |
|---|---|---|---|
domain | string | 1 | Provider domain |
path | string | 2 | Content path |
content_id | optional string | 3 | Content identifier |
title | optional string | 4 | Content title |
word_count | optional int32 | 5 | Word count |
estimated_quantity | optional int32 | 6 | Estimated quantity in the metering unit |
content_hash | optional string | 7 | Content hash |
hash_method | optional string | 8 | Hash algorithm |
source | optional IngestionSource | 9 | How the entry was discovered |
provenance_source | optional string | 10 | Who provided this resource metadata. Creates audit trail for "where did this catalog entry come from?" |
provenance_timestamp | optional Timestamp | 11 | When this metadata was collected/generated. |
attestations | repeated ResourceAttestation | 12 | Signed attestations about this resource entry. Same semantics as Offer.attestations — see ResourceAttestation message for verification levels and claim vocabulary. Attestations pushed via CatalogService are verified at push time: the Exchange checks that the attestation verifier is authorized to push for this provider (via catalog_contributors in the provider's WellKnownManifest) and validates the attestation signature against the verifier's public key from their /.well-known/ramp.json endpoint (WellKnownManifest, role determined by the verifier's operator). |
terms | repeated LicenseTerm | 13 | Publisher-declared licensing terms for this resource. See LicenseTerm for the full model. For ENUMERATED terms, Pricing MUST be present. For REFERENCE_ONLY terms, License.uri is authoritative. The Exchange validates ENUMERATED terms at push time and surfaces them in Offer.terms on discovery. |
resource_mutability | optional ResourceMutability | 14 | Optional mutability hint. When omitted, the Exchange applies the STATIC default at Offer build; an explicit UNSPECIFIED is rejected. A value in ext is not read — the typed field is authoritative, so an ext-only value is treated as omitted. Mirrors the required Offer-side ResourceIdentity.resource_mutability. |
ext | Struct | 15 | Extension point |
ext_critical | repeated string | 90 | Critical extension keys (COSE crit pattern, RFC 9052). Lists keys within ext that the consumer MUST understand. Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION. Empty (default) → all ext keys are safe to ignore. |
PushResourcesResponse
Section titled “PushResourcesResponse”The accepted / rejected counts are entry-level tallies of a successful push — not the removed failure bools. A push that could not be applied at all travels as a non-OK transport error carrying ErrorDetail.catalog_rejection.
| Field | Type | Number | Description |
|---|---|---|---|
ver | string | 1 | RAMP protocol version — "1.0". Stamped by the sender from a single constant; advisory on receive. See "Protocol version" in the file header. |
accepted | int32 | 2 | Number of entries accepted |
rejected | int32 | 3 | Number of entries rejected |
warnings | repeated string | 4 | Non-fatal issues encountered during ingestion. Examples: unrecognized vocab token in a Restriction (term accepted but flagged), REFERENCE_ONLY term missing License.uri (informational). Warnings do not cause rejection — they are surfaced so publishers can fix their feeds without a hard failure. |
ext | Struct | 15 | Extension point |
ext_critical | repeated string | 90 | Critical extension keys (COSE crit pattern, RFC 9052). Lists keys within ext that the consumer MUST understand. Unknown keys in this list → reject with UNKNOWN_CRITICAL_EXTENSION. Empty (default) → all ext keys are safe to ignore. |
RemoveResourcesRequest
Section titled “RemoveResourcesRequest”| Field | Type | Number | Description |
|---|---|---|---|
ver | string | 1 | RAMP protocol version — "1.0". Stamped by the sender from a single constant; advisory on receive. See "Protocol version" in the file header. |
tenant_id | string | 2 | Tenant identifier |
paths | repeated string | 3 | Paths to remove |
RemoveResourcesResponse
Section titled “RemoveResourcesResponse”| Field | Type | Number | Description |
|---|---|---|---|
ver | string | 1 | RAMP protocol version — "1.0". Stamped by the sender from a single constant; advisory on receive. See "Protocol version" in the file header. |
removed | int32 | 2 | Number of entries removed |
RefreshCatalogRequest
Section titled “RefreshCatalogRequest”| Field | Type | Number | Description |
|---|---|---|---|
ver | string | 1 | RAMP protocol version — "1.0". Stamped by the sender from a single constant; advisory on receive. See "Protocol version" in the file header. |
tenant_id | string | 2 | Tenant identifier |
RefreshCatalogResponse
Section titled “RefreshCatalogResponse”| Field | Type | Number | Description |
|---|---|---|---|
ver | string | 1 | RAMP protocol version — "1.0". Stamped by the sender from a single constant; advisory on receive. See "Protocol version" in the file header. |
started | bool | 2 | Whether the refresh was started |
Universal Licensing Core
Section titled “Universal Licensing Core”A resource carries zero or more LicenseTerm entries — each term is a complete commercial arrangement. Multiple terms are the normal case: a news article may be free for academic use and paid for commercial use; a stock photo may be perpetually licensed with an impressions cap.
See /protocol/licensing-terms for a full conceptual walkthrough with examples.
LicenseTerm
Section titled “LicenseTerm”One complete access arrangement for a resource. Lives at both ingestion (ResourceEntry.terms) and emission (Offer.terms).
Validation rules (wire-enforced via protovalidate unless noted). A parenthetical
code is the cross-field CEL rule id: that enforces the rule (e.g. license_term.reference_only.requires_uri); single-field rules use protovalidate’s standard constraints (enum.not_in, string.pattern, …).
pricingMUST be present on every term, any semantics — absent Pricing is a validation error.model = FREEmust be stated explicitly — absent Pricing is not free.semanticsMUST be set —TERM_SEMANTICS_UNSPECIFIEDis rejected (the field’senum.not_in:[0]rule).REFERENCE_ONLYrequireslicense.urito be non-empty (license_term.reference_only.requires_uri); aLicensewith aurirequires auri_digest(license.digest_required_with_uri).- At most one
Restrictionperkind(license_term.one_restriction_per_kind); a token cannot be both permitted and prohibited (restriction.permitted_prohibited_disjoint). - Unknown tokens in
restrictions[].permitted/prohibitedproducePushResourcesResponse.warnings[]but do NOT cause hard rejection (ingest-time, not CEL).
| Field | Type | Number | Description |
|---|---|---|---|
license | optional License | 1 | Governing license document. Authoritative for REFERENCE_ONLY terms, which MUST carry a License with a non-empty uri — a REFERENCE_ONLY term that references nothing is rejected at ingest. |
semantics | TermSemantics | 2 | How to interpret the machine fields. |
restrictions | repeated Restriction | 3 | Usage restrictions (function, geography, user-type). Multiple restrictions are AND-combined — the agent must satisfy all of them. |
quotas | repeated Quota | 4 | Usage caps. The agent must not exceed any individual Quota. |
obligations | repeated Obligation | 5 | Post-use behavioral requirements. |
pricing | optional Pricing | 6 | Pricing for this term. REQUIRED for every term regardless of semantics — an agent cannot act on a priceless term, so absent Pricing is a validation error at ingest. model = FREE must be stated explicitly (absent Pricing is not free). A REFERENCE_ONLY term states its price here too; its License governs the human-readable terms but does not replace the machine-readable price. |
scopes | repeated string | 7 | Delegation scope-gating: the Exchange returns this term to an agent iff the agent's delegation grant covers ALL of these scopes (AND-semantics). Empty = public. A subscription term is Pricing{model:FREE} + scopes:["subscription:..."]. Coverage uses the SAME matching rule as Requester/delegation scopes: segment-wise (":" separated), each granted segment must equal the corresponding required segment or be "", a terminal "" matches all remaining segments, and there is NO implicit prefix match (a grant narrower than the requirement does not cover it). "dist:*" covers "dist:US" and "dist:US:CA"; "dist" covers only "dist". There is exactly one scope-matching algorithm across the protocol. |
part_label | optional string | 8 | Informational human-readable name for this sub-part (sub-part terms). |
License
Section titled “License”Identifies the governing license document for a LicenseTerm.
| Field | Type | Number | Description |
|---|---|---|---|
uri | optional string | 1 | Canonical identity of the license document (RFC 3986). MUST NOT be URL-validated — data-labels TDL identifiers use non-URL schemes. For REFERENCE_ONLY terms this is the authoritative specification. Examples: "https://creativecommons.org/licenses/by/4.0/" "https://techcrunch.com/licensing/ai-terms-2026" "MUST NOT URL-validate" means do not REJECT non-URL schemes — it does NOT mean fetch blindly. A consumer that dereferences this URI MUST apply the SSRF countermeasures in the security threat model (T-LIC-1): scheme allowlist, block loopback/private/metadata addresses (resolve-then-check), fetch via an egress proxy, and treat the response as untrusted content. Verify the fetched bytes against uri_digest before use. |
id | optional string | 2 | Stable short identifier: SPDX short-id ("GPL-3.0-only"), TollBit cuid, or catalog doc-id. Used by agents and the vocab linter for known-license lookup; SHARE_ALIKE derivatives default their scope_license to this. |
name | optional string | 3 | Human-readable name (licenseType, schema.org node name). |
immutable | optional bool | 4 | Data-labels TDL: the document at uri is versioned and will not change. |
uri_digest | optional string | 5 | Cryptographic digest of the document at uri, in "method:hexdigest" form (e.g. "sha256:9f86d081..."). Pins the referenced document so a consumer can verify the bytes it fetches match what was offered; covered by the offer signature, so it is tamper-evident end to end. REQUIRED whenever uri is non-empty — any semantics, mutable or not: without a pinned digest a MitM (or the publisher) can swap the document the agent reads. The Exchange pins it at ingestion (computing it over the safely-fetched document, or accepting a publisher-supplied value when uri is not HTTP-fetchable, e.g. a non-URL TDL scheme). The method MUST be a collision-resistant hash — sha256, sha384, or sha512. Legacy md5/sha1 are rejected on the wire: a forgeable digest would defeat the swap-protection this field exists for. The CEL is STRUCTURE ONLY (allowlisted prefix + matching hex length); presence (digest-when-uri) is enforced at ingest. |
Restriction
Section titled “Restriction”A single constraint on one licensing dimension: function (what), geography (where), or user-type (who).
Reading a restriction: a value is in-scope when it matches at least one permitted[] token AND none of the prohibited[] tokens. Empty permitted[] = any value permitted on this axis. Restrictions ride on the offer; the agent self-selects the term it can honour (the Exchange does not pre-filter terms against requester attributes).
Vocabulary sources: proto-native — (ramp.v1.vocab_enum) on the RESTRICTION_KIND_FUNCTION / RESTRICTION_KIND_GEOGRAPHY / RESTRICTION_KIND_USER_TYPE enum values (no side-car JSON registry).
| Field | Type | Number | Description |
|---|---|---|---|
kind | RestrictionKind | 1 | Which dimension this restriction applies to. |
permitted | repeated string | 2 | Tokens allowed on this axis. Empty = all permitted. For FUNCTION: "ai-input", "ai-train", "search", "editorial", "commercial", … For GEOGRAPHY: "US", "DE", "EU", "EEA", "*", … For USER_TYPE: "individual", "academic", "commercial_entity", … |
prohibited | repeated string | 3 | Tokens blocked on this axis. Takes precedence over permitted[]. |
advisory | bool | 4 | Fail-closed by default. When false (the default), this restriction is BINDING: an agent that cannot evaluate every token in it — including an unknown vendor token — MUST decline the term. Set advisory = true to downgrade an unverifiable restriction to non-blocking. This deliberately inverts the COSE-crit opt-in default: a license restriction a consumer does not understand should stop it, not be silently ignored. |
A usage cap that gates whether a LicenseTerm remains valid. Quotas limit consumption before a term expires or must be renegotiated — they are NOT billing quantities.
Metric vocabulary: proto-native — (ramp.v1.vocab) on Quota.metric. The registered metrics, rendered from the proto at build time:
display-words impressions tokens input-tokens units-manufactured accesses copies seats
| Field | Type | Number | Description |
|---|---|---|---|
metric | string | 1 | The unit being capped — an open vocabulary axis. The (ramp.v1.vocab) entries below are the SOLE authored source of the registered bare metric tokens. A buf plugin reads them structurally and emits the quotametrics constants + IsRegistered; ingest enforces membership from those. The CEL is STRUCTURE ONLY (non-empty bare token or vendor:namespaced) — it never lists the tokens, so it cannot drift. Token meanings: display-words Words of content text rendered to an end user. impressions Times the content is displayed to an end user. tokens LLM output tokens generated using this content. input-tokens LLM input tokens consumed from this content. units-manufactured Physical units manufactured from this design/pattern. accesses Distinct content access / retrieval events. copies Digital or physical copies produced. seats Distinct named users licensed to access the content. |
limit | int64 | 2 | Maximum allowed value in the given window. A quota of 0 grants nothing — express "no access" by omitting the term, not a zero quota. |
window | QuotaWindow | 3 | Time window over which the limit accumulates. |
Obligation
Section titled “Obligation”A post-use behavioral requirement attached to a LicenseTerm. Attribution and contribution are behavioral requirements here, not pricing models.
| Field | Type | Number | Description |
|---|---|---|---|
kind | ObligationKind | 1 | What the agent must do. |
trigger | ObligationTrigger | 2 | When the obligation activates. |
scope_license | optional License | 3 | The license that derivatives must be released under. REQUIRED for SHARE_ALIKE (rejected if absent), where it MUST identify a license — set id (SPDX short-id, the common copyleft case, often the term's own License.id) and/or uri. Because it is a License, a referenced uri inherits the uri_digest swap-protection rule: a uri without a digest is rejected, exactly as for any other license reference. |
detail | optional string | 4 | Free-form detail: attribution string, notice file URI, etc. OBLIGATION_KIND_OTHER without it → lint warning. |
Unified Error Model
Section titled “Unified Error Model”One way to communicate failure across every RPC. The transport carries a coarse canonical code (gRPC / Connect Code) as the error class; an ErrorDetail message — attached to the non-OK transport error’s details (the same mechanism protovalidate uses to attach its Violations) — carries the precise, machine-readable reason and structured context. Clients branch on the typed reason, never on a human string.
Error vs. body. A query that ran successfully returns its answer in the response body, and “no results” / per-item absence (OfferGroup.absence_reason) is a success, not an error. Only a method that could not perform the requested action returns a non-OK code plus an ErrorDetail. The per-domain reason enums below are the single source of truth that replaces the former in-body failure fields (denial_reason, rejection_reason, accepted, verified, failure_reason).
ErrorDetail
Section titled “ErrorDetail”The structured detail attached to every non-OK transport error.
| Field | Type | Number | Description |
|---|---|---|---|
message | string | 1 | Developer-facing, NON-authoritative human message. Clients MUST branch on the typed reason below, never on this text. Servers SHOULD NOT place secrets, PII, or existence/authorization detail here that the closed typed reason deliberately withholds: unlike the enum, this free text is unbounded and easily becomes an existence oracle or leak channel (see metadata). |
domain | string | 2 | Stable grouping for the failing surface, e.g. "ramp.v1.ExchangeService". Mirrors google.rpc.ErrorInfo.domain so generic tooling can group errors. |
metadata | map<string, string> | 3 | Dynamic key/value context that also appears in message (ids, limits, axes). Mirrors google.rpc.ErrorInfo.metadata. Strongly-typed context rides in the per-domain reason block below instead. Same leakage rule as message: servers SHOULD NOT put secrets, PII, or withheld existence/authorization detail here — it is the same potential side channel as the absence oracle. |
transaction_denial | TransactionDenial | 10 | reason oneof — ExecuteTransaction denial |
catalog_rejection | CatalogRejection | 11 | reason oneof — CatalogService rejection |
registration_failure | RegistrationFailure | 12 | reason oneof — agent/provider registration refused |
dispute_failure | DisputeFailure | 13 | reason oneof — DisputeTransaction filing refused |
domain_verification_failure | DomainVerificationFailure | 14 | reason oneof — domain verification failed |
retrieval_auth_failure | RetrievalAuthFailure | 15 | reason oneof — signed-URL / proof-of-possession check failed |
usage_report_rejection | UsageReportRejection | 16 | reason oneof — ReportUsage filing rejected |
Exactly one typed reason block is set, selected by the failing method. The reason oneof is absent for generic transport-class failures (e.g. INVALID_ARGUMENT, INTERNAL) that carry no domain-specific reason.
TransactionDenial
Section titled “TransactionDenial”ExecuteTransaction could not complete. Reuses the DenialReason vocabulary.
| Field | Type | Number | Description |
|---|---|---|---|
reason | DenialReason | 1 | The denial reason (defined-only, non-zero) |
restriction_mismatches | repeated RestrictionKind | 2 | When reason = RESTRICTION_NOT_SATISFIED, the failed axes (same RestrictionKind vocabulary the terms use). |
offer_id | optional string | 3 | Batch mode: the offer this denial pertains to. |
CatalogRejection
Section titled “CatalogRejection”A CatalogService call could not be applied.
| Field | Type | Number | Description |
|---|---|---|---|
reason | CatalogRejectionReason | 1 | The rejection reason (defined-only, non-zero) |
rejected_paths | repeated string | 2 | For partial-batch failures: the entry paths that were rejected. |
RegistrationFailure
Section titled “RegistrationFailure”A registration request could not be completed.
| Field | Type | Number | Description |
|---|---|---|---|
reason | RegistrationFailureReason | 1 | The failure reason (defined-only, non-zero) |
field_errors | repeated RegistrationFieldError | 2 | When reason = INVALID_REGISTRATION_DATA: the registration_data members that are missing or do not conform. Empty for every other reason. |
RegistrationFieldError
Section titled “RegistrationFieldError”One registration_data member that failed the Exchange’s published registration_schema, carried on RegistrationFailure.field_errors.
| Field | Type | Number | Description |
|---|---|---|---|
path | string | 1 | RFC 6901 JSON Pointer to the offending member, relative to registration_data (e.g. "/vat_id", "/address/postal_code"). The empty string addresses registration_data itself, for whole-object failures (oneOf, minProperties) that belong to no single member. |
error | string | 2 | Developer-facing, NON-authoritative description of what failed (e.g. "required", "must match ^[A-Z]{2}[0-9]+$"). Wording is validator-defined and not stable across Exchanges; clients branch on reason, never on this text. States the constraint, NEVER the submitted value — the ErrorDetail leakage rule applies here too. |
DisputeFailure
Section titled “DisputeFailure”A dispute could not be filed (distinct from DisputeReason, why the agent disputes, and DisputeStatus, an accepted dispute’s lifecycle).
| Field | Type | Number | Description |
|---|---|---|---|
reason | DisputeFailureReason | 1 | The failure reason (defined-only, non-zero) |
DomainVerificationFailure
Section titled “DomainVerificationFailure”RequestDomainVerification / ConfirmDomainVerification failed.
| Field | Type | Number | Description |
|---|---|---|---|
reason | DomainVerificationFailureReason | 1 | The failure reason (defined-only, non-zero) |
RetrievalAuthFailure
Section titled “RetrievalAuthFailure”A signed-URL retrieval or its proof-of-possession check failed at the delivery edge.
| Field | Type | Number | Description |
|---|---|---|---|
reason | RetrievalAuthFailureReason | 1 | The failure reason (defined-only, non-zero) |
UsageReportRejection
Section titled “UsageReportRejection”A usage report could not be accepted.
| Field | Type | Number | Description |
|---|---|---|---|
reason | UsageReportRejectionReason | 1 | The rejection reason (defined-only, non-zero) |
CatalogRejectionReason
Section titled “CatalogRejectionReason”| Value | Name | Description |
|---|---|---|
| 0 | UNSPECIFIED | unset — rejected at ingest |
| 1 | NOT_CATALOG_CONTRIBUTOR | caller is not an authorized contributor for the domain |
| 2 | TENANT_MISMATCH | tenant_id does not match the authenticated caller |
| 3 | DOMAIN_NOT_VERIFIED | contributing domain is not verified |
| 4 | SIGNATURE_INVALID | request signature missing or invalid |
| 5 | MALFORMED_ENTRY | a resource entry failed schema/validation |
| 6 | UNKNOWN_VOCAB_TOKEN | an unregistered vocab token in a restriction/term |
| 7 | QUOTA_EXCEEDED | contributor push quota exceeded (per-caller) |
| 8 | TERMS_LIMIT_EXCEEDED | a single entry carries more license terms than allowed (per-entry cap) |
| 9 | URI_UNAVAILABLE | The URI cannot be claimed by this caller's entries. Named from the caller's own perspective ON PURPOSE: it MUST NOT disclose that another resource/ contributor already owns the URI. Within one publisher, mutually-untrusting contributors share a catalog, so an "owned by another" reason would be a confirmed-existence oracle a contributor could use to map a competitor's catalog. The conflict is resolvable only by the publisher (who is authorized to see full ownership); the human-readable message routes the caller there without confirming who, if anyone, holds the URI. |
RegistrationFailureReason
Section titled “RegistrationFailureReason”| Value | Name | Description |
|---|---|---|
| 0 | UNSPECIFIED | unset — rejected at ingest |
| 1 | DOMAIN_NOT_VERIFIED | caller domain is not verified |
| 2 | INVALID_KEY | signing key malformed or unsupported |
| 3 | SIGNATURE_INVALID | request signature invalid |
| 4 | ALREADY_REGISTERED | identity already registered |
| 5 | QUOTA_EXCEEDED | registration quota exceeded |
| 6 | INVALID_REGISTRATION_DATA | registration_data does not conform to the Exchange's published registration_schema |
DisputeFailureReason
Section titled “DisputeFailureReason”| Value | Name | Description |
|---|---|---|
| 0 | UNSPECIFIED | unset — rejected at ingest |
| 1 | TRANSACTION_NOT_FOUND | transaction_id is unknown |
| 2 | REPORT_NOT_FILED | no UsageReport precedes the dispute (report_id missing/unknown) |
| 3 | WINDOW_EXPIRED | filed outside the allowed dispute window |
| 4 | DUPLICATE | a dispute already exists for this transaction |
| 5 | INELIGIBLE | the transaction/state is not disputable |
DomainVerificationFailureReason
Section titled “DomainVerificationFailureReason”| Value | Name | Description |
|---|---|---|
| 0 | UNSPECIFIED | unset — rejected at ingest |
| 1 | CHALLENGE_NOT_FOUND | token not served at the well-known path |
| 2 | CHALLENGE_MISMATCH | served token does not match the issued token |
| 3 | CHALLENGE_EXPIRED | confirmation arrived after the challenge expired |
| 4 | FETCH_FAILED | Exchange could not fetch the verification URL |
| 5 | EXCHANGE_NOT_AUTHORIZED | ramp.json does not list this Exchange |
| 6 | KEY_REGISTRATION_FAILED | signing-key registration failed during confirmation |
RetrievalAuthFailureReason
Section titled “RetrievalAuthFailureReason”Single-sources the delivery-edge token unions (signed-URL checks in verify.ts, RFC 9421 proof-of-possession in pop.ts).
| Value | Name | Description |
|---|---|---|
| 0 | UNSPECIFIED | unset — rejected at ingest |
| 1 | URL_EXPIRED | Signed-URL checks (verify.ts). |
| 2 | URL_SIGNATURE_MISSING | 'missing_sig' |
| 3 | URL_EXPIRY_MISSING | 'missing_exp' |
| 4 | URL_SIGNATURE_MISMATCH | 'signature_mismatch' |
| 5 | AGENT_KEY_MISSING | Proof-of-possession checks (pop.ts). |
| 6 | PROOF_SIGNATURE_MISSING | 'missing_sig' |
| 7 | KEYID_MISMATCH | 'keyid_mismatch' |
| 8 | THUMBPRINT_MISMATCH | 'thumbprint_mismatch' |
| 9 | PROOF_CREATED_MISSING | 'pop_missing_created' |
| 10 | PROOF_EXPIRY_MISSING | 'pop_missing_exp' |
| 11 | PROOF_EXPIRED | 'pop_expired' |
| 12 | PROOF_SIGNATURE_INVALID | 'pop_sig_invalid' |
UsageReportRejectionReason
Section titled “UsageReportRejectionReason”| Value | Name | Description |
|---|---|---|
| 0 | UNSPECIFIED | unset — rejected at ingest |
| 1 | TRANSACTION_NOT_FOUND | transaction_id is unknown |
| 2 | DUPLICATE | a report was already filed for this transaction |
| 3 | WINDOW_EXPIRED | filed outside the reporting window |
| 4 | MISSING_REQUIRED_FIELDS | ReportingObligation.required_fields not satisfied |
| 5 | MALFORMED | report payload failed validation |
PricingModel
Section titled “PricingModel”The charging structure only (a closed set). The open-ended metering basis (“per what”) is NOT enumerated here — it lives in Pricing.unit as a registry-governed vocabulary. UNSPECIFIED is rejected on Pricing.model (omission cannot default to FREE).
| Value | Name | Description |
|---|---|---|
| 0 | UNSPECIFIED | unset — zero allowed on WellKnownManifest.pricing_models_supported (capability list); rejected (not_in:[0]) as the Pricing.model discriminator (omission cannot default to FREE) |
| 1 | FREE | no charge; rate must be 0 (state FREE explicitly — absent Pricing is not free) |
| 2 | PER_UNIT | rate per Pricing.unit; unit REQUIRED (registered token or vendor:custom) |
| 3 | FLAT | one-time flat fee; rate is the total, no unit |
The metering basis (“per what”) is the Pricing.unit vocabulary, not a model; a subscription is FREE + scopes; attribution and contribution are Obligation.kinds; revenue-share settlement is off-protocol.
Registered Pricing.unit tokens (the metering basis — rendered from the proto, any vendor:namespaced token also accepted):
fetches accesses tokens calls pages seconds minutes records streams images seats units-manufactured characters bytes items sq-km
PricingMetering
Section titled “PricingMetering”How usage is tracked for billing reconciliation. Used in Pricing.metering (field 9). Absent = ONLINE.
| Value | Name | Description |
|---|---|---|
| 0 | ONLINE | Default. Exchange tracks usage events in real time. ReportUsage is required. |
| 1 | NONE | One-time perpetual sale. No ongoing metering; billing_id is issued at ExecuteTransaction and the ledger entry is closed. No ReportUsage required. |
| 2 | OFFLINE_SELF_REPORTED | Agent self-reports physical-world consumption (e.g. units manufactured from a licensed design). Exchange audits. |
TermSemantics
Section titled “TermSemantics”How the Exchange interprets a LicenseTerm’s machine fields.
| Value | Name | Description |
|---|---|---|
| 0 | UNSPECIFIED | unset — rejected at ingest |
| 1 | ENUMERATED | Machine restrictions/quotas/obligations are the complete, authoritative expression of the term (internally consistent, no self-contradiction) and are enforced. Pricing MUST be present. |
| 2 | REFERENCE_ONLY | The document at License.uri (MUST be non-empty) is the authoritative, complete source; the agent reads it before using. Machine restrictions/quotas/obligations are optional here (the publisher MAY send Pricing alone) but any that are sent must be accurate (MUST NOT contradict the referenced document) and are enforced just like ENUMERATED. Pricing is still required. |
RestrictionKind
Section titled “RestrictionKind”Which dimension a Restriction constrains.
| Value | Name | Description |
|---|---|---|
| 0 | UNSPECIFIED | unset — zero allowed on AcceptableRestriction.axis / OfferGroup.restriction_filters / *.restriction_mismatches; rejected (not_in:[0]) as the Restriction.kind discriminator |
| 1 | FUNCTION | What the agent may do with the content. Seeded from RSL 1.0 AI-use vocabulary and extended with established IP/copyright terms. |
| 2 | GEOGRAPHY | Where the agent may use the content. ISO 3166-1 alpha-2 codes (US, DE, GB) are valid structurally; only the non-ISO specials are registered here. |
| 3 | USER_TYPE | Who may access and use the content. |
| 4 | OTHER | Custom axis; values carried in permitted/prohibited |
Registered tokens
Section titled “Registered tokens”The complete token list for each axis, rendered directly from the proto’s
(ramp.v1.vocab_enum) options at build time — not hand-maintained here, so it
cannot drift from the contract. Any vendor:namespaced token is also accepted
on every axis; these are the registered bare tokens.
FUNCTION — what the agent may do:
all ai-all ai-train ai-input ai-index search crawl text-and-data-mining tts commercial advertising editorial research reproduce distribute modify display sync broadcast stream print manufacture sell
GEOGRAPHY — registered specials (plus structural ISO 3166-1 alpha-2 codes):
* EU EEA
USER_TYPE — what kind of entity the agent represents:
individual academic non_profit news_publisher broadcaster commercial_entity
QuotaWindow
Section titled “QuotaWindow”Time window over which a Quota.limit accumulates.
| Value | Name | Description |
|---|---|---|
| 0 | UNSPECIFIED | unset — rejected at ingest |
| 1 | HOURLY | Resets each hour |
| 2 | DAILY | Resets each day |
| 3 | MONTHLY | Resets each month |
| 4 | TOTAL | Lifetime cap — never resets |
ObligationKind
Section titled “ObligationKind”What the agent must do after use. ATTRIBUTION and CONTRIBUTION are behavioral requirements, not pricing models.
| Value | Name | Description |
|---|---|---|
| 0 | UNSPECIFIED | unset — rejected at ingest |
| 1 | ATTRIBUTION | Credit the author or publisher whenever the resource is used |
| 2 | CONTRIBUTION | Good-faith payment — amount suggested, not contractually fixed |
| 3 | SHARE_ALIKE | Derivatives must be released under the same / compatible license (CC-BY-SA / GPL style); scope_license required |
| 4 | NETWORK_COPYLEFT | Network service triggers copyleft (AGPL style) |
| 5 | NOTICE | Include the specified copyright notice |
| 6 | OTHER | Custom requirement, described in Obligation.detail |
ObligationTrigger
Section titled “ObligationTrigger”When an Obligation activates.
| Value | Name | Description |
|---|---|---|
| 0 | UNSPECIFIED | unset — rejected at ingest |
| 1 | ON_USE | Triggered on any use |
| 2 | ON_DISTRIBUTION | Triggered when copies are distributed |
| 3 | ON_NETWORK_SERVICE | Triggered when served over a network (AGPL) |
| 4 | ON_DERIVATIVE | Triggered when a derivative work is produced |
DenialReason
Section titled “DenialReason”| Value | Name | Description |
|---|---|---|
| 0 | UNSPECIFIED | output enum; zero = not-applicable on TransactionResultItem.denial_reason, rejected (not_in:[0]) where set on TransactionDenial.reason |
| 1 | BILLING_REF_INACTIVE | the requester's account (the billing_ref minted at Register) is not active / not recognized by the billing system |
| 2 | INSUFFICIENT_BALANCE | Requester's balance too low |
| 3 | RATE_LIMITED | Too many requests |
| 4 | CONTENT_UNAVAILABLE | Resource no longer available |
| 5 | RESTRICTION_NOT_SATISFIED | Accepted term's restriction not satisfied by the request; the axes are in TransactionDenial.restriction_mismatches (single) / TransactionResultItem.restriction_mismatches (batch), same RestrictionKind vocabulary as the terms |
| 6 | REPORTING_OVERDUE | Requester has >20% overdue reports (MAY threshold) |
| 7 | OFFER_EXPIRED | Offer TTL exceeded |
| 8 | SIGNATURE_INVALID | Offer signature verification failed |
| 9 | QUOTA_EXCEEDED | Subscription access count exhausted for this period |
| 10 | DELEGATION_INVALID | Delegation missing, unverifiable, expired, holder binding failed, or scopes/caps do not cover the request |
| 11 | SCOPE_INSUFFICIENT | Requester scopes don't cover this resource |
| 12 | ENTITLEMENT_MISSING | Entitlement family — subscription/entitlement access failures on a subscription-gated offer. Finer-grained than DELEGATION_INVALID so callers and operator tooling can triage each mode. These single-source the Exchange's KindEntitlement* refusal taxonomy, which today is distinguishable only by a server-side tag (all collapse to UNAUTHENTICATED on the wire). Format-neutral: they classify a JWT/opaque entitlement-token failure, not a format-specific one. |
| 13 | ENTITLEMENT_MALFORMED | entitlement token failed to decode (malformed) |
| 14 | ENTITLEMENT_EXPIRED | entitlement token's validity window has passed |
| 15 | ENTITLEMENT_WRONG_BUYER | token's subscriber_org does not match the asserted requester |
| 16 | SUBSCRIPTION_LAPSED | the covering subscription contract has lapsed |
| 17 | ENTITLEMENT_NOT_GRANTED | subscription exists but no buyer-side grant ties this caller to it |
DisputeReason
Section titled “DisputeReason”| Value | Name | Description |
|---|---|---|
| 0 | UNSPECIFIED | unset — rejected at ingest |
| 1 | CONTENT_MISMATCH | Content hash does not match what was promised in the Offer. |
| 2 | DELIVERY_FAILED | Resource was not delivered (signed URL returned 404/403/5xx). |
| 3 | WRONG_CONTENT | Resource was delivered but is entirely different from what was described. |
| 4 | EXPIRED_BEFORE_FETCH | Signed URL expired before the agent could fetch the resource. |
| 5 | INCOMPLETE_CONTENT | Resource was truncated or incomplete. |
DisputeStatus
Section titled “DisputeStatus”Full dispute lifecycle from filing to final resolution.
| Value | Name | Description |
|---|---|---|
| 0 | UNSPECIFIED | unset — output/optional; zero is a valid not-applicable/unset state |
| 1 | FILED | Agent submitted DisputeRequest. Initial state. |
| 2 | AUTO_RESOLVED | Exchange auto-resolved via Tier 1 rules (CDN logs, hash comparison). |
| 3 | EVIDENCE_NEEDED | Exchange requests additional evidence from the agent or provider. |
| 4 | UNDER_REVIEW | Exchange is reviewing with Tier 2 resolution rules. |
| 5 | ESCALATED | Escalated to Tier 3 pattern-based investigation. |
| 6 | RESOLVED | Decision made (credit, redelivery, rejected). See resolution field. |
| 7 | APPEALED | Losing party appealed with new evidence. Re-enters review. |
| 8 | SETTLED | Financial settlement applied. |
| 9 | FINAL | No further appeals. Dispute closed. |
ResolutionType
Section titled “ResolutionType”Outcome of a resolved dispute.
| Value | Name | Description |
|---|---|---|
| 0 | UNSPECIFIED | unset — output/optional; zero is a valid not-applicable/unset state |
| 1 | CREDIT | Account credit applied to the agent's next billing cycle. |
| 2 | REDELIVERY | New signed URL issued for the same resource (e.g., when content hash now matches after provider correction). |
| 3 | REJECTED | Dispute reviewed and rejected; no remedy applied. |
| 4 | INVESTIGATION | Escalated to Tier 3 pattern analysis for further investigation. |
CitationFormat
Section titled “CitationFormat”How the citation is presented to the user.
| Value | Name | Description |
|---|---|---|
| 0 | LINK | Hyperlink citation |
| 1 | FOOTNOTE | Footnote citation |
| 2 | INLINE | Inline text citation |
OfferAbsenceReason
Section titled “OfferAbsenceReason”Why no offers are available for a requested URI.
| Value | Name | Description |
|---|---|---|
| 0 | UNSPECIFIED | unset — output/optional; zero is a valid not-applicable/unset state |
| 1 | NOT_IN_CATALOG | Resource URI is not in this Exchange's catalog. |
| 2 | CONTENT_BLOCKED | Resource exists but the provider has opted out of AI access for it (the provider's consent/opt-out signal blocks licensing). |
| 3 | RESTRICTION_FILTERED | Resource exists but its offers were pre-filtered out for one or more restriction axes the requester stated (a convenience filter matched to the query, not an enforcement verdict — see Restriction). The filtered axes are listed in OfferGroup.restriction_filters, in the same RestrictionKind vocabulary the terms use. The agent MAY still be eligible. |
| 4 | TEMPORARILY_UNAVAILABLE | Resource is temporarily unavailable (e.g., provider feed refresh in progress). |
| 5 | NOT_AUTHORIZED | Exchange is not authorized by the provider to sell this resource. |
| 6 | SCOPE_INSUFFICIENT | Requester's scopes/subscription do not cover this resource. Applies wherever access is gated by subscription or scope entitlements (not only enterprise deployments): the resource exists but the requester's delegation token or subscription does not grant it. The Exchange returns this so the requester learns the resource is reachable under the right subscription/scope. (Where existence itself must stay hidden, the Exchange MAY omit it silently instead.) |
| 7 | UNKNOWN_CRITICAL_EXTENSION | Consumer encountered ext_critical keys it does not recognize. The unrecognized keys SHOULD be listed in the OfferGroup's ext field under "unrecognized_critical_extensions" for diagnostic purposes. |
| 8 | BUDGET_EXCEEDED | Offers exist, but none fit within the requester's budget (e.g. every offer's price exceeds RequestConstraints.period_budget). Returned by Resolve as a successful "no result" answer when a budget/price ceiling filtered out every otherwise-licensable offer. |
RequesterType
Section titled “RequesterType”What kind of entity is making the request.
| Value | Name | Description |
|---|---|---|
| 0 | UNSPECIFIED | unset — rejected at ingest |
| 1 | AGENT | Autonomous AI agent (LLM, RAG system, research bot). |
| 2 | HUMAN_TOOL | Human using an AI-powered tool (copilot, assistant). |
| 3 | SERVICE | Enterprise service account (automated pipeline, cron job). |
| 4 | DELEGATED | Agent acting on behalf of a user (delegated identity). |
| 5 | RESEARCH | Research pipeline (batch data collection, model training). |
Identifies which RAMP participant a WellKnownManifest describes.
| Value | Name | Description |
|---|---|---|
| 0 | UNSPECIFIED | unset — rejected at ingest |
| 1 | AGENT | |
| 2 | EXCHANGE | |
| 3 | BROKER | |
| 4 | PUBLISHER |
DiscoveryMethod
Section titled “DiscoveryMethod”| Value | Name | Description |
|---|---|---|
| 0 | UNSPECIFIED | unset — output/optional; zero is a valid not-applicable/unset state |
| 1 | EXCHANGE | URI was requested by the agent directly or found via Exchange query. |
| 2 | SEARCH | URI was discovered via a search engine (e.g., Exa, Tavily, Brave Search). The Broker searched on the agent's behalf, then routed through Exchange. |
| 3 | RECOMMENDATION | URI was recommended by a resource recommendation service. |
| 4 | SYNDICATION | URI was found via resource syndication tracking (e.g., same article on another domain). |
DeliveryMethod
Section titled “DeliveryMethod”| Value | Name | Description |
|---|---|---|
| 0 | UNSPECIFIED | unset — output/optional and capability-list; zero is a valid not-applicable/unset state |
| 1 | DIRECT | Exchange returns resource inline or via its own endpoint. |
| 2 | INSTRUCTIONS | Exchange returns access info (signed URL, token) for retrieval from a Resource Owner / Resource Delivery Endpoint. |
| 3 | STREAMING | Resource delivered via real-time streaming connection (WebSocket, SSE, gRPC stream). The signed URL points to a streaming endpoint. Agent connects and receives continuous data for the duration of the session. |
ResourceMutability
Section titled “ResourceMutability”Signals whether resource content changes over time. Drives hash verification behavior.
| Value | Name | Description |
|---|---|---|
| 0 | UNSPECIFIED | unset — the Exchange defaults to STATIC at Offer build; an explicit UNSPECIFIED is rejected on ResourceEntry.resource_mutability and the Offer's ResourceIdentity.resource_mutability (both {not_in:[0]}) |
| 1 | STATIC | Content is immutable. Hash computed at offer time will match at delivery time. Agent SHOULD verify content_hash on delivery. Mismatch is disputable. |
| 2 | DYNAMIC | Content changes between offer generation and agent fetch. Hash reflects state at offer time — mismatch is expected, not disputable. Offer.data_as_of indicates when the snapshot was taken. |
| 3 | LIVE | Content does not exist at offer time (real-time streaming). No content_hash is applicable. The "resource" is the stream endpoint/channel. Metering is time-based (per-minute, per-hour). |
IngestionSource
Section titled “IngestionSource”| Value | Name | Description |
|---|---|---|
| 0 | UNSPECIFIED | unset — output/optional; zero is a valid not-applicable/unset state |
| 1 | RAMP_SITEMAP | RAMP XML namespace in sitemap |
| 2 | RSL | RSL rsl.txt |
| 3 | SITEMAP | Standard sitemap.xml |
| 4 | HTML_CRAWL | HTML crawl + readability extraction |
| 5 | CMS_API | CMS REST API (WordPress, etc.) |
| 6 | MANUAL | Manual configuration |
| 7 | CATALOG_API | Third-party CatalogService push |
ProviderRelationship
Section titled “ProviderRelationship”| Value | Name | Description |
|---|---|---|
| 0 | UNSPECIFIED | unset — rejected at ingest |
| 1 | DIRECT | Provider has a direct contract with this Exchange. |
| 2 | RESELLER | Exchange resells resources via another authorized party. |
AuthMethod
Section titled “AuthMethod”Authentication methods a participant advertises in its WellKnownManifest.
| Value | Name | Description |
|---|---|---|
| 0 | UNSPECIFIED | unset — zero allowed on WellKnownManifest.supported_auth_methods (capability list); no discriminator carrier |
| 1 | GNAP | GNAP (RFC 9635) — key-first identity, key-bound tokens. Recommended. |
| 2 | OAUTH_DPOP | OAuth 2.0 + DPoP (RFC 9449) — sender-constrained tokens. Enterprise recommended. |
| 3 | OAUTH_BEARER | OAuth 2.0 Bearer JWT — acceptable, widely deployed. |
| 4 | OAUTH_MTLS | OAuth 2.0 + mTLS — high-security environments. |
C2PAStatus
Section titled “C2PAStatus”C2PA / content-provenance status carried on ResourceIdentity.
| Value | Name | Description |
|---|---|---|
| 0 | C2PA_STATUS_UNSPECIFIED | unset — output/optional; zero is a valid not-applicable/unset state |
| 1 | C2PA_STATUS_TRUSTED | Manifest is valid AND signer certificate chains to a C2PA Trust List root. Highest assurance: provenance is cryptographically verified by a trusted CA. |
| 2 | C2PA_STATUS_VALID | Manifest is structurally correct and signature verifies, but signer certificate does NOT chain to a C2PA Trust List root. The content has provenance, but the signer is not institutionally vouched for. |
| 3 | C2PA_STATUS_INVALID | Manifest is present but validation failed (signature mismatch, malformed JUMBF, certificate expired, hard binding broken). |
| 4 | C2PA_STATUS_ABSENT | Content was checked and has no C2PA manifest. |