# API Reference Supabase REST endpoints used by the proton-pulse-web site and the decky-proton-pulse plugin. For plugin-only APIs (Python callables, Steam CEF), see the [decky-proton-pulse wiki](https://github.com/mdeguzis/decky-proton-pulse/wiki/API-Reference). --- ## Supabase REST API **Base URL:** `https://ilsgdshkaocrmibwdezk.supabase.co/rest/v1` **Anon key:** `sb_publishable_3Oqhm4JneafJNQw9BuUaxw_L9qZa-5V` (safe to embed in client) All requests carry: ``` apikey: Authorization: Bearer (or Bearer for authenticated web requests) ``` --- ### `user_configs` — Pulse native reports (web) One row per `client_id + app_id`. Source of truth for reports submitted via the web app. #### GET — fetch reports for a game ``` GET /user_configs ?app_id=eq.{appId} &select=id,client_id,app_id,title,cpu,gpu,gpu_driver,gpu_vendor,ram,os,kernel, proton_version,rating,duration,notes,vram_mb,created_at,source &order=created_at.desc ``` Used by: `app.js → fetchNativeReports()` #### GET — report count (home stat) ``` GET /user_configs?select=count Headers: Prefer: count=exact ``` Used by: `index.js → loadPulseStats()` #### POST — submit / upsert report ``` POST /user_configs?on_conflict=client_id,app_id Headers: Prefer: resolution=merge-duplicates,return=minimal Authorization: Bearer {session.access_token} Body: { client_id, app_id, title, cpu, gpu, gpu_driver, gpu_vendor, ram, os, kernel, proton_version, duration, rating, notes, launch_options, enabled_vars, confidence_score, source, vram_mb } ``` Used by: `app.js → submitReport()` (web, authenticated) Used by: `src/lib/userConfigs.ts → submitUserConfig()` (plugin, adds `cpu_cores`, `display_resolution`, `steam_deck_model`) #### DELETE — user deletes own report ``` DELETE /user_configs ?client_id=eq.{clientId}&app_id=eq.{appId} Headers: x-client-id: {clientId} ``` Used by: `app.js` delete-report-btn, `src/lib/userConfigs.ts → deleteMyReport()` --- ### `user_proton_configs` — plugin-synced configs One row per `voter_id + app_id`. Stores the full `TrackedConfig` JSON blob. #### GET — home page recent configs ``` GET /user_proton_configs ?select=id,voter_id,app_id,app_name,config,updated_at &order=updated_at.desc ``` Used by: `app.js → renderHomePage()` #### GET — search configs ``` GET /user_proton_configs ?select=id,voter_id,app_id,app_name,config,updated_at &order=updated_at.desc&limit=60 &or=(app_id.eq.{q},app_name.ilike.*{q}*) // numeric query &app_name=ilike.*{q}* // text query ``` Used by: `app.js → fetchMatchingPulseConfigs()` #### GET — configs for a specific game ``` GET /user_proton_configs ?app_id=eq.{appId} &select=id,voter_id,app_id,app_name,config,updated_at &order=updated_at.desc ``` Used by: `app.js → fetchSupabase()` #### GET — fetch own configs (plugin) ``` GET /user_proton_configs ?select=voter_id,app_id,app_name,config,updated_at &voter_id=eq.{voterId} ``` Used by: `src/lib/cloudSync.ts → fetchCloudConfigs()` #### GET — check cloud backup exists (plugin) ``` GET /user_proton_configs ?select=app_id&voter_id=eq.{voterId}&limit=1 Headers: Range: 0-0 ``` Used by: `src/lib/cloudSync.ts → checkHasCloudBackup()` #### POST — push / upsert config (plugin) ``` POST /user_proton_configs?on_conflict=voter_id,app_id Headers: Prefer: resolution=merge-duplicates,return=minimal Body: { voter_id, app_id, app_name, config: TrackedConfig, updated_at } ``` Used by: `src/lib/cloudSync.ts → pushConfig()` #### DELETE — user deletes own config ``` DELETE /user_proton_configs ?voter_id=eq.{voterId}&app_id=eq.{cfgAppId} Headers: x-client-id: {voterId} ``` Used by: `app.js` delete-cfg-btn --- ### `report_votes` — per-user votes on reports/configs One row per `voter_id + app_id + report_key`. `report_key` format: - Reports: `{timestamp}:{gpu.slice(0,20)}:{protonVersion.slice(0,15)}` - Configs: `cfg:{configId}` #### GET — fetch user's votes for a game ``` GET /report_votes ?voter_id=eq.{voterId}&app_id=eq.{appId} &select=report_key,vote ``` Used by: `app.js → fetchUserVotes()` #### GET — fetch single existing vote (plugin) ``` GET /report_votes ?select=vote&voter_id=eq.{voterId}&app_id=eq.{appId}&report_key=eq.{reportKey} Headers: Accept: application/vnd.pgrst.object+json ``` Used by: `src/lib/voting.ts → fetchExistingVote()` #### POST — create new vote ``` POST /report_votes?on_conflict=voter_id,app_id,report_key Headers: Prefer: resolution=merge-duplicates,return=minimal Body: { voter_id, app_id: string, report_key, vote: 1 | -1 } ``` Used by: `app.js → castVote()`, `src/lib/voting.ts → submitVote()` #### PATCH — change existing vote ``` PATCH /report_votes ?voter_id=eq.{voterId}&app_id=eq.{appId}&report_key=eq.{reportKey} Headers: Prefer: return=minimal Body: { vote: 1 | -1 } ``` Used by: `app.js → castVote()`, `src/lib/voting.ts → submitVote()` --- ### `report_vote_totals` — aggregated vote counts (read-only view) #### GET — vote totals for a game ``` GET /report_vote_totals ?app_id=eq.{appId} &select=report_key,upvotes,downvotes ``` Used by: `app.js → fetchVotes()`, `src/lib/voting.ts → getVoteTotals()` --- ### `user_plugin_settings` — per-user plugin settings backup One row per `voter_id + plugin_id`. #### POST — push settings ``` POST /user_plugin_settings?on_conflict=voter_id,plugin_id Headers: Prefer: resolution=merge-duplicates,return=minimal Body: { voter_id, plugin_id: "proton-pulse", payload: LocalDataBackupPayload, updated_at } ``` Used by: `src/lib/cloudSync.ts → pushPluginSettings()` #### GET — fetch settings ``` GET /user_plugin_settings ?select=voter_id,plugin_id,payload,updated_at &voter_id=eq.{voterId}&plugin_id=eq.proton-pulse&limit=1 Headers: Range: 0-0 ``` Used by: `src/lib/cloudSync.ts → fetchCloudPluginSettings()` --- ### `config_playtime` — per-session playtime tracking (plugin) #### POST — record session start ``` POST /config_playtime Headers: Prefer: return=representation Query: ?select=id Body: { voter_id, app_id, config_key, proton_version, source, session_start: ISO8601, duration_minutes: 0 } ``` #### PATCH — update session end ``` PATCH /config_playtime?id=eq.{rowId} Headers: Prefer: return=minimal Body: { session_end: ISO8601, duration_minutes: number } ``` #### POST — insert completed session (fallback, no row ID) ``` POST /config_playtime Headers: Prefer: return=minimal Body: { voter_id, app_id, config_key, proton_version, source, session_start, session_end, duration_minutes } ``` Used by: `src/lib/playtime.ts` --- ### `config_playtime_totals` — aggregated playtime (read-only view) ``` GET /config_playtime_totals ?app_id=eq.{appId} &select=config_key,total_minutes,session_count,unique_players ``` Used by: `src/lib/playtime.ts → getConfigPlaytimeTotals()` --- ### Edge Function: `steam-callback` **URL:** `https://ilsgdshkaocrmibwdezk.supabase.co/functions/v1/steam-callback` Handles the Steam OpenID return leg: 1. Verifies the OpenID assertion against `https://steamcommunity.com/openid/login` 2. Fetches the Steam profile from the Steam Web API 3. Uses the **service role key** (server-side only) to upsert a Supabase Auth user keyed by `steam_{steamId}@steam.protonpulse.local` 4. Returns `access_token` + `refresh_token` in the redirect URL fragment Auth level: service role (never expose this key client-side). --- ## Table Summary | Table | GET | POST | PATCH | DELETE | Who | |---|---|---|---|---|---| | `user_configs` | ✓ | ✓ | — | ✓ | Web + Plugin | | `user_proton_configs` | ✓ | ✓ | — | ✓ | Web + Plugin | | `report_votes` | ✓ | ✓ | ✓ | — | Web + Plugin | | `report_vote_totals` | ✓ | — | — | — | Web + Plugin | | `user_plugin_settings` | ✓ | ✓ | — | — | Plugin | | `config_playtime` | — | ✓ | ✓ | — | Plugin | | `config_playtime_totals` | ✓ | — | — | — | Plugin | --- ## Running Supabase Commands Locally The Supabase CLI binary does not run on Android/Termux (requires a non-PIE Linux environment). Use the REST API directly via `curl` or the `psql`/`pg_dump` tools installed by `make install-pg`. **Ad-hoc REST queries:** ```bash SB="https://ilsgdshkaocrmibwdezk.supabase.co/rest/v1" KEY="sb_publishable_3Oqhm4JneafJNQw9BuUaxw_L9qZa-5V" # count reports curl -s -H "apikey: $KEY" -H "Prefer: count=exact" "$SB/user_configs?select=count" # fetch configs for a game curl -s -H "apikey: $KEY" "$SB/user_proton_configs?app_id=eq.1245620&select=app_name,config" ``` **Database backup** (`SUPABASE_DB_URL` from Supabase dashboard → Settings → Database → URI): ```bash SUPABASE_DB_URL='postgresql://...' make backup-supabase ``` ---