API reference
A plain HTTP JSON API with Ed25519 challenge-response authentication Use the HTTP API or the JavaScript client Wallet signatures authorize chain changes, and Xity Connect handles application consent
Account IDs and asset workflows
Resolve account names and short Xity IDs before signing canonical wallet transactions, creator access needs identity verification and operator approval before the first launch, each asset still requires a wallet signature
Five minutes to a working session
Everything happens client-side except the HTTP calls Generate an Ed25519 keypair, register the public key, sign a challenge, and call the API with the session token
xity1 + sha256d(public_key)[:20] hexPOST /auth/signup with the PEM This creates the wallet, the identity record, and a WALLET_BIND transaction on the chainPOST /auth/challenge?address=xity1…, then sign the canonical JSON {"address":…,"challenge":…} (exact byte rules below)POST /auth/signin returns a bearer token Attach Authorization: Bearer <token> to every authenticated call/identity, store values in the vault, request verifications You are a first-class identity on the chainHow authentication works
There are no passwords A client holds an Ed25519 private key and proves possession by signing a server-issued challenge Sessions are bearer tokens; every privileged operation is proven by a signature
- Challenges are single-use and expire in 300 seconds
- Signing in from a suspicious context raises a risk score; CRITICAL blocks the attempt, HIGH notifies the owner
/auth/signout-allrevokes every session for the caller- Recovery-critical operations require an additional step-up signature: a fresh signature over
{ address, purpose, timestamp }, valid ±300 seconds and single-use - Server-side key generation is refused permanently (410) Keys are born on the client
Canonical JSON , byte-exact rules
Signatures are over canonical JSON, byte-identical to Python's json.dumps(obj, sort_keys=True, separators=(',', ':'), ensure_ascii=True)Get one byte wrong and the signature fails
- Keys sorted alphabetically, recursively
- No whitespace:
{"a":1,"b":2}, no spaces after:or, - Non-ASCII escaped as
\uXXXX; astral-plane characters as surrogate pairs - Integers without a decimal point;
nullfor None; NaN/Infinity are rejected
Conventions
- Consent page (send users here):
https://xityconnect.com/authorize - API base (token exchange, userinfo, and the rest of this reference): production
https://api.xityconnect.org; developmenthttp://localhost:8086 - All bodies and responses are JSON (
Content-Type: application/json) - Errors are FastAPI-shaped:
{ "detail": "human readable reason" }with a 4xx/5xx status Unexpected server errors return an opaque reference , quote it when contacting the operator - Browser origins must be allowlisted server-side (
XITY_CORS_ORIGINS); the API never allows*with credentials - Addresses are
xity1+ 40 hex chars, derived assha256d(public_key)[:20] - Durable state is stored server-side; your key is sealed in the browser, and the platform only holds the PIN-sealed backup
Endpoint reference
Authentication
POST/auth/signupRegister an account from a client-generated public key Creates the wallet, the identity record, and a WALLET_BIND transaction
POST/auth/challenge?address=xity1…Issue a one-time auth challenge
POST/auth/signinVerify the challenge signature and open a session
POST/auth/signout-allRevoke every session for the caller
Auth: Bearer
Identity & wallet
GET/identityThe caller's identity record (private evidence excluded)
Auth: Bearer
GET/walletThe caller's wallet: type, balance, bound identity
Auth: Bearer
GET/wallet/transactions?limit=50The caller's transaction history (PII-scrubbed)
Auth: Bearer
POST/verify/requestIssue a verification code for email/phone/device/wallet The code is relayed by the operator; it is NEVER returned by the API
Auth: Bearer
POST/verify/confirm?method=email&code=…Confirm a code On success the method is timestamped on chain Codes expire in 10 minutes and work once
Auth: Bearer
Vault
POST/vault/storeStore a value in your vault Sealed with a SHA-256 integrity hash, verified on every read Values are JSON objects up to 256 KiB; up to 64 keys per identity
Auth: Bearer
GET/vault/{key}Read a vault value Owner-only; tampered values are refused Reserved keys (ceremony records) are server-written only
Auth: Bearer
Recovery
POST/recovery/startInitiate recovery Starts the 24-hour clock and notifies the wallet owner Proofless by design , proofs come at completion
GET/recovery/status/{request_id}Status of a request The linked address is never disclosed
GET/recovery/pendingThe caller's pending recovery requests
Auth: Bearer
POST/recovery/cancelCancel a pending request Owner-only , requires the owner's session
Auth: Bearer
POST/recovery/enroll/guardians?guardians=xity1a&guardians=xity1bReplace the enrolled guardian set Each guardian must be a registered wallet; minimum two
Auth: Bearer
GET/recovery/guardiansList the caller's enrolled guardians
Auth: Bearer
POST/recovery/backup-codeProvision (or replace) the caller's single-use backup code Returned exactly once
Auth: Bearer
POST/recovery/device-tokenProvision (or replace) a trusted-device recovery token Returned exactly once
Auth: Bearer
POST/recovery/completeComplete a recovery after the waiting period with a per-method proof, rotating the wallet key
Profile, permissions & notifications
GET/profileThe caller's presentation profile
Auth: Bearer
PUT/profileUpdate display name / preferences / privacy settings
Auth: Bearer
GET/permissionsList the caller's grants in both directions: made by them and held by them
Auth: Bearer
POST/permissions/grantDelegate scoped access Scopes/levels are a closed allowlist; admin and wildcards are rejected
Auth: Bearer
GET/notifications?unread_only=falseThe caller's notifications
Auth: Bearer
Chain
GET/chain/infoChain the platform anchors to
GET/blocks/{height}A sealed block: full header plus transaction ids/types , no payloads, no signatures Everything a verifying node needs
GET/chain/specThe public chain-spec pin: everything a new node needs to join and independently verify the ledger
Operator (admin key)
GET/operator/biometric/statusWhether the server-side biometric verifier is loaded, with the pinned model checksums
Auth: X-Xity-Admin-Key
POST/operator/biometric/verify?session_id=…Run the server-side face match + liveness over a completed ceremony's hash-bound evidence Re-verifies every frame hash, attaches the report, and grants the biometric level on-chain when the decision is VERIFIED Fails closed: 503 when the pipeline is unavailable, REVIEW/REJECT on any anomaly
Auth: X-Xity-Admin-Key
Sign in with XityConnect
Let users sign in to your app with their Xity identity , the same authorization-code + PKCE flow as Google, in three steps Create the app yourself in the developer console: you get a client_id, choose public (PKCE) or confidential (secret), and register your redirect URIs No node to run, no admin key
code_verifier, send SHA256(verifier) (base64url) as the code_challenge, with state for CSRF protection?code=…&state=… Verify the state matches, exchange within 60 secondsPOST /oauth/token with the code + verifier The token lasts 24 hours and sees only the approved scopeRequest only the scopes your application needs and explain each request before consent
identity:readreads the approved identity fieldsrecords:readreads application records the wallet can accessrecords:writeprepares record changes that still require a wallet signaturespaces:writeprepares signed Space NFT operationscoins:transferprepares coin transfers that still require a wallet signature
Tokens expire after 24 hours and users can revoke access from Security
The verification ceremony
Enrolling an identity card runs a four-message protocol that binds real capture bytes to wallet-signed manifests The server never sees a private key; the operator verifier runs on hardware the applicant never controls
The operator then runs POST /operator/biometric/verify(admin key): face detection, quality gates, ArcFace matching of the live poses against the document photo, and a presentation-attack ensemble A frame must contain exactly one readable face , a second person makes that frame unusable rather than silently choosing the largest box , and a verification without presentation-attack scores cannot return VERIFIED A VERIFIED decision grants the on-chain biometric level; anything anomalous returns REVIEW or REJECT , the pipeline can fail downward, never upward
Errors & rate limits
Rate limits are per client IP and per address, applied on the sensitive endpoints A 429 means back off Everything else is FastAPI-shaped JSON
| Endpoint | Limit | Window |
|---|---|---|
| /auth/signup | 5 | hour |
| /auth/challenge | 20 | 5 min |
| /auth/signin | 10 | 5 min |
| /verify/request | 5 | 10 min |
| /recovery/start | 3 | hour |
| /recovery/complete | 3 | hour |
| /verify/ceremony/* | 30 | 10 min |
Success is idempotent where it matters: re-submitting byte-identical signed manifests is accepted silently; any different re-submission is rejected
Glossary
- Identity
- An on-chain record bound to one wallet address
- Vault
- Storage protected by access controls and integrity checks Encrypt sensitive content on your device before sending it
- Verification
- A timestamped, on-chain assertion about an identity (email, phone, device, wallet , and the operator-granted biometric level)
- Ceremony
- The enrollment protocol binding real capture bytes to wallet-signed manifests
- Guardian
- A registered wallet trusted to co-approve recovery
- Step-up signature
- A fresh single-use Ed25519 signature over a purpose-bound payload, required for security-critical operations
- Evidence
- The real frame bytes of a ceremony, provably identical to the signed hashes