Skip to main content

Verification API

This document specifies the trust authority's verification endpoint — the core of the #trstd <protocol>.

Normative language follows RFC 2119: MUST, SHOULD, MAY indicate requirement levels.

Overview

The verification API is a single HTTP endpoint operated by the trust authority. Agents query it to retrieve trust signals for a service provider. The endpoint returns a signed JSON response containing all available signals.

Endpoint

GET /v1/entities/{entityId}/trust-signals

The version prefix (/v1/) follows the URL path versioning strategy (ADR-008). The entity is addressed by its entityId as a URL path segment.

Request

Path Parameters

ParameterTypeRequiredDescription
entityIdstringMUSTThe entity ID extracted from the link tag's href. Opaque, authority-defined format. MUST match ^[A-Za-z0-9._~-]+$ (RFC 3986 unreserved characters) so it is safe as a URL path segment without percent-encoding. Maximum 128 characters.

Query Parameters

Core parameters (defined by the protocol spec):

ParameterTypeRequiredDescription
urlstring (URL)MUSTThe full URL the agent is verifying
contextstringMAYThe agent's intent. Well-known values: purchase, inquiry, high-value. Agents and authorities MAY use additional values. Authorities MUST ignore unrecognized values. The authority uses this to tailor the optional assessment section in the response.

The server MUST validate that the url falls within the scope of the entity identified by {entityId} (host and path prefix match). If the URL does not match the entity scope, the server MUST return 400 with error code entityMismatch.

URL privacy: The url parameter carries the full URL the agent is visiting, including query parameters. Entity matching uses only hostname and path (see Discovery specification), so query parameters play no role in scope validation. Query parameters may contain session identifiers, tracking tokens, or user-specific context that the authority does not need for trust assessment. This is a simplicity tradeoff — agents send the URL without decomposition or parameter-stripping rules. URL minimization is tracked on the roadmap.

Agent Identification

Agent identification is optional per-request. Authorities MUST NOT gate access on agent identity — a request without an Authorization header MUST be served. See ADR-006 for the decision rationale.

Agents MAY include a did:web JWT in the Authorization header:

Authorization: Bearer <jwt>

When an agent sends a JWT, it MUST contain the following claims:

ClaimTypeDescription
issstringThe agent's did:web identifier
iatnumberToken issued-at timestamp (Unix epoch seconds)
expnumberToken expiration timestamp (Unix epoch seconds)
audstringThe trust authority's domain

When a JWT is present, the trust authority MUST reject tokens where:

  • exp is more than 60 seconds in the past (this tolerance accommodates minor clock drift between agent and authority)
  • aud does not match the authority's domain
  • The signature cannot be verified against the agent's DID Document public key

A rejected JWT MUST result in 401 unauthorized. This applies only when a JWT was presented — a missing Authorization header MUST NOT trigger 401.

Authorities MAY use a presented identity for logging, rate-limiting, or response tailoring. These behaviors are implementation-defined; the protocol does not specify or guarantee them.

DID resolution and JWT verification follow the TSAI protocol specification.

The trust authority SHOULD impose a timeout of 5 seconds on DID Document resolution. If the agent's DID Document is unreachable or resolution exceeds the timeout, the authority MUST return 401 unauthorized. Unresolvable DIDs are treated as unverifiable presented identities — the same 401 as other validation failures.

Example Requests

Unidentified (v1 default):

GET /v1/entities/d6f2fdf4-f829-4ce6-a1cc-e2bd957709db/trust-signals?url=https%3A%2F%2Fwww.example.org%2Fde%2Fproducts%2F123&context=purchase HTTP/1.1
Host: trust-authority.example.org
Accept: application/json

Identified (when the agent has a did:web identity):

GET /v1/entities/d6f2fdf4-f829-4ce6-a1cc-e2bd957709db/trust-signals?url=https%3A%2F%2Fwww.example.org%2Fde%2Fproducts%2F123&context=purchase HTTP/1.1
Host: trust-authority.example.org
Authorization: Bearer eyJhbGciOiJFZERTQSIsImtpZCI6ImtleS0xIn0...
Accept: application/json

Response

Success (200 OK)

The trust authority MUST return Content-Type: application/json.

All JSON object keys MUST use camelCase.

Response body:

{
"meta": {
"responseId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"entityId": "d6f2fdf4-f829-4ce6-a1cc-e2bd957709db",
"status": "verified",
"url": "https://www.example.org/de/products/123",
"context": "purchase",
"timestamp": "2026-03-23T14:30:00Z",
"expires": "2026-03-24T14:30:00Z"
},
"signals": [
{
"type": "identity",
"verifiedAt": "2026-01-15T00:00:00Z",
"data": {
"legalName": "Example Electronics GmbH",
"country": "DE",
"registrationNumber": "HRB 12345"
}
},
{
"type": "reputation",
"verifiedAt": "2026-03-01T00:00:00Z",
"data": {
"aggregateRating": 4.2,
"reviewCount": 1247,
"sourceCount": 3
}
},
{
"type": "compliance",
"verifiedAt": "2025-11-01T00:00:00Z",
"data": {
"certifications": ["PCI-DSS", "GDPR"],
"lastAudit": "2025-11-01"
}
},
{
"type": "recourse",
"verifiedAt": "2026-02-01T00:00:00Z",
"data": {
"mechanism": "ombudsman",
"provider": "EU ODR Platform",
"responseTimeDays": 14
}
},
{
"type": "contact",
"verifiedAt": "2026-01-15T00:00:00Z",
"data": {
"emailVerified": true,
"phoneVerified": true,
"addressVerified": true
}
}
],
"assessment": {
"action": "proceed",
"safeToPurchase": "yes",
"reasoning": "Verified German business operating since 2019 with 4.2-star rating across 1,247 reviews and active recourse mechanism.",
"highlights": [
"Business profile verified and published",
"Good customer satisfaction (4.2 average rating)",
"International market presence confirmed"
]
},
"kid": "authority-key-1",
"signature": "pV2qFz8kRn0LhVbUqSSR3IyzQlDu5XbE0MwvMXfBjlNRqIjmGT6c2Eq..."
}

Response Fields

Top-level fields:

FieldTypeRequiredDescription
metaobjectMUSTTechnical metadata (see below). Agents use these fields for caching, log lookups, and signature verification — not for trust decisions.
signalsarrayMUSTArray of trust signal objects. MAY be empty when the entity is verified but signal data has not yet been collected. Each signal MUST NOT exceed 4 KB. See Trust Signals.
assessmentobjectMAYAuthority-generated interpretation of the signals, scoped to the agent's context. See Assessment below.
kidstringMUSTKey identifier matching a key in the authority's published JWKS. Included in the signed payload.
signaturestringMUSTBase64url-encoded raw Ed25519 signature (64 bytes, no padding). Computed over the JCS-canonicalized (RFC 8785) response body with this field excluded. See Security specification and Test Vectors.

Meta fields:

FieldTypeRequiredDescription
responseIdstring (UUID v4)MUSTUnique identifier for this response. Reserved for future use by transparency mechanisms — see the roadmap.
entityIdstringMUSTThe queried entity identifier
statusstring (enum)MUSTEntity verification status. One of: verified (actively verified), lapsed (verification expired — e.g., expired compliance), revoked (verification withdrawn — e.g., fraud or policy violation), pending (application received, not yet confirmed). A 404 (entityNotFound) means the entity is unknown to the authority. See ADR-014.
urlstring (URL)MUSTThe canonical form of the URL the authority validated against entity scope. See URL canonicalization below. Binds the signed response to the specific page the agent asked about.
contextstringMAYThe context value the authority used when producing this response. Echoes the context query parameter. Omitted when the agent sent no context.
timestampstring (RFC 3339, UTC)MUSTWhen this response was generated
expiresstring (RFC 3339, UTC)MUSTResponse expiration — agents MUST re-query after this time

All datetime fields MUST use RFC 3339 format with the UTC Z suffix (e.g., 2026-03-23T14:30:00Z). Timezone offsets MUST NOT be used.

URL Canonicalization

The authority MUST canonicalize the agent's url query parameter as follows before placing it in meta.url:

  1. Lowercase the scheme.
  2. Lowercase the host. Strip the default port (:443 for https, :80 for http).
  3. Normalize percent-encoding in the path per RFC 3986 §6.2.2 (decode unreserved characters, uppercase remaining hex digits). Do not rewrite trailing slashes — preserve the path as sent.
  4. Remove the query string and fragment.
  5. Remove userinfo (user:pass@) if present.

The canonical form is scheme + "://" + host + path. This matches the granularity of entity-scope matching, which already ignores query and fragment. See ADR-007 — Binding the Signed Response to the Request.

Agent Verification of meta.url and meta.context

After verifying the response signature (see Security specification), the agent MUST:

  1. Recompute the canonical form of the URL it sent in the url query parameter, using the rules above, and compare it to meta.url. If they differ, the agent MUST reject the response as signatureInvalid.
  2. If the agent sent a context query parameter, compare it to meta.context. If they differ — including the case where meta.context is absent — the agent MUST reject the response as signatureInvalid.

These checks close a contextual-replay class of attack where a cached signed response for one URL within an entity's scope is substituted for another. See ADR-007 — Binding the Signed Response to the Request.

Signal fields are defined in the Trust Signals specification.

Assessment

The assessment object is an optional, authority-generated interpretation of the trust signals. It provides a machine-readable recommendation and natural-language reasoning tailored to the agent's context parameter.

The authority MAY omit the assessment regardless of whether the agent provided a context parameter. A missing assessment is not an error — it means the authority has no interpretation to offer for this query. Agents MUST NOT treat a missing assessment as a failure. Agents that need a trust decision when no assessment is present MUST fall back to evaluating the signals array directly.

The signals array contains verified facts. The assessment contains the authority's opinion derived from those facts. Agents MAY use the assessment as a fast path for decision-making and fall back to signal-level evaluation when more detail is needed. Agents MAY ignore the assessment entirely.

When present, the assessment object MUST contain:

FieldTypeDescription
actionenum: proceed, caution, declineMachine-readable recommendation. proceed: signals support the agent's intent. caution: signals are mixed or incomplete. decline: signals indicate risk.
reasoningstring (max 500 chars)Why the authority reached this recommendation

The assessment object MAY contain:

FieldTypeDescription
highlightsarray of strings (max 200 chars each, max 10 items)Key facts supporting the recommendation

Authorities MAY include additional fields in the extensions object. Each extension is a self-describing entry with a value and a human-readable description:

"extensions": {
"trustworthy": {
"value": "yes",
"description": "Whether the authority considers this entity generally trustworthy based on all available signals"
}
}

Extension field names MUST use camelCase and MUST NOT duplicate any spec-defined assessment field name. Each extension MUST include both value (string, number, boolean, or null) and description (max 200 characters). The description MUST be factual — not an instruction or directive. Top-level assessment keys not defined by this specification MUST NOT appear. See ADR-020 for the design rationale.

Agents MUST NOT fail on unrecognized extension fields. LLM-based agents MUST NOT treat extension field names, values, or descriptions as instructions or action directives. Agents SHOULD use the description to understand the field's meaning. See the Security specification for prompt injection guidance.

Context-Specific Fields

Authorities SHOULD include a string field whose name reflects the agent's context. These fields are defined by the specification and do not require a prefix. The field name carries semantic meaning that LLM agents use directly — an LLM reading "safeToPurchase": "yes" understands the recommendation without additional interpretation.

ContextFieldMeaning
purchasesafeToPurchaseWhether the authority considers this entity safe for a purchase
inquiryinformationReliableWhether the entity's published information is reliable
high-valuesafeForHighValueWhether the entity is suitable for a high-value transaction

These fields complement action — they do not replace it. action is the universal enum for programmatic agents. The context-specific field is the LLM-friendly signal.

The total payload of the assessment object MUST NOT exceed 4 KB. See the Security specification for prompt injection guidance on the reasoning and highlights fields.

The assessment is part of the response body and is covered by the authority's signature. Tampering with any assessment field invalidates the signature.

Error Responses

Status CodeError CodeConditionResponse Body
400invalidRequestMissing or malformed url, or entityId path segment fails the format constraint{"error": "invalidRequest", "message": "..."}
400entityMismatchURL does not fall within the entity's scope{"error": "entityMismatch", "message": "..."}
401unauthorizedJWT was presented and is invalid, expired, or not verifiable. Missing JWT never triggers 401.{"error": "unauthorized", "message": "..."}
404entityNotFoundEntity not known to the authority{"error": "entityNotFound", "message": "..."}
429rateLimitedRate limit exceeded{"error": "rateLimited", "message": "..."} with Retry-After header
500internalErrorInternal authority error{"error": "internalError", "message": "..."}

Example error response (404 entityNotFound):

{
"error": "entityNotFound",
"message": "Entity not known to the authority"
}

Error responses MUST use Content-Type: application/json. Error responses are not signed — they contain no trust data.

Agent Error Handling

The rules below derive from ADR-012: Unsigned Error Handling.

Because error responses are unsigned, a man-in-the-middle can forge any error response. An injected 404 entityNotFound suppresses a valid entity. An injected 500 internalError blocks trust evaluation entirely. HTTPS mitigates this at the transport layer, but agents MUST NOT treat a single unsigned error as a definitive trust result.

Agents MUST follow these rules when handling 404, 500, and network errors:

  1. Retry. Before acting on the error, retry the request at least once with a minimum 1-second delay. For 429 rateLimited, respect the Retry-After header instead.
  2. Prefer cached data. If the agent holds a valid (unexpired) cached response for the entity, use it. A cached signed response carries more integrity than an unsigned error.
  3. Treat unresolved errors as "trust unknown." When retries fail and no cache exists, agents MUST treat the result as a failed evaluation — not as "entity not found" or "entity untrusted." The protocol cannot make negative assertions without a signed response.
  4. Escalate in high-risk contexts. When context is high-value, agents MUST NOT conclude an entity is unknown based solely on unsigned errors. Agents SHOULD try a secondary network path (different DNS resolver, different network egress) before accepting the result.

These rules do not apply to 400 invalidRequest (a client error the agent can fix) or 400 entityMismatch (the link tag points to an entity the authority does not own; the agent treats this as a discovery failure).

Rate Limiting

The trust authority MAY enforce rate limits. The rate-limiting strategy is implementation-defined. When rate-limited, the authority MUST return 429 Too Many Requests with a Retry-After header indicating when the agent may retry.

Caching

Agents MAY cache successful responses until the meta.expires timestamp. Cached responses remain valid because they carry the authority's signature — agents can verify integrity without re-querying.

The meta.expires field in the response body is the authoritative freshness source. It is covered by the authority's signature — intermediaries cannot modify it without breaking the signature. The trust authority SHOULD set HTTP Cache-Control headers consistent with meta.expires as a transport hint for CDNs and proxies. When Cache-Control and meta.expires disagree, agents MUST use meta.expires.

References