{"openapi":"3.0.3","info":{"title":"HashAnchor API","version":"1.0.0","description":"Blockchain attestation API for anchoring data hashes on-chain via Merkle trees."},"servers":[{"url":"https://hashanchor.xid.network","description":"Production"},{"url":"http://localhost:3001","description":"Local"}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","description":"API key (ha_xxx) or JWT token"}},"schemas":{"HexHash":{"type":"string","pattern":"^0x[0-9a-fA-F]{64}$","example":"0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"},"Error":{"type":"object","properties":{"error":{"type":"string"}}}}},"paths":{"/health":{"get":{"tags":["System"],"summary":"Health check","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"ok"}}}}}}}}},"/v1/hashes":{"post":{"tags":["Hashes"],"summary":"Submit a single hash","security":[{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["hash"],"properties":{"hash":{"$ref":"#/components/schemas/HexHash"},"externalId":{"type":"string","maxLength":255}}}}}},"responses":{"200":{"description":"Duplicate hash"},"202":{"description":"Hash accepted"},"400":{"description":"Invalid request"},"401":{"description":"Unauthorized"},"429":{"description":"Rate limit or quota exceeded"}}}},"/v1/hashes/batch":{"post":{"tags":["Hashes"],"summary":"Submit up to 100 hashes","security":[{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["hashes"],"properties":{"hashes":{"type":"array","maxItems":100,"items":{"type":"object","required":["hash"],"properties":{"hash":{"$ref":"#/components/schemas/HexHash"},"externalId":{"type":"string"}}}}}}}}},"responses":{"202":{"description":"Batch accepted"}}}},"/v1/hashes/{hash}":{"get":{"tags":["Hashes"],"summary":"Query hash status","security":[{"BearerAuth":[]}],"parameters":[{"name":"hash","in":"path","required":true,"schema":{"$ref":"#/components/schemas/HexHash"}}],"responses":{"200":{"description":"Hash status"},"404":{"description":"Hash not found"}}}},"/v1/verify/{hash}":{"get":{"tags":["Verify"],"summary":"Verify a hash on-chain (public, no auth)","parameters":[{"name":"hash","in":"path","required":true,"schema":{"$ref":"#/components/schemas/HexHash"}}],"responses":{"200":{"description":"Verification result"},"404":{"description":"Hash not found"}}}},"/v1/receipts/{hash}":{"get":{"tags":["Receipts"],"summary":"Download receipt JSON (public, no auth)","parameters":[{"name":"hash","in":"path","required":true,"schema":{"$ref":"#/components/schemas/HexHash"}}],"responses":{"200":{"description":"Receipt data"},"404":{"description":"Receipt not found"}}}},"/v1/stats":{"get":{"tags":["Query"],"summary":"Dashboard statistics (tenant-scoped)","security":[{"BearerAuth":[]}],"responses":{"200":{"description":"Stats","content":{"application/json":{"schema":{"type":"object","properties":{"totalHashes":{"type":"integer"},"pendingHashes":{"type":"integer"},"totalBatches":{"type":"integer"},"failedBatches":{"type":"integer"}}}}}}}}},"/v1/batches":{"get":{"tags":["Query"],"summary":"List batches (paginated, tenant-scoped)","security":[{"BearerAuth":[]}],"parameters":[{"name":"page","in":"query","schema":{"type":"integer","default":1}},{"name":"limit","in":"query","schema":{"type":"integer","default":20}}],"responses":{"200":{"description":"Paginated batch list"}}}},"/v1/batches/{id}":{"get":{"tags":["Query"],"summary":"Batch detail (tenant-scoped)","security":[{"BearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Batch with hashes"},"404":{"description":"Batch not found"}}}},"/v1/public-key":{"get":{"tags":["Verify"],"summary":"Get Ed25519 public key for receipt signature verification","responses":{"200":{"description":"Public key info"}}}},"/v1/webhooks":{"get":{"tags":["Webhooks"],"summary":"List webhooks","security":[{"BearerAuth":[]}],"responses":{"200":{"description":"Webhook list"}}},"post":{"tags":["Webhooks"],"summary":"Create webhook","security":[{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string"}},"secret":{"type":"string","minLength":16}}}}}},"responses":{"201":{"description":"Webhook created"}}}},"/auth/register":{"post":{"tags":["Auth"],"summary":"Register a new tenant","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","email","password"],"properties":{"name":{"type":"string"},"email":{"type":"string","format":"email"},"password":{"type":"string","minLength":8}}}}}},"responses":{"201":{"description":"Account created with JWT token"},"409":{"description":"Email already registered"}}}},"/auth/login":{"post":{"tags":["Auth"],"summary":"Login and get JWT token","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","password"],"properties":{"email":{"type":"string","format":"email"},"password":{"type":"string"}}}}}},"responses":{"200":{"description":"JWT token"},"401":{"description":"Invalid credentials"}}}},"/auth/me":{"get":{"tags":["Auth"],"summary":"Get current user info","security":[{"BearerAuth":[]}],"responses":{"200":{"description":"Tenant info"}}}},"/v1/hashes/anchor":{"post":{"tags":["Hashes"],"summary":"Anchor content by computing its SHA-256 hash","security":[{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["content"],"properties":{"content":{"type":"string","description":"Text or base64 encoded content"},"format":{"type":"string","enum":["text","base64"],"default":"text"},"metadata":{"type":"object","description":"Optional metadata (max 4KB)"},"externalId":{"type":"string","maxLength":255}}}}}},"responses":{"200":{"description":"Duplicate hash"},"202":{"description":"Content hashed and accepted"},"400":{"description":"Invalid request"},"401":{"description":"Unauthorized"},"429":{"description":"Rate limit or quota exceeded"}}}},"/auth/provision":{"post":{"tags":["Auth"],"summary":"One-step agent provisioning (no auth required)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","maxLength":255,"description":"Agent or organization name"},"email":{"type":"string","format":"email","description":"Optional email"}}}}}},"responses":{"201":{"description":"Account and API key created","content":{"application/json":{"schema":{"type":"object","properties":{"tenantId":{"type":"string","format":"uuid"},"apiKey":{"type":"string","description":"ha_xxx API key"},"plan":{"type":"string","example":"free"},"quota":{"type":"object","properties":{"limit":{"type":"integer"},"remaining":{"type":"integer"}}}}}}}},"409":{"description":"Email already registered"}}}},"/v1/hashes/status":{"post":{"tags":["Query"],"summary":"Bulk hash status query (max 100)","security":[{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["hashes"],"properties":{"hashes":{"type":"array","maxItems":100,"items":{"$ref":"#/components/schemas/HexHash"}}}}}}},"responses":{"200":{"description":"Batch status results","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"hash":{"$ref":"#/components/schemas/HexHash"},"status":{"type":"string","enum":["pending","batched","anchored","failed","not_found"]},"txHash":{"type":"string"},"blockTimestamp":{"type":"string","format":"date-time"}}}}}}}}}}}},"/v1/quota":{"get":{"tags":["Query"],"summary":"Check plan quota and usage","security":[{"BearerAuth":[]}],"responses":{"200":{"description":"Quota info","content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"type":"string","example":"free"},"monthly":{"type":"object","properties":{"limit":{"type":"integer"},"used":{"type":"integer"},"remaining":{"type":"integer"}}},"rateLimit":{"type":"object","properties":{"limit":{"type":"integer"},"perMinute":{"type":"boolean"}}}}}}}}}}},"/v1/device/register":{"post":{"tags":["Devices"],"summary":"Register a new IoT device","security":[{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["deviceId","publicKey"],"properties":{"deviceId":{"type":"string","maxLength":255,"example":"sensor-001"},"publicKey":{"type":"string","description":"Hex-encoded public key"},"algorithm":{"type":"string","enum":["ed25519","secp256k1"],"default":"ed25519"},"certificate":{"type":"string","description":"Optional X.509 certificate (PEM)"},"metadata":{"type":"object"}}}}}},"responses":{"201":{"description":"Device registered"},"400":{"description":"Invalid request"},"409":{"description":"Device already exists"}}}},"/v1/device/submit-signed":{"post":{"tags":["Devices"],"summary":"Submit a device-signed hash","security":[{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["deviceId","hash","signature"],"properties":{"deviceId":{"type":"string","example":"sensor-001"},"hash":{"$ref":"#/components/schemas/HexHash"},"signature":{"type":"string","description":"Hex-encoded signature"},"algorithm":{"type":"string","enum":["ed25519","secp256k1"]},"timestamp":{"type":"integer","description":"Unix timestamp from device"},"metadata":{"type":"object"},"externalId":{"type":"string","maxLength":255}}}}}},"responses":{"200":{"description":"Duplicate hash"},"202":{"description":"Signed hash accepted"},"400":{"description":"Invalid request or signature verification failed"},"404":{"description":"Device not found"}}}},"/v1/device/list":{"get":{"tags":["Devices"],"summary":"List registered devices","security":[{"BearerAuth":[]}],"responses":{"200":{"description":"Device list","content":{"application/json":{"schema":{"type":"object","properties":{"devices":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"deviceId":{"type":"string"},"algorithm":{"type":"string"},"publicKey":{"type":"string"},"status":{"type":"string","enum":["active","disabled"]},"metadata":{"type":"object"},"createdAt":{"type":"string","format":"date-time"}}}}}}}}}}}},"/v1/device/{deviceId}/history":{"get":{"tags":["Devices"],"summary":"Get device submission history","security":[{"BearerAuth":[]}],"parameters":[{"name":"deviceId","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","default":50,"maximum":100}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Submission history"},"404":{"description":"Device not found"}}}}},"tags":[{"name":"Hashes","description":"Hash submission endpoints"},{"name":"Query","description":"Authenticated query endpoints"},{"name":"Devices","description":"Physical AI / IoT device endpoints"},{"name":"Verify","description":"Public verification endpoints"},{"name":"Receipts","description":"Receipt retrieval"},{"name":"Webhooks","description":"Webhook management"},{"name":"Auth","description":"Authentication"},{"name":"System","description":"System endpoints"}]}