Integration Lab Prototype
Ivan Tsang · Hong Kong UTC+8

Integrations that keep talking at 3am.

A working lab of the things that actually break in production — OAuth token refresh, webhook retries, rate limits, reconciliation. Press a button and watch the failure path, not just the happy path.

10 yrscorporate eBanking delivery
5exchange APIs in production
142instruments, 60s refresh
POST /hooks/whatsapp/inbound
live

        
Total
Retries
LLM cost
What runs here

Seven modules, each answering a job clients are actually posting

Every module exists because that work keeps appearing in real integration briefs. Each one is runnable in under 30 seconds and shows its own failure modes.

01

OAuth 2.0 Playground

Full authorization-code flow with PKCE. Watch the token lifecycle, force an expiry, trigger a revoked consent, see it recover.

Proves: secure third-party auth
MVP
02

Five APIs, One Model

How five incompatible exchange schemas collapse into a single canonical model — with the rate-limit budget that keeps it inside every venue's ceiling.

Proves: schema normalisation
MVP
03

Health & Retry

A status board for every demo integration — plus a button that deliberately breaks one so you can watch backoff and the dead-letter queue work.

Proves: reliability engineering
MVP
04

WhatsApp × n8n

WhatsApp Cloud API into an n8n workflow with an LLM intent router. Execution traces visible; workflow JSON downloadable.

Proves: messaging automation
Phase 2
05

AI Action Layer

Function calling with JSON Schema validation, confirm-before-write on destructive calls, and per-request cost metering.

Proves: LLM wired to real APIs
Phase 2
06

Universal CRM Connector

HubSpot, Zoho and Airtable behind one canonical contact model. Adding a fourth CRM is one adapter class, not a rebuild.

Proves: connector architecture
Phase 3
07

Reconciliation Engine

Synthetic bank and card feeds, categorised, matched against invoices, with an exception list for everything that didn't match.

Proves: finance-grade data handling
Phase 3
Module 05 · Interactive

The AI action layer

The risk in wiring an LLM to your API isn't that it fails — it's that it confidently invents a parameter and writes it to your database. This layer treats the model as an untrusted caller: every tool call is schema-validated, every destructive action waits for a human, and anything ambiguous gets asked about rather than guessed.

Inbound message
Guardrail order: classify → validate against JSON Schema → check write scope → execute or hold. The model never reaches an adapter directly.
llm.route → tool_call → validate → dispatch
ready
Latency
Tokens in/out
Cost
Module 06 · Interactive

AI field mapping, with a confidence floor

Every CRM migration starts with somebody's spreadsheet where the columns are named Ph and status2. The model proposes a mapping to the canonical model; anything under 0.85 confidence is held for a human instead of being silently written.

Source columns detected
contacts_export_final_v3.csv
6 columns · 1,284 rows
Why the floor matters: two of six columns here are genuinely ambiguous. Auto-accepting them would corrupt 1,284 records quietly — the expensive kind of bug.
Awaiting run
Source columnCanonical fieldConf.
press “Propose mapping”
Overall architecture

One path in, one path out, and somewhere to put the failures

Everything on this site runs through the same spine. The interesting parts are the two that most integrations skip: the gate at the front that makes replays harmless, and the queue at the bottom that catches what couldn't be delivered.

Inbound Gate Route Decide Deliver Events WhatsApp Cloud API Stripe webhooks Forms · schedules Gateway HMAC verify idempotency key rate limit · 202 ack Router n8n workflows FastAPI handlers queue-backed AI action layer intent + tool call schema validation write-scope guard Adapters HubSpot · Zoho · Airtable Sheets · Stripe one canonical model On failure Retry 1s · 2s · 4s · 8s jittered backoff Dead-letter queue inspect · fix · replay nothing silently lost Observability traces · error rate rate-limit headroom Every step emits a span. A replayed webhook hits the same idempotency key and becomes a no-op.
Where these patterns come from

None of this is invented here

Every mechanism in the lab is a documented practice from a system that runs it at scale. Knowing which pattern a problem calls for — and being able to name where it's proven — is most of the job.

Stripe

Idempotency keys

A client-supplied key makes a retried request a no-op instead of a duplicate charge. Network timeouts stop being dangerous.

→ Gateway, every inbound event
Stripe · Twilio

Signed webhooks, retried on a curve

HMAC-signed payloads with a timestamp window defeat replay attacks; failed deliveries retry on exponential backoff over hours, not once.

→ HMAC verify · 1s/2s/4s/8s jittered
Shopify

Leaky-bucket rate limiting

Cost-based throttling means you budget calls against a refilling bucket rather than discovering the ceiling with a 429 in production.

→ Rate-limit headroom on the status board
Slack Events API

Acknowledge fast, work async

Return 202 within the delivery window, then process on a queue. Slow downstream work stops causing upstream retries.

→ 202 ack at the gate, queue-backed router
OAuth 2.0 · RFC 7636

PKCE and refresh rotation

Proof Key for Code Exchange removes the intercepted-code attack; rotating refresh tokens limit the blast radius of a leaked one.

→ Module 01, end to end
Segment · Airbyte

Canonical model with adapters

Sources and destinations both translate to one internal schema, so N systems need N adapters rather than N² point-to-point integrations.

→ Module 06, three CRMs one model
AWS SQS

Dead-letter queues

After the retry budget is spent the message lands somewhere inspectable and replayable, instead of vanishing into a log nobody reads.

→ Module 03, with one-click replay
Double-entry bookkeeping

Reconcile, don't trust

Two independent records of the same event get compared, and the exceptions are the output. Assume delivery failed until both sides agree.

→ Module 07, exception list first
What this looks like in practice

Three shapes these problems usually arrive in

Composite scenarios drawn from common integration briefs — illustrations of the failure mode and the fix, not descriptions of past clients.

Illustrative

Orders in one system, books in another

BeforeA 40-person retailer re-keys ~200 orders a week from a fulfilment portal into accounting. Month-end takes three days because nobody trusts either total.
AfterCanonical order model, adapters both ends, nightly reconciliation that outputs only the mismatches. Month-end becomes reviewing an exception list.
Modules 06 · 07
Illustrative

The automation that quietly stopped

BeforeA lead-routing workflow has been failing for eleven days. Nobody noticed, because a failed run looks exactly like a quiet week.
AfterEvery run emits a span; absence of expected traffic raises an alert. Failures land in a dead-letter queue and replay once the upstream is fixed.
Modules 03 · 04
Illustrative

Customers messaging a number nobody watches

BeforeEnquiries arrive on WhatsApp at all hours. Replies take a day; the details get retyped into a CRM later, sometimes.
AfterIntent routing answers the routine ones instantly and opens a properly-typed CRM record for the rest. Anything ambiguous escalates rather than guessing.
Modules 04 · 05 · 06
The one that isn't a demo

CryptoFundingWatch — in production, continuously

Everything above this line is a lab: purpose-built to be inspected. This one is a real system I built and still run, and it is where the patterns were actually paid for.

It reads perpetual funding rates from five exchanges — Binance, OKX, Hyperliquid, Coinbase and Kraken — and presents them as one ranked view. That sentence hides the entire problem: five venues means five different authentication schemes, five pagination styles, five ways of expressing an interval, and five independent opinions about what a funding rate even is. One returns eight-hour rates; another returns an hourly rate that must be annualised before the two can sit in the same column.

The normalisation layer is the product. Every venue gets an adapter that translates into one canonical instrument model, so the scoring engine never learns that Kraken exists. Adding a sixth exchange is one adapter, not a refactor — the same shape as the CRM connector in Module 06.

Refreshing 142 instruments every 60 seconds means the request budget is the real constraint. Calls are batched and staggered against each venue's published limit with deliberate headroom, because the failure mode of getting this wrong isn't slowness — it's a rate-limit ban.

And venues go down. When one does, the dashboard doesn't blank: that venue's rows go stale-flagged with their last-good timestamp while the other four keep updating. Partial availability beats a spinner, every time. Undocumented schema changes ship without warning, so responses are validated on arrival and a shape change raises an alert before a user finds it.

5
exchange APIs, one model
142
instruments tracked
60s
full refresh cycle
4/5
venues still useful when one fails
health · venue status
live
binance
ok · 41ms
okx
ok · 88ms
hyperliquid
retry 2/4
coinbase
ok · 62ms
kraken
ok · 73ms
dlq depth
0
Start here

Tell me what keeps breaking

If something in your stack fails quietly, or someone on your team is copying data between two tools every week, send me the details. I'll tell you whether it's a two-hour fix or a real project before you spend anything.