Unclassified
Phase 4: Gateway SDK Foundation
Tier C — Historical. This document is a point-in-time snapshot preserved for reference. For current architecture, see CANONICAL_PLATFORM_ARCHITECTURE.md.
The minimal, reusable SDK that every frontend imports. Platform capabilities only—no business logic.
What It Does
The Gateway SDK is a thin HTTP client that:
- Handles authentication (Stack Auth tokens → Gateway tokens)
- Manages request/response envelopes
- Implements retry logic with exponential backoff
- Provides React hooks for common patterns
- Streams SSE and WebSocket events
- Handles errors consistently
- Provides TypeScript types for all namespaces
What It Does NOT Do
- Business logic (getting "customers" vs "deals")
- UI components
- State management beyond request/response caching
- Feature flags or A/B testing
Those belong in the application layer.
File Structure
packages/gateway-sdk/
├── package.json
├── tsconfig.json
├── src/
│ ├── client/
│ │ ├── index.ts # createGatewayClient()
│ │ ├── httpClient.ts # HTTP request logic
│ │ ├── auth.ts # Stack Auth token → Gateway token exchange
│ │ ├── retry.ts # Exponential backoff
│ │ └── envelope.ts # Request/response shape
│ ├── hooks/
│ │ ├── index.ts
│ │ ├── useGateway.ts # Get client context
│ │ ├── useQuery.ts # Fetch + cache + refetch
│ │ ├── useMutation.ts # POST/PUT/DELETE + error handling
│ │ ├── useStream.ts # SSE streaming
│ │ ├── useRealtime.ts # WebSocket subscriptions
│ │ └── useInfinite.ts # Pagination with cursor
│ ├── types/
│ │ ├── index.ts
│ │ ├── auth.ts # AuthToken, GatewayToken, User
│ │ ├── request.ts # Request envelope
│ │ ├── response.ts # Response envelope
│ │ ├── error.ts # Error types
│ │ └── namespaces.ts # Workspace, Memory, Twin, etc.
│ ├── errors/
│ │ ├── index.ts
│ │ ├── GatewayError.ts
│ │ ├── AuthError.ts
│ │ └── handlers.ts # Error handling strategies
│ ├── realtime/
│ │ ├── index.ts
│ │ ├── websocket.ts # WebSocket manager
│ │ ├── eventEmitter.ts # Event subscription
│ │ └── reconnection.ts # Automatic reconnect
│ ├── cache/
│ │ ├── index.ts
│ │ ├── queryCache.ts # SWR-like cache
│ │ └── invalidation.ts # Cache busting strategies
│ ├── context/
│ │ ├── index.ts
│ │ ├── GatewayProvider.tsx # React context provider
│ │ └── useGatewayContext.ts
│ ├── mock/
│ │ ├── index.ts
│ │ ├── mockClient.ts # In-memory client for testing
│ │ └── fixtures.ts # Sample data
│ └── index.ts # Public API
├── __tests__/
│ ├── client.test.ts
│ ├── auth.test.ts
│ ├── hooks.test.ts
│ └── realtime.test.ts
└── README.md
Public API (Main Exports)
1. Client Creation
const gateway = createGatewayClient({
gatewayUrl: 'https://gateway.integratewise.com',
stackAuthToken: '<from-stack-auth>',
org_id: 'org_456',
team_id: 'team_789'
})
// On success, gateway.auth returns Gateway token
// Automatically renewed on expiry
2. Request Method
const response = await gateway.request({
namespace: 'workspace',
action: 'listEntities',
version: 'v1',
params: {
type: 'customer',
limit: 50
}
})
// Returns: { success: true, data: {...}, context: {...} }
3. React Hook: useQuery
function CustomerList() {
const { data, isLoading, error, refetch } = useQuery({
namespace: 'workspace',
action: 'listEntities',
params: { type: 'customer' }
})
if (isLoading) return <Loading />
if (error) return <Error error={error} />
return (
<>
{data.entities.map(e => <Customer key={e.id} entity={e} />)}
<button onClick={refetch}>Refresh</button>
</>
)
}
4. React Hook: useMutation
function UpdateCustomer({ customerId }) {
const mutation = useMutation({
namespace: 'workspace',
action: 'updateEntity',
onSuccess: () => queryClient.invalidateQueries('workspace')
})
const handleUpdate = async (changes) => {
await mutation.mutate({
params: { id: customerId, ...changes }
})
}
return (
<form onSubmit={e => {
e.preventDefault()
handleUpdate({ status: 'active' })
}}>
<button disabled={mutation.isLoading}>
{mutation.isLoading ? 'Saving...' : 'Save'}
</button>
</form>
)
}
5. React Hook: useRealtime
function LiveCustomers() {
const events = useRealtime({
namespaces: ['workspace'],
filters: { workspace: { type: 'customer' } }
})
useEffect(() => {
const unsubscribe = events.subscribe((event) => {
console.log('Customer updated:', event.entity_id)
queryClient.invalidateQueries('workspace')
})
return unsubscribe
}, [])
return <div>Listening for live updates...</div>
}
6. React Hook: useStream
function Search({ query }) {
const { isStreaming, results, error, stop } = useStream({
namespace: 'workspace',
action: 'search',
params: { query }
})
return (
<div>
{results.map(r => <Result key={r.id} result={r} />)}
{isStreaming && <LoadingIndicator />}
{error && <Error error={error} />}
<button onClick={stop}>Stop</button>
</div>
)
}
7. Mock Client (for testing)
const gateway = createMockGateway({
fixtures: {
'workspace.listEntities': [
{ id: 'ent_1', type: 'customer', name: 'Acme' },
{ id: 'ent_2', type: 'customer', name: 'TechCorp' }
]
}
})
// Behaves identically to real Gateway
// No network calls, no auth needed
Type Definitions
// From packages/gateway-sdk/src/types/index.ts
export interface GatewayRequest {
namespace: 'workspace' | 'memory' | 'twin' | 'approvals' | 'integrations' | 'skills' | 'entity360' | 'identity'
action: string
version?: string
params: Record<string, any>
context?: {
request_id?: string
trace_id?: string
}
}
export interface GatewayResponse<T = any> {
success: boolean
data?: T
error?: GatewayError
context: {
request_id: string
processed_at: number
latency_ms: number
}
}
export interface GatewayError {
code: string
message: string
details?: Record<string, any>
request_id: string
timestamp: number
}
export interface User {
user_id: string
org_id: string
team_id: string
role: string
permissions: string[]
}
export interface GatewayToken {
access_token: string
refresh_token?: string
expires_at: number
scope: string[]
}
Configuration
// packages/gateway-sdk/src/client/index.ts
interface GatewayClientOptions {
// Required
gatewayUrl: string
stackAuthToken: string
org_id: string
team_id: string
// Optional
maxRetries?: number // default: 3
retryDelay?: number // default: 1000ms
requestTimeout?: number // default: 30000ms
cacheTime?: number // default: 60000ms
staleTime?: number // default: 0ms (immediately stale)
// Callbacks
onAuthRefresh?: (token: GatewayToken) => void
onError?: (error: GatewayError) => void
onRateLimit?: (retryAfter: number) => void
}
Example: Provider Setup
// apps/ai-workspace/app/layout.tsx
const { stackAuthToken, isAuthenticated } = useAuth()
if (!isAuthenticated) {
return <LoginPage />
}
return (
<GatewayProvider
gatewayUrl={process.env.NEXT_PUBLIC_GATEWAY_URL}
stackAuthToken={stackAuthToken}
org_id={getCurrentOrgId()}
team_id={getCurrentTeamId()}
>
{children}
</GatewayProvider>
)
}
Retry & Error Handling
// Automatic retry with exponential backoff
// Retries on: 408, 429, 500, 502, 503, 504
// Not on: 400, 401, 403, 404, 409
// Example: fails with 429 (rate limit)
// Retry 1: wait 1000ms → fails
// Retry 2: wait 2000ms → fails
// Retry 3: wait 4000ms → succeeds
// On auth error (401)
// No retry — request onAuthRefresh callback
// App must get new Stack Auth token
// Gateway SDK automatically exchanges it for new Gateway token
Testing
// apps/ai-workspace/__tests__/customers.test.tsx
test('loads customers', async () => {
const gateway = createMockGateway({
fixtures: {
'workspace.listEntities': {
entities: [
{ id: '1', type: 'customer', name: 'Acme' }
]
}
}
})
const wrapper = ({ children }) => (
<GatewayProvider gateway={gateway}>
{children}
</GatewayProvider>
)
const { result } = renderHook(() =>
useQuery({
namespace: 'workspace',
action: 'listEntities',
params: { type: 'customer' }
}),
{ wrapper }
)
await waitFor(() => expect(result.current.isLoading).toBe(false))
expect(result.current.data.entities).toHaveLength(1)
})
Implementation Checklist
- Create
packages/gateway-sdkfolder structure - Implement HTTP client and envelope handling
- Implement auth token exchange (Stack → Gateway)
- Implement retry logic with backoff
- Implement React hooks (useQuery, useMutation, useStream, useRealtime)
- Implement cache invalidation strategies
- Implement mock client for testing
- Implement WebSocket manager for realtime
- Write comprehensive tests
- Write documentation
- Create example app using Gateway SDK
- Publish to npm registry
Next Steps
- Build Gateway SDK Foundation (this phase)
- Use it in AI Workspace (Phase 5)
- Validate all namespace contracts work
- Document patterns other apps should follow
- Migrate Wise Docs, Wise Ops, Wise ERMS, Wise Branding (Phase 6+)
Every app after AI Workspace will import the same SDK and follow the same patterns. No variations.