For licensed bank operators integrating KXCO settlement rails into their consumer product.
All institution API calls require a Bearer token in the Authorization header. Post-quantum ML-DSA-65 signing is available for enhanced security.
Bearer Token
GET /api/v1/payments HTTP/1.1
Host: api.kxco.io
Authorization: Bearer kxco_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxML-DSA-65 (Post-Quantum) — per-request signing
POST /api/v1/payments HTTP/1.1
Authorization: Bearer kxco_live_xxxx
X-KXCO-Timestamp: 1748000000
X-KXCO-Signature: <ML-DSA-65 signature of "timestamp.method.path.body_hash">
Content-Type: application/jsonRegister your ML-DSA public key
POST /api/admin/institutions/:id/ml-dsa
{
"publicKey": "<1952-byte ML-DSA-65 public key, hex-encoded>"
}Submit cross-currency payment instructions. KXCO handles FX rate locking and ARMR settlement. Your consumer app calls your backend; your backend calls KXCO.
Initiate a payment
POST /api/v1/payments
{
"endToEndId": "E2E-2026-001",
"debtor": {
"name": "Jane Smith",
"account": "KXCO-ACC-20260101-AABBCC",
"agent": "YOURBANK"
},
"creditor": {
"name": "Supplier Co Ltd",
"account": "KXCO-ACC-20260101-XXYYZZ",
"agent": "PARTNERBANK"
},
"amount": {
"currency": "USD",
"value": "10000.00"
},
"remittanceInfo": "Invoice #2026-089"
}Response
{
"uetr": "550e8400-e29b-41d4-a716-446655440000",
"txRef": "KXCO-PMNT-20260518-AB12CD",
"status": "INITIATED",
"riskHeld": false,
"createdAt": "2026-05-18T10:22:00.000Z"
}Payment lifecycle
| Status | Meaning | Next action |
|---|---|---|
| INITIATED | Created, pending admin validation | Admin: validate |
| VALIDATED | Validated, awaiting rate lock | Admin: apply-rate |
| RATE_LOCKED | Rate locked, ready to settle | Admin: settle |
| PENDING_SETTLEMENT | Settlement in progress | Automatic |
| SETTLED | Funds transferred, ledger updated | Terminal |
| FAILED | Payment rejected | Terminal |
| RETURNED | Settled then returned (FOCR / NARR) | Terminal |
Register HTTPS endpoints to receive real-time payment events. Every delivery carries two independent signatures: a classical HMAC-SHA-256 for compatibility, and a post-quantum ML-DSA-65 signature (NIST FIPS 204) for non-repudiation.
Register an endpoint
POST /api/admin/institutions/:id/webhooks
{
"url": "https://yourbank.com/webhooks/kxco",
"events": ["payment.settled", "payment.failed", "payment.returned"]
}Request headers on delivery
| Header | Value |
|---|---|
| Content-Type | application/json |
| X-KXCO-Event | payment.settled |
| X-KXCO-Timestamp | 1748000000 (Unix seconds) |
| X-KXCO-Signature | sha256=<HMAC-SHA-256 hex> |
| X-KXCO-PQ-Signature | ml-dsa-65=<6618 hex chars> |
| X-KXCO-PQ-Kid | <16-hex platform-key fingerprint> |
| X-KXCO-Delivery | <job-id UUID> |
Verifying either signature alone is sufficient. Verifying both gives defence-in-depth: HMAC blocks tampering by any party who knows the shared secret, and ML-DSA-65 cryptographically binds the message to the KXCO platform identity even if the HMAC secret is ever leaked.
Fetch the platform public key (one-time, pin and cache)
GET /wallet/api/.well-known/kxco-pq-pubkey
// Response
{
"alg": "ML-DSA-65",
"spec": "NIST FIPS 204",
"publicKey": "<3904 hex chars = 1952 bytes>",
"kid": "aa29f37ab7f4b2cf",
"sigEncoding": "hex",
"msgFormat": "{X-KXCO-Timestamp}.{raw_request_body}"
}Pin the public key in your config and treat the kid as the active key identifier. If you receive a delivery with an unexpected kid, refresh the well-known endpoint.
Verify the classical HMAC signature (Node.js)
const crypto = require('crypto')
function verifyHmac(secret, timestamp, rawBody, sigHeader) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${rawBody}`)
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(sigHeader),
Buffer.from(expected)
)
}Verify the post-quantum signature — recommended (Node.js)
// npm install kxco-post-quantum
const { webhook } = require('kxco-post-quantum')
const PINNED_KID = 'aa29f37ab7f4b2cf' // current KXCO production kid
const PINNED_PUBKEY = Buffer.from('...3904 hex chars...', 'hex')
function verify(req) {
const headers = Object.fromEntries(
Object.entries(req.headers).map(([k, v]) => [k.toLowerCase(), v])
)
const r = webhook.verifyDelivery({
headers,
rawBody: req.rawBody, // byte-for-byte exactly as received
pqPublicKey: PINNED_PUBKEY,
pinnedKid: PINNED_KID,
})
return r.pqOk && r.timestampOk && r.kidOk
}The same package handles HMAC verification, kid pinning, and the timestamp replay window. Source: npmjs.com/package/kxco-post-quantum.
Or use any FIPS 204 implementation directly
// npm install @noble/post-quantum
const { ml_dsa65 } = require('@noble/post-quantum/ml-dsa')
function verifyPq(headers, rawBody) {
if (headers['x-kxco-pq-kid'] !== PINNED_KID) return false
const sig = headers['x-kxco-pq-signature'].replace(/^ml-dsa-65=/, '')
const ts = headers['x-kxco-timestamp']
if (Math.abs(Date.now() / 1000 - parseInt(ts, 10)) > 300) return false
const msg = Buffer.from(`${ts}.${rawBody}`, 'utf8')
return ml_dsa65.verify(PINNED_PUBKEY, msg, Buffer.from(sig, 'hex'))
}Works with @noble/post-quantum, liboqs, the BoringSSL PQ branch, or any FIPS 204 implementation.
Example payload — payment.settled
{
"event": "payment.settled",
"uetr": "550e8400-e29b-41d4-a716-446655440000",
"txRef": "KXCO-PMNT-20260518-AB12CD",
"instrAmt": 10000.00,
"instrAmtCcy": "USD",
"settlAmt": 8471.23456789,
"onChainTxHash": "0xabc123...",
"sttlmTm": "2026-05-18T10:35:00.000Z"
}Retry policy
| Attempt | Delay after failure |
|---|---|
| 1 | Immediate |
| 2 | 30 seconds |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours — then marked DEAD |
Available events
| Event | Fired when |
|---|---|
| payment.initiated | Payment instruction created |
| payment.rate_locked | Rate locked, settlAmt confirmed |
| payment.settled | Funds transferred, ledger updated |
| payment.failed | Payment rejected at any stage |
| payment.returned | Settled payment returned to sender |
| * | Subscribe to all events |
An admin creates a rate lock before a payment is settled. The rate is guaranteed for the lock's validity window (default 60 minutes). Your consumer app should display the rate to the user before they confirm.
POST /api/admin/rates
{
"institutionCode": "YOURBANK",
"fromCcy": "USD",
"toCcy": "ARMR",
"rate": 0.84712,
"inverseRate": 1.18046,
"midRate": 0.84700,
"expiresInMinutes": 60
}
// Response
{
"id": "clxyz...",
"rate": 0.84712,
"expiresAt": "2026-05-18T11:22:00.000Z"
}The KXCO consumer PWA is deployed at /wallet/app. The bank registers customers using their institution code and the customers transact in a fully branded mobile-first interface.
Customer registration flow
POST /api/app/auth/register
{
"name": "Jane Smith",
"email": "jane@example.com",
"phone": "+44 7700 000000",
"password": "••••••••",
"institutionCode": "YOURBANK" // your 8-char BIC-style code
}
// On success: session cookie set, redirect to /app/homeConsumer API endpoints
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/app/auth/login | Customer sign-in — returns session cookie |
| POST | /api/app/auth/register | Register with institution code |
| GET | /api/app/me | Profile + all currency balances |
| POST | /api/app/send | P2P transfer to another registered user |
| POST | /api/app/exchange | In-app FX conversion (uses KXCO rate lock) |
| GET | /api/app/transactions | Paginated transaction history |
PWA routes
| Route | Screen |
|---|---|
| /wallet/app/login | Sign in |
| /wallet/app/register | Create account |
| /wallet/app/home | Balance overview + recent transactions |
| /wallet/app/send | Send money to another user |
| /wallet/app/exchange | FX currency conversion |
| /wallet/app/history | Full transaction history |
| /wallet/app/card | Card management (Mastercard — coming soon) |
| /wallet/app/profile | Account details + sign out |
Each client institution is provisioned with a sandboxed environment on request. All data is isolated. Rate locks, payments, and balances are independent from production.
# Sandbox base URL (provided per client)
https://sandbox-<client>.chain.kxco.ai/wallet/api
# Test institution code
TESTBANK
# Test bearer token
kxco_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxContact enterprise@knightsbridge.asia to request sandbox access.
| HTTP | Meaning | Common cause |
|---|---|---|
| 400 | Bad Request | Missing required fields or invalid state transition |
| 401 | Unauthorised | Missing or invalid Bearer token |
| 403 | Forbidden | Token valid but insufficient permissions |
| 404 | Not Found | Unknown UETR, institution code, or account |
| 405 | Method Not Allowed | Wrong HTTP method for this endpoint |
| 409 | Conflict | Duplicate endToEndId or idempotency key |
| 429 | Rate Limited | Exceeded RPM limit for institution |
| 500 | Server Error | Contact support with X-KXCO-Delivery header |