Skip to content
Hub documentation

Content owners and usage reports

Use Onward delivery to see where your edges’ usage goes, and Content owners, linked from it, to register publishers, download their usage reports and save destinations for them. The hub holds no content-owner accounts and opens no owner-facing view. Only members of your organisation use these screens.

Under Register an owner, enter its name and optionally its first host and the basis for the registration, then choose Register owner. Open the owner to add or remove hosts. Adding or removing a host and deleting an owner need an organisation owner. A host another owner already holds is refused without changing its registration.

The basis is recorded, unverified, and included as typed in reports handed to the owner. Do not enter anything there you would not send them. Hosts have no scheme or path. An exact host registration takes precedence; otherwise a registration of its immediate parent can claim it, as described under Host resolution.

Delete owner requires the owner’s exact name. It deletes registrations, the destination and credential, and the records of deliveries to the saved destination. Records of deliveries to endpoints the sources declared are kept. Events stay and become unattributed.

Open an owner and read Report. It shows retrieved and grounded counts, first and last event times, session count, enrolled edge key ids, licence references and per-URL counts. The resolution rule and URL limit appear above it. A dash for sessions means deletion has made that count unknown.

Download JSON saves the API’s report unchanged. It contains no wire session identifier, engagement or another owner’s URLs. Give this file to the content owner under your agreement. Enrolled edge key ids are pseudonymous identifiers the owner can match to signed requests; host tool names are omitted.

Open Unattributed hosts from Content owners. The list contains the 50 hosts with the most unclaimed events, with counts and last event times. Choose an existing owner or name a new one, enter the basis and select Register. The registration claims existing matching events as well as future ones in reports. Onward delivery sends only events from the registration onward (see Onward delivery).

Usage goes to each publisher’s declared endpoint whether or not you register an owner for it.

Onward delivery, in the Network group, shows every destination:

  • Where usage goes: each endpoint a licence or a host manifest declared, with the declarations that named it, its host and event counts (and how many of those events had no registered owner), queued, delivered and dead counts, what was held (suppressed, or withheld because a licence named it from another site), the last delivery, the next attempt and the latest error; then each destination saved for an owner. The count of events still awaiting their declarations is at the top. The page reads the saved destinations of the first 20 owners by name; the others are on each owner’s delivery page, and an owner whose status could not be read shows on its own row.
  • Hosts read: each host manifest read, with its outcome, the status the host answered, the reason and when the read expires, and each licence resolution. A publisher whose CDN challenged the hub’s reader shows here as refused with the reason challenge.
  • Suppressed endpoints: each stopped origin, who stopped it and why. Owners can suppress an endpoint and lift a suppression here.
  • Dead letters: up to 50 declared-endpoint deliveries that failed ten times. Owners can requeue them all.

Each list shows up to 50 entries and says so when it is full. URLs, hosts, reasons and errors come from licences, manifests and receivers and are shown as text. A manifest or licence reason or a delivery error the hub records is cut to 512 bytes; an owner’s suppression reason is not, and nor are entries recorded before the hub applied the cut. A declared endpoint longer than 2,048 bytes is not used: its manifest read or licence is listed invalid with that reason. A licence whose telemetry reporting profile is longer than 512 bytes is listed not_rsl with that reason and routes no events. Any member can read the page; the actions are an owner’s. Support acting as an owner can suppress an endpoint; lifting a suppression and requeueing are refused, and the page shows the refusal.

Open a content owner, then Onward delivery. The destination lists its URL, set time and whether a credential was provided. The credential is never returned. Any signed-in member can read status; only an organisation owner can save a destination or requeue dead letters.

Enter the public HTTPS endpoint designated by the content owner and, if required, its consumer credential. Save destination stores the credential encrypted, sends it as X-API-Key, queues the raw events the owner is owed (below) and requeues all dead letters. Enter the credential again whenever saving; a blank field removes the stored credential. The hub refuses private addresses. An unresolved hostname can be saved, but must pass the address check before delivery.

Onward batches include wire session and agent identifiers, event URLs and the event data the edge sent. This differs from the downloadable report’s omissions. Citations (content_cited: an agent’s answer named the page) are delivered with their output_id, which groups the citations of one answer. Turn boundaries and session refused counts are not delivered onward. The hub forwards terms_ref separately from license_ref without interpreting the terms reference. An HTTPS license_ref can supply the reporting endpoint below. Duplicate events keep the first accepted values.

Every event goes to every route that applies to it, not to one of them:

  1. Each endpoint the event’s source declared: the public HTTPS endpoint in its RSL licence <reporting type="telemetry" profile="…" endpoint="…"> binding, and the endpoint its host declares in its Content Telemetry manifest. Two declarations naming one endpoint deliver once.
  2. The destination you saved on the owner’s registration.

A declared endpoint is delivered to whether or not you registered an owner for the host, and a destination you save adds to it rather than replacing it. A declared endpoint is sent no credential: it is the source’s own designation, and only the saved destination uses the credential you stored. terms_ref never chooses a route.

A licence’s endpoint takes only the events of hosts under the licence document’s own registrable domain: a licence many publishers cite would otherwise send every publisher’s URLs to the endpoint its author declared. The registrable domain is read from the Public Suffix List, compiled into the hub: a licence at licences.example.com takes the events of www.example.com, one at www.example.com those of example.com, and one at example.com those of a.b.example.com, but one at attacker.co.uk never takes the events of bbc.co.uk, since the two share only the public suffix co.uk. A host the list does not cover (an IP address, a name under an unlisted top-level label) is compared exactly. An event routed away from its registrable domain this way is recorded at that endpoint as withheld, with the reason endpoint_off_origin, and is not sent, unless the host’s own manifest names the same URL. A later declaration of that URL from the event’s own origin releases it. A manifest carries no such test: it is the host’s own declaration for its own events. This guard uses the suffix list; Host resolution, which attributes events to owners you registered, does not.

The hub reads a licence once per distinct reference in your organisation, and a host’s manifest once per host, caching a usable endpoint for 24 hours and an unsuccessful result for one hour. It records the profile, endpoint, outcome and reason. Missing endpoints, failed reads, private addresses and ambiguous reporting bindings supply no route. The hub does not interpret profile-specific configuration or obtain a consumer credential from a licence or a manifest. Where nothing is declared and no destination is saved, the event remains stored and undelivered.

An event’s declared endpoints are fixed once, from the declarations in force when it arrived: as it is ingested where the licence and the manifest are already read, otherwise once the hub has read them. Each route settles on its own read, so an unreachable manifest does not hold back the licence’s endpoint. A licence read that could not connect or complete, or whose endpoint did not resolve, and a manifest read that was unreachable or answered 401, 403, a redirect, 429 or 5xx, are retried about hourly for 24 hours after the event arrived; any other answer is final. A 401 or 403 from a bot-protection service is recorded as refused with the reason challenge, and a redirect as redirect to its origin; the hub does not follow redirects, because a manifest must be served from the URL it names. The event is then fixed with what was read. A licence or manifest that names a new endpoint later takes the events that arrive after it, never earlier ones, so a host that changes hands does not pass the previous holder’s record to the new one.

Saving a destination changes future claims of the saved route only. A batch already being sent can finish at the previous endpoint. Completed deliveries keep their original route and are not sent again to the new one.

Delivery status covers every route owed on this owner’s events, declared endpoints included: complete queued, delivered and dead counts, the last delivery, next attempt and latest error. Refresh status loads current values. Routes gives counts by route and endpoint; one event can appear under several, since the routes add to each other. Attempted events show their last route; events awaiting their first attempt show their current route. Recent batches lists up to 50 batches, with their route, destination, status, event count, the count the receiver created (unknown when its answer did not state one) and attempt time. Retried events move to the new batch. Licence resolutions lists up to 50 recent outcomes and their cache expiry.

Dead letters lists up to 50 events with their URLs, attempt counts and last errors. After fixing the cause, Requeue all dead letters gives every dead letter a fresh attempt budget, including those outside the displayed list. Saving a destination does the same. Failed attempts retry after 1, 2, 4, 8, 16 and 32 minutes, then hourly; the tenth failure becomes a dead letter.

Back-fill lists the owner’s back-fills. An owner can back-fill a range of events there, with times in UTC (Onward delivery API explains what a back-fill sends); support acting as an owner is refused.

Clearing the destination is available through the API. Clearing it records the person making the change in the audit log, pauses the saved route and drops its queued and dead records, except batches already in flight. Those can finish; their records and completed delivery records stay. Saving a destination resumes the saved route. Endpoints the sources declared are not paused by clearing: they are the sources’ own designations, not yours to withdraw. What reaches the hub at all is what your edges’ policy and reporting approvals clear. Raw events deleted under retention cannot be delivered onward.

Requests use the organisation resolved from the signed-in session or an appropriately scoped API key. Destination changes and retry require an owner session; API keys cannot perform them.

Hosts are spelled the way events carry them: lower-case, no scheme or path, port kept. An event resolves to the owner of its exact host first; where no owner registered that host, to the owner of its parent, the host with its first label removed (www.example.com resolves to a registration of example.com, news.example.co.uk to one of example.co.uk). One level only, never for a two-label host, and with no list of public suffixes: the parent of bbc.co.uk is co.uk, which nobody registers, so a three-label host under such a suffix is registered exactly and its own subdomains resolve to it. An exact registration by one owner beats a parent registration by another, so example.com cannot claim a subdomain someone else registered. Registering the apex covers the www. and section hosts an ownership map usually omits; register a host exactly where the parent rule would give it to the wrong owner. Within your organisation one host is registered to at most one owner.

Terminal window
curl -X POST https://hub.example/api/v1/owners \
-H 'content-type: application/json' \
-d '{"name": "Example Press"}'
curl -X POST https://hub.example/api/v1/owners/{id}/hosts \
-H 'content-type: application/json' \
-d '{"host": "news.example.com", "basis": "licence agreement ref EP-1"}'

GET /api/v1/owners is the listing the screen shows. DELETE /api/v1/owners/{id}/hosts/{host} removes a registration. DELETE /api/v1/owners/{id} removes the owner with the same cascade and asks for no confirmation. Registering a host another owner holds answers 409.

Removing an owner records who removed it, the destination origin and the counts of delivered and undelivered rows deleted with it. If that audit record cannot be saved, the removal fails and the owner stays.

GET /api/v1/owners/{id}/report answers with owner, resolution, hosts with each basis, content_retrieved, content_grounded, sessions, agent_ids, first_event_at, last_event_at, license_refs, url_limit and urls. sessions is null once deletion has removed a session from any of the owner’s events. Counts include retained aggregates.

GET /api/v1/telemetry/unattributed lists content_host, events and last_seen_at for the 50 hosts with the most unclaimed events.

An owner that runs or designates a telemetry consumer can receive its events directly, as they arrive, instead of a report. The routes follow the per-event rule above: each endpoint the source declared, and the destination you save. Set a destination yourself, or leave the hub to deliver to what the licence and the host’s manifest declare.

Each delivered event keeps its optional terms_ref, the reference to its governing terms, separately from license_ref, the reference to its grant. The hub forwards terms_ref as supplied and does not interpret it or use it to select a destination. It can resolve license_ref for routing. Events received without a reference carry none onward. A duplicate event keeps the first accepted value.

Set the destination directly when the owner names a consumer outside its manifest or when the consumer issued you a credential. A saved destination adds a route beside the endpoints a licence or manifest declares; it replaces neither. Only an owner of your organisation can set it, with PUT /api/v1/owners/{id}/destination and a JSON body:

{ "url": "https://consumer.example/api/v1/telemetry/events", "credential": "…" }

The URL must be https, with no userinfo or fragment, at most 2,048 bytes once percent-encoded, and its host must resolve to public addresses. A host that resolves to an address that is not public is refused with a 400 naming the address: resolves to 10.0.0.8, which is not a public address. Not public means private, loopback, link-local, shared (100.64.0.0/10), multicast, reserved, unspecified or documentation ranges, and the IPv6 forms that carry such an IPv4 address (mapped, compatible, NAT64 and 6to4); in IPv6, only global unicast outside Teredo and the documentation prefixes counts. A name that does not resolve yet is accepted. The credential is stored encrypted, sent as X-API-Key, and never shown again. The destination stands until you change or clear it.

An owner is sent only the events that occurred while it held their host: from its registration of that host until the registration is removed. An event’s time is the one the edge recorded, or the time the hub received it if that is earlier. Setting a destination queues the events the owner is owed and has not been sent; every later event is queued as it is ingested. Events from before the owner registered a host are not sent, including events of an owner that held the host before it, so a host that changes hands does not pass the previous holder’s record to the new one. Removing a host stops new events for it; events that occurred while the owner held it are still delivered, unless you clear the destination or delete the owner.

To send an owner earlier events, back-fill a range with POST /api/v1/owners/{id}/delivery/backfill and a JSON body:

{ "from": "2026-09-01T00:00:00Z", "until": "2026-09-15T00:00:00Z" }

A back-fill queues, once, the events in that range, from from up to but not including until, whose hosts the owner holds at that moment, whoever held them when the events occurred. It is an act, not a standing rule: a host you register for the owner later brings none of its earlier events unless you back-fill again. Events already owed to the owner are left as they are. The rows are queued on the owner’s saved destination whether or not it has one yet, and are sent once it has one. A back-fill does not send anything to the endpoints a licence or manifest declares: those are fixed from each event’s own declarations when it arrived. Clearing the destination drops back-filled rows not yet sent, and the back-fill’s record counts them as dropped; saving a destination again does not bring them back, so back-fill again if you want them. until must be later than from and not in the future. The answer gives the back-fill’s id, the hosts it covered, the number of events queued, and under held_by_others how many of those were owed, when they occurred, to another owner (through the exact host or its parent) and which owners those were: a back-fill across a hand-over sends the previous holder’s events to this owner’s receiver, so check that count before you rely on it. Only an organisation owner can back-fill, not while a super admin acts as someone, and each back-fill is recorded in the audit log with the same detail.

The worker checks for delivery every fifteen seconds; licence reads and a backlog can delay an attempt. It sends Content Telemetry event_batch documents of up to 500 events: for the saved destination, grouped by session; for a declared endpoint, by session and content host, so one host’s endpoint never receives another host’s events. Deliveries are not signed, so an endpoint that requires a signature refuses them and they become dead letters. The hub builds each document from what it stored of the edge’s delivery: the session’s identifier, agent id and start time, and each event’s id, type, time, source role, URL, licence, its data object as the edge sent it (which carries the supplier field and can name the tool the agent ran in), and the content_telemetry_id of the fetch’s Content-Telemetry-ID header where the edge sent one, so the consumer can match the event to the request the owner’s servers logged. On a retrieval sent under a positive Crawl-delay, the edge includes data.commonmeasure-crawl-delay.delay_ms: the delay it kept, in integer milliseconds, at most 60,000. The Hub forwards it unchanged to that content owner. It is absent for a zero delay or a crossing sent without a delay. The session’s refused count is not sent. Any 2xx answer is an acceptance, with or without a body. When the answer is JSON with events_created, the hub records that count against the batch; otherwise the count is unknown, never 0. Any other status is a failure. A URL that answers 2xx to any request, such as a web page, is taken as accepting, so check a destination’s URL before relying on its delivered count. A failed attempt is retried after 1, 2, 4, 8, 16 and 32 minutes, then hourly; the tenth failure dead-letters the delivery, which stops retrying and keeps its last error.

Each delivery resolves the destination’s host again and connects only to the addresses it resolved. If any of them is not public, however the destination was set, the hub sends nothing and records a failed attempt whose error begins destination refused: and names the address. A name that takes longer than 30 seconds to resolve is a failed attempt too. The hub is never a route into a private network.

GET /api/v1/owners/{id}/delivery shows the destination and its source, paused, backfills with each range, its hosts, the counts it queued and when it was made, licences with their resolution outcomes and cache expiry, routes with counts by route and endpoint, and batches with the latest attempted batches, each delivered batch with the events_created its receiver stated or null when it stated none. batch_limit and licence_limit state the list bounds. It also shows the complete counts of queued, delivered and dead deliveries, the last delivery time, the next attempt time, the most recent error, and the dead letters themselves, each with its URL, attempt count and last error (bounded; dead_letter_limit states the bound). POST /api/v1/owners/{id}/delivery/retry requeues the dead letters with a fresh attempt budget; setting the destination again does the same. DELETE /api/v1/owners/{id}/destination stops the saved route and drops what it still owed; what was delivered stays recorded, and declared endpoints go on receiving events.

Deliveries to declared endpoints, whether or not an owner is registered for their hosts, are read with GET /api/v1/onward/declared (a signed-in member or a telemetry:read key):

Terminal window
curl https://hub.example/api/v1/onward/declared

It answers each endpoint with the declarations that named it (routes), the number of hosts and events it serves, how many of those events had no registered owner, complete queued, delivered, dead, suppressed and withheld counts with the reasons anything was held (withheld_reasons), the last delivery, the next attempt and the latest error; then the dead letters, the recent batches (with events_created as on the owner’s answer), the suppressed endpoints, the latest host manifest reads and licence resolutions, each bounded by limit; and awaiting_declarations, the number of events whose licence or host manifest the hub has not read yet. POST /api/v1/onward/declared/retry gives every declared dead letter a fresh attempt budget (a signed-in owner). Requeueing an owner’s dead letters does the same for the declared endpoints of the events that owner held. Neither requeues a dead letter at a suppressed endpoint.

Stopping a declared endpoint. A source’s declaration is the source’s, and the hub cannot edit it; what you can do is refuse to deliver to it. POST /api/v1/onward/declared/suppressions with {"url": "…", "reason": "…"} (a signed-in owner) stops every declared route to that URL’s origin, whatever declares it — the stop for a hostile endpoint, or for one a compromised licence document or manifest names. Nothing is dropped in silence: what was queued there becomes suppressed, is counted on the endpoint and against the owner’s routes, and returns to the queue with a fresh attempt budget when DELETE /api/v1/onward/declared/suppressions with {"url": "…"} (any URL at that origin) lifts the stop. Suppressing is allowed under impersonation, since it stops data leaving; lifting one is not.

  • A stop covers the endpoint’s origin: its scheme, host and port, read as delivery reads them (case, a default port and a trailing dot on the host ignored). The party that declares an endpoint controls its paths and queries; a stop on one exact URL would last only until its licence document or manifest named /events/, /events?v=2 or /v2. The answer names the origin in destination_origin. Another host, a subdomain included, is another origin: stop each one.
  • Any http or https URL may be given, so an origin can be stopped before anything names it. A URL with no host is refused with 400.
  • A batch already on its way when the stop lands is not recalled. The answer counts it as in_flight beside held; if the endpoint accepts it, those rows read delivered.
  • An origin already stopped keeps the URL, reason and name of the stop that made it. A second request holds whatever has been queued there since and answers "already_suppressed": true with the reason in force, not the one it offered; both requests are in the audit log. To change the reason, lift the stop and make it again.
  • The stop list (suppressions in the status answer) names who made each stop in suppressed_by. Where support made it while acting as one of your people, acted_as names that person: the stop is support’s act, made in their name, and only you can lift it.
  • It stops declared routes only. A destination you saved for an owner at the same URL is your own route and keeps delivering until you clear it (DELETE /api/v1/owners/{id}/destination) or delete the owner.
Terminal window
curl -X POST https://hub.example/api/v1/onward/declared/suppressions \
-H 'content-type: application/json' \
-d '{"url": "https://receiver.example/events", "reason": "endpoint not the publisher's"}'

GET /api/v1/onward/events/{event_id} answers every route one event is owed — each saved route (kind saved) and each declared endpoint (kind declared) — with its destination, state, attempts, last error and batch, and whether the event is still awaiting its declarations.

A batch the receiver recorded but whose answer the hub did not receive is sent again with the same event ids. A receiver that deduplicates on event id, as this hub’s own receiver does, counts it once.

The Onward delivery screens render this status and post suppressions, lifts, retries and back-fills through these endpoints.

The Onward delivery, Content owners, owner and unattributed hosts screens are open to every signed-in member of your organisation.

Registering an owner needs a signed-in member or an identity:write key; adding or removing a host and deleting an owner need a signed-in owner. Reports, the unattributed listing, the delivery status, the declared-endpoint status and an event’s routes need a signed-in member or a telemetry:read key. Setting or clearing a destination and back-filling need a signed-in owner. Requeueing dead letters requires a signed-in owner. An ingest key can deliver events and nothing else.

Registering an owner here groups matching events delivered by your organisation’s Common Measure edges. To receive reports from other agents that use a public site you own, declare where they should report:

  • Publish a Content Telemetry manifest at https://<host>/.well-known/content-telemetry.json, or a telemetry reporting binding in the site’s RSL licence, naming the endpoint of the consumer you choose. Any consumer that conforms to Content Telemetry v1.0 can receive the reports. Participating agents deliver their own evidence there; this hub delivers there every event about your site that its organisations’ edges release (§Onward delivery).
  • Your organisation can be that consumer: a receiver gives a URL to declare, and Received reports shows what other parties’ agents report to it, unverified and apart from your fleet evidence.
  • A site-side collector, at your server or CDN, observes requests that do not report themselves. It sends content_retrieved events with source_role: edge to your consumer under the credential that consumer issues. A hub key does not work for it; a receiver takes its events only in batches the hub’s ingest accepts (Received reports).

The manifest and a collector serve separate purposes: publishing the manifest installs no collection, and a collector’s observations do not establish grounding or citation. Detection depends on the signals an agent exposes and does not identify every automated request. Inspect collected events with your consumer. The hub’s owner report counts only the events your fleet delivered. Neither route reports intranet or private-source activity.