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
@leash/sdkleash-sdkgithub.com/leash-build/leash-sdk-goleash-sdkleash-sdkbuild.leash:leash-sdkInstallation
Install the SDK for your language of choice.
$ 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.
import { LeashIntegrations } from '@leash/sdk/integrations'// Minimal setup — reads LEASH_PLATFORM_URL from envconst integrations = new LeashIntegrations()// With explicit configconst 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.
const gmail = integrations.gmail// List recent messagesconst messages = await gmail.listMessages({ maxResults: 10 })// Filter by labelconst starred = await gmail.listMessages({labelIds: ['STARRED'],maxResults: 5,})// With query filterconst unread = await gmail.listMessages({query: 'is:unread',maxResults: 20,})// Paginateconst nextPage = await gmail.listMessages({pageToken: messages.nextPageToken,})
getMessage
Retrieve a single message by ID. Supports format options: full, metadata, minimal, or raw.
// Full message with bodyconst 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.
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', // optionalbcc: 'archive@example.com', // optional})
searchMessages
Search messages using Gmail search syntax. Returns a paginated list.
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.
// List all labelsconst 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.
const calendar = integrations.calendarconst calendars = await calendar.listCalendars()// => [{ id: 'primary', summary: 'My Calendar', ... }, ...]
listEvents
List events from a calendar. Supports time range filtering, search queries, and pagination.
// Upcoming events from primary calendarconst events = await calendar.listEvents({calendarId: 'primary',timeMin: new Date().toISOString(),maxResults: 20,singleEvents: true,orderBy: 'startTime',})// Search events by keywordconst 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.
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.
const event = await calendar.getEvent(eventId)// From a specific calendarconst 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.
const drive = integrations.drive// List recent filesconst files = await drive.listFiles({ maxResults: 25 })// List files in a specific folderconst folderFiles = await drive.listFiles({folderId: 'folder-id-here',maxResults: 50,})
getFile
Get metadata for a single file by ID.
const file = await drive.getFile(fileId)// => { id, name, mimeType, size, modifiedTime, ... }
searchFiles
Search files using Google Drive query syntax.
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.
// Download file contentconst content = await drive.downloadFile(fileId)// Create a folderconst folder = await drive.createFolder('My Folder')const subfolder = await drive.createFolder('Sub Folder', parentFolderId)// Upload a fileconst uploaded = await drive.uploadFile({name: 'report.txt',content: 'File content here',mimeType: 'text/plain',parentId: folderId, // optional})// Delete a fileawait 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.
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 Gmailconst url = integrations.getConnectUrl('gmail')// redirect or show link...}
getConnections
Retrieve the connection status for all providers at once.
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.
// Basic connect URLconst url = integrations.getConnectUrl('gmail')// => "https://leash.build/api/integrations/connect/gmail"// With return URLconst 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
| Code | Meaning | Action |
|---|---|---|
| not_connected | User has not connected this provider | Redirect to getConnectUrl() |
| token_expired | OAuth token has expired | Prompt user to re-authorize |
| invalid_api_key | API key is missing or invalid | Check your API key configuration |
| rate_limited | Too many requests to the provider | Back off and retry after a delay |
| provider_error | Upstream provider returned an error | Check the error message for details |
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 yetconst url = integrations.getConnectUrl('gmail')// Redirect them to authorizebreakcase 'token_expired':// OAuth token expired, prompt re-authconst reAuthUrl = integrations.getConnectUrl('gmail','https://my-app.un.leash.build/settings')breakcase 'rate_limited':// Back off and retryawait new Promise(r => setTimeout(r, 5000))breakdefault: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
Go to your app's Settings page on leash.build
Navigate to the API Keys section
Click Create Key, give it a name, and copy the value
Creating a key via the API
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.
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