Where the EA Blueprint answers "what do we build", this document answers "how does a stranger implement it". Every public endpoint, every database table, the canonical signing format, the idempotency contract, the deployment topology, the observability plan, and every error code the platform can return. This is the deliverable a CTO reads before they let a team commit code.
Every external surface. Method, path, auth posture, idempotency guarantee, and sample request/response bodies. Hardware-bound signatures are required on every member-authored handshake.
/api/handshakes/intent
member
idempotent
{
"client_phone": "+61 4xx xxx xxx",
"client_name": "Sarah Chen",
"vertical_code": "MORTGAGE",
"product_code": "HOME_LOAN_OO",
"receiving_member_id": 1042,
"estimated_deal_cents": 80000000,
"device_signature": "<base64 P-256 ECDSA over canonical payload>"
}{
"handshake_id": "H-2026-05-00417",
"block_height_pending": 8821,
"hash_prev": "a7c2…",
"hash_self": "f19e…",
"created_at": "2026-05-21T04:31:18Z"
}400 INVALID_SIGNATURE — Signature does not verify against the member’s registered hardware key409 DUPLICATE_INTENT — Same idempotency-key replay — original handshake returned422 RATE_CARD_MISSING — No active Rate Card for this vertical+product combination/api/handshakes/:id/acknowledge
public
idempotent
{
"magic_token": "mlt_a1b2c3d4…",
"client_consent_text": "I consent to being referred and to my contact details being shared with the receiving party.",
"ip": "203.0.113.42",
"user_agent": "<UA string>"
}{
"handshake_id": "H-2026-05-00417",
"acknowledged_at": "2026-05-21T05:02:44Z",
"chain_state": "H1+H2_COMPLETE"
}410 TOKEN_EXPIRED — Magic-link is older than 7 days404 HANDSHAKE_NOT_FOUND — Bad token or already-consumed/api/handshakes/:id/intake
member
idempotent
{
"intake_meeting_at": "2026-05-22T09:30:00+10:00",
"intake_notes": "Met with Sarah, discussed her PPOR refinance options",
"device_signature": "<base64 ECDSA>"
}{ "handshake_id": "H-2026-05-00417", "chain_state": "H1+H2+H3_COMPLETE" }/api/handshakes/:id/settlement
member
idempotent
{
"settled_at": "2026-06-18T14:00:00+10:00",
"settled_amount_cents": 81200000,
"reference": "Loan ID LOAN-2026-3142",
"device_signature": "<base64 ECDSA>"
}{
"handshake_id": "H-2026-05-00417",
"chain_state": "H1+H2+H3+H4_COMPLETE",
"commission_intent_id": "CI-2026-00417",
"commission_breakdown": {
"gross_cents": 81200000,
"referrer_cents": 81200,
"recipient_cents": 81200,
"platform_cents": 8120,
"rate_card_version": 14
}
}/api/handshakes/:id/outcome
public
idempotent
{
"magic_token": "mlo_z9y8x7w6…",
"outcome_rating": 5,
"outcome_text": "Settlement went smoothly, thanks for the referral"
}{
"handshake_id": "H-2026-05-00417",
"chain_state": "COMPLETE",
"payout_scheduled_for": "2026-06-25T00:00:00+10:00"
}/api/payouts/initiate
admin
idempotent
{ "commission_intent_id": "CI-2026-00417" }{
"payout_id": "PO-2026-00417",
"stripe_transfer_id": "tr_1Q5z…",
"stripe_application_fee_id": "fee_3Mz…",
"rcti_pdf_url": "https://flip360.com.au/rcti/CI-2026-00417.pdf",
"expected_arrival": "2026-06-26"
}409 ALREADY_PAID — Idempotency replay — original payout returned424 STRIPE_KYC_INCOMPLETE — Receiving member’s Stripe Connect account not fully onboarded/api/stripe/webhook
webhook
idempotent
{ "id": "evt_3Mz…", "type": "transfer.paid", "data": { "object": { ... } } }{ "received": true, "ledger_row_id": 88412 }400 BAD_SIGNATURE — Stripe webhook signature does not verify — request rejected/api/simulate
admin
idempotent
{ "vertical_code": "MORTGAGE", "product_code": "HOME_LOAN_OO", "gross_cents": 80000000 }{
"rate_card_version": 14,
"referrer_cents": 80000,
"recipient_cents": 80000,
"platform_cents": 8000,
"explanation": "0.10% to referrer, 0.10% to recipient, 0.01% platform fee per Rate Card v14"
}/api/me/commissions
member
idempotent
/api/me/payouts
member
idempotent
/api/dashboard
admin
idempotent
/api/ledger
admin
idempotent
/api/rules
admin
idempotent
/api/rules
admin
/api/disputes
member
idempotent
/api/disputes/:id/resolve
admin
idempotent
The six tables that carry the trust posture. All money in INTEGER cents. Chain tables enforced INSERT-only at the application layer. Soft deletes on operational tables only.
handshakes
CREATE TABLE handshakes ( id INTEGER PRIMARY KEY AUTOINCREMENT, handshake_uid TEXT UNIQUE NOT NULL, -- H-2026-05-00417 type TEXT NOT NULL, -- 'INTENT'|'ACK'|'INTAKE'|'SETTLEMENT'|'OUTCOME' chain_seq INTEGER NOT NULL, -- 1..5 referrer_id INTEGER NOT NULL, recipient_id INTEGER, client_id INTEGER, vertical_id INTEGER NOT NULL, product_id INTEGER NOT NULL, gross_cents INTEGER, -- populated at H4 signature TEXT NOT NULL, -- ECDSA P-256 over canonical payload signature_alg TEXT NOT NULL DEFAULT 'ES256', signing_key_id TEXT NOT NULL, -- which device key payload_hash TEXT NOT NULL, -- SHA-256 of canonical JSON hash_prev TEXT, -- previous handshake hash (chain link) hash_self TEXT UNIQUE NOT NULL, -- SHA-256(payload_hash || hash_prev) block_id INTEGER, -- which sealed block this row was anchored into idempotency_key TEXT UNIQUE, -- request-supplied or derived created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (referrer_id) REFERENCES contacts(id), FOREIGN KEY (recipient_id) REFERENCES contacts(id), FOREIGN KEY (client_id) REFERENCES contacts(id), FOREIGN KEY (vertical_id) REFERENCES verticals(id), FOREIGN KEY (product_id) REFERENCES products(id), FOREIGN KEY (block_id) REFERENCES chain_blocks(id) ); CREATE INDEX idx_handshakes_uid ON handshakes(handshake_uid); CREATE INDEX idx_handshakes_referrer ON handshakes(referrer_id); CREATE INDEX idx_handshakes_recipient ON handshakes(recipient_id); CREATE INDEX idx_handshakes_chain ON handshakes(handshake_uid, chain_seq); -- App-layer enforcement: INSERT-only. No UPDATE, no DELETE. -- Enforced via wrangler binding policy + code review checklist.
chain_blocks
CREATE TABLE chain_blocks ( id INTEGER PRIMARY KEY AUTOINCREMENT, block_height INTEGER UNIQUE NOT NULL, block_hash TEXT UNIQUE NOT NULL, prev_block_hash TEXT, -- NULL only on genesis merkle_root TEXT NOT NULL, event_count INTEGER NOT NULL, period_start DATETIME NOT NULL, period_end DATETIME NOT NULL, sealed_at DATETIME DEFAULT CURRENT_TIMESTAMP, external_anchor_status TEXT NOT NULL DEFAULT 'PENDING', -- PENDING|ANCHORED|FAILED external_anchor_method TEXT, -- 'NOTARY_HTTP'|'BTC_OPRETURN'|'ETH_L2' external_anchor_ref TEXT, external_anchored_at DATETIME );
commission_intents
CREATE TABLE commission_intents ( id INTEGER PRIMARY KEY AUTOINCREMENT, intent_uid TEXT UNIQUE NOT NULL, -- CI-2026-00417 handshake_uid TEXT NOT NULL, -- link to H4 rate_card_version INTEGER NOT NULL, gross_cents INTEGER NOT NULL, referrer_cents INTEGER NOT NULL, recipient_cents INTEGER NOT NULL, platform_cents INTEGER NOT NULL, status TEXT NOT NULL DEFAULT 'PENDING', -- PENDING|PAID|DISPUTED|VOID created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (handshake_uid) REFERENCES handshakes(handshake_uid) );
payouts
CREATE TABLE payouts ( id INTEGER PRIMARY KEY AUTOINCREMENT, payout_uid TEXT UNIQUE NOT NULL, -- PO-2026-00417 commission_intent_id INTEGER UNIQUE NOT NULL, -- one-to-one — idempotent payee_member_id INTEGER NOT NULL, amount_cents INTEGER NOT NULL, currency TEXT NOT NULL DEFAULT 'AUD', stripe_transfer_id TEXT, stripe_application_fee_id TEXT, status TEXT NOT NULL DEFAULT 'QUEUED', -- QUEUED|SENT|PAID|FAILED|REVERSED initiated_at DATETIME, arrived_at DATETIME, rcti_pdf_path TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (commission_intent_id) REFERENCES commission_intents(id) );
rate_cards
CREATE TABLE rate_cards ( id INTEGER PRIMARY KEY AUTOINCREMENT, version INTEGER UNIQUE NOT NULL, vertical_id INTEGER NOT NULL, product_id INTEGER, -- NULL = applies to whole vertical referrer_bps INTEGER NOT NULL, -- basis points (10 = 0.10%) recipient_bps INTEGER NOT NULL, platform_bps INTEGER NOT NULL, effective_from DATETIME NOT NULL, effective_to DATETIME, -- NULL = currently active notes TEXT, created_by INTEGER, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (vertical_id) REFERENCES verticals(id), FOREIGN KEY (product_id) REFERENCES products(id) );
audit_log
CREATE TABLE audit_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, actor_id INTEGER NOT NULL, actor_role TEXT NOT NULL, action TEXT NOT NULL, target_table TEXT NOT NULL, target_id INTEGER, payload_json TEXT, ip TEXT, user_agent TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP );
What an iPhone Secure Enclave (or Android StrongBox, or a WebAuthn passkey) actually signs. RFC-8785-style JSON canonicalisation, ECDSA over P-256, hardware-isolated key.
// Canonical handshake payload — what the device hashes & signs
//
// Rules:
// - JSON.stringify with sorted keys (RFC 8785 JCS-style)
// - No whitespace
// - All timestamps in UTC ISO-8601 with millisecond precision
// - All money in INTEGER cents
//
// Example (handshake H1 — INTENT):
{
"alg": "ES256",
"kid": "device-key-uid-7f3e2a1c-…",
"handshake_uid": "H-2026-05-00417",
"type": "INTENT",
"chain_seq": 1,
"referrer_id": 1042,
"client_phone_hash": "sha256:e3b0c44…",
"vertical_id": 1,
"product_id": 3,
"estimated_deal_cents": 80000000,
"timestamp": "2026-05-21T04:31:18.412Z",
"nonce": "8b2c4f1e9a7d3b6c"
}
// Signing scheme:
// ECDSA over the secp256r1 curve (NIST P-256)
// Hash function: SHA-256
// Hardware: Apple Secure Enclave / Android StrongBox / WebAuthn passkey
// Key never leaves the secure element — application sees signature only
// Verification (server side, in C2 commission engine):
// 1. Look up device's public key from members.signing_key_pubkey
// 2. Canonicalise payload (sorted keys, no whitespace)
// 3. SHA-256 → message digest
// 4. ECDSA-verify(pubkey, digest, signature) — constant-time
// 5. Reject if any of the above fails — return 400 INVALID_SIGNATURE
The rule that makes "exactly once" actually work — even when networks drop, webhooks retry, or a user double-clicks the submit button.
// Every state-changing endpoint accepts an Idempotency-Key header.
// Same key + same body → same response (no second side-effect)
// Same key + different body → 409 CONFLICT
// Missing key on a state-changing endpoint → 400 IDEMPOTENCY_KEY_REQUIRED
//
// Storage (D1 table):
CREATE TABLE idempotency_keys (
key TEXT PRIMARY KEY,
method TEXT NOT NULL,
path TEXT NOT NULL,
request_hash TEXT NOT NULL, -- SHA-256 of canonical body
response_status INTEGER NOT NULL,
response_body TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
expires_at DATETIME NOT NULL -- 24h retention default
);
CREATE INDEX idx_idem_expires ON idempotency_keys(expires_at);
// Handler pseudocode:
async function withIdempotency(req, handler) {
const key = req.headers.get('idempotency-key')
if (!key) return error(400, 'IDEMPOTENCY_KEY_REQUIRED')
const existing = await db.get('SELECT * FROM idempotency_keys WHERE key = ?', key)
if (existing) {
if (existing.request_hash !== hashBody(req.body)) return error(409, 'CONFLICT')
return new Response(existing.response_body, { status: existing.response_status })
}
const result = await handler(req)
await db.insert('idempotency_keys', { key, ..., response_body: result.body })
return result
}
// Stripe webhooks: idempotency on event.id (Stripe-provided, globally unique).
// Internal endpoints: client-generated UUIDv4 + sliding 24-hour TTL.
From referrer intent (H1) through to payout reconciliation and hourly notary anchoring. Solid arrows are synchronous request/response; teal dashed arrows are asynchronous (SMS, webhook).
Every runtime, every region, every third-party. Australia-resident by default.
| Layer | Technology | Region | Notes |
|---|---|---|---|
| Edge runtime | Cloudflare Workers (V8 isolates) | Sydney + global PoPs | Stateless. Cold-start ~5ms. Per-request CPU budget 30ms (paid plan). |
| Database | Cloudflare D1 (SQLite) | Sydney (AU pin) | Read replicas at edge. Durable. Daily backups + WAL. |
| Static assets | Cloudflare Pages | Global CDN | Vite build → dist/. Brotli compressed. Long-cache hashed assets. |
| Payment rail | Stripe Connect (AU) | Stripe AU | Application-fee-amount model. KYC handled by Stripe. AUD only at MVP. |
| SMS | Twilio Australia | Sydney | Magic-link delivery for H2/H5. Inbound-route stub for replies. |
| Email transactional | Postmark | AU sending | RCTI PDFs, payout confirmations, dispute notifications. |
| Object storage | Cloudflare R2 | AU bucket | RCTI PDFs, dispute attachments. S3-compatible API. |
| Secrets | Cloudflare Worker Secrets | Encrypted at rest | Stripe keys, Twilio keys, JWT signing key. Rotated quarterly. |
| Observability | Workers Logpush + Logflare | AU log sink | Structured JSON logs. Error budget tracking. P95 latency dashboards. |
| Notary | External multisig service | Independent operator | Hourly Merkle-root anchoring. Detached signature returned + stored on chain_blocks row. |
The eight metrics instrumented from day one. Every one has a target threshold, a dashboard, and an on-call response rule.
| Severity | Metric | What it measures |
|---|---|---|
| critical | Chain integrity | Every minute: re-verify hash-chain across last 1,000 handshakes. Alert on any mismatch. |
| critical | Payout success rate | Rolling 24h % of payouts that reach status=PAID without retry. Target ≥ 99.5%. |
| high | Stripe webhook lag | P95 time from Stripe event creation to D1 ledger write. Target < 5s. |
| medium | Idempotency hit rate | % of POSTs that match a prior Idempotency-Key. Detects retries & double-clicks. |
| high | Signature reject rate | % of /handshakes POSTs that fail signature verification. Detects abuse & key rotation issues. |
| high | Block anchoring lag | Time from block seal to external notary confirmation. Target < 1h. |
| medium | RCTI generation latency | P95 time from H5 to RCTI PDF available. Target < 30s. |
| medium | Member onboarding funnel | Daily funnel: applied → vetted → onboarded → first referral. Drives Sprint G decisions. |
Every error code the platform can return — what it means, what client should do.
| Code | Meaning | Remediation |
|---|---|---|
400 INVALID_SIGNATURE |
Hardware signature does not verify against the registered device key. | Member re-registers their device. |
400 IDEMPOTENCY_KEY_REQUIRED |
State-changing endpoint called without an Idempotency-Key header. | Client retries with a UUIDv4. |
409 CONFLICT |
Same Idempotency-Key reused with different request body. | Client generates a new key for the new operation. |
409 DUPLICATE_INTENT |
Two intents for the same client+vertical in the same 24h window. | Original intent returned — no second handshake created. |
410 TOKEN_EXPIRED |
Magic-link is older than 7 days. | Member re-sends. |
422 RATE_CARD_MISSING |
No active Rate Card for the supplied vertical+product. | Admin creates Rate Card; intent retries. |
424 STRIPE_KYC_INCOMPLETE |
Receiving member’s Connect account has not finished KYC. | Member completes Stripe onboarding; payout queues automatically. |
503 CHAIN_INTEGRITY_FAILURE |
Hash mismatch detected on read. All writes paused. | Incident response. Read from last-known-good block; freeze writes until reconciled. |
npm run db:migrate:local, hit the APIs with curl, and start shipping. No tribal knowledge required.