Civiqapp architecture

Server API reference

Every call the app makes to the Civiq backend — 43 in all — with its verb, the code that calls it, and which auth layer it rides on.

Reading the auth column: device means the request carries device-attestation headers (per-request App Attest assertions on iOS, the session token on Android — see Attestation); device + bearer adds the user's Authorization: Bearer token on top; none marks the two endpoints that mint attestation and therefore cannot require it. The transport applies one automatic retry after recovery on 403 (attestation) and 401 (token refresh) for every call below.

Device attestation

PathCallerAuth
GET/device/challengefetchDeviceChallenge — ceremony on both platforms, plus every iOS signed requestnone
POST/device/attestDeviceCheckService / AndroidIntegrityService._verifyAttestationnone

Congressional & state data

PathCallerAuth
GET/representatives/congress/statusRepresentativesService._getCongressStatus — five dataset SHAsdevice
GET/representatives/congress/datalegislators sync (file-cached)device
GET/representatives/congress/officesoffices sync (file-cached)device
GET/representatives/congress/socialssocials sync (file-cached)device
GET/representatives/congress/committeescommittees sync (file-cached)device
GET/representatives/congress/committee-membershipsmemberships sync (file-cached; JSON object)device
GET/representatives/states/status?states=…&districts=all_getPeopleStateStatus — per-state SHAdevice
GET/representatives/states?states=…&districts=…_insertPeopleStateData — state legislator slicedevice
GET/districts/{STATE}DistrictBoundaryService._load — GeoJSON, ETag-cacheddevice
POST/geocodio/districtGeocodioService.getGeocodioInfo — district resolutiondevice

Bills & images

PathCallerAuth
POST/representatives/bills/highlight_queryBillsHighlights{representative_id}device
POST/representatives/imagesfetchRepresentativeImages — batch ≤ 100 uuidsdevice
GET/representatives/images/{uuid}ImageUtil — per-rep fallbackdevice
POST/representatives/photo-hintImageUtil — Wikipedia-title signal, fire-and-forgetdevice

Contact delivery & AI

PathCallerAuth
POST/representatives/contactContactService.sendMessagedevice + bearer
GET/representatives/contact/{message_id}ContactService.getMessage — 3 s polldevice + bearer
GET/representatives/contactContactService.listMessages — no UI caller yetdevice + bearer
POST/ai-emailAiService.generateEmail — proxied DeepSeek, non-streamingdevice

Account

PathCallerAuth
POST/account/signupsignUpWithEmaildevice
POST/account/signinsignInWithEmaildevice
POST/account/oauth/googlesignInWithGoogledevice
POST/account/oauth/applesignInWithAppledevice
POST/account/refreshrefreshTokens — rotates the pairdevice
POST/account/signoutsignOut — failure ignored, local state cleareddevice + bearer
GET/account/profilefetchProfiledevice + bearer
PUT/account/profileupdateProfile — name / ZIPdevice + bearer
POST/account/photouploadPhoto — raw JPEG bytesdevice + bearer
GET/account/photoloadPhotodevice + bearer
DELETE/account/photodeletePhotodevice + bearer
DELETE/accountdeleteAccount — server must succeed firstdevice + bearer

Community

PathCallerAuth
GET/community/hubsloadHubsdevice + bearer
GET/community/posts?hub=…&cursor=…loadPosts — cursor paginationdevice + bearer
POST/community/postscreatePost — optional bill/rep anchordevice + bearer
DELETE/community/posts/{post_id}deletePost — author onlydevice + bearer
GET/community/posts/{post_id}/commentsloadComments — flat list, depth fielddevice + bearer
POST/community/posts/{post_id}/commentsaddCommentdevice + bearer
DELETE/community/comments/{comment_id}deleteComment — author onlydevice + bearer
PUT/community/likessetPostLike / setCommentLike — optimisticdevice + bearer
POST/community/reportsreportdevice + bearer

Reporting

PathCallerAuth
POST/reports/bugsubmitManualBugReport — 202 Accepteddevice
POST/reports/crashsubmitPendingCrashReport — consent-gateddevice

Adjacent traffic not in the count

Map tiles are fetched from the server's own proxy at /tiles/{z}/{x}/{y} by the map widget's tile layer (keeping the MapTiler key server-side), with a keyless CartoCDN basemap as fallback after five consecutive tile errors. Three third-party hosts are reached directly — the unitedstates.github.io congress images, the Wikipedia pageimages API, and CartoCDN tiles — always through a separate HTTP client so attestation and account headers never leave the Civiq API, HTTPS-only, with a 10-second timeout.

Transport behavior common to all calls: no client-side timeouts on Civiq API calls (only third-party image fetches have one); success on any 2xx for writes (202 for reports, 204 for likes); one automatic retry after 401/403 recovery; requests serialized per path+query through 256 striped locks; no multipart anywhere — the single binary upload (profile photo) posts raw bytes.