Mobile + Service APIs
The only surface that doesn’t emit HTML. Three controllers covering JSON endpoints for the iOS app and a small set of service-to-service integrations (webhooks in, webhooks out).
By the freeze, the iOS app had been unpublished from the App Store for about two years, so the /api/v1/* surface mostly served whatever legacy installations were still running. The service-API layer was actively used through to the freeze.
What it covers
/api/v1/*— JSON for the iOS app. Auth via a token issued at login, passed in theAuthorization: Bearerheader. Coverage matches the iOS feature set: boards, posts, profile, basic outreach./api/svc/*— service-to-service. Used by the in-house Whisper transcription job, the AI co-facilitator process (when it called out from a different host), and one or two analytics consumers.- Inbound webhooks — for Hunter.io enrichment results, Stripe Connect transfer status, and (historically) Calendly / Zoom callbacks.
Representative routes
| Method | Path | Handler | Notes |
|---|---|---|---|
| POST | /api/v1/auth/login | ApiAuthController.login | Email + password; returns bearer token. |
| POST | /api/v1/auth/refresh | ApiAuthController.refresh | Refresh long-lived session token. |
| GET | /api/v1/boards | ApiBoardController.list | Boards the authed user has access to. |
| GET | /api/v1/boards/:id | ApiBoardController.detail | |
| GET | /api/v1/boards/:id/posts | ApiBoardController.posts | Paginated; cursor in `since` param. |
| POST | /api/v1/boards/:id/posts | ApiBoardController.createPost | Plain-text body. |
| GET | /api/v1/profile | ApiProfileController.show | |
| PATCH | /api/v1/profile | ApiProfileController.update | |
| POST | /api/svc/transcribe/callback | SvcController.transcribeCallback | In-house Whisper job posts result back. |
| POST | /api/svc/hunter/callback | SvcController.hunterCallback | Email-enrichment results. |
| POST | /api/svc/stripe/webhook | SvcController.stripeWebhook | Connect transfer status updates. |
Headline flow: iOS login → board feed
Step 1: User opens iOS app; app POSTs /api/v1/auth/login with stored credentials.
Step 2: ApiAuthController.login verifies SHA-256 password hash, issues a
bearer token (random 48-byte hex), persists session row.
Step 3: App makes GET /api/v1/boards with the bearer token.
Step 4: ApiBoardController.list returns JSON list of boards the user can see.
Step 5: User taps a board; app GETs /api/v1/boards/:id and
/api/v1/boards/:id/posts in parallel.
Step 6: User posts a reply; app POSTs /api/v1/boards/:id/posts with text.
Step 7: Server fans out notifications (push and email) via dispenserd lane
`notify`.