Tidal
The Tidal connector syncs listening history from your Tidal account into
the tidal_listens substrate table. The manifest is tier: "core",
so the Desktop client surfaces a Tidal configuration card alongside
Google + GitHub by default.
Highlights
Section titled “Highlights”- Substrate-first. Every play lands in
tidal_listensunconditionally; sync rules only control the additional routing (folio commits, daily-note injection, harvest). This is the same model as Strava — substrate is always populated socarabase_query_timeseriescan answer trend questions even when no rule matches. - Multi-account. A workspace can hold multiple Tidal accounts (a personal account + a family member). The OAuth callback rejects cross-account row mutations.
- Daily + weekly emitters.
30 4 * * *(daily) and0 5 * * 0(Sundays, weekly) — pre-aggregated artifact summaries with play count- total listening duration. Bucket keys include
account_idso a personal + family account can’t collide.
- total listening duration. Bucket keys include
- Per-account hard-delete. Disconnect via
DELETE /api/v1/auth/tidalflips the integration row tois_active=falsetokenStatus='revoked'; substrate rows are NOT deleted by default. Use the per-account hard-delete on the Connections page to purge everything (runs the manifest’spurgeAccountSubstratehook).
See the Tidal OAuth setup guide in the repo for OAuth Developer Application registration, scopes, the PKCE flow, multi-account semantics, and re-auth handling.
Sync cadence
Section titled “Sync cadence”tidal-sync cron at 0 * * * * (hourly). Tidal’s recently-played
feed updates near-real-time and the listing endpoint is well within
the per-app rate budget.
Filter shape
Section titled “Filter shape”The per-rule filter type TidalFilters lives in
src/types/sync-rules.ts. Key fields:
minTrackDurationSeconds— drop plays shorter than this. Default 30 (filters out 5-second skips and brief previews). NULL-duration plays are also dropped.excludeArtists— case-insensitive substring match againstartist_canonical_name.injectMode—"none"/"daily_rollup"/"per_listen". The default"none"keeps per-listen rows in substrate only and lets the daily emitter produce the artifact surface.syncLibrary+libraryFolio— declared on the type for a future opt-in mirror of saved albums / playlists / tracks into a folio README. Not honored by the current sync path — the library-sync branch is explicitly deferred; setting these fields today silently does nothing.
Substrate columns
Section titled “Substrate columns”The tidal_listens table carries one row per play event:
(workspace_id, account_id, external_id) UNIQUE, plus played_at
(timeseries column), track_id, track_title, artist_canonical_name
(lowercase, used for matching), album_title, duration_seconds, and
raw_payload (full Tidal response). See the database schema
reference.