fora.json Example
The fora.json file is served at /.well-known/fora.json and carries a WellKnownManifest. Every FORA participant serves one — the role field says which (ROLE_PUBLISHER, ROLE_EXCHANGE, ROLE_AGENT, ROLE_BROKER). The example below is a publisher manifest (role=ROLE_PUBLISHER): it declares which Exchanges are authorized to sell its resources — like ads.txt for AI resource access. Signing keys no longer live in fora.json; they live in the provider’s WBA directory (the JWK Set at /.well-known/http-message-signatures-directory, introduced below).
Complete Example
Section titled “Complete Example”{ "ver": "1.0", "role": "ROLE_PUBLISHER", "domain": "techprovider.com", "contact": "licensing@techprovider.com", "catalog_contributors": [ { "domain": "doubleverify.com", "relationship": "verifier" }, { "domain": "gumgum.com", "relationship": "verifier" } ], "exchanges": [ { "domain": "exchange.ssp-alpha.com", "endpoint": "https://exchange.ssp-alpha.com/v1", "relationship": "DIRECT" }, { "domain": "exchange.ssp-beta.com", "endpoint": "https://exchange.ssp-beta.com/v1", "relationship": "RESELLER" } ]}An Agent Manifest
Section titled “An Agent Manifest”An agent’s manifest is much smaller: a role marker. Its signing keys are not here — they live in the WBA directory described below, which a verifier resolves from the covered Signature-Agent header.
{ "ver": "1.0", "role": "ROLE_AGENT", "domain": "research-bot.example"}The WBA Directory
Section titled “The WBA Directory”Signing keys are not carried in fora.json. Each participant publishes its keys in a separate, pure WBA directory — a JWK Set served at /.well-known/http-message-signatures-directory with Content-Type: application/jwk-set+json. Keys are identified by their RFC 7638 thumbprint (the RFC 9421 keyid), not by a kid label, and the optional emergency revocation list now lives here as revocation_url:
// GET /.well-known/http-message-signatures-directory// Content-Type: application/jwk-set+json{ "keys": [ { "kty": "OKP", "crv": "Ed25519", "use": "sig", "alg": "EdDSA", "x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", "not_before": "2026-04-01T00:00:00Z", "not_after": "2026-10-01T00:00:00Z" } ], "revocation_url": "https://techprovider.com/.well-known/fora-invalidations.json"}Field Reference
Section titled “Field Reference”| Field | Type | Required | Description |
|---|---|---|---|
ver | string | Yes | Version of this manifest document’s layout — "1.0", stamped from the SDK’s WellKnownManifestVersion, a namespace separate from the RPC envelope ver and never derived from it. A manifest layout change bumps both numbers; a protocol change that leaves the manifest untouched bumps only the envelope’s. Consumers read it before any other member: a recognised major is accepted whatever the minor, an unrecognised major, a malformed value, or an absent ver is refused |
role | string | Yes | Participant role — ROLE_PUBLISHER for a provider’s manifest |
domain | string | Yes | Canonical domain serving this manifest |
contact | string | No | Contact email for licensing inquiries |
| (signing keys) | — | — | Not in fora.json. See the WBA directory at /.well-known/http-message-signatures-directory |
catalog_contributors | array | No | Authorized third-party catalog pushers |
catalog_contributors[].domain | string | Yes | Canonical domain of the contributor (e.g., doubleverify.com) |
catalog_contributors[].relationship | string | Yes | Relationship type: verifier, exchange, etc. |
exchanges | array | Yes* | Authorized Exchanges (*publisher manifests) |
exchanges[].domain | string | Yes | Canonical domain of the Exchange |
exchanges[].endpoint | string | Yes | FORA ExchangeService endpoint URL |
exchanges[].relationship | string | Yes | DIRECT or RESELLER (mirrors ads.txt) |
How Agents Use fora.json
Section titled “How Agents Use fora.json”Proactive Discovery (Preferred)
Section titled “Proactive Discovery (Preferred)”The agent checks /.well-known/fora.json before attempting to access content:
1. Agent wants content from techprovider.com2. GET https://techprovider.com/.well-known/fora.json3. Finds Exchange endpoint: exchange.ssp-alpha.com/v14. Calls DiscoverResources → gets Offers with pricing5. Calls ExecuteTransaction → gets signed URL6. Fetches content from CDNFallback Discovery (via 403)
Section titled “Fallback Discovery (via 403)”If the agent doesn’t know about FORA and tries to crawl directly:
1. Agent hits techprovider.com/premium/article2. Edge function returns 403 + X-Content-Rules header3. Agent discovers fora.json from the header4. Follows standard FORA flow from step 2 aboveRelationship Types
Section titled “Relationship Types”| Relationship | Description | Ad-Tech Equivalent |
|---|---|---|
DIRECT | Provider has a direct contract with this Exchange | ads.txt DIRECT |
RESELLER | Exchange resells content via another authorized party | ads.txt RESELLER |
Provider Trust Model
Section titled “Provider Trust Model”The Exchange periodically re-fetches fora.json for each provider’s domain. If the Exchange is removed from a provider’s fora.json, the tenant is revoked and offers stop being served. This provides ongoing verification, not just onboarding.
Protobuf Definition
Section titled “Protobuf Definition”The fora.json structure maps to the WellKnownManifest message (with role=ROLE_PUBLISHER) in fora/v1/fora.proto. Publisher-relevant fields shown; see Proto: FORA v1 for the full message (exchange-only capability fields, etc.):
message WellKnownManifest { string ver = 1; // "1.0" — document schema, not the RPC envelope Role role = 2; // ROLE_PUBLISHER here string domain = 3; optional string contact = 4; repeated AuthorizedExchange exchanges = 7; // publisher-only repeated CatalogContributor catalog_contributors = 8; // ... exchange-only capability fields (9-31) omitted ... google.protobuf.Struct ext = 15;}
// Pure WBA directory, served at /.well-known/http-message-signatures-directorymessage WBAFile { repeated JsonWebKey keys = 1; optional string revocation_url = 2; // KeyRevocationList URL}
message JsonWebKey { string kty = 2; // "OKP" string crv = 3; // "Ed25519" string use = 4; // "sig" string alg = 5; // "EdDSA" string x = 6; // base64url 32-byte public key string not_before = 7; // RFC3339 string not_after = 8; // RFC3339}
message AuthorizedExchange { string domain = 1; string endpoint = 2; ProviderRelationship relationship = 3;}
// Third party authorized to push catalog metadata on behalf of a provider.message CatalogContributor { string domain = 1; // e.g., "doubleverify.com" string relationship = 2; // e.g., "verifier", "exchange"}
enum Role { ROLE_UNSPECIFIED = 0; ROLE_AGENT = 1; ROLE_EXCHANGE = 2; ROLE_BROKER = 3; ROLE_PUBLISHER = 4;}
enum ProviderRelationship { PROVIDER_RELATIONSHIP_UNSPECIFIED = 0; PROVIDER_RELATIONSHIP_DIRECT = 1; PROVIDER_RELATIONSHIP_RESELLER = 2;}