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
| Parameter | Type | Required | Description |
|---|---|---|---|
entityId | string | MUST | The 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):
| Parameter | Type | Required | Description |
|---|---|---|---|
url | string (URL) | MUST | The full URL the agent is verifying |
context | string | MAY | The 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:
| Claim | Type | Description |
|---|---|---|
iss | string | The agent's did:web identifier |
iat | number | Token issued-at timestamp (Unix epoch seconds) |
exp | number | Token expiration timestamp (Unix epoch seconds) |
aud | string | The trust authority's domain |
When a JWT is present, the trust authority MUST reject tokens where:
expis more than 60 seconds in the past (this tolerance accommodates minor clock drift between agent and authority)auddoes 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:
| Field | Type | Required | Description |
|---|---|---|---|
meta | object | MUST | Technical metadata (see below). Agents use these fields for caching, log lookups, and signature verification — not for trust decisions. |
signals | array | MUST | Array 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. |
assessment | object | MAY | Authority-generated interpretation of the signals, scoped to the agent's context. See Assessment below. |
kid | string | MUST | Key identifier matching a key in the authority's published JWKS. Included in the signed payload. |
signature | string | MUST | Base64url-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:
| Field | Type | Required | Description |
|---|---|---|---|
responseId | string (UUID v4) | MUST | Unique identifier for this response. Reserved for future use by transparency mechanisms — see the roadmap. |
entityId | string | MUST | The queried entity identifier |
status | string (enum) | MUST | Entity 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. |
url | string (URL) | MUST | The 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. |
context | string | MAY | The context value the authority used when producing this response. Echoes the context query parameter. Omitted when the agent sent no context. |
timestamp | string (RFC 3339, UTC) | MUST | When this response was generated |
expires | string (RFC 3339, UTC) | MUST | Response 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:
- Lowercase the
scheme. - Lowercase the
host. Strip the default port (:443forhttps,:80forhttp). - Normalize percent-encoding in the
pathper RFC 3986 §6.2.2 (decode unreserved characters, uppercase remaining hex digits). Do not rewrite trailing slashes — preserve the path as sent. - Remove the query string and fragment.
- 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:
- Recompute the canonical form of the URL it sent in the
urlquery parameter, using the rules above, and compare it tometa.url. If they differ, the agent MUST reject the response assignatureInvalid. - If the agent sent a
contextquery parameter, compare it tometa.context. If they differ — including the case wheremeta.contextis absent — the agent MUST reject the response assignatureInvalid.
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:
| Field | Type | Description |
|---|---|---|
action | enum: proceed, caution, decline | Machine-readable recommendation. proceed: signals support the agent's intent. caution: signals are mixed or incomplete. decline: signals indicate risk. |
reasoning | string (max 500 chars) | Why the authority reached this recommendation |
The assessment object MAY contain:
| Field | Type | Description |
|---|---|---|
highlights | array 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.
| Context | Field | Meaning |
|---|---|---|
purchase | safeToPurchase | Whether the authority considers this entity safe for a purchase |
inquiry | informationReliable | Whether the entity's published information is reliable |
high-value | safeForHighValue | Whether 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 Code | Error Code | Condition | Response Body |
|---|---|---|---|
| 400 | invalidRequest | Missing or malformed url, or entityId path segment fails the format constraint | {"error": "invalidRequest", "message": "..."} |
| 400 | entityMismatch | URL does not fall within the entity's scope | {"error": "entityMismatch", "message": "..."} |
| 401 | unauthorized | JWT was presented and is invalid, expired, or not verifiable. Missing JWT never triggers 401. | {"error": "unauthorized", "message": "..."} |
| 404 | entityNotFound | Entity not known to the authority | {"error": "entityNotFound", "message": "..."} |
| 429 | rateLimited | Rate limit exceeded | {"error": "rateLimited", "message": "..."} with Retry-After header |
| 500 | internalError | Internal 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:
- Retry. Before acting on the error, retry the request at least once with a minimum 1-second delay. For
429 rateLimited, respect theRetry-Afterheader instead. - 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.
- 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.
- Escalate in high-risk contexts. When
contextishigh-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
- Glossary — Canonical definitions for protocol terms
- Discovery specification — How agents find this endpoint
- Trust Signals specification — Signal types and structure
- Security specification — Signature verification, anti-gaming
- Roadmap — Future work (transparency log, manifest discovery, federation)
- ADR-002: Verification Endpoint Schema
- ADR-004: Response Format
- ADR-006: Agent Identity
- ADR-008: Versioning Strategy
- ADR-010: Assessment Layer
- ADR-012: Unsigned Error Handling