flowchart LR V[Vendor API] -->|paged pull / webhook| C[Vendor client] C --> N[Normalise to internal model] N --> Q[(Task queue)] Q --> U[Idempotent upsert] U --> DB[(App DB)] V -. webhook .-> WH[Webhook endpoint] WH --> Q
Integrating Real Healthcare APIs: Cliniko, Nookal, Snapforms
This is a patterns post. It deliberately contains no client data, no endpoints with real records, no keys, and no PII - just the integration shapes that recur across Cliniko, Nookal, and Snapforms. Treat every credential as radioactive and every payload as PHI.
Reference material lives in the WEB_doc and Software_Tools repos.
- Docs hub: https://bthek1.github.io/WEB_doc
The three APIs, side by side
Different vendors, same integration muscles. What changes is auth, how they paginate, and whether they push events to you.
| API | Domain | Auth | Pagination | Rate limits | Webhooks |
|---|---|---|---|---|---|
| Cliniko | Practice management | API key (Basic, base64) | Link headers / page URLs | Per-account throttle | Yes |
| Nookal | Practice management | API key + signed params | Page + page_length | Documented ceiling | Limited |
| Snapforms | Online forms | API key | Submission cursor | Modest | Yes (submission events) |
Rule 1: never hardcode keys. Env vars / secret manager only - see Secrets in Django.
A safe integration architecture
The shape that has served me across all three: a thin vendor client, a normaliser into a common internal model, an idempotent upsert, and a queue so a slow vendor never blocks a request.
Sync: pull vs webhook
Two ways data arrives. Poll for the initial backfill; subscribe to webhooks for deltas. Always reconcile - webhooks get missed.
sequenceDiagram participant App participant Vendor App->>Vendor: GET /records?page=1 (backfill) Vendor-->>App: page 1 ... n (cursor) Vendor->>App: webhook: record.updated App->>App: enqueue + idempotent upsert App->>Vendor: nightly reconcile (missed events)
Rate limits are a budget, not a suggestion
Backfills are where you get throttled. Respect Retry-After, add jitter, and cap concurrency per vendor. The chart shows why naive parallelism backfires - past the vendor’s ceiling, throughput collapses into retries.
Checklist that keeps data safe
| Concern | Practice |
|---|---|
| Credentials | Secret manager / env vars; never in notebooks or git |
| PII / PHI | Minimise, encrypt at rest, never log payloads |
| Idempotency | Upsert on a stable external id; dedupe webhooks |
| Failure | Retry with backoff + jitter; dead-letter queue |
| Auditing | Log that a record synced, never what |
More on the mechanics (auth, retries, queues) in the API reference notebooks under Software_Tools and WEB_doc.