> ## Documentation Index
> Fetch the complete documentation index at: https://docs.deep.space/llms.txt
> Use this file to discover all available pages before exploring further.

# Integrations reference

> The `integration` client and OAuth helpers.

The `integration` object fronts 215+ third-party API endpoints through the platform's signed proxy. For discovery, billing, and the full workflow see the [external APIs guide](/guides/external-apis); for the CLI catalog, see [`deepspace integrations`](/cli-reference/commands#integrations-and-invoke).

```ts theme={null}
import { integration } from 'deepspace'
import type { IntegrationResponse, RequestOptions } from 'deepspace'
```

## `integration`

The integration client exposes four HTTP verbs. All return a typed envelope.

```ts theme={null}
const integration: {
  get<T>:    (endpoint: string, params?: Record<string, string | number | boolean | null | undefined>, options?: RequestOptions) => Promise<IntegrationResponse<T>>
  post<T>:   (endpoint: string, data?: unknown, options?: RequestOptions) => Promise<IntegrationResponse<T>>
  put<T>:    (endpoint: string, data?: unknown, options?: RequestOptions) => Promise<IntegrationResponse<T>>
  delete<T>: (endpoint: string, data?: unknown, options?: RequestOptions) => Promise<IntegrationResponse<T>>
}
```

`get` takes a query-parameter object as its middle argument; the others take an optional JSON body. `delete` also accepts a body - the api-worker dispatches `DELETE` with a JSON payload when one is provided.

Endpoint names are two segments: `<integration>/<endpoint>` (e.g. `openai/chat-completion`).

<Tabs>
  <Tab title="POST">
    The most common verb. Use for actions, completions, lookups, and anything with a request body.

    ```ts theme={null}
    const result = await integration.post('openai/chat-completion', {
      model: 'claude-sonnet-4-6',
      messages: [{ role: 'user', content: 'Hello' }],
    })

    if (result.success) {
      console.log(result.data)
    } else {
      console.error(result.error, result.issues)
    }
    ```
  </Tab>

  <Tab title="GET">
    For idempotent reads. Pass query parameters as the second argument - they're serialised onto the URL automatically (null/undefined values are skipped).

    ```ts theme={null}
    const result = await integration.get('finnhub/market-news', {
      category: 'crypto',
      minId: 100,
    })
    ```
  </Tab>

  <Tab title="PUT">
    For idempotent updates against REST-style endpoints.

    ```ts theme={null}
    const result = await integration.put('notion/update-page', {
      pageId: 'abc123',
      properties: { Status: 'Done' },
    })
    ```
  </Tab>

  <Tab title="DELETE">
    For destructive operations. The platform proxy is signed and rate-limited per integration; OAuth scopes apply if the integration is OAuth-backed.

    ```ts theme={null}
    const result = await integration.delete('notion/delete-block', {
      blockId: 'xyz789',
    })
    ```
  </Tab>
</Tabs>

<Warning>
  **Auth-gate any UI that calls `integration.post(...)` for `'developer'`-billed endpoints.** The api-worker accepts anonymous callers, so a public endpoint silently bills the owner for every visitor (or bot) hit. Wrap calling components in [`useAuth().isSignedIn`](/sdk-reference/client/auth#useauth-authstate). See the [external APIs guide](/guides/external-apis#billing--developer-vs-user) for billing routing.
</Warning>

## `IntegrationResponse<T>`

```ts theme={null}
type IntegrationResponse<T> =
  | { success: true; data: T }
  | { success: false; error: string; issues?: ValidationIssue[] }

type ValidationIssue = {
  path?: string[]
  message: string
  code?: string
}
```

`issues` appears when the api-worker's Zod validator rejects the body shape. Read it instead of guessing field names - or run [`deepspace integrations info <endpoint>`](/cli-reference/commands#integrations-and-invoke) to print the schema before you call.

## `RequestOptions`

```ts theme={null}
type RequestOptions = {
  headers?: Record<string, string>
  timeoutMs?: number   // default 120000 (120s)
}
```

```ts theme={null}
const r = await integration.post('exa/search', body, {
  timeoutMs: 30_000,
  headers: { 'X-Custom': 'value' },
})
```

## Cross-app platform context

These exports support a small set of cross-app surfaces (inbox, platform fetch). The scaffold does **not** include `<PlatformProvider>` - wrap manually if you need this surface.

```ts theme={null}
import { PlatformProvider, usePlatform, useInbox, usePlatformWS } from 'deepspace'

<PlatformProvider>{/* tree */}</PlatformProvider>
```

### `usePlatform()`

```ts theme={null}
function usePlatform(): {
  platformFetch: (path: string, init?: RequestInit) => Promise<Response>
  inbox: InboxEntry[]
  /** Activate the inbox WebSocket. Returns an unsubscribe function. */
  subscribeInbox: () => () => void
}
```

`platformFetch` prepends `/platform` and adds the auth header automatically. Throws if no `<PlatformProvider>` is mounted. `subscribeInbox` is the primitive `useInbox()` calls under the hood - apps rarely call it directly.

### `useInbox()`

```ts theme={null}
function useInbox(): InboxEntry[]

type InboxEntry = {
  conversationId: string
  scope: { type: string; participants?: string[]; appId?: string; contentRef?: string; ticketNumber?: string }
  displayName: string
  muted: boolean
  joinedAt: string
  lastMessageAt: string | null
  lastMessagePreview: string | null
  lastMessageAuthor: string | null
  unreadCount: number
}
```

The inbox WebSocket activates only when at least one component subscribes via `useInbox()`.

### `usePlatformWS<S>(options)`

Generic platform WebSocket subscription for custom platform-side streams. `S` is the state shape; the constraint requires a `status: ConnectionStatus` field so the hook can drive reconnect UI.

```ts theme={null}
function usePlatformWS<S extends { status: ConnectionStatus }>(
  options: PlatformWSOptions<S>,
): PlatformWSResult<S>

type PlatformWSOptions<S> = {
  /** DO path segment - e.g. 'orders' for /platform/ws/orders/{scopeId} */
  path: string
  /** Scope ID - e.g. 'app:demo-corp'. Pass undefined to skip the connection. */
  scopeId: string | undefined
  initialState: S
  onMessage: (msg: Record<string, unknown>, prev: S) => S
}

type PlatformWSResult<S> = {
  state: S
  send: (msg: Record<string, unknown>) => void
}
```

## OAuth endpoints

For OAuth-backed integrations (currently Google), these REST endpoints manage per-user connections. Call via `fetch` with the session token.

| Endpoint                                       | Method   | Purpose                                          |
| ---------------------------------------------- | -------- | ------------------------------------------------ |
| `/api/integrations/status`                     | `GET`    | Returns connection flags for all OAuth providers |
| `/api/integrations/oauth/:provider/connect`    | `GET`    | Initiates OAuth flow (browser navigation)        |
| `/api/integrations/oauth/:provider/disconnect` | `DELETE` | Revokes the current user's stored tokens         |

```ts theme={null}
const r = await fetch('/api/integrations/status', {
  headers: { Authorization: `Bearer ${await getAuthToken()}` },
})
const { google } = await r.json()
// { connected: boolean, scopes: string[] }
```

### `requiresOAuth` response

When a user calls an OAuth endpoint without a connected account:

```ts theme={null}
{ success: false, error: 'requiresOAuth', provider: 'google', connectUrl: '...' }
```

Redirect the user to `connectUrl` (or call `/api/integrations/oauth/google/connect` directly), then retry the original call.

## See also

* [External APIs guide](/guides/external-apis) - patterns, billing routing, and discovery.
* [CLI integrations command](/cli-reference/commands#integrations-and-invoke) - `list` / `info` / `invoke`.
* [AI chat guide](/guides/ai-chat) - streamed LLM responses with tool use (uses a different pipeline).
