Platform-to-Frontend Contract
Generated: July 29, 2026
Purpose: Define the clear frontend contract from the Platform
Core Principle
The frontend should not infer architecture, call multiple internal services, or reconstruct tenant state. It should receive a server-authoritative description of:
Who the user is, which workspace they are in, what they can see, what is available, what state it is in, and what actions they may perform.
1. One Platform Boundary
The frontend should call only one base URL:
NEXT_PUBLIC_IW_API_URL=https://api.integratewise.ai
All browser requests should flow through:
Frontend
↓
Platform Gateway
↓
Identity / Configuration / Projection / Integration / Governance
↓
Spine and internal services
The frontend must not call:
- MCP Pool directly
- Provider Management MCP directly
- Nango or Descope directly
- D1, Neon, KV or R2
- Provider APIs
- Hermes
- Individual Cloudflare Workers
2. The First API Call
After login or workspace change, the frontend should call:
GET /api/v1/workspace/discovery
This should be the frontend's initial source of truth.
Suggested Response
interface WorkspaceDiscoveryResponse {
contractVersion: string;
traceId: string;
generatedAt: string;
state: "ready" | "partial" | "degraded" | "pending";
principal: {
userId: string;
displayName: string;
email?: string;
roles: string[];
permissions: string[];
};
workspace: {
tenantId: string;
workspaceId: string;
spineNetworkId: string;
name: string;
environment: "development" | "customer-zero" | "production";
};
navigation: NavigationSection[];
projections: ProjectionDescriptor[];
connectors: {
available: number;
connected: number;
degraded: number;
syncRunning: number;
};
capabilities: CapabilityDescriptor[];
approvals: {
pending: number;
urgent: number;
};
continuity: {
freshness: string;
lastHydratedAt?: string;
contextAvailable: boolean;
};
activation: {
complete: boolean;
blockers: ActivationBlocker[];
};
health: {
gateway: "healthy" | "degraded" | "unavailable";
spine: "healthy" | "degraded" | "unavailable";
integrations: "healthy" | "degraded" | "unavailable";
};
}
This prevents the frontend from hardcoding:
- Sidebar entries
- Role-specific pages
- Tenant IDs
- Connector availability
- Capability visibility
- Approval availability
- Activation status
3. What the Platform Must Expose
A. Identity and Workspace Context
The frontend needs a trusted context describing:
interface WorkspaceContext {
userId: string;
tenantId: string;
workspaceId: string;
spineNetworkId: string;
roles: string[];
permissions: string[];
department?: string;
industry?: string;
plan?: string;
entitlements: string[];
}
The Platform must derive this from the authenticated session.
The browser must never be authoritative for:
tenantId
userId
role
permissions
workspaceId
environment
The frontend may request a workspace switch, but the Platform validates and resolves it.
B. Navigation Contract
The Platform should tell the frontend which modules to render.
interface NavigationItem {
id: string;
label: string;
icon?: string;
route: string;
projectionId?: string;
visibility: "visible" | "hidden" | "disabled";
access: "read" | "operate" | "admin";
badge?: {
value: number;
severity?: "info" | "warning" | "critical";
};
reasonDisabled?: string;
}
interface NavigationSection {
id: string;
label: string;
items: NavigationItem[];
}
Example:
{
"id": "platform",
"label": "Platform",
"items": [
{
"id": "integration-manager",
"label": "Integration Manager",
"route": "/platform/integrations",
"visibility": "visible",
"access": "admin"
},
{
"id": "approvals",
"label": "Governance",
"route": "/approvals",
"visibility": "visible",
"access": "operate",
"badge": {
"value": 6,
"severity": "warning"
}
}
]
}
The frontend renders this. It should not independently decide what an admin, manager or member sees.
C. Projection Contract
The Platform should expose views as projection definitions rather than making the frontend understand database tables.
interface ProjectionDescriptor {
id: string;
version: string;
title: string;
type: "table" | "cards" | "timeline" | "canvas" | "graph" | "inbox" | "queue" | "detail";
entityTypes: string[];
endpoint: string;
capabilities: string[];
freshness: {
generatedAt: string;
sourceUpdatedAt?: string;
stale: boolean;
};
state: "ready" | "partial" | "degraded" | "pending";
}
Fetching a view:
GET /api/v1/projections/inbox
GET /api/v1/projections/approvals
GET /api/v1/projections/account-success
Response:
interface ProjectionResponse<T> {
contractVersion: string;
projectionId: string;
traceId: string;
state: "ready" | "partial" | "degraded" | "pending";
generatedAt: string;
sourceFreshness?: string;
schema: ProjectionSchema;
data: T;
allowedCapabilities: string[];
provenance: {
sources: string[];
evidenceIds?: string[];
};
remediation?: {
code: string;
message: string;
action?: string;
};
}
The frontend should render the projection rather than assemble raw Spine entities itself.
D. Connector Catalog
The Platform must expose a canonical connector catalog.
GET /api/v1/connectors/catalog
interface ConnectorDefinition {
id: string;
providerKey: string;
name: string;
description: string;
category: string;
iconUrl?: string;
authentication: "oauth2" | "api-key" | "service-account" | "mcp" | "none";
supportedEntities: string[];
supportedCapabilities: string[];
availability: {
status: "available" | "private-alpha" | "beta" | "unavailable";
reason?: string;
};
}
The Customer Zero marketplace can keep its UI, but the actual connector list and status should come from this endpoint.
E. Tenant Connections
The frontend needs a clear connection model.
GET /api/v1/connectors/connections
POST /api/v1/connectors/:provider/connect
GET /api/v1/connectors/connections/:id
DELETE /api/v1/connectors/connections/:id
interface ConnectionSummary {
connectionId: string;
provider: string;
status: "pending" | "connected" | "degraded" | "expired" | "revoked";
health: "healthy" | "degraded" | "unknown";
owner: {
type: "user" | "workspace";
id: string;
};
scopes: string[];
lastValidatedAt?: string;
lastUsedAt?: string;
sync: {
status: "idle" | "running" | "failed" | "disabled";
lastCompletedAt?: string;
nextScheduledAt?: string;
};
availableActions: string[];
}
The response must never contain:
- Access tokens
- Refresh tokens
- Client secrets
- API keys
- Provider-management credentials
- Raw authorization references unless required for a trusted server flow
F. OAuth Connection Initiation
The frontend should request a connection:
POST /api/v1/connectors/vercel/connect
Request:
{
"workspaceId": "ws_customer_zero",
"returnPath": "/platform/connectors"
}
Response:
{
"state": "human_consent_required",
"authorizationUrl": "https://...",
"connectionAttemptId": "attempt_123",
"expiresAt": "2026-07-29T06:00:00Z"
}
The frontend redirects the user.
It should not:
- Generate OAuth state
- Exchange OAuth codes
- Store credentials
- Mark a connection as active
- Derive tenant identity from callback parameters
G. Sync Contract
The Platform should expose sync independently from connection state.
POST /api/v1/connectors/connections/:id/sync
GET /api/v1/connectors/connections/:id/sync-status
GET /api/v1/connectors/connections/:id/sync-history
interface SyncStatus {
syncJobId?: string;
state: "idle" | "queued" | "running" | "completed" | "failed" | "cancelled";
stage?: "loading" | "normalizing" | "storing" | "projecting" | "reconciling";
progress?: {
processed: number;
total?: number;
percentage?: number;
};
startedAt?: string;
completedAt?: string;
error?: {
code: string;
message: string;
retryable: boolean;
};
}
This gives the marketplace real state instead of showing a fake "connected" status.
H. Capability Contract
The frontend should know which actions are available for the current user and object.
GET /api/v1/capabilities?projectionId=inbox
GET /api/v1/capabilities?entityType=account&entityId=acc_123
interface CapabilityDescriptor {
id: string;
name: string;
description: string;
mode: "read" | "propose" | "execute";
availability:
| "available"
| "requires-approval"
| "missing-connection"
| "not-authorized"
| "unavailable";
requiredPermissions: string[];
requiredConnections: string[];
governance?: {
approvalRequired: boolean;
policyId?: string;
};
inputSchema?: Record<string, unknown>;
}
This lets the frontend render:
- Enabled button
- Disabled button
- Connect provider
- Request approval
- Execute
- Explain why unavailable
I. Governance and Approvals
The frontend needs explicit proposal and approval APIs.
GET /api/v1/governance/approvals
GET /api/v1/governance/approvals/:id
POST /api/v1/governance/approvals/:id/approve
POST /api/v1/governance/approvals/:id/reject
interface ApprovalItem {
id: string;
title: string;
description: string;
proposedCapability: string;
requestedBy: {
type: "user" | "twin" | "system";
id: string;
};
target: {
entityType?: string;
entityId?: string;
provider?: string;
};
risk: "low" | "medium" | "high" | "critical";
consequences: string[];
evidence: {
id: string;
label: string;
source: string;
}[];
status: "pending" | "approved" | "rejected" | "expired" | "executing" | "completed" | "failed";
availableActions: string[];
}
The approval endpoint should return an approval result, not let the frontend call the provider afterward.
Correct flow:
Frontend approves
→ Governance validates
→ Governance issues authority
→ Hermes executes
→ Platform returns execution state
J. Twin Contract
The frontend should not call models directly.
It should call semantic Twin operations:
POST /api/v1/twin/ask
POST /api/v1/twin/assign
GET /api/v1/twin/assignments
GET /api/v1/twin/assignments/:id
Ask request:
interface AskTwinRequest {
context: {
projectionId?: string;
entityRefs?: string[];
selectedRecordIds?: string[];
};
question: string;
}
Assign request:
interface AssignTwinRequest {
objective: string;
context: {
projectionId?: string;
entityRefs?: string[];
};
constraints?: string[];
desiredOutcome?: string;
}
The Platform resolves the actual permitted context. The frontend only sends references to the active surface.
K. Signals and Attention
For Home and Inbox, expose a common attention contract.
GET /api/v1/signals
GET /api/v1/work/items
GET /api/v1/notifications
interface AttentionItem {
id: string;
kind: "task" | "approval" | "signal" | "notification" | "blocked-work" | "sync-error";
title: string;
summary?: string;
severity: "info" | "warning" | "critical";
status: string;
dueAt?: string;
relatedEntities: {
type: string;
id: string;
label: string;
}[];
projectionTarget?: {
route: string;
projectionId?: string;
};
allowedCapabilities: string[];
}
This enables Inbox, My Work, Approvals and Notifications to share a consistent data shape.
4. Common Response Envelope
Every Platform response should have the same operational metadata.
interface PlatformResponse<T> {
contractVersion: string;
traceId: string;
tenantId: string;
workspaceId: string;
generatedAt: string;
freshness?: string;
state: "ready" | "partial" | "degraded" | "pending";
data: T;
errors?: {
code: string;
message: string;
retryable: boolean;
}[];
provenance?: {
sources: string[];
evidenceIds?: string[];
};
}
The frontend should never silently replace an error with demo data.
Example:
{
"contractVersion": "2026-07-29.1",
"traceId": "tr_01J...",
"tenantId": "iw-customer-zero",
"workspaceId": "ws_primary",
"generatedAt": "2026-07-29T05:20:00Z",
"state": "degraded",
"data": {
"items": []
},
"errors": [
{
"code": "SALESFORCE_SYNC_UNAVAILABLE",
"message": "Salesforce data is temporarily unavailable.",
"retryable": true
}
]
}
5. Frontend Vocabulary
The frontend should understand only these core concepts:
Entity
Projection
Capability
Signal
Context
Twin
Optionally, for operational implementation:
Connection
Approval
Sync
It should not understand internal terms such as:
MCP Pool binding
Cloudflare service binding
Nango connection ID
Descope management app
D1 table
Neon migration
Hermes queue
Provider adapter
Worker name
Those are Platform implementation details.
6. Recommended Frontend SDK Structure
lib/platform/
├── client.ts
├── types.ts
├── discovery.ts
├── projections.ts
├── connectors.ts
├── capabilities.ts
├── governance.ts
├── twin.ts
├── signals.ts
└── hooks/
├── use-workspace-discovery.ts
├── use-projection.ts
├── use-connectors.ts
├── use-capabilities.ts
├── use-approvals.ts
└── use-twin-assignment.ts
The frontend should use domain-level calls:
platform.workspace.discover();
platform.projections.get("inbox");
platform.connectors.connect("vercel");
platform.connectors.sync(connectionId);
platform.capabilities.forEntity(entityRef);
platform.governance.approve(approvalId);
platform.twin.ask(input);
Not generic calls like:
fetch("/api/platform/some-worker/some-route");
7. Minimum Platform APIs Needed Now
To make the current Customer Zero frontend real, the immediate minimum is:
| Endpoint | Purpose |
|---|---|
GET /api/v1/workspace/discovery | Initial frontend contract |
GET /api/v1/connectors/catalog | Connector Marketplace |
GET /api/v1/connectors/connections | Connection states |
POST /api/v1/connectors/:provider/connect | Start authorization |
DELETE /api/v1/connectors/connections/:id | Disconnect |
POST /api/v1/connectors/connections/:id/sync | Trigger sync |
GET /api/v1/connectors/connections/:id/sync-status | Show progress |
GET /api/v1/projections/inbox | Inbox Workbench |
GET /api/v1/projections/approvals | Approvals Workbench |
GET /api/v1/capabilities | Available actions |
POST /api/v1/governance/approvals/:id/approve | Governed approval |
POST /api/v1/governance/approvals/:id/reject | Rejection |
POST /api/v1/twin/ask | Ask Your Twin |
POST /api/v1/twin/assign | Assign Your Twin |
GET /api/v1/platform/health | Platform health |
8. Marketplace Onboarding Contract
Partially aligned — the frontend contract is mostly post-onboarding, not the complete marketplace onboarding contract.
The proposed Platform APIs correctly support the Workbench after a marketplace installation has been bound to an IntegrateWise workspace:
- Workspace discovery
- Connector catalog
- Connection status
- OAuth initiation
- Sync
- Projections
- Capabilities
- Governance
- Twin operations
But marketplace onboarding needs an additional layer before /api/v1/workspace/discovery.
Correct Marketplace Onboarding Sequence
Marketplace installation initiated
→ Validate marketplace installation context
→ Authenticate or create IntegrateWise user
→ Select or create IntegrateWise workspace
→ Resolve tenant and Spine Network
→ Review scopes and requested access
→ Reuse existing provider connection when permitted
→ Request provider consent when required
→ Create marketplace installation binding
→ Create connector and Spine connection binding
→ Verify connection health
→ Begin initial hydration
→ Compile initial Workbench projections
→ Redirect user to activated Workbench
→ Frontend calls /api/v1/workspace/discovery
So /workspace/discovery is the first Workbench request, but it is not the first marketplace onboarding request.
Two Separate Platform Contracts
1. Marketplace Acquisition and Installation Contract
This owns the channel-specific onboarding process.
Vercel / ChatGPT / GitHub / Notion / Coda
→ marketplace installation API
→ IntegrateWise identity and workspace selection
→ tenant-bound installation
2. Product Runtime Contract
This is the contract already described:
IntegrateWise Workbench
→ workspace discovery
→ projections
→ connectors
→ capabilities
→ governance
→ Twin
The frontend should not combine these into one generic connector flow.
Marketplace Onboarding APIs Required
A provider-neutral API family would be preferable:
POST /api/v1/marketplace/installations/start
GET /api/v1/marketplace/installations/:attemptId
POST /api/v1/marketplace/installations/:attemptId/workspace
POST /api/v1/marketplace/installations/:attemptId/consent
POST /api/v1/marketplace/installations/:attemptId/activate
GET /api/v1/marketplace/installations/:attemptId/status
POST /api/v1/marketplace/installations/:attemptId/cancel
DELETE /api/v1/marketplace/installations/:installationId
Channel-specific callbacks can remain separate:
GET /api/v1/marketplace/vercel/callback
POST /api/v1/marketplace/vercel/webhooks
GET /api/v1/marketplace/github/callback
POST /api/v1/marketplace/github/webhooks
The channel adapter converts provider-specific payloads into one canonical installation model.
Canonical Installation Model
interface MarketplaceInstallation {
id: string;
channel: "vercel" | "chatgpt" | "github" | "notion" | "coda" | "mcp-directory";
externalInstallationId: string;
externalAccountId?: string;
externalTeamId?: string;
tenantId: string;
workspaceId: string;
spineNetworkId: string;
connectorBindingId?: string;
spineConnectionId?: string;
credentialRef?: string;
grantedScopes: string[];
status:
| "initiated"
| "identity_required"
| "workspace_required"
| "consent_required"
| "binding"
| "hydrating"
| "active"
| "degraded"
| "revoked"
| "failed";
createdAt: string;
activatedAt?: string;
}
This installation record is different from a normal connector connection.
A connector answers:
"Can this workspace communicate with Vercel?"
A marketplace installation answers:
"Which external Vercel installation is bound to which IntegrateWise tenant, workspace, Spine Network, connector, permissions, and lifecycle?"
Marketplace Installation Start Request
POST /api/v1/marketplace/installations/start
{
"channel": "vercel",
"externalInstallationId": "icfg_123",
"externalTeamId": "team_456",
"requestedScopes": ["projects:read", "deployments:read"],
"returnUrl": "https://vercel.com/dashboard/integrations"
}
Response:
{
"installationAttemptId": "mai_123",
"nextStep": "identity_required",
"continueUrl": "https://app.integratewise.ai/marketplace/onboarding/mai_123",
"expiresAt": "2026-07-29T08:00:00Z"
}
The browser should receive only a resumable installation attempt. It should not receive credentials or authoritative tenant information.
Workspace Selection
After authentication:
POST /api/v1/marketplace/installations/mai_123/workspace
{
"workspaceId": "ws_customer_zero"
}
The Platform validates:
- The user belongs to that workspace.
- The user has installation authority.
- The workspace belongs to the correct environment.
- The external installation is not already bound elsewhere.
- Existing connector connections can be reused.
- Requested scopes comply with workspace policy.
Consent Model
Marketplace consent and provider OAuth consent are not always the same thing.
Marketplace Consent
The user approves:
- Binding the Vercel installation to the IW workspace
- Requested project access
- Data ingestion scope
- Available capabilities
- Retention policy
- Write/action policy
Provider Consent
The user authorizes Vercel, GitHub, Salesforce, or another provider to issue credentials.
A marketplace may already provide a usable installation credential. In that case, a second OAuth flow should not be created unnecessarily.
The Platform should determine:
Existing approved connection exists
→ reuse it
Marketplace credential is sufficient
→ bind it
Additional provider authorization required
→ request consent
No permitted credential path
→ fail closed
Activation Response
POST /api/v1/marketplace/installations/mai_123/activate
{
"state": "hydrating",
"installationId": "mi_789",
"workspaceId": "ws_customer_zero",
"connector": {
"provider": "vercel",
"connectionId": "conn_456",
"status": "connected"
},
"hydration": {
"status": "running",
"stage": "loading"
},
"redirectUrl": "https://app.integratewise.ai/workbench"
}
The user can enter the Workbench while hydration continues, provided the frontend clearly renders:
pending
partial
degraded
ready
It must not show seed data as though the installation succeeded.
/workspace/discovery After Marketplace Activation
Once the installation is bound, the existing discovery contract becomes correct.
It should expose marketplace context indirectly:
{
"state": "partial",
"workspace": {
"workspaceId": "ws_customer_zero",
"spineNetworkId": "spn_123"
},
"activation": {
"complete": false,
"blockers": [
{
"code": "VERCEL_INITIAL_HYDRATION_RUNNING",
"message": "Vercel project data is still loading."
}
]
},
"connectors": {
"connected": 1,
"degraded": 0,
"syncRunning": 1
},
"marketplaceInstallations": [
{
"channel": "vercel",
"status": "hydrating"
}
]
}
The frontend does not need Vercel-specific implementation details beyond what is required to render status and remediation.
Frontend Onboarding Screens
The Product repo should render these screens:
1. Welcome / sign in
2. Select or create workspace
3. Review requested access
4. Choose projects or account scope
5. Confirm connection reuse or authorize
6. Activation progress
7. Success / remediation
8. Enter Workbench
The Platform must supply all authoritative data and transitions.
The frontend must not decide:
- Whether a connection can be reused
- Which tenant the installation belongs to
- Whether requested scopes are allowed
- Whether installation is active
- Whether OAuth succeeded
- Whether hydration completed
- Which capabilities are enabled
Alignment Assessment
| Component | Marketplace onboarding alignment |
|---|---|
| One typed Gateway client | Aligned |
| Workspace discovery | Aligned, but post-installation |
| Connector catalog | Aligned |
| Connector connection APIs | Aligned as a subordinate step |
| OAuth initiation | Aligned, but must be installation-aware |
| Sync and hydration APIs | Aligned |
| Projection APIs | Aligned after activation |
| Capability APIs | Aligned |
| Governance APIs | Aligned |
| Marketplace installation state | Missing |
| External installation-to-workspace binding | Missing |
| Workspace selection during install | Missing |
| Scope review and marketplace consent | Missing |
| Connection-reuse decision | Missing |
| Channel callback abstraction | Missing |
| Lifecycle webhooks and uninstall | Partially present |
| Post-install redirect contract | Missing |
Final Rule
The Platform must expose business and operational contracts; the frontend must never be required to understand the Platform's internal topology.
The frontend should receive clear answers to:
Who am I?
Which workspace am I in?
What can I see?
What work needs attention?
Which connectors are available?
Which connections are active?
What can I do?
What requires approval?
What is fresh, partial or degraded?
Where should the UI take me next?
That is the contract the Platform now needs to formalize.
Final Answer
The Platform-to-frontend contract is aligned with the operational phase of marketplace onboarding, but it does not yet cover the acquisition and installation phase.
Add a canonical MarketplaceInstallation contract and installation state machine ahead of /workspace/discovery. After activation, the existing discovery, connectors, sync, projections, capabilities, governance, and Twin APIs form the correct frontend boundary.