---
title: Platform Tools
description: Access 25 adapters (13 integration + 12 platform) from your SDK workflows -- typed adapters, credential security model, and working code examples
---

Your SDK workflows have access to 25 adapters (13 integration + 12 platform) -- Gmail, Stripe, Google Sheets, PDF generation, human-in-the-loop approvals, storage, scheduling, and more. Credentials are managed server-side and never appear in your code.

**Typed adapters** are the recommended way to call tools. They provide full TypeScript autocomplete, compile-time method checking, and eliminate boilerplate. Use `platform.call()` only for tools that don't have an adapter yet.

## Usage

**Preferred: Typed adapters** (available for all integration tools + key platform services)

```typescript
import { createResendAdapter, scheduler, llm } from '@elevasis/sdk/worker'

// Integration tools: factory pattern (credential bound once)
const resend = createResendAdapter('my-resend')
await resend.sendEmail({ from: 'hi@app.com', to: 'user@example.com', subject: '...' })

// Platform services: singleton (no credential needed)
await scheduler.createSchedule({ ... })
const result = await llm.generate({ messages: [...] })
```

**Fallback: `platform.call()`** (for tools without typed adapters)

```typescript
import { platform } from '@elevasis/sdk/worker'

const result = await platform.call({
  tool: 'acqDb',            // tool name
  method: 'upsertDeal',     // method exposed by that tool
  params: { ... },          // method-specific parameters
})
```

Both patterns return a Promise that resolves with the tool result or rejects with a `PlatformToolError` containing a message, error code, and a `retryable` flag.

## Integration Adapters

The SDK ships 13 integration adapters (credential-bound factory functions) covering third-party APIs -- Attio, Apify, ClickUp, Dropbox, Gmail, Google Sheets, Instantly, MillionVerifier, Anymailfinder, Tomba, Resend, SignatureAPI, and Stripe. Pass the credential name once at adapter creation. Supabase is covered separately under [Database Access](#database-access) below.

For the full per-adapter method tables, credential shapes, and code examples, see [Integration Adapters](adapters-integration.mdx) -- generated from the adapter source so it never drifts.

## Credential Security

Integration credentials are never stored in `.env` and never available via `process.env` inside worker threads. Credentials live in the platform credential system and are accessed through three controlled channels.

### Layer 1: Platform Tools (Default)

All platform tools resolve credentials server-side. The credential value never crosses the postMessage boundary into the worker.

```typescript
// Credential 'my-gmail' is resolved server-side -- value never enters worker memory
const result = await platform.call({
  tool: 'gmail',
  method: 'sendEmail',
  credential: 'my-gmail',
  params: { to: '...', subject: '...', body: '...' },
})
```

### Layer 2: HTTP Platform Tool

For APIs without a dedicated adapter, use the `http` platform tool. Credentials are injected server-side before the outgoing request.

```typescript
const result = await platform.call({
  tool: 'http',
  method: 'request',
  credential: 'my-custom-api',
  params: {
    url: 'https://api.example.com/v1/data',
    method: 'GET',
    headers: { 'Content-Type': 'application/json' },
  },
})
```

**Supported injection patterns:**

| Pattern         | Credential Config                                                | How Injected                             |
| --------------- | ---------------------------------------------------------------- | ---------------------------------------- |
| Bearer token    | `{ type: 'bearer', token: 'sk_...' }`                            | `Authorization: Bearer sk_...` header    |
| API key header  | `{ type: 'api-key-header', header: 'X-API-Key', key: 'ak_...' }` | Custom header                            |
| Basic auth      | `{ type: 'basic', username: '...', password: '...' }`            | `Authorization: Basic base64(user:pass)` |
| Query parameter | `{ type: 'query-param', param: 'api_key', value: 'ak_...' }`     | Appended to URL                          |
| Body field      | `{ type: 'body-field', field: 'apiKey', value: 'ak_...' }`       | Merged into JSON body                    |
| Custom header   | `{ type: 'custom-header', header: 'X-Custom', value: '...' }`    | Arbitrary header                         |

### Layer 3: getCredential() (Explicit Opt-In)

For third-party SDKs that require a raw key (e.g., `new Stripe(key)`), use `platform.getCredential()`. This explicitly causes the credential value to enter worker memory.

```typescript
import { platform } from '@elevasis/sdk/worker'

const cred = await platform.getCredential('my-stripe-key')
const stripe = new Stripe(cred.credentials.secretKey)
```

All `getCredential()` calls are logged. Use `platform.call()` with the `http` tool when possible.

### Credential Setup

Credentials are created in the command center UI: navigate to Credentials -> Add Credential -> select type -> enter values -> save. The name you give the credential is what you pass as `credential: 'my-cred-name'` in `platform.call()`. Credential names are case-sensitive -- a mismatch causes `PlatformToolError: credential not found`.

### Choosing a Pattern

| Situation                                              | Pattern                                 |
| ------------------------------------------------------ | --------------------------------------- |
| Using a supported adapter (Gmail, Attio, Stripe, etc.) | `platform.call()` with the adapter tool |
| Calling an API not in the catalog                      | `platform.call()` with `tool: 'http'`   |
| Third-party SDK that requires a raw key                | `platform.getCredential()`              |

`.env` contains only `ELEVASIS_PLATFORM_KEY` for CLI authentication -- never integration credentials.

---

## Platform Services

The SDK ships 12 platform service singletons available without a `credential` field -- imported directly from `@elevasis/sdk/worker`: `scheduler`, `llm`, `storage`, `notifications`, `acqDb`, `projects`, `crm`, `list`, `pdf`, `approval`, `execution`, and `email`. The platform injects context server-side, so no credential is passed.

For the full per-service method tables (including the `acqDb` 56-method surface), see [Platform Adapters](adapters-platform.mdx) -- generated from the adapter source.

## LLM Tool

Call any supported LLM from your workflow with no API keys required. Keys are resolved server-side from environment variables.

**Supported models:**

| Provider     | Models                                                    |
| ------------ | --------------------------------------------------------- |
| `google`     | `gemini-3-flash-preview`, `gemini-3.1-flash-lite-preview` |
| `openai`     | `gpt-5`, `gpt-5.4-mini`, `gpt-5.4-nano`                   |
| `anthropic`  | `claude-sonnet-4-5`                                       |
| `openrouter` | `openrouter/z-ai/glm-5`                                   |

**Key params:** `provider`, `model`, `messages` (`{ role, content }[]`), `responseSchema` (optional JSON Schema), `temperature` (optional).

See [Platform Adapters](adapters-platform.mdx#llm-adapter) for full typed usage with structured output.

## Execution Tool

Trigger another resource as a nested child of the current execution. The child runs synchronously and its result is returned. Maximum depth: 5 levels. The invoked resource must belong to the same organization.

See [Platform Adapters](adapters-platform.mdx#execution-adapter) for usage.

## Database Access

Supabase is a first-class integration adapter for persistent storage. Pass the `credential` field with your stored Supabase credential name.

**Methods:** `insert`, `select`, `update`, `delete`, `upsert`, `rpc`, `count`

```typescript
const qualified = await platform.call({
  tool: 'supabase',
  credential: 'my-database',
  method: 'select',
  params: {
    table: 'leads',
    filter: { status: 'eq.qualified', score: 'gte.80' },
    order: { column: 'score', ascending: false },
    limit: 50,
  },
})
```

**Filter syntax** uses PostgREST format (`operator.value`):

| Filter                   | Example                            | Meaning       |
| ------------------------ | ---------------------------------- | ------------- |
| `eq`                     | `{ status: 'eq.qualified' }`       | Equals        |
| `neq`                    | `{ status: 'neq.lost' }`           | Not equals    |
| `gt`, `gte`, `lt`, `lte` | `{ score: 'gte.80' }`              | Comparisons   |
| `like`, `ilike`          | `{ name: 'ilike.%jane%' }`         | Pattern match |
| `in`                     | `{ status: 'in.(new,contacted)' }` | In set        |
| `is`                     | `{ deleted_at: 'is.null' }`        | Null check    |

**Credential setup:** Create a credential with provider `supabase` -- config fields are `url` and `serviceRoleKey`. Workflows always use the service role key (server-side, no RLS).

**`/database init`:** Guided setup that stores your Supabase credential, generates `data/schema.ts`, and creates `docs/database.mdx`.

---

## Documentation

- [Integration Adapters](adapters-integration.mdx) - All 13 integration adapters with method tables and code examples
- [Platform Adapters](adapters-platform.mdx) - All 12 platform service adapters with method tables and code examples
- [Adapter Type Safety](type-safety.mdx) - Required fields, discriminated unions, and intentionally loose adapter types

---

**Last Updated:** 2026-03-06
