/api/wearables. Instead of each provider exposing a separate storage model, Ritual keeps a shared connection layer, canonical samples/events, and sync run history, then projects matched data back into habits and analytics.
Legacy Whoop-specific routes still exist for older clients, but new integrations should use the unified wearable endpoints below.
Financial integrations currently use a separate API under /api/financial. Plaid-backed spending sync is not modeled as a wearable feed. Instead, Ritual ingests connection, account, and transaction data, computes daily spending totals, and projects those totals into a managed Spending habit.
Unified wearables API
Core endpoints
OAuth, callback, and webhook endpoints
Canonical model
When a canonical sample or event matches a habit with the same
integration_source and metric_type, Ritual automatically projects it into habit_logs and forwards the compatible record to Tinybird when enabled.
Provider matrix
Plaid (bank connections for spending tracking)
Plaid is the first financial integration in Ritual. It is used to help users track spending behavior, not to provide a full banking or budgeting product. In the current build, Ritual:- creates a Plaid Link token on the backend
- exchanges the public token for an access token server-side
- stores financial connections, accounts, and normalized transactions
- backfills available transaction history
- computes one positive daily spending total per day
- projects that total into a managed
Spendinghabit
Financial integration API
Current spending rules
Plaid-backed spending is intentionally narrow in scope. In the current build, Ritual counts:- posted outflows on depository accounts
- positive daily spending totals only
- pending transactions
- transfers
- loan-like and credit-card payment transactions
- non-depository accounts from the daily spending rollup
Current connect and sync behavior
Data Ritual stores from Plaid
Ritual stores:- connection status and institution metadata
- account metadata such as name, mask, type, and subtype
- normalized transactions including date, amount, merchant/transaction name, direction, pending state, and provider category/code metadata
- derived daily spending habit logs and sync metadata
Current scope and limitations
The current Plaid integration should be documented as:- Plaid-only financial connectivity
- spending tracking, not budgeting or accounting
- daily rollups into a
Spendinghabit - best suited for supported Plaid institutions and available transaction history
Apple Health (HealthKit via iOS Companion)
Apple Health uses the Ritual iOS companion app, not OAuth. The device registers once, stores a device secret in Keychain, and signs every ingest request with HMAC-SHA256. The backend enforces idempotency withclient_event_id.
Apple Health endpoints
Incremental sync behavior
/api/wearables/apple/ingest/v2 is the current path for HealthKit sync. The client sends:
Apple Health metric mapping
The iOS app syncs provider metric types such assteps, hr, hrv, sleep_session, and workout. Ritual stores provider-neutral canonical names where possible.
Whoop
Whoop is connected through the unified wearables connection flow, but the actual sync still uses the provider-specific Whoop service under the hood.OAuth scopes
offline read:recovery read:sleep read:workout read:cycles read:profile
Whoop endpoints Ritual calls
Whoop sync behavior
Canonical Whoop data Ritual stores
Oura Ring
Oura is now part of the unified cloud wearable flow. Ritual exchanges OAuth tokens, refreshes them automatically, fetches recent collections, and normalizes the results into shared samples and events.OAuth scopes
email personal daily heartrate session workout tag
Oura endpoints Ritual calls
Oura sync behavior
Canonical Oura data Ritual stores
Garmin
Garmin is connected through unified OAuth plus a webhook ingest path. The manual sync endpoint is mainly an account refresh step that loads the provider user id and permissions; actual activity data is expected to arrive through Garmin webhooks.OAuth and webhook flow
If
GARMIN_WEBHOOK_SECRET is configured, the webhook requires the x-garmin-webhook-secret header.