{"openapi":"3.0.0","info":{"title":"IdClawPassport API","version":"1.1.3","description":"API for AI agent identities with IdentyClaw Passports on NEAR, including facial token_id encoding. Canonical OpenAPI document: `GET /openapi.json`.\n\n**Errors:** Application and nginx edge failures use `{ error: { code, message, details? }, requestId, timestamp }` (see `components.schemas.ErrorResponse`). Server and upstream failures return stable machine codes with generic `message` text; correlate with `requestId` in structured logs. Clients may send `X-Request-Id` or `X-Correlation-Id`; the edge forwards `X-Request-Id` to the API.","x-policies":{"termsOfService":"/.well-known/terms-of-service","privacyPolicy":"/.well-known/privacy-policy","dataRetention":"/.well-known/data-retention","whyidentyclaw":"/.well-known/why-identyclaw"},"contact":{"name":"IdentyClaw Support","email":"support@identyclaw.com","url":"https://identyclaw.com/contact"},"license":{"name":"Proprietary","url":"https://identyclaw.com/license"},"externalDocs":{"description":"IdentyClaw developer docs, cheat sheet, and browser API reference","url":"https://api.identyclaw.com/docs"},"x-publicRateLimit":{"comment":"Public route rate limits are enforced at the nginx edge, not in the Node app. The limit key is the direct TCP peer `$remote_addr` (not `X-Forwarded-For`). Use `set_real_ip_from` / `real_ip_header` only when a trusted upstream is documented.","rateLimitKey":"$remote_addr","sustainedRequestsPerMinutePerIp":1101,"burstPerIp":6101,"configFiles":["nginx/nginx.development.conf","nginx/nginx.main.conf"]},"x-edgeGateway":{"comment":"Responses below may be returned by nginx before the Node app (see nginx @request_error and @handle_error).","requestIdHeaders":["X-Request-Id","X-Correlation-Id"],"errors":[{"status":400,"code":"REQUEST_HEADER_TOO_LARGE","message":"Request headers or payload exceed size limits"},{"status":502,"code":"UPSTREAM_UNAVAILABLE","message":"The API gateway could not reach the application server"},{"status":504,"code":"UPSTREAM_UNAVAILABLE","message":"The API gateway could not reach the application server"}]}},"servers":[{"url":"/","description":"Current server"}],"security":[],"components":{"parameters":{"XRequestId":{"name":"X-Request-Id","in":"header","required":false,"schema":{"type":"string"},"description":"Optional client-supplied correlation id (ULID recommended). Echoed on error responses; nginx forwards it to the API when present, otherwise generates one."},"XCorrelationId":{"name":"X-Correlation-Id","in":"header","required":false,"schema":{"type":"string"},"description":"Fallback correlation header when `X-Request-Id` is omitted."}},"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"RODiT-JWT","description":"RODiT-issued JWT access token validated by @rodit/rodit-auth-be (RoditClient.authenticate), obtained via the RODiT login flow (e.g. /api/login). This is not an API key."}},"schemas":{"TokenId":{"type":"string","pattern":"^[a-z]{12}$","description":"Facial token_id encoding: 12 lowercase letters a-z (11 category indices + 1 checksum)."},"DidVerificationMethod":{"type":"object","properties":{"id":{"type":"string","description":"Identifier for the verification method fragment."},"type":{"type":"string","description":"Verification method type, e.g. Ed25519VerificationKey2020."},"controller":{"type":"string","description":"Controller DID for the verification method."},"publicKeyBase58":{"type":"string","description":"Base58-encoded Ed25519 public key."}},"required":["id","type","controller","publicKeyBase58"]},"DidService":{"type":"object","properties":{"id":{"type":"string","description":"Service fragment identifier."},"type":{"type":"string","description":"Service type identifier."},"serviceEndpoint":{"description":"Service endpoint location or descriptor.","oneOf":[{"type":"string"},{"type":"object"}]}},"required":["id","type","serviceEndpoint"]},"DidDocument":{"type":"object","properties":{"@context":{"type":"array","description":"Ordered list of JSON-LD contexts used in the DID document.","items":{"oneOf":[{"type":"string"},{"type":"object"}]}},"id":{"type":"string","description":"Primary DID identifier."},"alsoKnownAs":{"type":"array","items":{"type":"string"},"description":"Alternate DID identifiers for the same subject."},"controller":{"type":"string","description":"Controller account for the DID document."},"verificationMethod":{"type":"array","items":{"$ref":"#/components/schemas/DidVerificationMethod"},"description":"Verification methods available on the DID document."},"authentication":{"type":"array","items":{"type":"string"}},"assertionMethod":{"type":"array","items":{"type":"string"}},"service":{"type":"array","items":{"$ref":"#/components/schemas/DidService"},"description":"Service endpoints advertised in the DID document."},"requestId":{"type":"string","description":"Request correlation identifier returned by the API."}},"required":["@context","id","controller","verificationMethod","authentication","assertionMethod","service","requestId"]},"PublicPassportProfile":{"type":"object","description":"Sanitized public passport profile derived from on-chain token metadata. Returned when POST /api/identity/verify is called with includeProfile: true.","properties":{"tokenId":{"$ref":"#/components/schemas/TokenId"},"did":{"type":"string","description":"did:rodit DID for the passport holder."},"ownerAccountId":{"type":"string","nullable":true,"description":"NEAR account ID that owns the passport."},"displayName":{"type":"string","nullable":true,"description":"Display name from DN metadata (NNSWF/NSWF)."},"creature":{"type":"string","nullable":true,"description":"Creature/profession/species from DN metadata."},"contactUris":{"type":"array","items":{"type":"string"},"description":"Self-declared ContactURI values from DN metadata (scheme:authority:identifier)."},"avatarUrl":{"type":"string","nullable":true},"emojiUrl":{"type":"string","nullable":true},"webhookUrl":{"type":"string","nullable":true,"description":"Self-declared webhook routing hint from token metadata."},"face":{"type":"object","nullable":true,"description":"Facial token ID decode when available.","properties":{"checksumValid":{"type":"boolean"},"categories":{"type":"object","additionalProperties":true}}},"error":{"type":"string","description":"Present when profile enrichment failed (e.g. profile_unavailable)."}}},"ConciergeTrustAnchor":{"type":"object","description":"Official trust anchor for the IdentyClaw A2A Onboarding Concierge (Lemuel Gulliver). Verifiers compare verified peerTokenId from HOLA to lobbyTokenId before treating inbound messages as the official concierge. See MCP doc:reference:concierge-lobby-passport.","properties":{"displayName":{"type":"string","example":"Lemuel Gulliver"},"host":{"type":"string","format":"uri","description":"Concierge A2A host (Agent Card and /a2a endpoint base)."},"verifyHost":{"type":"string","format":"uri","description":"Public HOLA verify web UI."},"lobbyTokenId":{"allOf":[{"$ref":"#/components/schemas/TokenId"}],"nullable":true,"description":"Canonical 12-letter passport tokenId for the concierge lobby. Null until CONCIERGE_LOBBY_TOKEN_ID is configured in deployment."},"supportEmail":{"type":"string","format":"email"},"a2aRegistryId":{"type":"string","description":"A2A Registry agent identifier."},"a2aRegistryUrl":{"type":"string","format":"uri"},"agentCardUrl":{"type":"string","format":"uri"},"a2aEndpoint":{"type":"string","format":"uri"},"contactUris":{"type":"object","description":"Canonical multi-channel ContactURI map for the concierge.","properties":{"a2a":{"type":"string"},"email":{"type":"string"},"discord":{"type":"string"},"telegram":{"type":"string"}},"additionalProperties":{"type":"string"}},"verifyCli":{"type":"string","description":"Recommended CLI for verify-before-execute reports.","example":"npx @rodit/verify-hola report --rpc"},"requestId":{"type":"string","description":"Request correlation identifier."}},"required":["displayName","host","verifyHost","supportEmail","a2aRegistryId","a2aRegistryUrl","agentCardUrl","a2aEndpoint","contactUris","verifyCli","requestId"]},"ErrorResponse":{"type":"object","description":"Unified error response structure (error-handling-standard.md). All application and documented nginx edge errors use this envelope. Server/upstream failures use stable `code` values and generic `message` text; raw upstream exception text is logged with `requestId`, not returned to clients.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Machine-readable error code (SCREAMING_SNAKE_CASE). Examples: HOLA_* validation; LOGIN_* / RODIT_* auth; WEBHOOK_SIGNATURE_INVALID; RATE_LIMIT_EXCEEDED; REQUEST_HEADER_TOO_LARGE / UPSTREAM_UNAVAILABLE (nginx edge); SIGNCLIENT_*; MCP_LIST_FAILED / MCP_RESOURCE_*; INVALID_DID / DID_RESOLUTION_FAILED / DID_NOT_FOUND; SESSIONS_RETRIEVE_FAILED / SESSION_CLEANUP_FAILED / SESSION_TERMINATION_FAILED; SERVICE_UNAVAILABLE."},"message":{"type":"string","description":"Human-readable error message safe for clients (generic for server/upstream failures)"},"details":{"type":"object","additionalProperties":true,"nullable":true,"description":"Optional client-safe context (validation hints, resource ids). Omits raw upstream exception messages."}},"required":["code","message"]},"requestId":{"type":"string","description":"Unique request identifier for tracing (ULID format)"},"timestamp":{"type":"string","format":"date-time","description":"ISO 8601 timestamp when error occurred"}},"required":["error","requestId","timestamp"],"example":{"error":{"code":"HOLA_VALIDATION_FAILED","message":"Token ID must be exactly 12 lowercase letters"},"requestId":"01HX9X0T9CS1EM0WQ7R6F5B2VY","timestamp":"2026-04-15T08:21:45.000Z"}},"McpJsonRpcErrorResponse":{"type":"object","description":"JSON-RPC 2.0 error envelope returned by MCP Streamable HTTP POST (and by the MCP SDK for transport/protocol failures). Does not use the application `ErrorResponse` envelope or `MCP_TRANSPORT_SESSION_REQUIRED`.","properties":{"jsonrpc":{"type":"string","example":"2.0"},"error":{"type":"object","properties":{"code":{"type":"integer","example":-32000},"message":{"type":"string"}},"required":["code","message"]},"id":{"type":"null"}},"required":["jsonrpc","error","id"]}},"examples":{"ErrorInvalidDid":{"summary":"Invalid DID or token","value":{"error":{"code":"INVALID_DID","message":"Invalid DID or token identifier"},"requestId":"01HX9X0T9CS1EM0WQ7R6F5B2VY","timestamp":"2026-04-15T08:21:45.000Z"}},"ErrorDidResolutionFailed":{"summary":"DID resolution server error","value":{"error":{"code":"DID_RESOLUTION_FAILED","message":"Failed to resolve DID document"},"requestId":"01HX9X0T9CS1EM0WQ7R6F5B2VY","timestamp":"2026-04-15T08:21:45.000Z"}},"ErrorSignclientFailed":{"summary":"Signclient upstream failure","value":{"error":{"code":"SIGNCLIENT_FAILED","message":"Failed to sign client request"},"requestId":"01HX9X0T9CS1EM0WQ7R6F5B2VY","timestamp":"2026-04-15T08:21:45.000Z"}},"ErrorSignclientFeeMismatch":{"summary":"Minting fee mismatch","value":{"error":{"code":"SIGNCLIENT_FEE_MISMATCH","message":"Minting fee mismatch","details":{"message":"The minting fee provided does not match the server's calculation","clientFee":"0.05","serverFee":"0.10"}},"requestId":"01HX9X0T9CS1EM0WQ7R6F5B2VY","timestamp":"2026-04-15T08:21:45.000Z"}},"ErrorMcpListFailed":{"summary":"MCP resource list failure","value":{"error":{"code":"MCP_LIST_FAILED","message":"Failed to list resources"},"requestId":"01HX9X0T9CS1EM0WQ7R6F5B2VY","timestamp":"2026-04-15T08:21:45.000Z"}},"ErrorMcpResourceNotFound":{"summary":"MCP resource not found","value":{"error":{"code":"MCP_RESOURCE_NOT_FOUND","message":"Resource not found","details":{"uri":"openapi:swagger"}},"requestId":"01HX9X0T9CS1EM0WQ7R6F5B2VY","timestamp":"2026-04-15T08:21:45.000Z"}},"ErrorMcpTransportSessionRequired":{"summary":"Bare GET /mcp without Mcp-Session-Id (curl probe guard)","value":{"error":{"code":"MCP_TRANSPORT_SESSION_REQUIRED","message":"MCP Streamable HTTP transport requires a valid Mcp-Session-Id from an MCP client.","details":{"mcpDiscovery":"/.well-known/mcp"}},"requestId":"01HX9X0T9CS1EM0WQ7R6F5B2VY","timestamp":"2026-04-15T08:21:45.000Z"}},"McpPostMissingSession":{"summary":"POST /mcp without session on non-initialize request","value":{"jsonrpc":"2.0","error":{"code":-32000,"message":"Bad Request: No valid MCP transport session."},"id":null}},"ErrorSessionsRetrieveFailed":{"summary":"Session list failure","value":{"error":{"code":"SESSIONS_RETRIEVE_FAILED","message":"Failed to retrieve sessions"},"requestId":"01HX9X0T9CS1EM0WQ7R6F5B2VY","timestamp":"2026-04-15T08:21:45.000Z"}},"ErrorEdgeRequestTooLarge":{"summary":"Nginx request/header size limit","value":{"error":{"code":"REQUEST_HEADER_TOO_LARGE","message":"Request headers or payload exceed size limits"},"requestId":"01HX9X0T9CS1EM0WQ7R6F5B2VY","timestamp":"2026-04-15T08:21:45.000Z"}},"ErrorEdgeUpstreamUnavailable":{"summary":"Nginx upstream unreachable","value":{"error":{"code":"UPSTREAM_UNAVAILABLE","message":"The API gateway could not reach the application server"},"requestId":"01HX9X0T9CS1EM0WQ7R6F5B2VY","timestamp":"2026-04-15T08:21:45.000Z"}}}},"paths":{"/health":{"get":{"summary":"Health check","responses":{"200":{"description":"Service health status with dependency checks.","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["healthy","degraded"]},"degraded":{"type":"boolean"},"checks":{"type":"object","additionalProperties":true},"service":{"type":"string"},"timestamp":{"type":"string","format":"date-time"},"instance":{"type":"object","properties":{"hostname":{"type":"string"},"pid":{"type":"integer"}},"required":["hostname","pid"]}},"required":["status","degraded","checks","service","timestamp","instance"]}}}}},"operationId":"get_health"}},"/":{"get":{"summary":"Root API discovery endpoint with enrollment and MCP agent guide","description":"Returns API discovery from `/`, including `agentGuide` for fetching `references/skills.md` via MCP (`doc:skills` / `skills:skills`), enrollment URL, documentation links, and endpoints grouped by access level.","tags":["Discovery"],"responses":{"200":{"description":"API discovery information","content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"API name"},"version":{"type":"string","description":"API version (from this OpenAPI document info.version)"},"description":{"type":"string","description":"API summary; points agents to agentGuide for skills.md via MCP"},"enrollment":{"type":"object","properties":{"url":{"type":"string","description":"URL for purchasing IdentyClaw Passports"},"guide":{"type":"string","description":"Well-known enrollment guide path"},"badge":{"type":"string","description":"Enrollment badge/call-to-action text"}},"required":["url","guide","badge"]},"documentation":{"type":"object","properties":{"swagger":{"type":"string","description":"Swagger specification URL"},"openapi":{"type":"string","description":"OpenAPI specification URL"},"html":{"type":"string","description":"HTML documentation URL"}}},"endpoints":{"type":"object","properties":{"public":{"type":"array","items":{"type":"string"},"description":"Publicly accessible endpoints"},"authenticated":{"type":"array","items":{"type":"string"},"description":"Endpoints requiring authentication"},"privileged":{"type":"array","items":{"type":"string"},"description":"Endpoints requiring admin privileges"}}},"requestId":{"type":"string","description":"Request correlation identifier"},"timestamp":{"type":"string","format":"date-time","description":"Response timestamp"},"agentGuide":{"type":"object","description":"How agents should fetch references/skills.md via MCP before other API calls","properties":{"summary":{"type":"string"},"skills":{"type":"object","properties":{"resourceUris":{"type":"array","items":{"type":"string"},"description":"Canonical MCP resource URIs for references/skills.md (prefer doc:skills)"},"http":{"type":"string","description":"REST path to fetch skills markdown (doc:skills)"},"mcpTools":{"type":"object","properties":{"list":{"type":"string"},"get":{"type":"string"}}},"steps":{"type":"object","properties":{"mcpClient":{"type":"string"},"curl":{"type":"string"}}}},"required":["resourceUris","http"]},"mcpDiscovery":{"type":"string"},"listResources":{"type":"string"}},"required":["summary","skills","mcpDiscovery","listResources"]},"wellKnown":{"type":"object","properties":{"enrollment":{"type":"string"},"termsOfService":{"type":"string"},"privacyPolicy":{"type":"string"},"dataRetention":{"type":"string"},"did":{"type":"string"}},"required":["enrollment","termsOfService","privacyPolicy","dataRetention","did"]},"mcp":{"type":"object","properties":{"endpoint":{"type":"string","description":"MCP streamable HTTP endpoint path"},"resources":{"type":"string","description":"MCP resource listing endpoint"},"schema":{"type":"string","description":"MCP schema endpoint"},"discovery":{"type":"string","description":"Well-known MCP discovery metadata endpoint"},"tools":{"type":"array","description":"Canonical MCP tool names supported by this server","items":{"type":"string"}},"skills":{"type":"object","description":"MCP mapping for references/skills.md agent workflows","properties":{"primaryUri":{"type":"string"},"alternateUri":{"type":"string"},"fetch":{"type":"string"},"source":{"type":"string"}},"required":["primaryUri","alternateUri","fetch","source"]},"httpAccessForAgents":{"type":"object","description":"REST paths for curl/shell agents (do not raw-GET /mcp)","properties":{"discovery":{"type":"string"},"list":{"type":"string"},"getByUri":{"type":"string"},"skills":{"type":"string","description":"GET references/skills.md via doc:skills"}}},"doNotUseRawHttpGetOn":{"type":"string"},"note":{"type":"string"}},"required":["endpoint","resources","schema","discovery","tools","skills","httpAccessForAgents","doNotUseRawHttpGetOn","note"]}},"required":["name","version","description","agentGuide","enrollment","documentation","endpoints","wellKnown","mcp","requestId","timestamp"]}}}}},"operationId":"get_root"}},"/.well-known/enrollment":{"get":{"summary":"Enrollment information and guidance","description":"Provides enrollment information including onboarding steps and support details.","tags":["Discovery"],"responses":{"200":{"description":"Enrollment information","content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","description":"Enrollment page title"},"enrollment":{"type":"object","properties":{"url":{"type":"string","description":"Purchase URL"},"description":{"type":"string","description":"Enrollment description"}}},"enrollmentSteps":{"type":"array","items":{"type":"object","properties":{"step":{"type":"integer"},"title":{"type":"string"},"description":{"type":"string"},"details":{"type":"object"}}}},"support":{"type":"object","properties":{"faq":{"type":"string"},"contact":{"type":"string"},"documentation":{"type":"string"},"examples":{"type":"string"}}},"requestId":{"type":"string","description":"Request correlation identifier"},"timestamp":{"type":"string","format":"date-time","description":"Response timestamp"}},"required":["title","enrollment","enrollmentSteps","support","requestId","timestamp"]}}}}},"operationId":"get_well_known_enrollment"}},"/.well-known/mcp":{"get":{"summary":"MCP discovery metadata","description":"Returns canonical MCP discovery metadata including transport endpoint (`/mcp`), `agentGuide` and `docs.skills` for fetching references/skills.md (`doc:skills` / `skills:skills`), REST resource paths (`/api/mcp/resources`, `/api/mcp/resource/{uri}`), MCP tools (`list_resources`, `get_resource`), and `httpAccessForAgents` for curl/shell agents (do not probe GET `/mcp` without an MCP client).","tags":["Discovery"],"responses":{"200":{"description":"MCP discovery metadata.","content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string"},"transport":{"type":"object","properties":{"protocol":{"type":"string"},"endpoint":{"type":"string"},"absoluteEndpoint":{"type":["string","null"]}},"required":["protocol","endpoint","absoluteEndpoint"]},"resources":{"type":"object","properties":{"list":{"type":"string"},"getByUri":{"type":"string"},"examples":{"type":"array","items":{"type":"string"}}},"required":["list","getByUri","examples"]},"tools":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"}},"required":["name","description"]}},"registration":{"type":"object","properties":{"recommendedServerName":{"type":"string"},"aliases":{"type":"array","items":{"type":"string"}}},"required":["recommendedServerName","aliases"]},"docs":{"type":"object","properties":{"skills":{"type":"string","description":"REST path for references/skills.md (doc:skills)"},"mcpResources":{"type":"string"},"enrollmentGuide":{"type":"string"},"openapi":{"type":"string"}},"required":["skills","mcpResources","enrollmentGuide","openapi"]},"agentGuide":{"type":"object","description":"Pointer to fetch references/skills.md before other API work","properties":{"summary":{"type":"string"},"skills":{"type":"object","properties":{"resourceUris":{"type":"array","items":{"type":"string"}},"http":{"type":"string"}},"required":["resourceUris","http"]}},"required":["summary","skills"]},"httpAccessForAgents":{"type":"object","description":"REST paths for curl, shell, and agents without an MCP Streamable HTTP client.","properties":{"discovery":{"type":"string"},"list":{"type":"string"},"getByUri":{"type":"string"},"health":{"type":"string"},"skills":{"type":"string","description":"GET /api/mcp/resource/doc:skills"},"examples":{"type":"array","items":{"type":"string"}}}},"doNotUseRawHttpGetOn":{"type":"string","description":"Transport path that returns MCP_TRANSPORT_SESSION_REQUIRED on bare GET (curl probe guard). POST without a session uses JSON-RPC -32000 instead — see POST /mcp 400."},"transportNote":{"type":"string","description":"Explains that Mcp-Session-Id is transport-scoped, not API login."},"requestId":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["title","transport","resources","tools","registration","docs","agentGuide","httpAccessForAgents","doNotUseRawHttpGetOn","transportNote","requestId","timestamp"]}}}}},"operationId":"get_well_known_mcp"}},"/openapi.json":{"get":{"summary":"Canonical OpenAPI specification","description":"Returns the canonical OpenAPI specification for this API. Use this endpoint for schema-driven tooling and client generation.","tags":["Discovery"],"responses":{"200":{"description":"OpenAPI specification","content":{"application/json":{"schema":{"type":"object","description":"OpenAPI 3.0 specification"}}}},"500":{"description":"Failed to load OpenAPI specification","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"operationId":"get_openapi_json"}},"/swagger.json":{"get":{"summary":"OpenAPI specification alias","description":"Returns the same OpenAPI document as `GET /openapi.json`. Provided for clients and discovery payloads that reference `/swagger.json` by convention.","tags":["Discovery"],"responses":{"200":{"description":"OpenAPI specification","content":{"application/json":{"schema":{"type":"object","description":"OpenAPI 3.0 specification"}}}},"500":{"description":"Failed to load OpenAPI specification","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"operationId":"get_swagger_json"}},"/api/v1/openapi.json":{"get":{"summary":"Legacy OpenAPI alias","description":"Deprecated compatibility endpoint. Redirects to `/openapi.json`.","deprecated":true,"tags":["Discovery"],"responses":{"301":{"description":"Redirect to /openapi.json"}},"operationId":"get_api_v1_openapi_json"}},"/docs/enrollment":{"get":{"summary":"Removed enrollment docs endpoint","description":"This endpoint has been removed and always returns `410 ENDPOINT_REMOVED`.","deprecated":true,"tags":["Discovery"],"responses":{"410":{"description":"Endpoint removed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"operationId":"get_docs_enrollment_removed"}},"/api/holanonce16ts":{"get":{"summary":"Get timestamp and nonce for HOLA handshake","security":[{"BearerAuth":[]}],"responses":{"200":{"description":"Returns a timestamp and nonce hex that can be directly concatenated into HOLA handshake messages.","content":{"application/json":{"schema":{"type":"object","properties":{"noncetsHex":{"type":"string","description":"32 uppercase hex characters (16 bytes) for the nonce component of HOLA handshake. This is the hex-encoded random nonce, already normalized to uppercase. Use this value directly when constructing HOLA messages. Do not attempt to parse or extract from a combined format."},"timestamp":{"type":"string","format":"date-time","description":"Server ISO-8601 timestamp when the nonce was generated."},"length":{"type":"integer","description":"Number of random bytes used to generate the nonce (always 16)."},"algorithm":{"type":"string","description":"Algorithm used to generate the nonce."},"requestId":{"type":"string","description":"Request correlation identifier."}},"required":["noncetsHex","timestamp","requestId"]}}}},"401":{"description":"Unauthorized - JWT token is missing or invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Agent discovery failed due to upstream service errors.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"examples":{"discoveryFailed":{"summary":"Public agent listing failed","value":{"error":{"code":"AGENT_DISCOVERY_FAILED","message":"Unable to fetch agent list"},"requestId":"01HX9X0T9CS1EM0WQ7R6F5B2VY","timestamp":"2026-04-15T08:21:45.000Z"}}}}}}},"operationId":"get_api_holanonce16ts"}},"/api/login/timestamp":{"get":{"summary":"Get timestamp for AI agent login","description":"Public endpoint for AI agents to obtain synchronized timestamp and ISO string needed to construct /api/login requests. Both values are generated from the same moment to ensure signature verification succeeds.","tags":["Authentication"],"responses":{"200":{"description":"Authentication parameters for constructing login requests.","content":{"application/json":{"schema":{"type":"object","properties":{"timestamp":{"type":"integer","description":"Unix timestamp in seconds for /api/login payload."},"timestamp_iso":{"type":"string","format":"date-time","description":"ISO 8601 timestamp for signing the login message."},"requestId":{"type":"string","description":"Request correlation identifier."}},"required":["timestamp","timestamp_iso","requestId"]}}}},"429":{"description":"Too Many Requests - public route rate limit exceeded at the nginx edge (1101 requests per minute per `$remote_addr` sustained, burst 6101). Limits apply only to public URIs listed in nginx `public_rate_limit_key`; see `info.x-publicRateLimit`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"examples":{"rateLimit":{"summary":"Rate limit exceeded","value":{"error":{"code":"RATE_LIMIT_EXCEEDED","message":"Exceeded anonymous auth-params rate limit"},"requestId":"01HX9WZ7F7B5DDXPKRZC4J8M0X","timestamp":"2026-04-15T08:20:00.000Z"}}}}}},"500":{"description":"Failed to generate authentication parameters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"examples":{"generationError":{"summary":"Entropy generator failure","value":{"error":{"code":"AGENT_AUTH_PARAMS_FAILED","message":"Random generator unavailable"},"requestId":"01HX9WZ8TF5X1Q4P3Q2D0A9RZT","timestamp":"2026-04-15T08:20:02.000Z"}}}}}}},"operationId":"get_api_login_timestamp"}},"/api/agents":{"get":{"summary":"List IdentyClaw Passport holders with facial descriptions","description":"Returns a paginated list of all IdentyClaw Passport holders with their facial descriptions decoded from Passport IDs. This is a public endpoint for agent discovery. For detailed identity info including DN and full metadata, use the protected /api/identity/token/{tokenId}/full endpoint.\n\n**Query parameters**: Only **`limit`** and **`cursor`** are supported. Extra parameters (e.g. `owner`) are **ignored** — do not use this endpoint to look up “your” Passport by NEAR account; save the **`token_id`** returned when the Passport was minted.\n\n**Current Capabilities**: Browse agents using pagination (`limit` and `cursor`).\n\n**Planned Features**: Search and owner filtering may be added later.\n\n⚠️ **DISCLAIMER**: The creature field and other agent metadata are self-declared by the agent. It is your responsibility to verify the accuracy and authenticity of this information before relying on it.","tags":["Agent"],"parameters":[{"name":"limit","in":"query","description":"Maximum number of agents to return (default: 20, max: 100)","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},{"name":"cursor","in":"query","description":"Pagination cursor for fetching the next page","schema":{"type":"string"}}],"responses":{"200":{"description":"Paginated list of agents with facial descriptions","content":{"application/json":{"schema":{"type":"object","properties":{"agents":{"type":"array","items":{"type":"object","properties":{"tokenId":{"type":"string","description":"The 12-character facial token ID"},"creature":{"type":"string","nullable":true,"description":"Creature/profession/species from the agent's DN metadata"},"face":{"type":"object","nullable":true,"description":"Facial description decoded from tokenId","properties":{"checksumValid":{"type":"boolean","description":"Whether the token ID checksum is valid"},"categories":{"type":"object","description":"Facial feature categories with their values"}}}}}},"nextCursor":{"type":"string","nullable":true,"description":"Cursor for fetching the next page, null if no more results"},"requestId":{"type":"string","description":"Request correlation identifier"},"disclaimer":{"type":"string","description":"Warning that creature field and agent metadata are self-declared and should be verified"}},"required":["agents","requestId","disclaimer"]}}}},"400":{"description":"Invalid request parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Failed to retrieve agents","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"operationId":"get_api_agents"}},"/api/concierge/trust-anchor":{"get":{"summary":"Get the official A2A onboarding concierge trust anchor","description":"Returns the canonical trust anchor for **Lemuel Gulliver**, the IdentyClaw A2A Onboarding Concierge: lobby `tokenId` (when configured), Agent Card URL, A2A registry links, and multi-channel ContactURIs.\n\nUse with `POST /api/identity/verify` (`includeProfile: true`) to implement **verify before execute** and **impersonation guards** — compare verified `peerTokenId` to `lobbyTokenId` before trusting concierge-originated HOLA.\n\n**Related:** MCP `doc:reference:concierge-lobby-passport`, `doc:reference:a2a-onboarding-concierge`; verify UI https://verify.identyclaw.com; CLI `npx @rodit/verify-hola report`.\n\n**Authentication:** Public endpoint (rate-limited at the nginx edge per `info.x-publicRateLimit`).","tags":["Discovery"],"responses":{"200":{"description":"Concierge trust anchor metadata.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConciergeTrustAnchor"}}}},"500":{"description":"Failed to load concierge configuration.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"operationId":"get_api_concierge_trust_anchor"}},"/api/me/identity":{"get":{"summary":"Get authenticated agent's own DN and facial description","description":"Returns the caller's own Distinguished Name (DN) and facial description based on their authenticated IdentyClaw Passport. This endpoint is for self-identification only - it derives the caller's tokenId from the JWT sub field. For looking up other agents, use GET /api/identity/token/{tokenId}/full.","tags":["Identity"],"security":[{"BearerAuth":[]}],"responses":{"200":{"description":"Authenticated agent's own DN and facial description.","content":{"application/json":{"schema":{"type":"object","properties":{"tokenId":{"$ref":"#/components/schemas/TokenId"},"dn":{"type":"object","nullable":true,"description":"Parsed Distinguished Name information from token metadata."},"face":{"type":"object","nullable":true,"description":"Facial description decoded from tokenId."},"notAfter":{"type":"string","nullable":true,"description":"Token expiration timestamp (ISO8601 format). Null if token has no expiration."},"metadata":{"type":"object","nullable":true,"description":"Full token metadata including openapijson_url, not_after, not_before, max_requests, webhook_url, userselected_dn, and userselected_dn_info."},"requestId":{"type":"string"}},"required":["tokenId","dn","face","notAfter","metadata","requestId"]}}}},"400":{"description":"IdentityUnavailable when sub is missing or unparsable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"examples":{"missingSub":{"summary":"JWT sub missing","value":{"error":{"code":"IDENTITY_SUB_MISSING","message":"JWT payload is missing required sub field for identity derivation"},"requestId":"01HX7HAGTDEJQ8G2KJ8JHB6Q1F","timestamp":"2026-04-15T07:45:00.000Z"}}}}}},"404":{"description":"IdentityNotFound when NEAR token does not exist.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"examples":{"missingToken":{"summary":"Token missing","value":{"error":{"code":"IDENTITY_NOT_FOUND","message":"RODiT identity not found","details":{"tokenId":"pkcnjdbdefcp"}},"requestId":"01HX7HANWC0VCG6F1D2Z1KQ7W4","timestamp":"2026-04-15T07:46:00.000Z"}}}}}},"500":{"description":"Identity lookup failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"operationId":"get_api_me_identity"}},"/api/identity/verify":{"post":{"summary":"Verify a peer's HOLA using Ed25519 and a Morse-compatible canonical message","description":"Peer-verification endpoint for HOLA messages received from another agent. Use `/api/identity/verify` for peer verification; use `/api/testhola` for self-testing your own outbound HOLA generation.\n\n**How-To:** See `references/hola-howto.md` (MCP: `doc:reference:hola-howto`) for the linear login → nonce → sign → verify path.\n\n**Fail-closed early validation (HTTP 400):** Uses the same format/checksum parser as `/api/testhola`. Rejects before peer signature/token checks when:\n- Payload envelope is invalid (`hola` must be a string, not a nested JSON object; `reasonCode: payload_is_json_object`)\n- Transport-shape lint fails (`unix_millis_timestamp`, `signature_not_base32`, `placeholder_signature`)\n- HOLA line format or checksum is invalid (`stage: format_checksum_and_payload_validation`, `reasonCode: invalid_format` or `checksum_invalid`)\n\nAll early failures return `400` with `code: HOLA_VALIDATION_FAILED` and `error.details.reasonCode` (same contract as `/api/testhola`).\n\n**Peer verification outcome (HTTP 200):** When format and checksum pass, returns `verified` plus per-check booleans. Timestamp freshness, nonce replay, token existence/activity, and Ed25519 signature outcomes are reported on the 200 body via `failureReasons` (e.g. `timestamp_stale_or_future`, `nonce_replay`, `token_missing`, `signature_invalid`) — not as `invalid_format` or `checksum_invalid`. Each `failureDetails` entry includes a prescriptive `hint`. When `nonce_replay` is present, `nonceReplayDetails` includes `holaTimestamp`, `maxAgeMs`, and optionally `firstSeenAt`.\n\nSet `includeProfile: true` to attach a sanitized `PublicPassportProfile` for `peerTokenId` — used by https://verify.identyclaw.com and `@rodit/verify-hola`. For concierge impersonation guards, compare `peerTokenId` to `lobbyTokenId` from `GET /api/concierge/trust-anchor` (MCP: `doc:reference:concierge-lobby-passport`).\n\nFor self-correction workflows and richer diagnostics/examples, validate sender-side payloads with `/api/testhola`.\n\n**Authentication:** Public endpoint — no bearer token is required (rate-limited at the nginx edge per `info.x-publicRateLimit`). A RODiT bearer token may optionally be supplied; when present, the caller's authenticated identity is used to emit the `RECIPIENT_MISMATCH` warning (suppressible via `expectedRecipient`).","security":[{},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"hola":{"type":"string","maxLength":512,"description":"Request field containing the HOLA handshake line exchanged between AI agents. CASE-INSENSITIVE: The protocol accepts any case (e.g., 'hola', 'HOLA', 'Hola'). For verification, the server canonicalizes the full signed payload to uppercase before Ed25519 and checksum validation. Token IDs are then normalized to lowercase only for blockchain lookup. STANDARD FORMAT: HOLA/<recipient>/<tokenId>/<ISO8601-timestamp>/<noncets-hex>/API.IDENTYCLAW.COM/<base32-ed25519-signature>/<checksum>. SUBAGENT FORMAT (delegated signers): HOLA/<recipient>/<delegateID>/<issuer_tokenId>/<publicKey-base32>/<ISO8601-timestamp>/<noncets-hex>/API.IDENTYCLAW.COM/<base32-ed25519-signature>/<checksum>. Base32 variant is RFC 4648 Base32 with alphabet A-Z2-7; uppercase canonical form is used and '=' padding is optional on input. For standard format, the Ed25519 signature is computed over HOLA/<recipient>/<tokenId>/<ISO8601-timestamp>/<noncets-hex>/API.IDENTYCLAW.COM/. For subagent format, the signature is computed over HOLA/<recipient>/<delegateID>/<issuer_tokenId>/<publicKey-base32>/<ISO8601-timestamp>/<noncets-hex>/API.IDENTYCLAW.COM/. The checksum is a single letter from ABCDEFGHJKMNPQRSTUVWXYZ (23 letters; omits I, L, O). Checksum algorithm: sum UTF-16 code units (JavaScript String semantics; ASCII-only prefixes match byte sums) over HOLA/<recipient>/.../<base32-ed25519-signature>/ including trailing slash (everything before this checksum field), modulo 23, index into that alphabet (NOT MD5/SHA). Recipient defaults to MUNDO if not specified. Note: noncets-hex is 32 uppercase hex characters from /api/holanonce16ts (hex-encoded, uppercase). delegateID is the hashOrDelegateId from /api/isauthorizedsigner (DID, name, or BLAKE3 hash). Maximum length: 512 characters. See /api/isauthorizedsigner for subagent authorization validation.","example":"HOLA/MUNDO/aaaaaaaaaaaa/2026-04-04T10:10:00Z/4F9A3C7E2D1B9A4C8E7F6A5B4C3D2E1F/API.IDENTYCLAW.COM/N3FZ5KQ8LH2BSM1XY/C"},"constraints":{"type":"object","properties":{"maxAgeMs":{"type":"integer","description":"Maximum allowed age in milliseconds for the hola. Defaults to 300000."}}},"expectedRecipient":{"type":"string","description":"Optional HOLA recipient token ID. When provided and it matches the HOLA recipient field, suppresses the RECIPIENT_MISMATCH warning (common when verifying a peer's HOLA while authenticated as yourself, or when testing with recipient=MUNDO)."},"includeProfile":{"type":"boolean","description":"When true, include a sanitized public passport profile for peerTokenId."}},"required":["hola"]}}}},"responses":{"200":{"description":"Verification result with detailed checks.","content":{"application/json":{"schema":{"type":"object","properties":{"verified":{"type":"boolean","description":"True when all checks pass and the peer hola is accepted."},"peerTokenId":{"type":"string","description":"token_id extracted from the peer hola. For subagent format, this is the issuerTokenId."},"destinatary":{"type":"string","description":"Recipient field extracted from the HOLA message."},"profile":{"allOf":[{"$ref":"#/components/schemas/PublicPassportProfile"}],"description":"Sanitized public passport profile when includeProfile is true."},"isSubagentFormat":{"type":"boolean","description":"True when the HOLA message uses the subagent format (11 fields) instead of standard format (8 fields). Only present when true."},"delegateId":{"type":"string","description":"The delegateID from the subagent HOLA message. Only present when isSubagentFormat is true."},"issuerTokenId":{"type":"string","description":"The parent agent's token ID who authorized this subagent. Only present when isSubagentFormat is true."},"checks":{"type":"object","description":"Detailed boolean flags indicating which verification stages passed.","properties":{"tokenExists":{"type":"boolean","description":"True when the peer token exists on NEAR."},"tokenActive":{"type":"boolean","description":"True when the token is considered active for verification. Currently false when token is expired based on metadata.not_after."},"timestampFresh":{"type":"boolean","description":"True when the timestamp is within the accepted age window."},"nonceReplaySafe":{"type":"boolean","description":"True when the nonce has not already been accepted for the same sender token within the active timestamp window."},"signatureValid":{"type":"boolean","description":"True when the Ed25519 signature matches the canonical message."},"checksumValid":{"type":"boolean","description":"True when the checksum matches the canonical hola payload. Always true on HTTP 200; checksum mismatch returns HTTP 400 with reasonCode checksum_invalid before this response."}},"required":["tokenExists","tokenActive","timestampFresh","nonceReplaySafe","signatureValid","checksumValid"]},"failureReasons":{"type":"array","description":"List of machine-readable codes describing why peer verification failed (timestamp, nonce, token, signature). Empty when verified is true. Format and checksum faults are not listed here; they fail closed with HTTP 400.","items":{"type":"string","enum":["timestamp_stale_or_future","nonce_replay","token_missing","token_expired","signature_invalid","public_key_unavailable","public_key_error","subagent_public_key_invalid_length","subagent_public_key_decode_failed"]}},"failureDetails":{"type":"array","description":"Human-readable descriptions and actionable hints for each failureReasons entry.","items":{"type":"object","properties":{"reasonCode":{"type":"string"},"description":{"type":"string"},"hint":{"type":"string","description":"Prescriptive next step for resolving this failure reason."}},"required":["reasonCode","description","hint"]}},"nonceReplayDetails":{"type":"object","description":"Present when failureReasons includes nonce_replay. Describes the HOLA timestamp and freshness window used for replay detection.","properties":{"holaTimestamp":{"type":"string","description":"ISO-8601 timestamp parsed from the HOLA line."},"maxAgeMs":{"type":"integer","description":"Maximum HOLA age in milliseconds used for freshness and replay checks."},"firstSeenAt":{"type":"string","description":"ISO-8601 time when this nonce was first consumed for this signer, if known."}},"required":["holaTimestamp","maxAgeMs"]},"signatureVerificationImplemented":{"type":"boolean","description":"Signals that signature verification is active for this server implementation."},"warnings":{"type":"array","description":"Optional warning entries for suspicious but non-fatal conditions such as recipient mismatch.","items":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}},"requestId":{"type":"string","description":"Request correlation identifier."}},"required":["verified","peerTokenId","checks","failureReasons","failureDetails","signatureVerificationImplemented","requestId"]}}}},"400":{"description":"Fail-closed HOLA validation failure: payload envelope, transport-shape lint, invalid format, bad checksum, invalid nonce hex, or invalid constraints. Returns error.details.stage and error.details.reasonCode (aligned with /api/testhola for format/checksum). Timestamp-only faults after parsing may use HOLA_TIMESTAMP_INVALID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"examples":{"invalidFormat":{"summary":"Invalid HOLA format (non-HOLA string)","value":{"error":{"code":"HOLA_VALIDATION_FAILED","message":"Unsupported protocol; expected HOLA prefix","details":{"valid":false,"stage":"format_checksum_and_payload_validation","reasonCode":"invalid_format","checks":{"formatValid":false,"checksumValid":false,"timestampValid":false,"noncetsValid":false},"selfTest":"POST /api/testhola for full diagnostics, documentation links, and corrected examples."}},"requestId":"01HX7HBE4X7AMP9Q4MP2YE2C41","timestamp":"2026-05-21T07:50:53.322Z"}},"checksumInvalid":{"summary":"Checksum invalid","value":{"error":{"code":"HOLA_VALIDATION_FAILED","message":"Invalid checksum: expected A, got B","details":{"valid":false,"stage":"format_checksum_and_payload_validation","reasonCode":"checksum_invalid","checks":{"formatValid":true,"checksumValid":false,"timestampValid":true,"noncetsValid":true},"selfTest":"POST /api/testhola for full diagnostics, documentation links, and corrected examples."}},"requestId":"01HX7HBE4X7AMP9Q4MP2YE2C41","timestamp":"2026-05-21T07:50:53.322Z"}},"payloadEnvelope":{"summary":"Payload envelope (hola must be a string)","value":{"error":{"code":"HOLA_VALIDATION_FAILED","message":"Property \"hola\" must be a string containing the full HOLA line, not a JSON object.","details":{"reasonCode":"payload_is_json_object","stage":"payload_envelope_validation","field":"hola","acceptedFields":["hola"],"preferredField":"hola"}},"requestId":"01HX7HBE4X7AMP9Q4MP2YE2C41","timestamp":"2026-05-21T07:50:53.322Z"}}}}}},"415":{"description":"UnsupportedMediaType - Content-Type must be application/json.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"operationId":"post_api_identity_verify"}},"/api/isauthorizedsigner":{"post":{"summary":"Verify if a public key is authorized to sign on behalf of an IdentyClaw Passport holder","description":"Validates whether a given Ed25519 public key has been authorized by an IdentyClaw Passport holder to act as a delegated signer. This is the primary mechanism for subagent authorization in OpenClaw. Subagents generate their own Ed25519 keypairs and request authorization from their parent agent. The parent signs the delegation record (tokenId:delegateId:timestamp:publicKey) using its Passport signing key, which is then verified by this endpoint. This enables hierarchical agent organizations where subagents can prove they're authorized by a specific parent without needing their own IdentyClaw Passport.","security":[{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"tokenId":{"type":"string","pattern":"^[a-z]{12}$","description":"The IdentyClaw Passport token ID (12 lowercase letters) of the passport holder (owner) authorizing the signer. The endpoint retrieves this owner's public key from NEAR blockchain to verify the signature."},"base64HashOrDelegateSignerId":{"type":"string","minLength":1,"maxLength":128,"description":"Unique identifier or hash for the delegated signer being authorized. Can be a DID, name, BLAKE3 hash, or any text identifier (1-128 characters). Used to identify which specific delegated signer is being authorized. If using a hash, canonicalization is the agent's responsibility."},"unixTimestamp":{"type":"integer","minimum":0,"description":"Unix timestamp (seconds since epoch) when the authorization was granted. Used to prevent replay attacks and establish temporal context."},"publicKey":{"type":"string","description":"Base64url-encoded Ed25519 public key (32 bytes when decoded) of the delegated signer (the entity being authorized to sign on behalf of the tokenId owner). This is the key we are validating as authorized."},"signature":{"type":"string","description":"Base64url-encoded Ed25519 signature (64 bytes when decoded) created by the tokenId owner's private key, proving they authorized this delegated signer's public key. The signature is computed over the message: tokenId:base64HashOrDelegateSignerId:unixTimestamp:publicKey"}},"required":["tokenId","base64HashOrDelegateSignerId","unixTimestamp","publicKey","signature"]}}}},"responses":{"200":{"description":"Authorization check completed. Returns detailed verification results regardless of authorization status.","content":{"application/json":{"schema":{"type":"object","properties":{"authorized":{"type":"boolean","description":"True if the public key is authorized by the passport holder; false otherwise."},"tokenId":{"type":"string","description":"The token ID that was checked."},"base64HashOrDelegateSignerId":{"type":"string","description":"The hash or delegated signer ID that was checked."},"checks":{"type":"object","description":"Detailed boolean flags indicating which verification stages passed.","properties":{"tokenExists":{"type":"boolean","description":"True when the passport token exists on NEAR."},"tokenActive":{"type":"boolean","description":"True when the token is considered active (not revoked/expired). NOTE: Currently a planned feature - returns true if token exists. Full implementation pending contract metadata support."},"publicKeyAuthorized":{"type":"boolean","description":"True when the Ed25519 signature is valid and proves the public key is authorized by the passport holder."}},"required":["tokenExists","tokenActive","publicKeyAuthorized"]},"failureReasons":{"type":"array","description":"List of machine-readable codes describing why authorization failed. Empty when authorized is true.","items":{"type":"string","enum":["token_owner_missing","owner_public_key_unavailable","signature_missing_or_invalid","signature_verification_failed","public_key_error"]}},"requestId":{"type":"string","description":"Request correlation identifier."}},"required":["authorized","tokenId","checks","failureReasons","requestId"]}}}},"400":{"description":"Invalid request - missing or malformed parameters (tokenId, unixTimestamp, publicKey, or signature).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized - JWT token is missing or invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"NotFound - The specified tokenId does not exist on the blockchain.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"415":{"description":"UnsupportedMediaType - Content-Type must be application/json.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"operationId":"post_api_isauthorizedsigner"}},"/api/login":{"post":{"summary":"RODiT client login to obtain a JWT access token","description":"Delegates to `RoditClient#login_client` (@rodit/rodit-auth-be): Express handler that reads `req.body` after JSON parsing. Signed payload is `identifier + timestamp_iso` (concatenated, no separator) where `identifier` is the chosen `roditid` or `accountid`. Server must have own RODiT config initialized (`getConfigOwnRodit`).","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","description":"Login with `roditid`; signature in `base64url_signature`.","required":["roditid","base64url_signature"],"properties":{"roditid":{"type":"string","description":"12-letter on-chain `token_id`; must match the identifier used when signing."},"timestamp":{"type":"integer","description":"Unix seconds from the same GET /api/login/timestamp challenge pair used to build the signing payload."},"base64url_signature":{"type":"string","description":"Ed25519 signature (base64url) over UTF-8 `roditid + timestamp_iso`."}},"additionalProperties":false},{"type":"object","description":"Login with `roditid`; signature alias `roditid_base64url_signature` (wire-compatible with login_server).","required":["roditid","roditid_base64url_signature"],"properties":{"roditid":{"type":"string","description":"12-letter on-chain `token_id`."},"timestamp":{"type":"integer","description":"Unix seconds from the same GET /api/login/timestamp challenge pair used to build the signing payload."},"roditid_base64url_signature":{"type":"string","description":"Same bytes as `base64url_signature`; do not send both signature fields non-empty."}},"additionalProperties":false},{"type":"object","description":"Login with NEAR implicit `accountid` (64-char hex); signature in `base64url_signature`.","required":["accountid","base64url_signature"],"properties":{"accountid":{"type":"string","description":"64-char lowercase hex NEAR implicit account id; signed message is `accountid + timestamp_iso`."},"timestamp":{"type":"integer","description":"Unix seconds from the same GET /api/login/timestamp challenge pair used to build the signing payload."},"base64url_signature":{"type":"string","description":"Ed25519 signature (base64url) over UTF-8 `accountid + timestamp_iso`."}},"additionalProperties":false},{"type":"object","description":"Login with NEAR implicit `accountid`; signature alias `roditid_base64url_signature`.","required":["accountid","roditid_base64url_signature"],"properties":{"accountid":{"type":"string","description":"64-char lowercase hex NEAR implicit account id."},"timestamp":{"type":"integer","description":"Unix seconds from the same GET /api/login/timestamp challenge pair used to build the signing payload."},"roditid_base64url_signature":{"type":"string","description":"Same bytes as `base64url_signature`; do not send both signature fields non-empty."}},"additionalProperties":false}],"description":"Exactly one of `roditid` or `accountid` after trim (not both, not neither). Exactly one of `base64url_signature` or `roditid_base64url_signature`. Provide exactly one timestamp format (`timestamp` or `timestamp_iso`) from the same GET /api/login/timestamp challenge pair used for signing. Deprecated keys `signature`, `account_id` → 400 `LOGIN_PAYLOAD_DEPRECATED`. Ambiguous timestamp/identifier/signature fields → 400 with codes such as `LOGIN_TIMESTAMP_AMBIGUOUS`, `INVALID_LOGIN_TIMESTAMP`, `LOGIN_IDENTIFIER_AMBIGUOUS`, `MISSING_LOGIN_IDENTIFIER`, `MISSING_BASE64URL_SIGNATURE`."}}}},"responses":{"200":{"description":"Login successful. Body includes `jwt_token` and `requestId`; header `New-Token` repeats the JWT.","headers":{"New-Token":{"description":"Same JWT string as `jwt_token` in the response body.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["jwt_token","requestId"],"properties":{"jwt_token":{"type":"string","description":"Bearer JWT for subsequent API calls."},"requestId":{"type":"string","description":"Request correlation id from the auth layer."}},"additionalProperties":true}}}},"400":{"description":"Validation error (payload shape, ambiguous identifiers, deprecated keys, etc.).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Credential verification failed when `SECURITY_OPTIONS.SILENT_LOGIN_FAILURES` is false. Typical `error.code` values include `LOGIN_CHALLENGE_TIMESTAMP_INVALID` (login challenge Unix `timestamp` too far in the future vs server), `LOGIN_BASE64URL_SIGNATURE_INVALID` (base64url Ed25519 over UTF-8 identifier + canonical `timestamp_iso` from GET /api/login/timestamp did not verify), `RODIT_NOT_FOUND`, `RODIT_MISSING_METADATA`, `RODIT_NOT_LIVE`, `RODIT_REVOKED`, `RODIT_FAMILY_MISMATCH`, `SMART_CONTRACT_NOT_TRUSTED`, `SERVER_CONFIG_INCOMPLETE`, `LOGIN_MODE_POLICY_REJECTED_*`, `LOGIN_ERROR`, `INVALID_CREDENTIALS`. Response uses unified `error` object with optional `error.details.failureReason` mirroring `error.code`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"examples":{"loginChallengeTimestamp":{"summary":"Login challenge timestamp invalid","value":{"error":{"code":"LOGIN_CHALLENGE_TIMESTAMP_INVALID","message":"Error 102: Login attempt failed: Login challenge timestamp invalid: the Unix `timestamp` from your POST body is too far in the future relative to server time (use the `timestamp` from the same GET /api/login/timestamp response as your login signing payload; check clock skew).","details":{"failureReason":"LOGIN_CHALLENGE_TIMESTAMP_INVALID","failureMessage":"Login challenge timestamp invalid: the Unix `timestamp` from your POST body is too far in the future relative to server time (use the `timestamp` from the same GET /api/login/timestamp response as your login signing payload; check clock skew)."}},"requestId":"01HX9X0T9CS1EM0WQ7R6F5B2VY","timestamp":"2026-05-10T12:00:00.000Z"}},"loginBase64urlSignature":{"summary":"Login base64url signature invalid","value":{"error":{"code":"LOGIN_BASE64URL_SIGNATURE_INVALID","message":"Error 102: Login attempt failed: Login base64url signature invalid: Ed25519 verification failed for the base64url_signature over UTF-8 (roditid or accountid) + canonical timestamp_iso from the login challenge (GET /api/login/timestamp). Wrong key, wrong payload, or wrong encoding (must be base64url, not standard base64).","details":{"failureReason":"LOGIN_BASE64URL_SIGNATURE_INVALID","failureMessage":"Login base64url signature invalid: Ed25519 verification failed for the base64url_signature over UTF-8 (roditid or accountid) + canonical timestamp_iso from the login challenge (GET /api/login/timestamp). Wrong key, wrong payload, or wrong encoding (must be base64url, not standard base64)."}},"requestId":"01HX9X0T9CS1EM0WQ7R6F5B2VY","timestamp":"2026-05-10T12:00:00.000Z"}}}}}},"415":{"description":"UnsupportedMediaType - Content-Type must be application/json.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Server error during login.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Authentication service unavailable (e.g. RoditClient not initialized on this instance).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"operationId":"post_api_login"}},"/api/logout":{"post":{"summary":"RODiT client logout to invalidate the current session","security":[{"BearerAuth":[]}],"responses":{"200":{"description":"Logout successful; response body is produced by the RODiT SDK.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"415":{"description":"UnsupportedMediaType - Content-Type must be application/json.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Logout failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Authentication service unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"operationId":"post_api_logout"}},"/api/signclient":{"post":{"summary":"Request minting of a client Passport via SignPortal","description":"Frontend calls this endpoint with tamperproofed values and a locally computed minting fee. The server validates permissions against its own permissioned_routes, recomputes the minting fee, and then delegates to SignPortal to mint a new client IdentyClaw Passport. Optionally, caller can specify a token_id to use for signing; SignPortal will validate the token.\n\n**Errors:** Validation failures use `SIGNCLIENT_*` codes with client-safe `details` where helpful. HTTP 500 responses use generic messages (`SIGNCLIENT_FAILED`); upstream/SignPortal detail is logged with `requestId`, not returned in the body.","parameters":[{"$ref":"#/components/parameters/XRequestId"},{"$ref":"#/components/parameters/XCorrelationId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"tobesignedValues":{"type":"object","description":"Tamperproof payload describing the client RODiT to mint (includes permissioned_routes, max_requests, maxrq_window, not_after, serviceprovider_signature, etc.)."},"mintingfee":{"type":"string","description":"Total minting fee computed by the client; must match the server's own calculation or the request will be rejected."},"token_id":{"type":"string","pattern":"^[bcdfhjkmnpqrtvwxy]{12}$","description":"Optional: Caller-provided token ID (12 characters from safe charset). If not provided, SignPortal generates one. Must be valid facial encoding with correct checksum."},"tier":{"type":"string","enum":["personal","enterprise","collectible"],"description":"Optional: Pricing tier for fee calculation. If provided, the server uses tier-specific rate limits and fee structures instead of the formula-based calculation. Tiers: personal (48 requests/60s, variable fee), enterprise (4,999 requests/60s, 1,806 NEAR/year), collectible (496 requests/60s, 1,000 NEAR one-time, immortal)."}},"required":["tobesignedValues","mintingfee"]}}}},"responses":{"200":{"description":"Client RODiT minted successfully; response body is the SignPortal result including token_id, signatures, and all token metadata.","content":{"application/json":{"schema":{"type":"object","properties":{"fee_data":{"type":"object","properties":{"token_id":{"type":"string"},"mintingfee":{"type":"string"},"mintingfeeaccount":{"type":"string"}},"required":["token_id","mintingfee","mintingfeeaccount"]}},"required":["fee_data"],"additionalProperties":true}}}},"201":{"description":"Client RODiT minted successfully. Response includes SignPortal result (token_id, signatures, metadata) plus fee_data object containing the exact fee values that were signed (token_id, mintingfee, mintingfeeaccount). Frontend uses fee_data to reconstruct and verify the fee_signature_base64url.","content":{"application/json":{"schema":{"type":"object","properties":{"fee_data":{"type":"object","properties":{"token_id":{"type":"string"},"mintingfee":{"type":"string"},"mintingfeeaccount":{"type":"string"}},"required":["token_id","mintingfee","mintingfeeaccount"]}},"required":["fee_data"],"additionalProperties":true}}}},"400":{"description":"Validation error (`SIGNCLIENT_*`), including missing fields, invalid permissioned_routes, fee mismatch (`SIGNCLIENT_FEE_MISMATCH`), fee params (`SIGNCLIENT_FEE_PARAMS_INVALID`), or invalid token_id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"examples":{"feeMismatch":{"$ref":"#/components/examples/ErrorSignclientFeeMismatch"}}}}},"401":{"description":"Unauthorized - SignPortal authentication failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Privileged operations cannot be granted to client tokens (`SIGNCLIENT_PRIVILEGED_OPERATIONS_DENIED`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"415":{"description":"UnsupportedMediaType - Content-Type must be application/json.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Server or SignPortal error while attempting to mint the client token (`SIGNCLIENT_FAILED` or `SIGNCLIENT_INVALID_REQUEST` for validation-shaped failures). Response message is generic; use `requestId` in logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"examples":{"signclientFailed":{"$ref":"#/components/examples/ErrorSignclientFailed"}}}}},"502":{"description":"Nginx edge: application upstream unavailable (`UPSTREAM_UNAVAILABLE`). See `info.x-edgeGateway`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"examples":{"upstreamUnavailable":{"$ref":"#/components/examples/ErrorEdgeUpstreamUnavailable"}}}}}},"operationId":"post_api_signclient"}},"/api/mcp/resources":{"get":{"summary":"List available MCP resources (including OpenAPI schema)","description":"Returns all MCP resources. Agents should start with skills documentation (`doc:skills` or `skills:skills` → references/skills.md). Also includes OpenAPI schema, enrollment and troubleshooting guides, policies, NEAR onboarding, JSON-LD contexts, and concierge/discovery references (`doc:reference:a2a-onboarding-concierge`, `doc:reference:concierge-lobby-passport`, `doc:reference:a2a-registry-submission`). Use `/api/mcp/resource/{uri}` to fetch a resource.","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"List of available MCP resources.","content":{"application/json":{"schema":{"type":"object","properties":{"resources":{"type":"array","items":{"type":"object","properties":{"uri":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"}},"required":["uri","name","type"]}},"nextCursor":{"type":"string","nullable":true},"requestId":{"type":"string"}},"required":["resources","requestId"]}}}},"400":{"description":"Invalid parameter - limit must be a numeric value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Failed to list resources (`MCP_LIST_FAILED`). Generic message only; use `requestId` in logs for detail.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"examples":{"listFailed":{"$ref":"#/components/examples/ErrorMcpListFailed"}}}}}},"operationId":"get_api_mcp_resources"}},"/api/mcp/resource/{uri}":{"get":{"summary":"Retrieve a specific MCP resource by URI","description":"Fetches a resource by URI. **Start here:** `doc:skills` (canonical references/skills.md) or alias `skills:skills`. Also: `openapi:swagger`, `doc:reference:*` published docs (including `doc:reference:a2a-onboarding-concierge`, `doc:reference:concierge-lobby-passport`, `doc:reference:a2a-registry-submission`), `config:default`, `readme:main`, `health:status`, `guide:*`, `policy:*`, `onboarding:near`, `jsonld:*`, `did:resolve:{tokenId}`, `token:facial-categories`.","parameters":[{"name":"uri","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Requested resource returned.","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string"},"content":{},"requestId":{"type":"string"}},"required":["type","content","requestId"]}}}},"404":{"description":"Resource not found (`MCP_RESOURCE_NOT_FOUND`). `details.uri` identifies the requested resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"examples":{"notFound":{"$ref":"#/components/examples/ErrorMcpResourceNotFound"}}}}},"500":{"description":"Failed to get resource (`MCP_RESOURCE_FAILED`). Generic message only; use `requestId` in logs (not a 404).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"operationId":"get_api_mcp_resource_by_uri"}},"/api/mcp/schema":{"get":{"summary":"Return the OpenAPI schema used by this server","description":"Returns the OpenAPI 3.0 schema directly with required fields: openapi, info, paths, and components.","responses":{"200":{"description":"OpenAPI 3.0 schema document.","content":{"application/json":{"schema":{"type":"object","required":["openapi","info","paths","components"],"properties":{"openapi":{"type":"string"},"info":{"type":"object"},"paths":{"type":"object"},"components":{"type":"object"}}}}}}},"operationId":"get_api_mcp_schema"}},"/api/metrics":{"get":{"summary":"Get current performance metrics for this IDClawserver instance (admin only)","security":[{"BearerAuth":[]}],"responses":{"200":{"description":"Aggregated request and session metrics, including request counts and active sessions.","content":{"application/json":{"schema":{"type":"object","properties":{"requestCount":{"type":"integer"},"errorCount":{"type":"integer"},"requestsPerMinute":{"type":"number"},"currentLoadLevel":{"type":"string"},"requests":{"type":"object","properties":{"total":{"type":"integer"},"errors":{"type":"integer"},"perMinute":{"type":"number"}},"required":["total","errors","perMinute"]},"sessions":{"type":"object","properties":{"active":{"type":"integer"},"active_count":{"type":"integer"},"total":{"type":"integer"}},"required":["active","active_count","total"]},"active":{"type":"integer"},"endpointMetrics":{"type":"object","additionalProperties":true},"metrics":{"type":"object","additionalProperties":true},"timestamp":{"type":"string","format":"date-time"},"requestId":{"type":"string"},"sessions.active":{"type":"integer"},"sessions.active_count":{"type":"integer"},"sessionDebug":{"type":"object","additionalProperties":true}},"required":["requestId","timestamp","requests","sessions","active"]}}}},"401":{"description":"Unauthorized - JWT token is missing or invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Failed to retrieve metrics.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"operationId":"get_api_metrics"}},"/api/metrics/system":{"get":{"summary":"Get system resource metrics (CPU, memory, etc.) (admin only)","security":[{"BearerAuth":[]}],"responses":{"200":{"description":"Current system metrics as reported by the performance service.","content":{"application/json":{"schema":{"type":"object","properties":{"metrics":{"type":"object","additionalProperties":true},"timestamp":{"type":"integer","description":"Unix timestamp in milliseconds."},"requestId":{"type":"string"}},"required":["metrics","timestamp","requestId"]}}}},"401":{"description":"Unauthorized - JWT token is missing or invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Failed to retrieve system metrics.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"operationId":"get_api_metrics_system"}},"/api/metrics/reset":{"post":{"summary":"Reset performance metrics counters (admin only)","description":"Resets in-memory performance counters such as request counts and error counts. Caller must have appropriate admin permissions via RODiT permissioned_routes.","security":[{"BearerAuth":[]}],"responses":{"200":{"description":"Metrics reset successfully.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"timestamp":{"type":"integer","description":"Unix timestamp in milliseconds."},"requestId":{"type":"string"}},"required":["message","timestamp","requestId"]}}}},"403":{"description":"Permission denied (admin permission required).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"415":{"description":"UnsupportedMediaType - Content-Type must be application/json.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Failed to reset metrics.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"operationId":"post_api_metrics_reset"}},"/api/metrics/debug":{"get":{"summary":"Debug endpoint for metrics subsystem (admin only)","description":"Returns a snapshot of performance metrics and client information for debugging.","security":[{"BearerAuth":[]}],"responses":{"200":{"description":"Debug information for metrics subsystem.","content":{"application/json":{"schema":{"type":"object","properties":{"debug":{"type":"object","properties":{"hasRoditClient":{"type":"boolean"},"clientType":{"type":"string"},"hasPerformanceService":{"type":"boolean"},"performanceServiceType":{"type":"string"},"metricsSnapshot":{"type":"object","additionalProperties":true},"timestamp":{"type":"integer","description":"Unix timestamp in milliseconds."},"requestProcessingTime":{"type":"integer"}},"required":["hasRoditClient","clientType","hasPerformanceService","performanceServiceType","metricsSnapshot","timestamp","requestProcessingTime"]},"requestId":{"type":"string"}},"required":["debug","requestId"]}}}},"401":{"description":"Unauthorized - JWT token is missing or invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Failed to retrieve debug information.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"operationId":"get_api_metrics_debug"}},"/api/sessions/list_all":{"get":{"summary":"List all active sessions (admin)","description":"Returns a list of active sessions managed by the RODiT SessionManager. Intended for administrative tooling.","security":[{"BearerAuth":[]}],"responses":{"200":{"description":"List of active sessions with basic metadata.","content":{"application/json":{"schema":{"type":"object","properties":{"sessions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"roditId":{"type":"string"},"ownerId":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time"},"lastAccessedAt":{"type":"string","format":"date-time"},"status":{"type":"string"}},"required":["id","roditId","ownerId","createdAt","expiresAt","lastAccessedAt","status"]}},"count":{"type":"integer"},"timestamp":{"type":"string","format":"date-time"}},"required":["sessions","count","timestamp"]}}}},"401":{"description":"Unauthorized - JWT token is missing or invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Failed to retrieve sessions (`SESSIONS_RETRIEVE_FAILED`). Generic message only; use `requestId` in logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"examples":{"retrieveFailed":{"$ref":"#/components/examples/ErrorSessionsRetrieveFailed"}}}}}},"operationId":"get_api_sessions_list_all"}},"/api/sessions/cleanup":{"post":{"summary":"Cleanup expired sessions (admin)","description":"Triggers cleanup of expired sessions and returns cleanup statistics.","security":[{"BearerAuth":[]}],"responses":{"200":{"description":"Session cleanup completed with statistics.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"stats":{"type":"object","properties":{"removedCount":{"type":"integer"},"activeSessions":{"type":"integer"},"totalSessions":{"type":"integer"},"cleanupResult":{"type":"object","additionalProperties":true}},"required":["removedCount","activeSessions","totalSessions","cleanupResult"]},"requestId":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["success","message","stats","requestId","timestamp"]}}}},"415":{"description":"UnsupportedMediaType - Content-Type must be application/json.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Session cleanup failed (`SESSION_CLEANUP_FAILED`). Generic message only; use `requestId` in logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"operationId":"post_api_sessions_cleanup"}},"/api/sessions/revoke":{"post":{"summary":"Revoke a specific session (admin)","description":"Terminates a specific session by ID. Caller must have permission to this endpoint in their RODiT permissioned_routes.","security":[{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"sessionId":{"type":"string","description":"Identifier of the session to revoke."},"reason":{"type":"string","description":"Optional human-readable reason for revocation."}},"required":["sessionId"]}}}},"responses":{"200":{"description":"Session terminated successfully.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"sessionId":{"type":"string"},"reason":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["message","sessionId","reason","timestamp"]}}}},"400":{"description":"Missing or invalid sessionId.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Session not found or already terminated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"415":{"description":"UnsupportedMediaType - Content-Type must be application/json.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Failed to terminate session (`SESSION_TERMINATION_FAILED`). Generic message only; `details.sessionId` may be present. Use `requestId` in logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"operationId":"post_api_sessions_revoke"}},"/mcp":{"get":{"summary":"Streamable MCP transport endpoint","description":"Streamable HTTP endpoint for Model Context Protocol clients. This is the MCP transport path (served from `/mcp`), distinct from the REST discovery/resource endpoints under `/api/mcp/*`.\n\n**Accept Header Requirement**: GET requests MUST include `text/event-stream` in the Accept header (e.g., `Accept: text/event-stream` or `Accept: text/event-stream, application/json`). Requests without this will receive HTTP 406 Not Acceptable.","tags":["MCP"],"parameters":[{"name":"Accept","in":"header","required":true,"schema":{"type":"string","example":"text/event-stream"},"description":"MUST include text/event-stream as a supported content type per MCP Streamable HTTP specification."}],"responses":{"200":{"description":"MCP response payload. Shape depends on the MCP request method and transport exchange."},"400":{"description":"Missing or invalid MCP transport session. **GET-only:** returns application `ErrorResponse` with code `MCP_TRANSPORT_SESSION_REQUIRED` (curl/raw-GET probe guard). POST and DELETE without a session do **not** use this code — they return JSON-RPC `-32000` per MCP Streamable HTTP. See `GET /.well-known/mcp` for REST discovery paths.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"examples":{"transportSessionRequired":{"$ref":"#/components/examples/ErrorMcpTransportSessionRequired"}}}}},"406":{"description":"Not Acceptable: Client must include text/event-stream in the Accept header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpJsonRpcErrorResponse"}}}},"415":{"description":"Unsupported Media Type: Invalid Content-Type header."},"426":{"description":"Upgrade Required: Protocol upgrade needed."},"500":{"description":"Server-side MCP transport error."}},"operationId":"get_mcp_streamable_transport"},"post":{"summary":"Streamable MCP transport endpoint","description":"Streamable HTTP endpoint for Model Context Protocol clients. This is the MCP transport path (served from `/mcp`), distinct from the REST discovery/resource endpoints under `/api/mcp/*`.\n\n**Accept Header Requirement**: POST requests MUST include BOTH `application/json` AND `text/event-stream` in the Accept header (e.g., `Accept: application/json, text/event-stream`). Requests without both will receive HTTP 406 Not Acceptable.\n\n**Content-Type Requirement**: POST requests MUST use `Content-Type: application/json`.","tags":["MCP"],"parameters":[{"name":"Accept","in":"header","required":true,"schema":{"type":"string","example":"application/json, text/event-stream"},"description":"MUST include both application/json and text/event-stream as supported content types per MCP Streamable HTTP specification."},{"name":"Content-Type","in":"header","required":true,"schema":{"type":"string","example":"application/json"},"description":"MUST be application/json."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"MCP JSON-RPC request payload"}}}},"responses":{"200":{"description":"MCP response payload. Shape depends on the MCP request method and transport exchange."},"400":{"description":"Invalid MCP request payload or protocol framing. Returns JSON-RPC 2.0 error code `-32000` (not `MCP_TRANSPORT_SESSION_REQUIRED`). Typical cases: non-initialize POST without `Mcp-Session-Id`, malformed JSON-RPC body, or unsupported protocol version. A bare POST probe without a session is expected to receive `-32000`, not the GET curl-guard `ErrorResponse`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpJsonRpcErrorResponse"},"examples":{"missingSession":{"$ref":"#/components/examples/McpPostMissingSession"}}}}},"406":{"description":"Not Acceptable: Client must include both application/json and text/event-stream in the Accept header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpJsonRpcErrorResponse"}}}},"415":{"description":"Unsupported Media Type: Content-Type must be application/json.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpJsonRpcErrorResponse"}}}},"426":{"description":"Upgrade Required: Protocol upgrade needed."},"500":{"description":"Server-side MCP transport error."}},"operationId":"post_mcp_streamable_transport"},"delete":{"summary":"Terminate MCP streamable HTTP session","description":"Closes an established MCP transport session. Requires a valid `Mcp-Session-Id` header for the session to terminate. Returns JSON-RPC `-32000` when no valid session is present (same family as POST without session).","tags":["MCP"],"responses":{"200":{"description":"Session terminated or DELETE handled by the active transport."},"400":{"description":"Missing or invalid MCP transport session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpJsonRpcErrorResponse"},"examples":{"missingSession":{"$ref":"#/components/examples/McpPostMissingSession"}}}}},"500":{"description":"Server-side MCP transport error."}},"operationId":"delete_mcp_streamable_transport"}},"/api/identity/token/{tokenId}/public":{"get":{"summary":"Get redacted public passport profile","description":"Anonymous read of a sanitized passport profile after HOLA verification or for display/kiosk use. Returns display name, creature, avatar, contact URIs, facial encoding, and `not_after` — no JWT required. Does not expose full DN, tax, or address fields available on `GET /api/identity/token/{tokenId}/full`.\n\nAlternatively, set `includeProfile: true` on `POST /api/identity/verify` to receive the same profile inline when `verified: true`.\n\n⚠️ **DISCLAIMER**: Metadata is self-declared by the passport holder.\n\n**Authentication:** Public endpoint (rate-limited at the nginx edge per `info.x-publicRateLimit`).","tags":["Identity"],"parameters":[{"name":"tokenId","in":"path","required":true,"schema":{"$ref":"#/components/schemas/TokenId"},"description":"IdentyClaw Passport ID."}],"responses":{"200":{"description":"Redacted public passport profile.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/PublicPassportProfile"},{"type":"object","properties":{"face":{"type":"object","nullable":true,"description":"Facial description decoded from tokenId."},"notAfter":{"type":"string","nullable":true,"description":"Passport expiry from on-chain metadata."},"disclaimer":{"type":"string"},"requestId":{"type":"string"}}}]}}}},"404":{"description":"Passport not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Lookup failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"operationId":"get_api_identity_token_tokenId_public"}},"/api/identity/token/{tokenId}/full":{"get":{"summary":"Get full identity with DN and facial encoding","description":"Returns the combined Distinguished Name (DN), facial description, and peer-routable metadata for a token. Combines data from both /dn and /face endpoints in a single response. Includes `metadata.webhook_url` when set on-chain (OpenClaw gateway base URL for inbound notifications).\n\n⚠️ **DISCLAIMER**: The DN metadata including creature, name, contact URI, address, and other attributes are self-declared by the agent. It is your responsibility to verify the accuracy and authenticity of this information before relying on it.","tags":["Identity"],"security":[{"BearerAuth":[]}],"parameters":[{"name":"tokenId","in":"path","required":true,"schema":{"type":"string"},"description":"IdentyClaw Passport ID."}],"responses":{"200":{"description":"Full identity with DN and facial encoding.","content":{"application/json":{"schema":{"type":"object","properties":{"tokenId":{"type":"string","description":"The requested token ID."},"dn":{"type":"object","nullable":true,"description":"Parsed Distinguished Name information.","properties":{"raw":{"type":"string","description":"Raw DN string from token metadata."},"nameNotSharedWithFamily":{"type":"string","nullable":true},"nameSharedWithFamily":{"type":"string","nullable":true},"displayName":{"type":"string","nullable":true},"contactUri":{"type":"string","nullable":true,"description":"Self-declared routing hint in format scheme:authority:identifier (e.g. email:example.com:user@example.com). Standard schemes: email, twitter, telegram, phone, linkedin, github. Extended schemes (community-documented): a2a, discord, fediverse, matrix, nostr, sms, irc, dns, onion, nfc, caldav. Full tables and registration guidance: MCP doc:reference:token-metadata § ContactURI."},"taxResidence":{"type":"string","nullable":true},"inceptDateTime":{"type":"string","nullable":true},"inceptPlace":{"type":"string","nullable":true},"taxPayerCode":{"type":"string","nullable":true},"address":{"type":"string","nullable":true},"creature":{"type":"string","nullable":true},"avatarUrl":{"type":"string","nullable":true},"emojiUrl":{"type":"string","nullable":true},"allAttributes":{"type":"object","description":"All DN attributes as key-value pairs"}}},"face":{"type":"object","nullable":true,"description":"Facial description decoded from tokenId.","properties":{"checksumValid":{"type":"boolean"},"categories":{"type":"object","properties":{"skin_tones":{"type":"object","properties":{"value":{"type":"string"},"index":{"type":"integer"},"letter":{"type":"string"}}},"regional_bone_structure":{"type":"object","properties":{"value":{"type":"string"},"index":{"type":"integer"},"letter":{"type":"string"}}},"face_shape":{"type":"object","properties":{"value":{"type":"string"},"index":{"type":"integer"},"letter":{"type":"string"}}},"age_related":{"type":"object","properties":{"value":{"type":"string"},"index":{"type":"integer"},"letter":{"type":"string"}}},"eyes":{"type":"object","properties":{"value":{"type":"string"},"index":{"type":"integer"},"letter":{"type":"string"}}},"eyebrow_style":{"type":"object","properties":{"value":{"type":"string"},"index":{"type":"integer"},"letter":{"type":"string"}}},"overall_structure":{"type":"object","properties":{"value":{"type":"string"},"index":{"type":"integer"},"letter":{"type":"string"}}},"nose":{"type":"object","properties":{"value":{"type":"string"},"index":{"type":"integer"},"letter":{"type":"string"}}},"lips":{"type":"object","properties":{"value":{"type":"string"},"index":{"type":"integer"},"letter":{"type":"string"}}},"skin_conditions":{"type":"object","properties":{"value":{"type":"string"},"index":{"type":"integer"},"letter":{"type":"string"}}},"hair_color":{"type":"object","properties":{"value":{"type":"string"},"index":{"type":"integer"},"letter":{"type":"string"}}}}}}},"metadata":{"type":"object","nullable":true,"description":"Peer-routable token metadata for collaboration.","properties":{"webhook_url":{"type":"string","nullable":true,"description":"Webhook endpoint base URL for inbound notifications (for example OpenClaw gateway host)."}}},"requestId":{"type":"string","description":"Request correlation identifier."},"disclaimer":{"type":"string","description":"Warning that DN metadata including creature, name, contact URI, and other attributes are self-declared and should be verified"}}}}}},"404":{"description":"Token not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Failed to retrieve identity.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"operationId":"get_api_identity_token_by_tokenId_full"}},"/.well-known/terms-of-service":{"get":{"summary":"Terms of Service","description":"Returns the Terms of Service document for the IdentyClaw API. Supports content negotiation: text/markdown (default) or text/html for browser viewing.","tags":["Policies"],"responses":{"200":{"description":"Terms of Service document.","content":{"text/markdown":{"schema":{"type":"string"}},"text/html":{"schema":{"type":"string"}}}}},"operationId":"get_well_known_terms_of_service"}},"/.well-known/privacy-policy":{"get":{"summary":"Privacy Policy","description":"Returns the Privacy Policy document describing how user data is collected, used, and protected.","tags":["Policies"],"responses":{"200":{"description":"Privacy Policy document.","content":{"text/markdown":{"schema":{"type":"string"}},"text/html":{"schema":{"type":"string"}}}}},"operationId":"get_well_known_privacy_policy"}},"/.well-known/data-retention":{"get":{"summary":"Data Retention Policy","description":"Returns the Data Retention Policy describing how long different types of data are stored and deletion procedures.","tags":["Policies"],"responses":{"200":{"description":"Data Retention Policy document.","content":{"text/markdown":{"schema":{"type":"string"}},"text/html":{"schema":{"type":"string"}}}}},"operationId":"get_well_known_data_retention"}},"/.well-known/why-identyclaw":{"get":{"summary":"Why IdentyClaw - Value Proposition","description":"Returns the value proposition document explaining how IdentyClaw enables AI-to-agent interaction, including endpoint benefits, use cases, security advantages, and complete agent communication flows.","tags":["Policies"],"responses":{"200":{"description":"Why IdentyClaw value proposition document.","content":{"text/markdown":{"schema":{"type":"string"}},"text/html":{"schema":{"type":"string"}}}}},"operationId":"get_well_known_why_identyclaw"}},"/.well-known/did/rodit/{tokenId}":{"get":{"summary":"Resolve did:rodit DID document","description":"EXPERIMENTAL: Returns the DID document for a specific IdentyClaw Passport ID. This endpoint requires authentication because DID resolution is a paid service. This endpoint is experimental and may change in future versions.","tags":["DID"],"security":[{"BearerAuth":[]}],"parameters":[{"name":"tokenId","in":"path","required":true,"schema":{"type":"string"},"description":"IdentyClaw Passport ID."}],"responses":{"200":{"description":"DID document for the requested token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DidDocument"}}}},"400":{"description":"Invalid DID or token identifier (`INVALID_DID`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"examples":{"invalidDid":{"$ref":"#/components/examples/ErrorInvalidDid"}}}}},"404":{"description":"Token not found (`DID_NOT_FOUND`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"DID resolution failed (`DID_RESOLUTION_FAILED`). Generic message only; use `requestId` in logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"examples":{"resolutionFailed":{"$ref":"#/components/examples/ErrorDidResolutionFailed"}}}}}},"operationId":"get_well_known_did_rodit_by_tokenId"}},"/.well-known/did/resolve":{"get":{"summary":"Resolve DID string","description":"EXPERIMENTAL: Resolves either did:rodit or did:web identifiers into DID documents. This endpoint is experimental and may change in future versions.","tags":["DID"],"security":[{"BearerAuth":[]}],"parameters":[{"name":"did","in":"query","required":true,"schema":{"type":"string"},"description":"DID string to resolve (did:rodit or did:web forms)."}],"responses":{"200":{"description":"Resolved DID document.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DidDocument"}}}},"400":{"description":"Invalid DID supplied (`INVALID_DID`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"examples":{"invalidDid":{"$ref":"#/components/examples/ErrorInvalidDid"}}}}},"404":{"description":"Token not found for the supplied DID (`DID_NOT_FOUND`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"DID resolution failed (`DID_RESOLUTION_FAILED`). Generic message only; use `requestId` in logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"examples":{"resolutionFailed":{"$ref":"#/components/examples/ErrorDidResolutionFailed"}}}}}},"operationId":"get_well_known_did_resolve"}},"/.well-known/did/web/token/{tokenId}":{"get":{"summary":"Resolve did:web DID document","description":"Returns the did:web representation for a token hosted by this service, providing interoperability with web DID resolvers.","tags":["DID"],"security":[{"BearerAuth":[]}],"parameters":[{"name":"tokenId","in":"path","required":true,"schema":{"type":"string"},"description":"IdentyClaw Passport ID."}],"responses":{"200":{"description":"did:web DID document for the requested token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DidDocument"}}}},"400":{"description":"Invalid DID or token identifier (`INVALID_DID`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"examples":{"invalidDid":{"$ref":"#/components/examples/ErrorInvalidDid"}}}}},"404":{"description":"Token not found (`DID_NOT_FOUND`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"DID resolution failed (`DID_RESOLUTION_FAILED`). Generic message only; use `requestId` in logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"examples":{"resolutionFailed":{"$ref":"#/components/examples/ErrorDidResolutionFailed"}}}}}},"operationId":"get_well_known_did_web_token_by_tokenId"}},"/.well-known/did/web/token/{tokenId}/did.json":{"get":{"summary":"Resolve did:web JSON DID document","description":"EXPERIMENTAL: Returns the JSON DID document commonly served at did.json endpoints for did:web identifiers. This endpoint is experimental and may change in future versions.","tags":["DID"],"security":[{"BearerAuth":[]}],"parameters":[{"name":"tokenId","in":"path","required":true,"schema":{"type":"string"},"description":"IdentyClaw Passport ID."}],"responses":{"200":{"description":"did:web DID document for the requested token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DidDocument"}}}},"400":{"description":"Invalid DID or token identifier (`INVALID_DID`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"examples":{"invalidDid":{"$ref":"#/components/examples/ErrorInvalidDid"}}}}},"404":{"description":"Token not found (`DID_NOT_FOUND`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"DID resolution failed (`DID_RESOLUTION_FAILED`). Generic message only; use `requestId` in logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"examples":{"resolutionFailed":{"$ref":"#/components/examples/ErrorDidResolutionFailed"}}}}}},"operationId":"get_well_known_did_web_token_by_tokenId_did_json"}},"/api/testhola":{"post":{"summary":"Test HOLA message validation and respond with server HOLA","description":"Self-testing endpoint to validate YOUR HOLA message generation before sending to peers. Use `/api/testhola` for self-testing; use `/api/identity/verify` for peer verification.\n\n**Authentication Scope:** `/api/testhola` is JWT-protected as an API endpoint, but HOLA validation outcomes are based on the submitted HOLA fields and cryptographic checks (not caller-token equality).\n\n**When to Use:**\n- `/api/testhola`: Self-testing to validate YOUR HOLA generation before sending to peers\n- `/api/identity/verify`: Peer verification to verify HOLA received from ANOTHER agent\n\nValidation is fail-closed and covers: format sanity, checksum, timestamp freshness, nonce replay, sender token existence, token expiration (`not_after`), blockchain/public-key availability, and Ed25519 signature verification.\n\nSupports both formats:\n- Standard: HOLA/<recipient>/<tokenId>/<ISO8601-timestamp>/<noncets-hex>/API.IDENTYCLAW.COM/<base32-signature>/<checksum>\n- Subagent: HOLA/<recipient>/<delegateID>/<issuer_tokenId>/<publicKey-base32>/<ISO8601-timestamp>/<noncets-hex>/API.IDENTYCLAW.COM/<base32-signature>/<checksum>\n\nOn rejection, the response includes machine-readable error details under `error.details` with:\n- `stage`: validation stage that failed\n- `reasonCode`: stable failure classifier for automated clients/tests\n- `hint`: remediation hint for common failure modes\n\nEarly shape-lint stages (before full parsing) may set `reasonCode` to: `payload_is_json_object`, `unix_millis_timestamp`, `signature_not_base32`, `placeholder_signature`. Each error includes `details.documentation` (relative reference paths) and `details.example` (a corrected snippet for the offending field) so agents can self-correct without external lookup.\n\n**Webhook Emission Behavior:** The server emits `/api/testhola` webhook events to the authenticated peer's configured webhook URL at `/hooks/wake` and `/hooks/agent` only when `WEBHOOK_TEST_ENABLED=true`.\n\nEmitted events:\n- `testhola_validation_failed`\n- `testhola_response_failed`\n- `testhola_validation_success`","tags":["Testing"],"security":[{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"hola":{"type":"string","description":"Request field for the HOLA message to validate. Supports standard and subagent formats. Standard: HOLA/<recipient>/<tokenId>/<ISO8601-timestamp>/<noncets-hex>/API.IDENTYCLAW.COM/<base32-signature>/<checksum>. Subagent: HOLA/<recipient>/<delegateID>/<issuer_tokenId>/<publicKey-base32>/<ISO8601-timestamp>/<noncets-hex>/API.IDENTYCLAW.COM/<base32-signature>/<checksum>. Base32 variant is RFC 4648 Base32 (A-Z2-7, uppercase canonical form, optional '=' padding on input). Checksum is one letter from ABCDEFGHJKMNPQRSTUVWXYZ (mod 23 over canonical prefix + signature + '/').","example":"HOLA/MUNDO/pkcnjdbdefcp/2026-04-14T14:00:00.000Z/4F9A3C7E2D1B9A4C8E7F6A5B4C3D2E1F/API.IDENTYCLAW.COM/MEQW4YLTORUW63THMV2GC3DBNVRWQ/R"},"expectedRecipient":{"type":"string","description":"Optional HOLA recipient token ID. When provided and it matches the HOLA recipient field, suppresses the RECIPIENT_MISMATCH warning (common when verifying a peer's HOLA while authenticated as yourself, or when testing with recipient=MUNDO)."}},"required":["hola"]}}}},"responses":{"200":{"description":"HOLA message is valid. Server responds with its own valid HOLA.","content":{"application/json":{"schema":{"type":"object","properties":{"valid":{"type":"boolean","description":"Whether the client's HOLA message is valid.","example":true},"peerTokenId":{"type":"string","description":"The token ID from the client's HOLA message."},"destinatary":{"type":"string","description":"Recipient extracted from the client's HOLA message."},"peerVerified":{"type":"boolean","description":"Whether the client's identity was verified.","example":true},"hola":{"type":"string","description":"The server's valid HOLA response signed with roditclient's key pair."},"serverTokenId":{"type":"string","description":"The server's token ID used in the response HOLA."},"serverDestinatary":{"type":"string","description":"Recipient used by the server in its response HOLA."},"serverTimestamp":{"type":"string","format":"date-time","description":"The ISO 8601 timestamp used in the server's HOLA response."},"checks":{"type":"object","properties":{"formatValid":{"type":"boolean"},"checksumValid":{"type":"boolean"},"timestampValid":{"type":"boolean"},"timestampFresh":{"type":"boolean"},"noncetsValid":{"type":"boolean"},"nonceReplaySafe":{"type":"boolean"},"tokenExists":{"type":"boolean"},"tokenActive":{"type":"boolean"},"signatureValid":{"type":"boolean"},"publicKeyAvailable":{"type":"boolean"}}},"requestId":{"type":"string","description":"Request correlation identifier."},"warnings":{"type":"array","description":"Optional warning entries for suspicious but non-fatal conditions such as recipient mismatch.","items":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}}}}}},"400":{"description":"HOLA message is invalid, stale, or signature verification failed. Returns detailed reason.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"examples":{"checksumInvalid":{"summary":"Checksum invalid","value":{"error":{"code":"HOLA_VALIDATION_FAILED","message":"Invalid checksum: expected A, got B","details":{"valid":false,"stage":"format_checksum_and_payload_validation","reasonCode":"checksum_invalid","hint":"Recompute checksum: sum UTF-16 code units over canonical uppercase prefix through signature plus trailing '/', mod 23, index into ABCDEFGHJKMNPQRSTUVWXYZ.","checks":{"formatValid":true,"checksumValid":false,"timestampValid":true,"noncetsValid":true}}},"requestId":"01HX7HBE4X7AMP9Q4MP2YE2C41","timestamp":"2026-04-15T07:47:00.000Z"}},"nonceReplay":{"summary":"Nonce replay detected","value":{"error":{"code":"HOLA_VALIDATION_FAILED","message":"Nonce replay detected within allowed timestamp window","details":{"valid":false,"stage":"nonce_replay_validation","reasonCode":"nonce_replay","hint":"Request a new nonce and sign a new hola message.","checks":{"formatValid":true,"checksumValid":true,"timestampValid":true,"noncetsValid":true,"nonceReplaySafe":false}}},"requestId":"01HX7HBE4X7AMP9Q4MP2YE2C41","timestamp":"2026-04-15T07:47:00.000Z"}},"timestampStale":{"summary":"Timestamp stale/future","value":{"error":{"code":"HOLA_TIMESTAMP_INVALID","message":"Timestamp stale or future: age is 601s, max allowed is 300s","details":{"valid":false,"stage":"timestamp_freshness_validation","reasonCode":"timestamp_stale_or_future","hint":"Generate a fresh hola using a recent timestamp within the allowed age window.","ageSeconds":601,"maxAgeSeconds":300}},"requestId":"01HX7HBE4X7AMP9Q4MP2YE2C41","timestamp":"2026-04-15T07:47:00.000Z"}},"signaturePublicKeyUnavailable":{"summary":"Public key unavailable","value":{"error":{"code":"HOLA_SIGNATURE_INVALID","message":"Public key unavailable or invalid length","details":{"valid":false,"stage":"signature_verification","reasonCode":"public_key_unavailable","hint":"Verify signer public key encoding, parent token status, and exact signed payload.","tokenExists":true,"tokenActive":true,"tokenExpired":false,"publicKeyAvailable":false}},"requestId":"01HX7HBE4X7AMP9Q4MP2YE2C41","timestamp":"2026-04-15T07:47:00.000Z"}},"signatureMismatch":{"summary":"Signature mismatch","value":{"error":{"code":"HOLA_SIGNATURE_INVALID","message":"Signature verification failed","details":{"valid":false,"stage":"signature_verification","reasonCode":"signature_mismatch","hint":"Verify signer public key encoding, parent token status, and exact signed payload.","tokenExists":true,"tokenActive":true,"tokenExpired":false,"publicKeyAvailable":true}},"requestId":"01HX7HBE4X7AMP9Q4MP2YE2C41","timestamp":"2026-04-15T07:47:00.000Z"}},"tokenExpired":{"summary":"Sender token expired","value":{"error":{"code":"HOLA_SIGNATURE_INVALID","message":"Token expired (not_after)","details":{"valid":false,"stage":"signature_verification","reasonCode":"token_expired","hint":"Verify signer public key encoding, parent token status, and exact signed payload.","tokenExists":true,"tokenActive":false,"tokenExpired":true,"publicKeyAvailable":false}},"requestId":"01HX7HBE4X7AMP9Q4MP2YE2C41","timestamp":"2026-04-15T07:47:00.000Z"}},"subagentPublicKeyDecodeFailed":{"summary":"Subagent public key decode failed","value":{"error":{"code":"HOLA_SIGNATURE_INVALID","message":"Public key unavailable or invalid length: Invalid base32 input","details":{"valid":false,"stage":"signature_verification","reasonCode":"public_key_decode_failed","hint":"Verify signer public key encoding, parent token status, and exact signed payload.","isSubagentFormat":true,"delegateId":"researcher-sub-001","issuerTokenId":"PKCNJDBDEFCP","tokenExists":true,"tokenActive":true,"tokenExpired":false,"publicKeyAvailable":false}},"requestId":"01HX7HBE4X7AMP9Q4MP2YE2C41","timestamp":"2026-04-15T07:47:00.000Z"}}}}}},"500":{"description":"Server error or unable to generate HOLA response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"operationId":"post_api_testhola"}}}}