External Integrations
Video conferencing, file storage, analytics—integrated with security boundaries between organizations.
What this enables
Video appointments: Patients join secure video calls for telehealth appointments (powered by Daily.co).
File uploads: Patients and specialists upload documents, prescriptions, images—stored securely in S3 with encryption.
Analytics & telemetry: Track exercise completion, patient adherence, program outcomes in dashboards—powered by internal Telemetry service.
Multi-clinic isolation: Each clinic's data completely separate—patient from Clinic A cannot see Clinic B's video rooms or files.
How it works
- Daily.co (video): Patient books appointment → system creates secure video room → patient joins with private token → call recorded (optional)
- S3 (storage): Specialist uploads consultation PDF → stored encrypted in S3 → patient downloads PDF (only from their clinic's folder)
- Telemetry (analytics): Every exercise completed, every appointment → recorded automatically → dashboards show program outcomes
Technical Reference
Overview
This directory documents the Core API's integrations with external services and sibling systems.
The Core API integrates with the following external services:
- Daily.co - Videocall room management for appointments
- AWS S3 - File storage for uploads, documents, and signatures
- Telemetry Service - Sibling Go service for audit, analytics, and rehab tracking
Important Notes
Scheduling is NOT an External Integration
Scheduling is not an external integration — it is part of the Core API. The availability engine, hold system, and booking flow run inside the Core API's Go binary, using the Core API's database and Redis. See ../scheduling/ for the full specification.
This was previously documented as an HTTP client for a standalone "Intakes" microservice. That service has been merged into the Core API. There is no HTTP client, no API keys, no separate database. The scheduling domain is accessed via internal Go function calls, not HTTP.
Webhooks
The Core API has a generic, org-scoped webhook system that delivers real-time event notifications to URLs configured by each organization. This replaces any vendor-specific integration — the Core API doesn't know or care whether the receiver is Make.com, Zapier, n8n, or a custom API.
See ../webhooks/ for the full specification: database schema, event types, payload format, HMAC signing, retry strategy, and API endpoints.
Cross-Tenant Integration Isolation
RLS protects the database layer, but every external integration must also enforce tenant boundaries. A bug in integration code should not leak org A's data to org B.
Isolation Matrix
| Integration | Shared Resource | Isolation Mechanism | Risk Without Isolation |
|---|---|---|---|
| Daily.co | Single API key, shared account | Room name includes org ID. Meeting tokens scoped per room. | User from org A joins a room created for org B |
| S3 | Single bucket | Key prefix per org: {org_id}/... Signed URLs are key-specific. | User from org A accesses org B's uploaded file |
| Scheduling | Same database | RLS on all scheduling tables (appointment_types, specialist_weekly_hours, etc.). Redis hold keys are scoped by appointment type ID (which is org-scoped). | RLS prevents cross-org access at the database level. No HTTP boundary to cross. |
| Webhooks | Per-org subscriptions | Each org registers its own URLs. Events are org-scoped. Payloads contain no PII (see ../webhooks/). | Org A's events never reach org B's subscriptions |
| Telemetry Service | Shared internal service (private network) | Audit events include organization_id. Telemetry is append-only. Read access is via Core API (RLS-filtered). | Telemetry stores org-scoped data. No public API — only reachable within the AWS VPC |
Integration Isolation Verification Checklist
Before production launch, verify:
- [ ] Daily.co room names include
orgIDprefix - [ ] Daily.co meeting tokens are used instead of room names in client responses
- [ ] S3 keys always start with
{orgID}/prefix - [ ] S3
orgScopedKey()rejects path traversal attempts - [ ] S3 Block Public Access is enabled
- [ ] S3 bucket policy denies unencrypted uploads
- [ ] Scheduling tables have RLS policies scoped to
current_app_org_id() - [ ] Webhook payloads contain zero PII (audit manually — see ../webhooks/)
- [ ] the Core API audit events include
organization_idand are only readable via the Core API's RLS-filtered API - [ ] Telemetry service is only reachable within the AWS VPC (no public endpoint)
- [ ] Telemetry ClickHouse analytics data contains hashed actor IDs, not PII
- [ ] No integration client accepts
orgIDfrom request parameters — always useuser.CurrentOrganizationIDfrom auth context
Integration Testing
// internal/integration/isolation_test.go
func TestS3OrgIsolation(t *testing.T) {
// Upload file as org 1
key1, _ := s3Client.Upload(ctx, 1, "uploads/test.pdf", body, "application/pdf")
assert.True(t, strings.HasPrefix(key1, "1/"))
// Attempt to access org 1's file as org 2 — should fail
_, err := s3Client.GetSignedURL(ctx, 2, "uploads/test.pdf")
// Returns signed URL for "2/uploads/test.pdf" which doesn't exist — no cross-org access
// Attempt path traversal — should fail
_, err = s3Client.Upload(ctx, 2, "../1/uploads/test.pdf", body, "application/pdf")
assert.Error(t, err) // Rejected by orgScopedKey()
}
func TestDailyCoRoomIsolation(t *testing.T) {
room, _ := dailyClient.CreateRoom(ctx, 1, 102, expiry)
assert.Equal(t, "restartix-1-102", room.Name) // Includes org ID
// Meeting token is scoped to specific room
token, _ := dailyClient.CreateMeetingToken(ctx, room.Name, 42, expiry)
assert.NotEmpty(t, token)
// Token can only join "restartix-1-102", not "restartix-2-102"
}Files in This Directory
- dailyco.md - Daily.co videocall integration, room creation, meeting tokens, isolation
- s3.md - AWS S3 file storage, upload/download, signed URLs, bucket structure, isolation
- telemetry.md - Telemetry service integration, audit forwarding, analytics, shared types
- migration.md - Data migration from legacy Strapi + Intakes databases, cutover procedure