Abstract
The Human Authorization Protocol (HAP) defines a standard mechanism for humans to delegate scoped, time-bounded, and revocable permissions to autonomous AI agents. Each authorization is encoded as a W3C Verifiable Credential 2.0 containing the agent’s identifier, a cryptographic hash of the authorizing principal’s identity, a structured permission scope, and a DataIntegrityProof signature from the issuing authority.
HAP addresses a critical gap in the emerging AI commerce ecosystem: there is no standard way for a human to provably authorize an AI agent to act on their behalf, nor for a third party to verify that such authorization exists, what it permits, and whether it remains valid. HAP fills this gap with a credential that is machine-verifiable, privacy-preserving, and compatible with existing W3C decentralized identity standards.
Motivation
As AI agents transition from answering questions to executing transactions — booking reservations, purchasing goods, managing subscriptions — the question of authorization becomes unavoidable. A business receiving a purchase request from an AI agent has no standard way to determine whether the agent has permission to make that purchase, what spending limits apply, or which human is ultimately accountable.
Existing authorization frameworks (OAuth 2.0, API keys, session tokens) were designed for human-to-machine or machine-to-machine interactions within a single trust domain. They do not address the cross-domain, multi-party nature of agentic commerce where an agent acting on behalf of Human A interacts with Business B’s systems, and Business B needs to verify the agent’s authority without direct access to Human A’s identity provider.
Design Goals
Protocol Overview
HAP operates through a four-phase lifecycle: issuance, presentation, verification, and revocation. The issuing authority (Nordax AI) acts as the trusted credential issuer, signing each authorization with an Ed25519 key pair registered under its DID:web identifier.
Phase 1: Issuance
An authenticated human requests authorization for a specific agent by providing the agent’s DID, the desired permission scope, and a verification level. The issuer validates the request, hashes the principal’s identity with SHA-256, constructs a W3C VC 2.0 credential, signs it with a DataIntegrityProof, and persists it to the authorization store.
Phase 2: Presentation
The agent presents the credential to a relying party (e.g., a business receiving a transaction request). The credential is self-contained — the relying party does not need to contact the issuer to parse the authorization scope, agent identity, or expiration. The credential is transmitted as a JSON-LD document with Content-Type: application/vc+ld+json.
Phase 3: Verification
The relying party verifies the credential through three independent checks: (1) cryptographic signature validation against the issuer’s public key resolved via DID:web, (2) expiration check against the expirationDate field, and (3) real-time status check against the credentialStatus.id endpoint to confirm the credential has not been revoked or suspended.
Phase 4: Revocation
The authorizing human can revoke or suspend a credential at any time through the issuer’s API. Revocation is immediate — subsequent status checks return revoked and verification fails. Suspended credentials can be reactivated if they have not expired; revoked credentials cannot.
Credential Structure
Every HAP credential conforms to the W3C Verifiable Credentials Data Model 2.0. The credential envelope contains the standard VC fields (@context, id, type, issuer, issuanceDate, expirationDate) plus a credentialSubject specific to HAP and a proof using the Data Integrity specification.
{
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://w3id.org/security/data-integrity/v2",
"https://nordax.ai/ns/hap/v1"
],
"id": "urn:uuid:a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"type": [
"VerifiableCredential",
"HAPAuthorizationCredential"
],
"issuer": {
"id": "did:web:nordax.ai",
"name": "Nordax AI"
},
"issuanceDate": "2026-03-16T12:00:00.000Z",
"expirationDate": "2026-04-15T12:00:00.000Z",
"credentialSubject": {
"id": "did:web:agent.example.com",
"type": "AgentAuthorization",
"agent_id": "did:web:agent.example.com",
"agent_type": "ai_assistant",
"principal_hash": "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9",
"authorization_scope": {
"actions": ["read", "recommend", "transact"],
"categories": ["dining", "retail"],
"spendingLimits": {
"currency": "USD",
"maxPerTransaction": 500,
"maxDaily": 2000
},
"geographicRestrictions": {
"allowedRegions": ["US-NY", "US-CA"],
"deniedRegions": []
}
},
"verification_level": "enhanced"
},
"credentialStatus": {
"id": "https://nordax.ai/api/hap/urn%3Auuid%3Aa1b2c3d4-e5f6-7890-abcd-ef1234567890/status",
"type": "NordaxHAPStatus2026"
},
"proof": {
"type": "DataIntegrityProof",
"created": "2026-03-16T12:00:00.000Z",
"verificationMethod": "did:web:nordax.ai#key-1",
"proofPurpose": "assertionMethod",
"proofValue": "z3MvGcVxzBMqJanZ4X8S9T..."
}
}Field Reference
| Field | Type | Description |
|---|---|---|
| @context | string[] | JSON-LD contexts: W3C VC 2.0, Data Integrity v2, HAP v1 namespace |
| id | string | Credential identifier — urn:uuid:{UUID v4} |
| type | string[] | Always ["VerifiableCredential", "HAPAuthorizationCredential"] |
| issuer.id | string | DID:web of the issuing authority (did:web:nordax.ai) |
| issuer.name | string | Human-readable issuer name |
| issuanceDate | ISO 8601 | Timestamp when the credential was issued |
| expirationDate | ISO 8601 | Timestamp when the credential expires |
| credentialSubject.id | string | DID of the authorized agent |
| credentialSubject.agent_type | enum | ai_assistant | api_client | automation | service_agent |
| credentialSubject.principal_hash | string | SHA-256 hex digest of the authorizing principal's identity |
| credentialSubject.authorization_scope | object | Structured permission scope (see Scope Definition) |
| credentialSubject.verification_level | enum | basic (7d) | enhanced (30d) | full (90d) |
| credentialStatus.id | URL | Endpoint for real-time status checks |
| credentialStatus.type | string | NordaxHAPStatus2026 |
| proof.type | string | DataIntegrityProof |
| proof.verificationMethod | string | DID URL resolving to the signing public key |
| proof.proofPurpose | string | assertionMethod |
| proof.proofValue | string | Base64url-encoded Ed25519 signature |
Scope Definition
The authorization_scope object defines exactly what the agent is permitted to do. Scopes are structured rather than string-based, enabling fine-grained access control that can be parsed and enforced by relying parties without custom logic.
Actions
The actions array is required and must contain at least one action string. Actions are intentionally simple verbs that relying parties can match against their own permission models.
| Action | Description |
|---|---|
| read | Query entity data, retrieve listings, access public information |
| recommend | Include entities in agent-generated recommendations to users |
| transact | Initiate transactions (bookings, reservations, orders) on behalf of the principal |
| purchase | Execute financial transactions up to the defined spending limits |
| book | Create reservations, appointments, or bookings |
Categories
Optional array of business category strings (e.g., "dining", "retail", "healthcare"). When present, the authorization applies only to entities matching the specified categories. When absent, the authorization applies to all categories.
Spending Limits
Optional object constraining financial transactions. Relying parties accepting payments MUST enforce these limits when present.
{
"currency": "USD", // ISO 4217 currency code
"maxPerTransaction": 500, // Maximum per single transaction
"maxDaily": 2000 // Maximum aggregate per calendar day
}Geographic Restrictions
Optional object constraining where the authorization is valid. Uses ISO 3166-2 region codes (e.g., US-NY, US-CA). When both allowedRegions and deniedRegions are present, deniedRegions takes precedence.
{
"allowedRegions": ["US-NY", "US-CA"], // ISO 3166-2 codes
"deniedRegions": [] // Takes precedence over allowed
}Verification Steps
Relying parties MUST perform all three verification steps before honoring an HAP credential. A credential is valid only if all three checks pass.
Cryptographic Signature Verification
Extract the proof object from the credential. Reconstruct the canonical JSON of the credential without the proof. Resolve the verificationMethod DID URL to obtain the issuer's Ed25519 public key. Verify the base64url-decoded proofValue against the canonical JSON using Ed25519. If verification fails, reject the credential immediately.
Expiration Check
Compare the current UTC timestamp against the expirationDate field. If the current time is past the expiration date, the credential is expired and MUST NOT be honored. The issuer automatically transitions expired credentials to "expired" status in the backing store, but relying parties MUST NOT rely on the status endpoint alone — the expirationDate is authoritative.
Real-Time Status Check
Issue an HTTP GET request to the URL in credentialStatus.id. The endpoint returns the current status (active, expired, revoked, or suspended). If the status is anything other than "active", the credential MUST NOT be honored. This check catches revocations and suspensions that occurred after issuance.
Status Lifecycle
Every HAP credential exists in exactly one of four states at any given time. Transitions are unidirectional unless explicitly noted.
| Status | Description | Transitions To |
|---|---|---|
| active | Credential is valid and may be honored by relying parties | expired, revoked, suspended |
| expired | Credential has passed its expirationDate — terminal state | (none) |
| revoked | Credential has been explicitly revoked by the principal — terminal state | (none) |
| suspended | Credential is temporarily disabled — may be reactivated | active (if not expired), revoked |
State Transition Diagram
┌──────────────────────┐
│ active │
└──┬───────┬───────┬───┘
│ │ │
expiry │ revoke│ suspend│
▼ ▼ ▼
┌─────────┐ ┌──────┐ ┌───────────┐
│ expired │ │revoked│ │ suspended │
│(terminal)│ │(term.)│ │ │
└─────────┘ └──────┘ └──┬────┬───┘
│ │
reactivate│ revoke│
▼ ▼
┌──────┐ ┌──────┐
│active│ │revoked│
└──────┘ └──────┘Expiration is checked both at verification time (by relying parties using the expirationDate field) and by the issuer’s backend, which automatically transitions stale active records to expired on status queries and list operations.
Privacy Model
HAP is designed to authorize agents without exposing the identity of the authorizing human. The protocol achieves this through SHA-256 principal hashing — a one-way transformation applied before any data reaches the credential or the backing store.
Principal Hashing
When a credential is issued, the principalIdentity input (which may be an email address, a user ID, or any identifying string) is immediately passed through SHA-256 to produce a 64-character hex digest. This digest is stored in the principal_hash field of the credential and the database. The raw identity string is never persisted, logged, or included in any API response.
// Input: "user_2abc123def456"
// Output: "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
principal_hash = SHA-256(principalIdentity)What the Hash Enables
Limitations
SHA-256 is not a slow hash function. If the input space is small (e.g., sequential user IDs), a motivated attacker could brute-force the hash. The protocol mitigates this by design: the principal hash alone reveals nothing about authorization scope, agent identity, or the entities involved. Future versions may introduce salted hashing or adopt a key-derivation function (e.g., Argon2id) for higher-entropy requirements.
Security Considerations
Key Management
The issuer’s Ed25519 signing key is encrypted at rest using AES-256-GCM with a server-side secret (NETP_KEY_SECRET). The private key is decrypted only at signing time and is never transmitted over the network. Key rotation is supported — each credential stores the keyId of the signing key, and the verifier resolves the correct public key via the proof’s verificationMethod DID URL.
Credential Forgery
Credentials are signed with Ed25519, which provides 128-bit security against forgery. An attacker cannot produce a valid credential without access to the issuer’s private key. The signature covers the entire credential body (excluding the proof itself), preventing field tampering.
Replay Attacks
Each credential has a unique urn:uuid identifier and a time-bounded validity period. Relying parties can track presented credential IDs to detect replays within a session. The real-time status endpoint ensures that even within the validity period, revoked credentials are rejected.
Rate Limiting
All HAP API endpoints enforce IP-based rate limiting with SHA-256 hashed IP addresses. Rate limit headers (X-RateLimit-Limit, X-RateLimit-Remaining, Retry-After) are included in every response. The issuance endpoint requires Clerk authentication; the verification and status endpoints are public but rate-limited.
Authentication Boundaries
| Endpoint | Auth Required | Rationale |
|---|---|---|
| POST /api/hap/issue | Yes (Clerk) | Only authenticated users may issue credentials |
| POST /api/hap/verify | No | Any relying party must be able to verify without an account |
| GET /api/hap/{authId}/status | No | Agents and relying parties query status in real time |
| POST /api/hap/{authId}/revoke | Yes (Clerk) | Only the credential owner may revoke |
| GET /api/hap | Yes (Clerk) | Users list only their own credentials |
Ownership Scoping
The revoke endpoint enforces ownership — it applies a WHERE user_id = :userId clause to ensure that a user can only revoke credentials they issued. The list endpoint similarly filters by the authenticated user’s ID. There is no admin override in the public API.
API Reference
The HAP API is exposed as a set of RESTful endpoints under /api/hap. All requests and responses use JSON. Credential responses use the application/vc+ld+json content type.
POST /api/hap/issue
Issue a new HAP authorization credential. Requires Clerk authentication.
{
"agentId": "did:web:agent.example.com",
"agentType": "ai_assistant",
"principalIdentity": "user@example.com",
"scope": {
"actions": ["read", "recommend"],
"spendingLimits": {
"currency": "USD",
"maxPerTransaction": 200
}
},
"verificationLevel": "enhanced",
"validityDays": 30,
"entityId": "optional-uuid"
}| Status | Description |
|---|---|
| 201 | Credential issued — returns full W3C VC 2.0 JSON |
| 400 | Validation error — missing or invalid fields |
| 401 | Unauthorized — Clerk authentication required |
| 429 | Rate limit exceeded |
POST /api/hap/verify
Verify an HAP credential. Public endpoint — no authentication required. Submit the full credential JSON as the request body.
{
"authId": "urn:uuid:a1b2c3d4-...",
"valid": true,
"status": "active",
"verifiedAt": "2026-03-16T12:05:00.000Z",
"verifier": "did:web:nordax.ai"
}| Status Field | Meaning |
|---|---|
| active | Credential is valid and current |
| expired | Credential has passed its expiration date |
| revoked | Credential has been explicitly revoked |
| suspended | Credential is temporarily suspended |
| not_found | No credential with this ID exists |
| invalid_signature | Cryptographic signature verification failed |
GET /api/hap/{authId}/status
Retrieve the real-time status of a credential. This is the endpoint referenced in the credential’s credentialStatus.id field. Public endpoint. The authId parameter must be URI-encoded (authIds are urn:uuid:... URIs).
{
"authId": "urn:uuid:a1b2c3d4-...",
"status": "active",
"issuedAt": "2026-03-16T12:00:00.000Z",
"validUntil": "2026-04-15T12:00:00.000Z",
"revokedAt": null,
"revocationReason": null
}POST /api/hap/{authId}/revoke
Revoke an active credential. Requires Clerk authentication. The authenticated user must be the credential owner (enforced by userId scoping).
{
"reason": "Agent access no longer needed"
}| Status | Description |
|---|---|
| 200 | Credential revoked successfully |
| 400 | Missing reason field |
| 401 | Unauthorized — Clerk authentication required |
| 404 | Credential not found or not owned by the authenticated user |
GET /api/hap
List all HAP credentials for the authenticated user, ordered by issuance date (newest first). Automatically expires stale active credentials on read.
Versioning
HAP follows semantic versioning. The current specification is v0.1 (draft). The version is encoded in the JSON-LD context URI (https://nordax.ai/ns/hap/v1) and will be incremented on breaking changes to the credential structure or verification algorithm.
| Version | Date | Status | Changes |
|---|---|---|---|
| v0.1 | March 2026 | Draft | Initial specification — credential structure, issuance, verification, revocation, privacy model |
Compatibility
Relying parties SHOULD check the @context array for the HAP namespace URI. If the version in the URI does not match a version the relying party supports, it SHOULD reject the credential rather than attempt partial parsing. Issuers MUST NOT change the credential structure within the same version number.
Standards Alignment
W3C Verifiable Credentials
Data Model 2.0W3C Data Integrity
1.0W3C Decentralized Identifiers
DID Core 1.0DID:web Method
did:web v1.0Ed25519
EdDSA (RFC 8032)SHA-256
FIPS 180-4Start issuing HAP credentials
Authorize AI agents with scoped, verifiable, and revocable credentials backed by W3C standards.
Human Authorization Protocol (HAP) · v0.1 Draft · March 2026