Skip to content

Discovery Paths

An AI agent can enter the FORA flow through six paths. All paths converge on the same Exchange protocol — the same DiscoverResources, ExecuteTransaction, and ReportUsage RPCs regardless of how discovery happened.

PathEntry PointUse Case
A: Proactive Discoveryfora.jsonAgent checks before accessing content
B: Broker-MediatedBrokerAgent delegates to multi-exchange router
C: Accidental Discovery403 responseWandering bot hits protected content
D: RSL-Firstrsl.txtAgent reads terms, then finds Exchange
E: Batch Multi-URLBrokerAgent needs multiple resources at once
F: SubscriptionExchangeAgent has existing subscription deal

The preferred path. The agent checks /.well-known/fora.json before attempting to access content. No 403 needed, no wasted requests.

Standards mapping:

StepMessageStandard
Check fora.jsonWellKnownManifest (role=ROLE_PUBLISHER)FORA (exchange routing)
Discover supplyResourceQuery / ResourceResponseFORA wrapping FORA Requester
Offers returnedOffer with Package + PricingFORA Pricing + CoMP Package
Content attestationsOffer.attestationsFORA ResourceAttestation (replaces ResourceAttestation)
Access restrictionsOffer.terms[].restrictions (repeated Restriction)FORA mapping of RSL permits/prohibits
Execute transactionTransactionRequest / TransactionResponseFORA (billing_id, signed URL)
Fetch contentSigned URL on CDNFORA extension (signed URL delivery)
Report usageUsageReportFORA (mandatory)

The agent delegates to a FORA Broker, which queries multiple Exchanges in parallel, compares offers, and selects the best deal.

Standards mapping:

StepMessageStandard
Agent to BrokerDiscoveryRequest with RequestConstraintsFORA (budget, authorized exchanges)
Broker to ExchangesResourceQuery (parallel fanout)FORA wrapping FORA Requester
Offer comparisonOffer.pricing.unit_costFORA unit cost normalization
Content dedupOffer.identity (canonical_url, content_hash)FORA ResourceIdentity extension
Content attestationsOffer.attestationsFORA v1.0 ResourceAttestation
Broker to AgentDiscoveryResponse (offer_groups: signed offers per URI)FORA — discovery only, no delivery
Execute winning offerExecuteTransaction / TransactionResponse (signed URL + cost + billing_id)FORA (separate execute step, routed via Offer.exchange)
Usage reportingUsageReport to the winning ExchangeFORA

Path C: Accidental Discovery via Edge Function (403)

Section titled “Path C: Accidental Discovery via Edge Function (403)”

A wandering bot hits protected content without knowing about FORA. The edge function on the provider’s CDN blocks it and points to the Exchange.

Standards mapping:

StepStandard
403 + X-Content-Rules headerFORA extension (discovery mechanism — not in CoMP or RSL)
Exchange endpoint in headerPoints to Exchange listed in fora.json
All subsequent stepsSame as Path A

The edge function is the “barking dog” — a fallback for bots that did not check fora.json first.

An agent reads rsl.txt to understand terms, then uses fora.json to find the Exchange for transacting. RSL tells the agent what it costs; FORA tells it where to pay.

Standards mapping:

StepStandard
Read rsl.txtRSL 1.0 — terms declaration
Read fora.jsonFORA — exchange routing
DiscoverResourcesExchange returns Offers with pricing derived from RSL
Offer.terms[].restrictionsMaps RSL permits/prohibits to FORA Restriction (kind RESTRICTION_KIND_FUNCTION) on each LicenseTerm

The agent needs multiple resources in a single request (e.g., RAG grounding across several sources). The Broker sends all URIs in one ResourceQuery to each Exchange, receives OfferGroups, and returns the ranked signed offers per URI in a single DiscoveryResponse. Discovery stops there: the agent selects the best offer per URI and commits the selections itself via batch ExecuteTransaction calls (one per Exchange, routed by Offer.exchange), each returning a TransactionResponse.

Standards mapping:

StepMessageStandard
Agent sends multi-URI requestDiscoveryRequest with multiple URIsFORA
Broker fans outResourceQuery with multiple URIs (one query per Exchange)FORA wrapping FORA Requester
Exchange returns grouped offersResourceResponse.offer_groups[] with OfferGroup per URIFORA OfferGroup extension
Broker returns offers to AgentDiscoveryResponse.offer_groups[] (signed offers per URI)FORA — discovery only, no delivery
Empty OfferGroup diagnosticOfferGroup.absence_reason (OfferAbsenceReason enum)FORA v1.0 — per-URI diagnostic when no offers available
Rate limit signalingResourceResponse.rate_limit (RateLimitInfo)FORA v1.0 — proactive throttling for Broker fanout
Batch transactionTransactionRequest.items[] with TransactionItem per offerFORA batch extension
Per-item resultsTransactionResponse.items[] with TransactionResultItem per offerFORA batch extension
Usage reportingOne UsageReport per resource (not per batch)FORA

When an OfferGroup has no offers, the absence_reason field explains why. This enables agents and Brokers to distinguish “content not in catalog” from “no offers matched your request” without trial-and-error transactions. The defined reasons:

ValueDescription
NOT_IN_CATALOGResource URI is not in this Exchange's catalog.
CONTENT_BLOCKEDResource exists but the provider has opted out of AI access for it (the provider's consent/opt-out signal blocks licensing).
RESTRICTION_FILTEREDResource 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.
TEMPORARILY_UNAVAILABLEResource is temporarily unavailable (e.g., provider feed refresh in progress).
NOT_AUTHORIZEDExchange is not authorized by the provider to sell this resource.
SCOPE_INSUFFICIENTRequester'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.)
UNKNOWN_CRITICAL_EXTENSIONConsumer 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.
BUDGET_EXCEEDEDOffers 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.

RESTRICTION_FILTERED reports an optional convenience pre-filter matched to the restriction attributes the requester volunteered (function / geography / user-type); the filtered axes are listed in OfferGroup.restriction_filters using the same RestrictionKind vocabulary the terms carry — not an enforcement verdict: restrictions ride on the offer and the agent self-selects (see Licensing terms).

The ResourceResponse carries an optional rate_limit field with the caller’s current rate limit status: limit, remaining, reset_at, and window. This is particularly important for Broker fanout — when a Broker sends batch queries to multiple Exchanges, mid-batch rate limiting can cause partial results if not signaled early. Modeled after IETF RateLimit header fields.

Each OfferGroup carries a discovery_method field indicating how the offers were sourced. The discovery methods:

ValueNameDescription
0DISCOVERY_METHOD_UNSPECIFIEDunset — output/optional; zero is a valid not-applicable/unset state
1DISCOVERY_METHOD_EXCHANGEURI was requested by the agent directly or found via Exchange query.
2DISCOVERY_METHOD_SEARCHURI was discovered via a search engine (e.g., Exa, Tavily, Brave Search). The Broker searched on the agent's behalf, then routed through Exchange.
3DISCOVERY_METHOD_RECOMMENDATIONURI was recommended by a resource recommendation service.
4DISCOVERY_METHOD_SYNDICATIONURI was found via resource syndication tracking (e.g., same article on another domain).

This allows agents and Brokers to understand the provenance of each offer group and apply method-specific ranking or filtering logic.

Batch transactions are non-atomic — individual items can fail independently. If one URI’s offer fails authorization, the others proceed.

The agent has an existing subscription deal with an Exchange. When querying supply, the Exchange includes subscription offers with zero marginal cost. The transaction skips per-request billing but still issues a signed URL and creates a usage reporting obligation.

Key points:

  • Subscription detection at query time: The Exchange checks the requester’s subscription status and includes offers with subscription_id and rate=0.
  • Reporting is still mandatory: Subscription offers carry ReportingObligation.required = true. Providers need consumption data for subscription renewals and content strategy.
  • Quota enforcement: The Exchange checks remaining quota before authorizing subscription transactions. If the subscription’s token quota is exhausted, the subscription offer is not included.
  • Broker preference: The Broker’s selection engine ranks subscription offers above all per-request offers (zero marginal cost). When preferred_exchanges is set and the agent has subscriptions there, those offers get top priority.
  • Financial attribution: The subscription_unit_value field carries the per-unit cost even though cost.amount is 0, enabling prepaid drawdown accounting.