RPC Gateway
The RPC Gateway is a public-facing service that exposes Flowstate’s backend functionality via Connect RPC, a modern HTTP-based RPC framework compatible with gRPC. It’s designed for desktop and mobile clients that need structured, type-safe API access.
Overview
Section titled “Overview”The RPC Gateway runs as a separate service but shares the same codebase as the backend REST API. It only exposes the /rpc endpoint and /health check.
Key Differences from REST API:
| Feature | REST API | RPC Gateway |
|---|---|---|
| Transport | HTTP/JSON | Connect RPC (HTTP/Protobuf or JSON) |
| Authentication | Cookie-based JWT | Bearer token in Authorization header |
| Exposure | Internal only (via proxy) | Public-facing |
| Clients | Web application | Desktop, mobile, CLI tools |
| CORS | Restricted origins | All origins allowed |
Base URL
Section titled “Base URL”https://rpc.goflowstate.com/rpcAuthentication
Section titled “Authentication”The RPC Gateway uses Bearer token authentication with a three-tier system:
1. Public Methods (No Auth Required)
Section titled “1. Public Methods (No Auth Required)”These methods can be called without any authentication:
AuthService.RequestLogin- Request magic linkAuthService.PollLoginStatus- Poll for login completionAuthService.GetAuthorizeInfo- Get authorization infoAuthService.AuthorizeLogin- Complete login flowAuthService.ConfirmDeletion- Confirm account deletionAuthService.Ping- Health checkOfficesService.GetInviteInfo- Get office invite detailsAuthService.GetConfig- Get public configuration
2. Optional Auth
Section titled “2. Optional Auth”These methods work with or without authentication, returning different data based on auth status:
RoomsService.ListRooms- Public rooms (no auth) vs user’s rooms (auth)OfficesService.ListOffices- Public offices (no auth) vs user’s offices (auth)
3. Required Auth
Section titled “3. Required Auth”All other methods require a valid Bearer token in the Authorization header:
Authorization: Bearer <jwt_token>Example Request:
curl -X POST https://rpc.goflowstate.com/rpc/flowstate.rooms.v1.RoomsService/GetRoom \ -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \ -H "Content-Type: application/json" \ -d '{"roomId": "room_abc123"}'Protocol
Section titled “Protocol”The gateway uses Connect RPC, which supports:
- HTTP/1.1 and HTTP/2 - Works with standard HTTP infrastructure
- JSON and Protobuf - Flexible serialization (JSON for debugging, Protobuf for performance)
- Streaming - Server streaming, client streaming, bidirectional streaming
- Type safety - Generated TypeScript/Swift/Kotlin clients from Protocol Buffer definitions
Content Types
Section titled “Content Types”The gateway accepts both JSON and Protobuf:
Content-Type: application/json (human-readable, debugging)Content-Type: application/proto (binary, production)Services
Section titled “Services”The gateway exposes 9 services with 53 RPC methods:
| Service | Methods | Description |
|---|---|---|
AuthService | 9 | Login, polling, session management, configuration |
UsersService | 4 | User profiles, display info, search |
RoomsService | 5 | Room CRUD, listing, membership |
SparksService | 6 | Spark creation, updates, deletion, positioning |
OfficesService | 10+ | Office management, invites, membership, settings |
ShelvesService | 6 | Shelf and link management, ordering |
CanvasService | 5 | Position updates, layout computation, collision detection |
PassportService | 8+ | Profile management, CV import, skills, experience |
AgoraService | 2 | Video/voice token generation, configuration |
AuthService
Section titled “AuthService”Handles authentication and session management.
Methods:
service AuthService { rpc RequestLogin(RequestLoginRequest) returns (RequestLoginResponse); rpc PollLoginStatus(PollLoginStatusRequest) returns (PollLoginStatusResponse); rpc GetAuthorizeInfo(GetAuthorizeInfoRequest) returns (GetAuthorizeInfoResponse); rpc AuthorizeLogin(AuthorizeLoginRequest) returns (AuthorizeLoginResponse); rpc GetMe(GetMeRequest) returns (GetMeResponse); rpc RefreshToken(RefreshTokenRequest) returns (RefreshTokenResponse); rpc Logout(LogoutRequest) returns (LogoutResponse); rpc RequestDeletion(RequestDeletionRequest) returns (RequestDeletionResponse); rpc ConfirmDeletion(ConfirmDeletionRequest) returns (ConfirmDeletionResponse); rpc GetConfig(GetConfigRequest) returns (GetConfigResponse); rpc Ping(PingRequest) returns (PingResponse);}Example: Request Login
// Request{}
// Response{ "pollToken": "poll_abc123", "expiresAt": "2026-02-28T10:40:00.000Z"}Example: Poll Login Status
// Request{ "pollToken": "poll_abc123"}
// Response (pending){ "status": "PENDING"}
// Response (completed){ "status": "COMPLETED", "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "refreshToken": "refresh_xyz789", "expiresAt": "2026-03-30T10:35:00.000Z"}RoomsService
Section titled “RoomsService”Manages rooms and room membership.
Methods:
service RoomsService { rpc ListRooms(ListRoomsRequest) returns (ListRoomsResponse); rpc GetRoom(GetRoomRequest) returns (GetRoomResponse); rpc CreateRoom(CreateRoomRequest) returns (CreateRoomResponse); rpc UpdateRoom(UpdateRoomRequest) returns (UpdateRoomResponse); rpc DeleteRoom(DeleteRoomRequest) returns (DeleteRoomResponse);}Example: Create Room
// Request{ "name": "Design Team", "description": "Collaborative design workspace", "isPublic": false}
// Response{ "room": { "id": "room_abc123", "name": "Design Team", "description": "Collaborative design workspace", "isPublic": false, "createdBy": "user_xyz789", "createdAt": "2026-02-28T10:30:00.000Z", "updatedAt": "2026-02-28T10:30:00.000Z" }}SparksService
Section titled “SparksService”Manages sparks (content tiles on the canvas).
Methods:
service SparksService { rpc ListSparks(ListSparksRequest) returns (ListSparksResponse); rpc GetSpark(GetSparkRequest) returns (GetSparkResponse); rpc CreateSpark(CreateSparkRequest) returns (CreateSparkResponse); rpc UpdateSpark(UpdateSparkRequest) returns (UpdateSparkResponse); rpc DeleteSpark(DeleteSparkRequest) returns (DeleteSparkResponse); rpc MoveSpark(MoveSparkRequest) returns (MoveSparkResponse);}Example: Create Spark
// Request{ "title": "Project Kickoff", "content": "Let's discuss the roadmap for Q2", "roomId": "room_abc123", "x": 100, "y": 200}
// Response{ "spark": { "id": "spark_def456", "title": "Project Kickoff", "content": "Let's discuss the roadmap for Q2", "roomId": "room_abc123", "x": 100, "y": 200, "createdBy": "user_xyz789", "createdAt": "2026-02-28T10:35:00.000Z" }}OfficesService
Section titled “OfficesService”Manages offices, invites, and membership.
Methods:
service OfficesService { rpc ListOffices(ListOfficesRequest) returns (ListOfficesResponse); rpc GetOffice(GetOfficeRequest) returns (GetOfficeResponse); rpc CreateOffice(CreateOfficeRequest) returns (CreateOfficeResponse); rpc UpdateOffice(UpdateOfficeRequest) returns (UpdateOfficeResponse); rpc DeleteOffice(DeleteOfficeRequest) returns (DeleteOfficeResponse); rpc InviteToOffice(InviteToOfficeRequest) returns (InviteToOfficeResponse); rpc GetInviteInfo(GetInviteInfoRequest) returns (GetInviteInfoResponse); rpc AcceptInvite(AcceptInviteRequest) returns (AcceptInviteResponse); rpc ListMembers(ListMembersRequest) returns (ListMembersResponse); rpc RemoveMember(RemoveMemberRequest) returns (RemoveMemberResponse); rpc UpdateMemberRole(UpdateMemberRoleRequest) returns (UpdateMemberRoleResponse);}CanvasService
Section titled “CanvasService”Handles canvas layout and positioning.
Methods:
service CanvasService { rpc UpdatePosition(UpdatePositionRequest) returns (UpdatePositionResponse); rpc BatchUpdatePositions(BatchUpdatePositionsRequest) returns (BatchUpdatePositionsResponse); rpc ComputeLayout(ComputeLayoutRequest) returns (ComputeLayoutResponse); rpc ValidateLayout(ValidateLayoutRequest) returns (ValidateLayoutResponse); rpc GetCanvasState(GetCanvasStateRequest) returns (GetCanvasStateResponse);}Example: Update Position
// Request{ "tileId": "spark_abc123", "x": 500, "y": 600}
// Response{ "position": { "tileId": "spark_abc123", "x": 500, "y": 600, "snapped": true, "collisionDetected": false }}PassportService
Section titled “PassportService”Manages user profiles and professional information.
Methods:
service PassportService { rpc GetPassport(GetPassportRequest) returns (GetPassportResponse); rpc UpdatePassport(UpdatePassportRequest) returns (UpdatePassportResponse); rpc ImportCV(ImportCVRequest) returns (ImportCVResponse); rpc AddSkill(AddSkillRequest) returns (AddSkillResponse); rpc RemoveSkill(RemoveSkillRequest) returns (RemoveSkillResponse); rpc AddExperience(AddExperienceRequest) returns (AddExperienceResponse); rpc UpdateExperience(UpdateExperienceRequest) returns (UpdateExperienceResponse); rpc DeleteExperience(DeleteExperienceRequest) returns (DeleteExperienceResponse);}AgoraService
Section titled “AgoraService”Generates tokens for Agora video/voice chat.
Methods:
service AgoraService { rpc GenerateToken(GenerateTokenRequest) returns (GenerateTokenResponse); rpc GetConfig(GetConfigRequest) returns (GetConfigResponse);}Example: Generate Token
// Request{ "channelName": "office_abc123", "uid": "user_xyz789", "role": "PUBLISHER"}
// Response{ "token": "006abc123def456...", "expiresAt": "2026-02-28T11:30:00.000Z", "appId": "a1b2c3d4e5f6g7h8"}Protocol Buffer Definitions
Section titled “Protocol Buffer Definitions”All RPC methods are defined using Protocol Buffers. Services are namespaced under flowstate.<domain>.v1 (e.g., flowstate.rooms.v1.RoomsService).
These definitions are used to generate type-safe clients for TypeScript, Swift, Kotlin, and other languages.
Client Generation
Section titled “Client Generation”The desktop app uses a generated TypeScript client:
import { createPromiseClient } from '@connectrpc/connect';import { createConnectTransport } from '@connectrpc/connect-web';import { RoomsService } from '@/generated/flowstate/rooms/v1/rooms_connect';
const transport = createConnectTransport({ baseUrl: 'https://rpc.goflowstate.com', interceptors: [authInterceptor], // Adds Bearer token});
const client = createPromiseClient(RoomsService, transport);
const response = await client.listRooms({ page: 1, limit: 20 });console.log(response.rooms);Error Handling
Section titled “Error Handling”RPC errors follow the Connect error model with standard error codes:
| Code | Description |
|---|---|
CANCELLED | Request cancelled by client |
UNKNOWN | Unknown error |
INVALID_ARGUMENT | Invalid request parameters |
DEADLINE_EXCEEDED | Request timeout |
NOT_FOUND | Resource not found |
ALREADY_EXISTS | Resource already exists |
PERMISSION_DENIED | Insufficient permissions |
UNAUTHENTICATED | Missing or invalid authentication |
RESOURCE_EXHAUSTED | Rate limit exceeded |
FAILED_PRECONDITION | Precondition not met |
ABORTED | Operation aborted |
OUT_OF_RANGE | Value out of valid range |
UNIMPLEMENTED | Method not implemented |
INTERNAL | Internal server error |
UNAVAILABLE | Service unavailable |
DATA_LOSS | Data loss or corruption |
Example Error Response:
{ "code": "INVALID_ARGUMENT", "message": "Invalid room name: must be between 1 and 100 characters", "details": [ { "@type": "type.googleapis.com/google.rpc.BadRequest", "fieldViolations": [ { "field": "name", "description": "String must contain at least 1 character(s)" } ] } ]}Shared Service Layer
Section titled “Shared Service Layer”The RPC Gateway and REST API share the same business logic layer. Both call the same service functions, just with different transport layers:
REST API (Express routes) ──┐ ├──> Service Layer (business logic) ──> DatabaseRPC Gateway (Connect RPCs) ──┘This ensures consistency between the web and desktop applications.
Health Check
Section titled “Health Check”GET /healthReturns the gateway’s health status:
{ "status": "healthy", "timestamp": "2026-02-28T10:30:00.000Z", "services": { "database": { "status": "healthy", "latency": 12 }, "redis": { "status": "healthy", "latency": 3 } }}Rate Limiting
Section titled “Rate Limiting”The gateway enforces rate limits per IP address:
- Default: 100 requests per minute
- Auth methods: 10 requests per minute
- Token generation: 20 requests per minute
Rate limit errors return RESOURCE_EXHAUSTED with a Retry-After header.
CORS Policy
Section titled “CORS Policy”The RPC Gateway allows all origins (*) since it’s a public API secured by Bearer tokens. Credentials are not used (no cookies).
Best Practices
Section titled “Best Practices”- Use generated clients instead of manual HTTP requests (type safety, error handling)
- Implement token refresh before tokens expire (check
expiresAtfield) - Handle errors gracefully using Connect error codes
- Use Protobuf in production for smaller payloads and better performance
- Cache tokens securely (never store in localStorage, use secure storage APIs)
- Implement exponential backoff for retries on transient errors
- Monitor rate limits and implement client-side throttling if needed