JavaScript/TypeScript SDK for epilot APIs. Full types, tree-shakeable imports, and lazy-loaded OpenAPI specs.
npm i @epilot/sdk axios openapi-client-axiosimport { epilot } from '@epilot/sdk'
epilot.authorize(() => '<my-bearer-token>')
const { data: entity } = await epilot.entity.createEntity(
{ slug: 'contact' },
{ first_name: 'John', last_name: 'Doe' },
)
const { data: file } = await epilot.file.getFile({ id: 'file-123' })
const { data: executions } = await epilot.workflow.getExecutions()API clients are built on openapi-client-axios, which generates fully typed operation methods on top of regular axios instances. All standard axios features (interceptors, defaults, config) work as expected. Each operation is forwarded to a lazy singleton — the spec is loaded and the client initialized on first use, then cached.
Full API documentation: https://docs.epilot.io/api
| Package | Description |
|---|---|
@epilot/sdk |
JavaScript/TypeScript SDK for epilot APIs |
@epilot/app-sdk |
SDK to build Apps for epilot XRM |
@epilot/app-bridge |
App bridge for communication between epilot apps and the host |
| API | Import | Docs |
|---|---|---|
epilot.accessToken |
@epilot/sdk/access-token |
docs |
epilot.address |
@epilot/sdk/address |
docs |
epilot.addressSuggestions |
@epilot/sdk/address-suggestions |
docs |
epilot.aiAgents |
@epilot/sdk/ai-agents |
docs |
epilot.app |
@epilot/sdk/app |
docs |
epilot.auditLogs |
@epilot/sdk/audit-logs |
docs |
epilot.automation |
@epilot/sdk/automation |
docs |
epilot.billing |
@epilot/sdk/billing |
docs |
epilot.blueprintManifest |
@epilot/sdk/blueprint-manifest |
docs |
epilot.consent |
@epilot/sdk/consent |
docs |
epilot.customerPortal |
@epilot/sdk/customer-portal |
docs |
epilot.dashboard |
@epilot/sdk/dashboard |
docs |
epilot.dataManagement |
@epilot/sdk/data-management |
docs |
epilot.deduplication |
@epilot/sdk/deduplication |
docs |
epilot.design |
@epilot/sdk/design |
docs |
epilot.document |
@epilot/sdk/document |
docs |
epilot.emailSettings |
@epilot/sdk/email-settings |
docs |
epilot.emailTemplate |
@epilot/sdk/email-template |
docs |
epilot.entity |
@epilot/sdk/entity |
docs |
epilot.entityMapping |
@epilot/sdk/entity-mapping |
docs |
epilot.environments |
@epilot/sdk/environments |
docs |
epilot.erpIntegration |
@epilot/sdk/erp-integration |
docs |
epilot.eventCatalog |
@epilot/sdk/event-catalog |
docs |
epilot.file |
@epilot/sdk/file |
docs |
epilot.iban |
@epilot/sdk/iban |
docs |
epilot.journey |
@epilot/sdk/journey |
docs |
epilot.kanban |
@epilot/sdk/kanban |
docs |
epilot.message |
@epilot/sdk/message |
docs |
epilot.metering |
@epilot/sdk/metering |
docs |
epilot.notes |
@epilot/sdk/notes |
docs |
epilot.notification |
@epilot/sdk/notification |
docs |
epilot.organization |
@epilot/sdk/organization |
docs |
epilot.partnerDirectory |
@epilot/sdk/partner-directory |
docs |
epilot.permissions |
@epilot/sdk/permissions |
docs |
epilot.pricing |
@epilot/sdk/pricing |
docs |
epilot.pricingTier |
@epilot/sdk/pricing-tier |
docs |
epilot.purpose |
@epilot/sdk/purpose |
docs |
epilot.sandbox |
@epilot/sdk/sandbox |
docs |
epilot.submission |
@epilot/sdk/submission |
docs |
epilot.targeting |
@epilot/sdk/targeting |
docs |
epilot.templateVariables |
@epilot/sdk/template-variables |
docs |
epilot.user |
@epilot/sdk/user |
docs |
epilot.validationRules |
@epilot/sdk/validation-rules |
docs |
epilot.webhooks |
@epilot/sdk/webhooks |
docs |
epilot.workflow |
@epilot/sdk/workflow |
docs |
epilot.workflowDefinition |
@epilot/sdk/workflow-definition |
docs |
sdk-js/
├── clients/ # 46 API client packages (source specs + types)
├── packages/
│ ├── epilot-sdk-v2/ # @epilot/sdk
│ ├── app-sdk/ # @epilot/app-sdk
│ └── app-bridge/ # @epilot/app-bridge
└── scripts/ # code generation scripts
import { epilot } from '@epilot/sdk'
epilot.authorize(() => '<my-token>')
// Get the cached singleton client
const entityClient = epilot.entity.getClient()
const { data } = await entityClient.getEntity({ slug: 'contact', id: '123' })
// Create a fresh (non-singleton) client instance
const freshClient = epilot.entity.createClient()
authorize(freshClient, () => '<my-token>')Import only what you need. Other APIs never touch your bundle.
import { getClient, authorize } from '@epilot/sdk/entity'
const entityClient = getClient()
authorize(entityClient, () => '<my-token>')
const { data } = await entityClient.getEntity({ slug: 'contact', id: '123' })
// Or use the handle for direct operation forwarding
import { entity } from '@epilot/sdk/entity'
const { data } = await entity.getEntity({ slug: 'contact', id: '123' })Each API subpath re-exports all schema types generated from the OpenAPI spec. Import them directly:
import type { Entity, EntitySchema, RelationAttribute } from '@epilot/sdk/entity'
import type { FileItem } from '@epilot/sdk/file'
import type { AutomationFlow } from '@epilot/sdk/automation'The Client, OperationMethods, and PathsDictionary types are also available for typing client instances:
import type { Client } from '@epilot/sdk/entity'
const entityClient: Client = epilot.entity.getClient()Set default headers applied to all clients. Useful for x-epilot-org-id, x-epilot-user-id, etc.
import { epilot } from '@epilot/sdk'
epilot.authorize(() => '<my-token>')
epilot.headers({
'x-epilot-org-id': 'org-123',
'x-epilot-user-id': 'user-456',
})
const { data } = await epilot.entity.searchEntities(...)Use standard axios defaults.headers.common on individual clients:
const entityClient = epilot.entity.getClient()
entityClient.defaults.headers.common['x-epilot-org-id'] = 'org-123'authorize() accepts a string or a function. The function form is preferred — it is called on every request, so tokens stay fresh.
import { authorize } from '@epilot/sdk'
import { getClient } from '@epilot/sdk/entity'
// Per-client — function predicate (recommended)
const entityClient = getClient()
authorize(entityClient, () => '<my-token>')
// Per-client — async function (e.g. OAuth / session)
authorize(entityClient, async () => {
return await getTokenFromSession()
})
// Per-client — static string (sets default header once)
authorize(entityClient, 'my-static-api-token')// Global — applies to all clients resolved from the SDK
import { epilot } from '@epilot/sdk'
epilot.authorize(() => '<my-token>')
epilot.authorize(async () => await getTokenFromSession())
epilot.authorize('my-static-api-token')import { createClient, authorize } from '@epilot/sdk/entity'
const entityClient = createClient()
authorize(entityClient, () => '<my-token>')
entityClient.defaults.headers.common['x-epilot-org-id'] = 'org-123'import { createSDK } from '@epilot/sdk'
const sdk1 = createSDK()
sdk1.authorize(() => '<token-for-org-1>')
sdk1.headers({ 'x-epilot-org-id': 'org-1' })
const sdk2 = createSDK()
sdk2.authorize(() => '<token-for-org-2>')
sdk2.headers({ 'x-epilot-org-id': 'org-2' })Use axios interceptors for custom request/response processing. Since clients are axios instances, you can use client.interceptors directly:
entityClient.interceptors.response.use((response) => {
console.debug(`${response.config.method?.toUpperCase()} ${response.config.url}`, {
status: response.status,
data: response.data,
})
return response
})Or register global interceptors applied to all clients:
epilot.interceptors.request((config) => {
config.headers['x-correlation-id'] = generateTraceId()
return config
})The SDK automatically retries requests that receive a 429 Too Many Requests response. It respects the Retry-After header (in seconds) to determine how long to wait before retrying.
Enabled by default with up to 3 retries.
import { epilot } from '@epilot/sdk'
// Customize retry behavior
epilot.retry({ maxRetries: 5, defaultDelayMs: 2000 })
// Disable retries
epilot.retry({ maxRetries: 0 })| Option | Default | Description |
|---|---|---|
maxRetries |
3 |
Maximum number of retries. Set to 0 to disable. |
defaultDelayMs |
1000 |
Fallback delay in ms when Retry-After header is missing. |
For individually imported clients (tree-shakeable imports), apply the interceptor manually:
import { getClient, authorize } from '@epilot/sdk/entity'
import { applyRetryInterceptor } from '@epilot/sdk'
const entityClient = getClient()
authorize(entityClient, () => '<my-token>')
applyRetryInterceptor({ client: entityClient, config: { maxRetries: 3 } })epilot APIs use a large response middleware to work around the AWS Lambda 6MB response limit. When a response exceeds ~5.1MB, the API uploads the payload to S3 and returns a presigned URL instead.
The SDK handles this transparently — it sends the opt-in Accept header and automatically fetches the full payload from S3 when a large response URL is returned. Enabled by default.
import { epilot } from '@epilot/sdk'
// Disable large response handling
epilot.largeResponse({ enabled: false })For individually imported clients (tree-shakeable imports), apply the interceptor manually:
import { getClient, authorize } from '@epilot/sdk/entity'
import { applyLargeResponseInterceptor } from '@epilot/sdk'
const entityClient = getClient()
authorize(entityClient, () => '<my-token>')
applyLargeResponseInterceptor({ client: entityClient, config: { enabled: true } })Override built-in API specs or register custom APIs via .epilot/sdk-overrides.json. This is useful for testing new versions of an API spec or getting the latest types without waiting for an SDK release.
{
"entity": "./specs/entity-openapi.json",
"myNewApi": "./specs/my-new-api-openapi.json"
}// Built-in API with overridden spec
const { data } = await epilot.entity.getEntity({ slug: 'contact', id: '123' })# Apply all overrides from .epilot/sdk-overrides.json
npx epilot-sdk override
# Override a single API
npx epilot-sdk override entity ./my-local-entity-spec.yaml
# Regenerate types after spec changes
npx epilot-sdk typegenMigration from @epilot/*-client
Drop-in replacement — just change the import path:
// Before
import { getClient, createClient, authorize } from '@epilot/entity-client'
import type { Client, Entity } from '@epilot/entity-client'
// After
import { getClient, createClient, authorize } from '@epilot/sdk/entity'
import type { Client, Entity } from '@epilot/sdk/entity'Client Lifecycle
When you call authorize(), headers(), retry(), largeResponse(), or interceptors, the SDK invalidates all cached client instances. The next operation call creates a fresh client with the updated configuration.
Operation methods are always up to date — calls like epilot.entity.getEntity(...) re-resolve the client on every invocation, so they always use the latest config.
Direct getClient() references can go stale — if you hold a reference and then change config, your reference still points to the old client:
const entityClient = epilot.entity.getClient()
epilot.authorize('new-token') // invalidates all cached clients
// entityClient still has the old token
// epilot.entity.getEntity(...) will use a new client with the new tokenIf you need a long-lived reference that survives config changes, call getClient() again after changing config, or use operation methods directly.
See CONTRIBUTING.md for internal development and publishing instructions.
