Skip to content

API Reference

mdeguzis edited this page Jun 10, 2026 · 2 revisions

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.


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: <anon key>
Authorization: Bearer <anon key>   (or Bearer <session.access_token> 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:

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):

SUPABASE_DB_URL='postgresql://...' make backup-supabase

Clone this wiki locally