SDK Reference

Leash SDK

Typed clients for TypeScript, Python, Go, Ruby, Rust, and Java. Access Gmail, Calendar, and Drive from your app code.

Overview

The Leash SDK gives your deployed apps typed access to third-party services on behalf of each user. Available for TypeScript, Python, Go, Ruby, Rust, and Java.

Connect once, use everywhere

Users connect their accounts on leash.build. Every app you deploy can access their integrations through a single SDK call.

Per-user authentication

Each user authorizes with their own account. Your app never sees other users' data. The SDK handles token management and refresh automatically.

API key support

For server-to-server usage, pass an API key instead of a user token. API keys are scoped per-app and can be created from the dashboard or API.

Three providers

Gmail, Google Calendar, and Google Drive are available today. Each provider has a dedicated client with typed methods for all supported operations.

SDK versions

TypeScript: @leash/sdk
Python: leash-sdk
Go: github.com/leash-build/leash-sdk-go
Ruby: leash-sdk
Rust: leash-sdk
Java: build.leash:leash-sdk

Installation

Install the SDK for your language of choice.

Terminal

$ npm install @leash/sdk

Setup

Initialize the SDK with your credentials. The TypeScript SDK reads LEASH_PLATFORM_URL from the environment by default. All SDKs accept an auth token and an optional API key.

app.ts
import { LeashIntegrations } from '@leash/sdk/integrations'
// Minimal setup — reads LEASH_PLATFORM_URL from env
const integrations = new LeashIntegrations()
// With explicit config
const integrations = new LeashIntegrations({
platformUrl: 'https://leash.build',
authToken: 'user-jwt-token',
apiKey: 'your-api-key', // optional, for server-to-server
})

Gmail

Send, read, search, and list email messages. The Gmail client also supports listing labels and getting the user's profile.

listMessages

List recent messages from the user's inbox. Supports filtering by query, label IDs, and pagination.

TypeScript
const gmail = integrations.gmail
// List recent messages
const messages = await gmail.listMessages({ maxResults: 10 })
// Filter by label
const starred = await gmail.listMessages({
labelIds: ['STARRED'],
maxResults: 5,
})
// With query filter
const unread = await gmail.listMessages({
query: 'is:unread',
maxResults: 20,
})
// Paginate
const nextPage = await gmail.listMessages({
pageToken: messages.nextPageToken,
})

getMessage

Retrieve a single message by ID. Supports format options: full, metadata, minimal, or raw.

TypeScript
// Full message with body
const msg = await gmail.getMessage(messageId)
// Metadata only (headers, no body)
const headers = await gmail.getMessage(messageId, 'metadata')

sendMessage

Send an email on behalf of the user. Supports to, subject, body, cc, and bcc fields.

TypeScript
await gmail.sendMessage({
to: 'recipient@example.com',
subject: 'Hello from my Leash app',
body: 'This email was sent via the Leash SDK.',
cc: 'teammate@example.com', // optional
bcc: 'archive@example.com', // optional
})

searchMessages

Search messages using Gmail search syntax. Returns a paginated list.

TypeScript
const results = await gmail.searchMessages(
'from:boss@company.com is:unread',
10 // maxResults (optional)
)

listLabels / getProfile

List all Gmail labels or get the authenticated user's profile info.

TypeScript
// List all labels
const labels = await gmail.listLabels()
// Get user profile (email, messages total, threads total)
const profile = await gmail.getProfile()

Calendar

List calendars and events, create events, and retrieve event details from Google Calendar.

listCalendars

Returns all calendars the user has access to.

TypeScript
const calendar = integrations.calendar
const calendars = await calendar.listCalendars()
// => [{ id: 'primary', summary: 'My Calendar', ... }, ...]

listEvents

List events from a calendar. Supports time range filtering, search queries, and pagination.

TypeScript
// Upcoming events from primary calendar
const events = await calendar.listEvents({
calendarId: 'primary',
timeMin: new Date().toISOString(),
maxResults: 20,
singleEvents: true,
orderBy: 'startTime',
})
// Search events by keyword
const meetings = await calendar.listEvents({
query: 'standup',
timeMin: '2026-04-10T00:00:00Z',
timeMax: '2026-04-17T00:00:00Z',
})

createEvent

Create a new event on the user's calendar. Supports attendees, location, and description.

TypeScript
const event = await calendar.createEvent({
calendarId: 'primary',
summary: 'Team standup',
description: 'Daily sync meeting',
location: 'Zoom',
start: { dateTime: '2026-04-11T09:00:00-04:00' },
end: { dateTime: '2026-04-11T09:30:00-04:00' },
attendees: [
{ email: 'alice@company.com' },
{ email: 'bob@company.com' },
],
})

getEvent

Retrieve a single event by its ID.

TypeScript
const event = await calendar.getEvent(eventId)
// From a specific calendar
const event = await calendar.getEvent(eventId, 'work-calendar-id')

Drive

List, get, search, upload, and download files from Google Drive. Also supports creating folders and deleting files.

listFiles

List files in the user's Drive. Supports filtering by folder and query.

TypeScript
const drive = integrations.drive
// List recent files
const files = await drive.listFiles({ maxResults: 25 })
// List files in a specific folder
const folderFiles = await drive.listFiles({
folderId: 'folder-id-here',
maxResults: 50,
})

getFile

Get metadata for a single file by ID.

TypeScript
const file = await drive.getFile(fileId)
// => { id, name, mimeType, size, modifiedTime, ... }

searchFiles

Search files using Google Drive query syntax.

TypeScript
const results = await drive.searchFiles(
'name contains "report" and mimeType = "application/pdf"',
10 // maxResults (optional)
)

Additional operations

The Drive client also supports downloading file content, creating folders, uploading files, and deleting files.

TypeScript
// Download file content
const content = await drive.downloadFile(fileId)
// Create a folder
const folder = await drive.createFolder('My Folder')
const subfolder = await drive.createFolder('Sub Folder', parentFolderId)
// Upload a file
const uploaded = await drive.uploadFile({
name: 'report.txt',
content: 'File content here',
mimeType: 'text/plain',
parentId: folderId, // optional
})
// Delete a file
await drive.deleteFile(fileId)

Connections

Check whether a user has connected a provider, list all connections, or get the OAuth URL to prompt users to connect.

isConnected

Check if a specific provider is connected and active for the current user.

TypeScript
const hasGmail = await integrations.isConnected('gmail')
const hasCalendar = await integrations.isConnected('google_calendar')
const hasDrive = await integrations.isConnected('google_drive')
if (!hasGmail) {
// Prompt user to connect Gmail
const url = integrations.getConnectUrl('gmail')
// redirect or show link...
}

getConnections

Retrieve the connection status for all providers at once.

TypeScript
const connections = await integrations.getConnections()
// => [
// { providerId: 'gmail', status: 'active' },
// { providerId: 'google_calendar', status: 'active' },
// { providerId: 'google_drive', status: 'expired' },
// ]

getConnectUrl

Generate the OAuth URL that kicks off the connection flow for a provider. Optionally pass a return URL to redirect back to your app after authorization.

TypeScript
// Basic connect URL
const url = integrations.getConnectUrl('gmail')
// => "https://leash.build/api/integrations/connect/gmail"
// With return URL
const url = integrations.getConnectUrl('gmail', 'https://my-app.un.leash.build/settings')
// => "https://leash.build/api/integrations/connect/gmail?return_url=https%3A%2F%2F..."

Error Handling

All SDK methods throw typed errors when the platform returns a non-success response. Errors include a machine-readable code field for programmatic handling.

Error codes

CodeMeaningAction
not_connectedUser has not connected this providerRedirect to getConnectUrl()
token_expiredOAuth token has expiredPrompt user to re-authorize
invalid_api_keyAPI key is missing or invalidCheck your API key configuration
rate_limitedToo many requests to the providerBack off and retry after a delay
provider_errorUpstream provider returned an errorCheck the error message for details
error-handling.ts
import { IntegrationError } from '@leash/sdk/integrations'
try {
const messages = await integrations.gmail.listMessages()
} catch (err) {
if (err instanceof IntegrationError) {
switch (err.code) {
case 'not_connected':
// User hasn't connected Gmail yet
const url = integrations.getConnectUrl('gmail')
// Redirect them to authorize
break
case 'token_expired':
// OAuth token expired, prompt re-auth
const reAuthUrl = integrations.getConnectUrl('gmail',
'https://my-app.un.leash.build/settings'
)
break
case 'rate_limited':
// Back off and retry
await new Promise(r => setTimeout(r, 5000))
break
default:
console.error(`Integration error: ${err.message}`)
}
}
}

API Keys

API keys let your server-side code authenticate with the Leash platform without a user JWT. Keys are scoped per-app and sent via the X-API-Key header.

Creating a key via the dashboard

1

Go to your app's Settings page on leash.build

2

Navigate to the API Keys section

3

Click Create Key, give it a name, and copy the value

Creating a key via the API

Terminal (cURL)
curl -X POST https://leash.build/api/keys \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "my-server-key", "appId": "app-id-here"}'
# Response:
# { "key": "lsk_live_abc123...", "name": "my-server-key" }

Using API keys in the SDK

Pass the API key when initializing the client. When using an API key, you still need an auth token to identify which user's integrations to access. The API key authorizes your app; the auth token identifies the user.

TypeScript
const integrations = new LeashIntegrations({
apiKey: process.env.LEASH_API_KEY,
authToken: userJwtToken, // identifies the user
})

Security best practices

  • Never expose API keys in client-side code or commit them to git
  • Store keys in environment variables (leash env set LEASH_API_KEY "lsk_live_...")
  • Rotate keys periodically from the dashboard
  • Use separate keys for development and production