{"openapi":"3.1.0","info":{"title":"Sweet Bridge","version":"0.6.0","description":"Sweet Bridge API — managed telemetry gateway for continuous glucose data via LibreLinkUp. Unofficial and unaffiliated with Abbott. Not for diagnosis or treatment. Default unit is mmol_l. History start is inclusive, end is exclusive."},"servers":[{"url":"http://localhost:8787","description":"Local wrangler dev"},{"url":"https://api.sweetbridge.dev","description":"Production API (api.sweetbridge.dev)"},{"url":"https://libre-data-connect.<account>.workers.dev","description":"Cloudflare Worker"}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key"},"BootstrapToken":{"type":"apiKey","in":"header","name":"X-Bootstrap-Token"}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"correlation_id":{"type":"string"}},"required":["code","message","correlation_id"]}}},"NormalizedReading":{"type":"object","properties":{"connection_id":{"type":"string"},"measured_at":{"type":"string","format":"date-time"},"value":{"type":"number"},"unit":{"type":"string","enum":["mg_dl","mmol_l"]},"value_mg_dl":{"type":"integer"},"value_mmol_l":{"type":"number"},"trend":{"type":"string","enum":["down_fast","down_slow","stable","up_slow","up_fast","unknown"]},"range_flag":{"type":"string","enum":["in_target","high","very_high","low","unknown"]},"sensor":{"type":"object","properties":{"state":{"type":"string"}}},"staleness_seconds":{"type":"integer"},"watermark":{"type":"string","format":"date-time","nullable":true}}}}},"paths":{"/health":{"get":{"summary":"Health check","security":[],"responses":{"200":{"description":"OK"}}}},"/":{"get":{"summary":"Status + disclaimer","security":[],"responses":{"200":{"description":"OK"}}}},"/v1/me":{"get":{"summary":"Current Account identity (account_id, email, plan, optional Access email)","description":"Account = users row. API key owner is the Account. Includes plan + subscription_status. plan=none → 403. Access email is present only when Worker JWT verification is enabled.","security":[{"ApiKeyAuth":[]}],"responses":{"200":{"description":"OK"},"401":{"description":"Unauthorized"}}}},"/v1/api-keys":{"get":{"summary":"List API keys (prefix only)","security":[{"ApiKeyAuth":[]}],"responses":{"200":{"description":"OK"}}},"post":{"summary":"Create API key for the authenticated Account (bootstrap first Account requires X-Bootstrap-Token in production)","responses":{"201":{"description":"Created (full key once)"},"401":{"description":"Unauthorized"}}}},"/v1/api-keys/{id}/revoke":{"post":{"summary":"Revoke an API key","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Revoked"},"404":{"description":"Not found"}}}},"/v1/admin/invites":{"get":{"summary":"List invites (operator; X-Bootstrap-Token)","security":[{"BootstrapToken":[]}],"responses":{"200":{"description":"OK"},"401":{"description":"Unauthorized"}}},"post":{"summary":"Mint invite token (operator; shown once)","security":[{"BootstrapToken":[]}],"responses":{"201":{"description":"Created"},"401":{"description":"Unauthorized"}}}},"/v1/admin/invites/{id}/revoke":{"post":{"summary":"Revoke unused invite","security":[{"BootstrapToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Revoked"},"404":{"description":"Not found"}}}},"/v1/invites/redeem":{"post":{"summary":"Redeem invite → Account + first API key (fail closed; no open signup)","security":[],"responses":{"201":{"description":"Created (api_key once)"},"403":{"description":"Invalid/expired/reused"},"422":{"description":"Validation"}}}},"/v1/connections":{"get":{"summary":"List connections","security":[{"ApiKeyAuth":[]}],"responses":{"200":{"description":"OK"}}},"post":{"summary":"Create LibreLinkUp follower connection","security":[{"ApiKeyAuth":[]}],"responses":{"201":{"description":"Created"},"409":{"description":"Need patient_id or MFA"},"502":{"description":"Upstream error"}}}},"/v1/connections/{id}":{"get":{"summary":"Get connection (includes latest_run summary)","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"404":{"description":"Not found"}}},"delete":{"summary":"Delete connection","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted"}}}},"/v1/connections/{id}/refresh":{"post":{"summary":"Manual refresh — creates ingest run with trigger=manual (1/min)","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"429":{"description":"Rate limited"}}}},"/v1/glucose/current":{"get":{"summary":"Current reading (default unit=mmol_l; includes staleness_seconds + watermark)","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"connection_id","in":"query","required":true,"schema":{"type":"string"}},{"name":"unit","in":"query","schema":{"type":"string","enum":["mmol_l","mg_dl"],"default":"mmol_l"}}],"responses":{"200":{"description":"OK"}}}},"/v1/glucose/graph":{"get":{"summary":"Graph points (default unit=mmol_l)","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"connection_id","in":"query","required":true,"schema":{"type":"string"}},{"name":"unit","in":"query","schema":{"type":"string","enum":["mmol_l","mg_dl"],"default":"mmol_l"}}],"responses":{"200":{"description":"OK"}}}},"/v1/glucose/logbook":{"get":{"summary":"Logbook events (default unit=mmol_l)","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"connection_id","in":"query","required":true,"schema":{"type":"string"}},{"name":"unit","in":"query","schema":{"type":"string","enum":["mmol_l","mg_dl"],"default":"mmol_l"}}],"responses":{"200":{"description":"OK"}}}},"/v1/glucose/history":{"get":{"summary":"Stored history (start inclusive, end exclusive; default unit=mmol_l; capped ~2000)","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"connection_id","in":"query","required":true,"schema":{"type":"string"}},{"name":"start","in":"query","required":true,"schema":{"type":"string","format":"date-time"}},{"name":"end","in":"query","required":true,"schema":{"type":"string","format":"date-time"}},{"name":"unit","in":"query","schema":{"type":"string","enum":["mmol_l","mg_dl"],"default":"mmol_l"}},{"name":"cursor","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","maximum":2000}}],"responses":{"200":{"description":"OK — { points, next_cursor }"}}}},"/v1/webhooks":{"get":{"summary":"List webhooks (URL host only; secrets never returned)","security":[{"ApiKeyAuth":[]}],"responses":{"200":{"description":"OK"}}},"post":{"summary":"Create webhook (secret shown once, stored encrypted)","security":[{"ApiKeyAuth":[]}],"responses":{"201":{"description":"Created"}}}},"/v1/webhooks/{id}":{"delete":{"summary":"Delete webhook","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted"}}}},"/v1/ingest/runs":{"get":{"summary":"List ingest runs","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"connection_id","in":"query","schema":{"type":"string"}},{"name":"status","in":"query","schema":{"type":"string"}},{"name":"source","in":"query","schema":{"type":"string"}},{"name":"q","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer"}}],"responses":{"200":{"description":"OK"}}}},"/v1/ingest/runs.csv":{"get":{"summary":"CSV export of ingest runs","security":[{"ApiKeyAuth":[]}],"responses":{"200":{"description":"text/csv"}}}},"/v1/ingest/runs/{id}":{"get":{"summary":"Get ingest run detail","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}}},"/v1/ingest/runs/{id}/retry":{"post":{"summary":"Retry failed run (creates new run, trigger=manual)","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"Created"}}}},"/v1/ingest/stats":{"get":{"summary":"Ingest run stats","security":[{"ApiKeyAuth":[]}],"responses":{"200":{"description":"OK"}}}}}}