@elevasis/sdk 0.5.13 → 0.5.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,1072 +1,868 @@
1
- ---
2
- title: Typed Adapters
3
- description: Type-safe wrappers over platform.call() for all integrations and platform tools -- Attio, Stripe, Notion, Google Sheets, and more -- with full autocomplete, compile-time checking, and zero boilerplate
4
- loadWhen: "Using typed adapters instead of raw platform.call(), or needs to know what adapter methods are available"
5
- ---
6
-
7
- Typed adapters are ergonomic wrappers over `platform.call()` that provide full TypeScript autocomplete and compile-time type checking. They eliminate stringly-typed method names, manual `as Promise<T>` casts, and repeated credential passing.
8
-
9
- Both patterns work simultaneously -- adapters compile down to `platform.call()` with zero runtime overhead.
10
-
11
- **Coverage:** 11 integration adapters (55 methods) + 9 platform tool adapters (57 methods) = 112 total typed methods.
12
-
13
- ```typescript
14
- // Before: raw platform.call()
15
- const records = await platform.call({
16
- tool: 'attio',
17
- method: 'listRecords',
18
- credential: 'my-attio',
19
- params: { object: 'deals' }
20
- }) as unknown as QueryRecordsResult
21
-
22
- // After: typed adapter
23
- const attio = createAttioAdapter('my-attio')
24
- const records = await attio.listRecords({ object: 'deals' })
25
- // ^-- QueryRecordsResult (fully typed, autocomplete on params)
26
- ```
27
-
28
- All adapters are imported from `@elevasis/sdk/worker` -- the same subpath as `platform`.
29
-
30
- ---
31
-
32
- ## Quick Reference
33
-
34
- ### Integration Adapters (factory pattern, credential required)
35
-
36
- | Import | Methods |
37
- |--------|---------|
38
- | `createAttioAdapter(cred)` | 12 |
39
- | `createStripeAdapter(cred)` | 6 |
40
- | `createNotionAdapter(cred)` | 8 |
41
- | `createGoogleSheetsAdapter(cred)` | 13 |
42
- | `createInstantlyAdapter(cred)` | 5 |
43
- | `createSignatureApiAdapter(cred)` | 4 |
44
- | `createResendAdapter(cred)` | 2 |
45
- | `createDropboxAdapter(cred)` | 2 |
46
- | `createApifyAdapter(cred)` | 1 |
47
- | `createGmailAdapter(cred)` | 1 |
48
- | `createMailsoAdapter(cred)` | 1 |
49
-
50
- ### Platform Adapters (singletons, no credential)
51
-
52
- | Import | Methods |
53
- |--------|---------|
54
- | `lead` | 35 |
55
- | `scheduler` | 9 |
56
- | `storage` | 5 |
57
- | `pdf` | 2 |
58
- | `approval` | 2 |
59
- | `notifications` | 1 |
60
- | `llm` | 1 |
61
- | `execution` | 1 |
62
- | `email` | 1 |
63
-
64
- ### Generic Factory
65
-
66
- | Import | Description |
67
- |--------|-------------|
68
- | `createAdapter<TMap>(tool, methods, cred?)` | Build custom adapters for any tool |
69
-
70
- ```typescript
71
- import {
72
- // Integration adapters
73
- createAttioAdapter, createStripeAdapter, createNotionAdapter,
74
- createGoogleSheetsAdapter, createInstantlyAdapter,
75
- createSignatureApiAdapter, createResendAdapter, createDropboxAdapter,
76
- createApifyAdapter, createGmailAdapter, createMailsoAdapter,
77
- // Platform adapters
78
- lead, scheduler, storage, pdf, approval, notifications, llm, execution, email,
79
- // Generic factory
80
- createAdapter,
81
- } from '@elevasis/sdk/worker'
82
- ```
83
-
84
- ---
85
-
86
- ## Attio CRM Adapter
87
-
88
- Factory pattern -- bind a credential once, use 12 typed methods.
89
-
90
- ```typescript
91
- const attio = createAttioAdapter('my-attio-credential')
92
- ```
93
-
94
- ### Methods
95
-
96
- | Method | Params | Returns |
97
- |--------|--------|---------|
98
- | `createRecord` | `CreateRecordParams` | `CreateRecordResult` |
99
- | `updateRecord` | `UpdateRecordParams` | `UpdateRecordResult` |
100
- | `listRecords` | `QueryRecordsParams` | `QueryRecordsResult` |
101
- | `getRecord` | `GetRecordParams` | `GetRecordResult` |
102
- | `deleteRecord` | `DeleteRecordParams` | `DeleteRecordResult` |
103
- | `listObjects` | none | `ListObjectsResult` |
104
- | `listAttributes` | `ListAttributesParams` | `ListAttributesResult` |
105
- | `createAttribute` | `CreateAttributeParams` | `CreateAttributeResult` |
106
- | `updateAttribute` | `UpdateAttributeParams` | `UpdateAttributeResult` |
107
- | `createNote` | `CreateNoteParams` | `CreateNoteResult` |
108
- | `listNotes` | `ListNotesParams` | `ListNotesResult` |
109
- | `deleteNote` | `DeleteNoteParams` | `DeleteNoteResult` |
110
-
111
- ### Examples
112
-
113
- ```typescript
114
- // Create a company record
115
- const company = await attio.createRecord({
116
- object: 'companies',
117
- values: {
118
- name: [{ value: 'Acme Corp' }],
119
- domains: [{ domain: 'acme.com' }],
120
- },
121
- })
122
-
123
- // Query deals with filters
124
- const deals = await attio.listRecords({
125
- object: 'deals',
126
- filter: {
127
- operator: 'and',
128
- filters: [
129
- { field: 'stage', operator: 'equals', value: 'proposal' },
130
- ],
131
- },
132
- sorts: [{ field: 'created_at', direction: 'desc' }],
133
- limit: 20,
134
- })
135
-
136
- // Get a specific record
137
- const record = await attio.getRecord({
138
- object: 'people',
139
- recordId: 'rec_abc123',
140
- })
141
-
142
- // List all objects (no params)
143
- const objects = await attio.listObjects()
144
-
145
- // Create a note on a record
146
- await attio.createNote({
147
- parentObject: 'deals',
148
- parentRecordId: 'rec_abc123',
149
- title: 'Discovery call notes',
150
- content: 'Key takeaways from the call...',
151
- })
152
- ```
153
-
154
- Multiple adapters with different credentials work simultaneously:
155
-
156
- ```typescript
157
- const prodAttio = createAttioAdapter('prod-attio')
158
- const testAttio = createAttioAdapter('test-attio')
159
- ```
160
-
161
- ---
162
-
163
- ## Stripe Adapter
164
-
165
- Factory pattern -- 6 methods for payment links and checkout sessions.
166
-
167
- ```typescript
168
- const stripe = createStripeAdapter('my-stripe-credential')
169
- ```
170
-
171
- ### Methods
172
-
173
- | Method | Params | Returns |
174
- |--------|--------|---------|
175
- | `createPaymentLink` | `CreatePaymentLinkParams` | `CreatePaymentLinkResult` |
176
- | `getPaymentLink` | `GetPaymentLinkParams` | `GetPaymentLinkResult` |
177
- | `updatePaymentLink` | `UpdatePaymentLinkParams` | `UpdatePaymentLinkResult` |
178
- | `listPaymentLinks` | `ListPaymentLinksParams` | `ListPaymentLinksResult` |
179
- | `createAutoPaymentLink` | `CreateAutoPaymentLinkParams` | `CreateAutoPaymentLinkResult` |
180
- | `createCheckoutSession` | `CreateCheckoutSessionParams` | `CreateCheckoutSessionResult` |
181
-
182
- ---
183
-
184
- ## Notion Adapter
185
-
186
- Factory pattern -- 8 methods for page and block operations.
187
-
188
- ```typescript
189
- const notion = createNotionAdapter('my-notion-credential')
190
- ```
191
-
192
- ### Methods
193
-
194
- | Method | Params | Returns |
195
- |--------|--------|---------|
196
- | `listAllPages` | none | `ListAllPagesResult` |
197
- | `readPage` | `ReadPageParams` | `ReadPageResult` |
198
- | `createPage` | `CreatePageParams` | `CreatePageResult` |
199
- | `updatePageTitle` | `UpdatePageTitleParams` | `UpdatePageTitleResult` |
200
- | `appendBlocks` | `AppendBlocksParams` | `AppendBlocksResult` |
201
- | `updateBlocks` | `UpdateBlocksParams` | `UpdateBlocksResult` |
202
- | `deletePage` | `DeletePageParams` | `DeletePageResult` |
203
- | `deleteBlocks` | `DeleteBlocksParams` | `DeleteBlocksResult` |
204
-
205
- ---
206
-
207
- ## Google Sheets Adapter
208
-
209
- Factory pattern -- 13 methods including workflow-friendly helpers (getRowByValue, upsertRow, filterRows).
210
-
211
- ```typescript
212
- const sheets = createGoogleSheetsAdapter('my-google-credential')
213
- ```
214
-
215
- ### Methods
216
-
217
- | Method | Params | Returns |
218
- |--------|--------|---------|
219
- | `readSheet` | `ReadSheetParams` | `ReadSheetResult` |
220
- | `writeSheet` | `WriteSheetParams` | `WriteSheetResult` |
221
- | `appendRows` | `AppendRowsParams` | `AppendRowsResult` |
222
- | `clearRange` | `ClearRangeParams` | `ClearRangeResult` |
223
- | `getSpreadsheetMetadata` | `GetSpreadsheetMetadataParams` | `GetSpreadsheetMetadataResult` |
224
- | `batchUpdate` | `BatchUpdateParams` | `BatchUpdateResult` |
225
- | `getHeaders` | `GetHeadersParams` | `GetHeadersResult` |
226
- | `getLastRow` | `GetLastRowParams` | `GetLastRowResult` |
227
- | `getRowByValue` | `GetRowByValueParams` | `GetRowByValueResult` |
228
- | `updateRowByValue` | `UpdateRowByValueParams` | `UpdateRowByValueResult` |
229
- | `upsertRow` | `UpsertRowParams` | `UpsertRowResult` |
230
- | `filterRows` | `FilterRowsParams` | `FilterRowsResult` |
231
- | `deleteRowByValue` | `DeleteRowByValueParams` | `DeleteRowByValueResult` |
232
-
233
- ---
234
-
235
- ## Instantly Adapter
236
-
237
- Factory pattern -- 5 methods for email campaign management.
238
-
239
- ```typescript
240
- const instantly = createInstantlyAdapter('my-instantly-credential')
241
- ```
242
-
243
- ### Methods
244
-
245
- | Method | Params | Returns |
246
- |--------|--------|---------|
247
- | `sendReply` | `SendReplyParams` | `SendReplyResult` |
248
- | `removeFromSubsequence` | `RemoveFromSubsequenceParams` | `RemoveFromSubsequenceResult` |
249
- | `getEmails` | `GetEmailsParams` | `GetEmailsResult` |
250
- | `updateInterestStatus` | `UpdateInterestStatusParams` | `UpdateInterestStatusResult` |
251
- | `addToCampaign` | `AddToCampaignParams` | `AddToCampaignResult` |
252
-
253
- ---
254
-
255
- ## SignatureAPI Adapter
256
-
257
- Factory pattern -- 4 methods for eSignature envelope operations.
258
-
259
- ```typescript
260
- const signatureApi = createSignatureApiAdapter('my-signatureapi-credential')
261
- ```
262
-
263
- ### Methods
264
-
265
- | Method | Params | Returns |
266
- |--------|--------|---------|
267
- | `createEnvelope` | `CreateEnvelopeParams` | `CreateEnvelopeResult` |
268
- | `voidEnvelope` | `VoidEnvelopeParams` | `VoidEnvelopeResult` |
269
- | `downloadDocument` | `DownloadDocumentParams` | `DownloadDocumentResult` |
270
- | `getEnvelope` | `GetEnvelopeParams` | `GetEnvelopeResult` |
271
-
272
- ---
273
-
274
- ## Resend Adapter
275
-
276
- Factory pattern -- 2 methods for transactional email.
277
-
278
- ```typescript
279
- const resend = createResendAdapter('my-resend-credential')
280
- ```
281
-
282
- ### Methods
283
-
284
- | Method | Params | Returns |
285
- |--------|--------|---------|
286
- | `sendEmail` | `ResendSendEmailParams` | `ResendSendEmailResult` |
287
- | `getEmail` | `ResendGetEmailParams` | `ResendGetEmailResult` |
288
-
289
- ---
290
-
291
- ## Dropbox Adapter
292
-
293
- Factory pattern -- 2 methods for file operations.
294
-
295
- ```typescript
296
- const dropbox = createDropboxAdapter('my-dropbox-credential')
297
- ```
298
-
299
- ### Methods
300
-
301
- | Method | Params | Returns |
302
- |--------|--------|---------|
303
- | `uploadFile` | `UploadFileParams` | `UploadFileResult` |
304
- | `createFolder` | `CreateFolderParams` | `CreateFolderResult` |
305
-
306
- ---
307
-
308
- ## Apify Adapter
309
-
310
- Factory pattern -- 1 method for running web scraping actors.
311
-
312
- ```typescript
313
- const apify = createApifyAdapter('my-apify-credential')
314
- ```
315
-
316
- ### Methods
317
-
318
- | Method | Params | Returns |
319
- |--------|--------|---------|
320
- | `runActor` | `RunActorParams` | `RunActorResult` |
321
-
322
- ---
323
-
324
- ## Gmail Adapter
325
-
326
- Factory pattern -- 1 method for sending email via Gmail API.
327
-
328
- ```typescript
329
- const gmail = createGmailAdapter('my-gmail-credential')
330
- ```
331
-
332
- ### Methods
333
-
334
- | Method | Params | Returns |
335
- |--------|--------|---------|
336
- | `sendEmail` | `GmailSendEmailParams` | `GmailSendEmailResult` |
337
-
338
- ---
339
-
340
- ## Mailso Adapter
341
-
342
- Factory pattern -- 1 method for email verification.
343
-
344
- ```typescript
345
- const mailso = createMailsoAdapter('my-mailso-credential')
346
- ```
347
-
348
- ### Methods
349
-
350
- | Method | Params | Returns |
351
- |--------|--------|---------|
352
- | `verifyEmail` | `MailsoVerifyEmailParams` | `MailsoVerifyEmailResult` |
353
-
354
- ---
355
-
356
- ## Scheduler Adapter
357
-
358
- Singleton -- 9 methods for task schedule management.
359
-
360
- ```typescript
361
- import { scheduler } from '@elevasis/sdk/worker'
362
- ```
363
-
364
- ### Methods
365
-
366
- | Method | Params | Returns |
367
- |--------|--------|---------|
368
- | `createSchedule` | `CreateScheduleInput` | `TaskSchedule` |
369
- | `updateAnchor` | `{ scheduleId, anchorAt }` | `TaskSchedule` |
370
- | `deleteSchedule` | `{ scheduleId }` | `void` |
371
- | `findByIdempotencyKey` | `{ idempotencyKey }` | `TaskSchedule | null` |
372
- | `deleteScheduleByIdempotencyKey` | `{ idempotencyKey }` | `void` |
373
- | `listSchedules` | `{ status?, limit?, offset? }` | `TaskSchedule[]` |
374
- | `getSchedule` | `{ scheduleId }` | `TaskSchedule` |
375
- | `cancelSchedule` | `{ scheduleId }` | `void` |
376
- | `cancelSchedulesByMetadata` | `{ metadata }` | `{ cancelledCount }` |
377
-
378
- ### Examples
379
-
380
- ```typescript
381
- // Create a relative schedule (follow-up sequence)
382
- const schedule = await scheduler.createSchedule({
383
- organizationId: context.organizationId,
384
- name: 'Proposal follow-up',
385
- target: { resourceType: 'workflow', resourceId: 'send-followup' },
386
- scheduleConfig: {
387
- type: 'relative',
388
- anchorAt: '2026-03-15T10:00:00Z',
389
- anchorLabel: 'proposal_sent_date',
390
- items: [
391
- { offset: '+3d', payload: { step: 'first-followup' }, label: 'First follow-up' },
392
- { offset: '+7d', payload: { step: 'second-followup' }, label: 'Second follow-up' },
393
- ],
394
- },
395
- metadata: { dealId: 'deal-123', contactEmail: 'jane@acme.com' },
396
- idempotencyKey: 'proposal-followup-deal-123',
397
- })
398
-
399
- // Reschedule (update anchor)
400
- await scheduler.updateAnchor({
401
- scheduleId: schedule.id,
402
- anchorAt: '2026-03-20T10:00:00Z',
403
- })
404
-
405
- // Find by idempotency key (check if already created)
406
- const existing = await scheduler.findByIdempotencyKey({
407
- idempotencyKey: 'proposal-followup-deal-123',
408
- })
409
-
410
- // Cancel all schedules matching metadata
411
- await scheduler.cancelSchedulesByMetadata({
412
- metadata: { dealId: 'deal-123' },
413
- })
414
-
415
- // List active schedules
416
- const schedules = await scheduler.listSchedules({
417
- status: 'active',
418
- limit: 50,
419
- })
420
- ```
421
-
422
- ---
423
-
424
- ## Storage Adapter
425
-
426
- Singleton -- 5 methods for file storage operations.
427
-
428
- ```typescript
429
- import { storage } from '@elevasis/sdk/worker'
430
- ```
431
-
432
- All paths are relative to the organization's storage prefix. The platform injects the organization prefix server-side.
433
-
434
- ### Methods
435
-
436
- | Method | Params | Returns |
437
- |--------|--------|---------|
438
- | `upload` | `StorageUploadInput` | `StorageUploadOutput` |
439
- | `download` | `StorageDownloadInput` | `StorageDownloadOutput` |
440
- | `createSignedUrl` | `StorageSignedUrlInput` | `StorageSignedUrlOutput` |
441
- | `delete` | `StorageDeleteInput` | `StorageDeleteOutput` |
442
- | `list` | `StorageListInput` | `StorageListOutput` |
443
-
444
- ### Examples
445
-
446
- ```typescript
447
- // Upload a PDF
448
- await storage.upload({
449
- bucket: 'acquisition',
450
- path: 'proposals/deal-123/proposal.pdf',
451
- content: base64PdfContent,
452
- contentType: 'application/pdf',
453
- })
454
-
455
- // Generate a signed URL (temporary access)
456
- const { signedUrl } = await storage.createSignedUrl({
457
- bucket: 'acquisition',
458
- path: 'proposals/deal-123/proposal.pdf',
459
- })
460
-
461
- // Download a file
462
- const file = await storage.download({
463
- bucket: 'acquisition',
464
- path: 'proposals/deal-123/proposal.pdf',
465
- })
466
-
467
- // List files under a prefix
468
- const files = await storage.list({
469
- bucket: 'acquisition',
470
- path: 'proposals/deal-123/',
471
- })
472
-
473
- // Delete a file
474
- await storage.delete({
475
- bucket: 'acquisition',
476
- path: 'proposals/deal-123/old-proposal.pdf',
477
- })
478
- ```
479
-
480
- ---
481
-
482
- ## Notification Adapter
483
-
484
- Singleton -- 1 method for sending in-app team notifications.
485
-
486
- ```typescript
487
- import { notifications } from '@elevasis/sdk/worker'
488
- ```
489
-
490
- `userId` and `organizationId` are injected server-side from the execution context -- you never pass them.
491
-
492
- ### Method
493
-
494
- | Method | Params | Returns |
495
- |--------|--------|---------|
496
- | `create` | `NotificationSDKInput` | `void` |
497
-
498
- `NotificationSDKInput` fields: `category`, `title`, `message`, `actionUrl?`, `metadata?`
499
-
500
- ### Example
501
-
502
- ```typescript
503
- await notifications.create({
504
- category: 'acquisition',
505
- title: 'New lead qualified',
506
- message: 'Acme Corp has been qualified and moved to proposal stage.',
507
- actionUrl: '/deals/deal-123',
508
- })
509
- ```
510
-
511
- ---
512
-
513
- ## LLM Adapter
514
-
515
- Singleton -- 1 method with a generic `<T>` for typed structured output.
516
-
517
- ```typescript
518
- import { llm } from '@elevasis/sdk/worker'
519
- ```
520
-
521
- No API keys needed -- keys are resolved server-side from environment variables.
522
-
523
- ### Method
524
-
525
- | Method | Params | Returns |
526
- |--------|--------|---------|
527
- | `generate<T>` | `Omit<LLMGenerateRequest, 'signal'>` | `LLMGenerateResponse<T>` |
528
-
529
- The `signal` property (AbortSignal) is not serializable over the postMessage boundary. Abort is handled at the worker level via the parent process sending an abort message.
530
-
531
- ### Example
532
-
533
- ```typescript
534
- interface Classification {
535
- category: 'interested' | 'not-interested' | 'bounced'
536
- confidence: number
537
- summary: string
538
- }
539
-
540
- const response = await llm.generate<Classification>({
541
- provider: 'anthropic',
542
- model: 'claude-sonnet-4-5',
543
- messages: [
544
- { role: 'system', content: 'Classify this email reply.' },
545
- { role: 'user', content: emailText },
546
- ],
547
- responseSchema: {
548
- type: 'object',
549
- properties: {
550
- category: { type: 'string', enum: ['interested', 'not-interested', 'bounced'] },
551
- confidence: { type: 'number', minimum: 0, maximum: 1 },
552
- summary: { type: 'string' },
553
- },
554
- required: ['category', 'confidence', 'summary'],
555
- },
556
- temperature: 0.1,
557
- })
558
-
559
- // response.output is typed as Classification
560
- const { category, confidence, summary } = response.output
561
- ```
562
-
563
- ---
564
-
565
- ## Lead Adapter
566
-
567
- Singleton -- 35 methods for acquisition lead management (lists, companies, contacts, deals, deal-sync). `organizationId` is injected server-side -- never pass it.
568
-
569
- ```typescript
570
- import { lead } from '@elevasis/sdk/worker'
571
- ```
572
-
573
- ### Methods
574
-
575
- **List operations:**
576
-
577
- | Method | Params | Returns |
578
- |--------|--------|---------|
579
- | `listLists` | none | `AcqList[]` |
580
- | `createList` | `Omit<CreateListParams, 'organizationId'>` | `AcqList` |
581
- | `updateList` | `{ id } & UpdateListParams` | `AcqList` |
582
- | `deleteList` | `{ id }` | `void` |
583
-
584
- **Company operations:**
585
-
586
- | Method | Params | Returns |
587
- |--------|--------|---------|
588
- | `createCompany` | `Omit<CreateCompanyParams, 'organizationId'>` | `AcqCompany` |
589
- | `upsertCompany` | `Omit<UpsertCompanyParams, 'organizationId'>` | `AcqCompany` |
590
- | `updateCompany` | `{ id } & UpdateCompanyParams` | `AcqCompany` |
591
- | `getCompany` | `{ id }` | `AcqCompany | null` |
592
- | `listCompanies` | `{ filters? }` | `AcqCompany[]` |
593
- | `deleteCompany` | `{ id }` | `void` |
594
-
595
- **Contact operations:**
596
-
597
- | Method | Params | Returns |
598
- |--------|--------|---------|
599
- | `createContact` | `Omit<CreateContactParams, 'organizationId'>` | `AcqContact` |
600
- | `upsertContact` | `Omit<UpsertContactParams, 'organizationId'>` | `AcqContact` |
601
- | `updateContact` | `{ id } & UpdateContactParams` | `AcqContact` |
602
- | `getContact` | `{ id }` | `AcqContact | null` |
603
- | `getContactByEmail` | `{ email }` | `AcqContact | null` |
604
- | `listContacts` | `{ filters?, pagination? }` | `PaginatedResult<AcqContact>` |
605
- | `deleteContact` | `{ id }` | `void` |
606
- | `bulkImportContacts` | `Omit<BulkImportParams, 'organizationId'>` | `BulkImportResult` |
607
-
608
- **Deal operations:**
609
-
610
- | Method | Params | Returns |
611
- |--------|--------|---------|
612
- | `upsertDeal` | `Omit<UpsertDealParams, 'organizationId'>` | `AcqDeal` |
613
- | `getDealByEmail` | `{ email }` | `AcqDeal | null` |
614
- | `getDealByEnvelopeId` | `{ envelopeId }` | `AcqDeal | null` |
615
- | `updateDealEnvelopeId` | `{ attioDealId, envelopeId }` | `AcqDeal | null` |
616
- | `getDealByAttioId` | `{ attioDealId }` | `AcqDeal | null` |
617
-
618
- **Deal-sync operations:**
619
-
620
- | Method | Params | Returns |
621
- |--------|--------|---------|
622
- | `updateDiscoveryData` | `Omit<UpdateDiscoveryDataParams, 'organizationId'>` | `void` |
623
- | `updateProposalData` | `Omit<UpdateProposalDataParams, 'organizationId'>` | `void` |
624
- | `markProposalSent` | `Omit<MarkProposalSentParams, 'organizationId'>` | `void` |
625
- | `markProposalReviewed` | `Omit<MarkProposalReviewedParams, 'organizationId'>` | `void` |
626
- | `updateCloseLostReason` | `Omit<UpdateCloseLostReasonParams, 'organizationId'>` | `void` |
627
- | `updateFees` | `Omit<UpdateFeesParams, 'organizationId'>` | `void` |
628
- | `syncDealStage` | `Omit<SyncDealStageParams, 'organizationId'>` | `void` |
629
- | `setContactNurture` | `Omit<SetContactNurtureParams, 'organizationId'>` | `void` |
630
- | `cancelSchedulesAndHitlByEmail` | `Omit<..., 'organizationId'>` | `{ schedulesCancelled, hitlDeleted }` |
631
- | `cancelHitlByDealId` | `Omit<..., 'organizationId'>` | `{ hitlDeleted }` |
632
- | `clearDealFields` | `Omit<ClearDealFieldsParams, 'organizationId'>` | `void` |
633
- | `deleteDeal` | `Omit<DeleteDealParams, 'organizationId'>` | `void` |
634
-
635
- ### Example
636
-
637
- ```typescript
638
- const deal = await lead.getDealByEmail({ email: 'jane@acme.com' })
639
- if (!deal) {
640
- await lead.upsertDeal({
641
- attioDealId: 'deal-123',
642
- contactEmail: 'jane@acme.com',
643
- })
644
- }
645
-
646
- // Bulk import contacts
647
- await lead.bulkImportContacts({
648
- listId: 'list-abc',
649
- contacts: [
650
- { email: 'a@example.com', firstName: 'Alice' },
651
- { email: 'b@example.com', firstName: 'Bob' },
652
- ],
653
- })
654
- ```
655
-
656
- ---
657
-
658
- ## PDF Adapter
659
-
660
- Singleton -- 2 methods for rendering PDFDocument structures to files or buffers. No credential required.
661
-
662
- ```typescript
663
- import { pdf } from '@elevasis/sdk/worker'
664
- ```
665
-
666
- ### Methods
667
-
668
- | Method | Params | Returns |
669
- |--------|--------|---------|
670
- | `render` | `{ document, theme?, storage: { bucket, path } }` | `{ success, pdfUrl, size }` |
671
- | `renderToBuffer` | `{ document, theme? }` | `{ buffer }` |
672
-
673
- ### Example
674
-
675
- ```typescript
676
- // Render to storage (returns a URL)
677
- const result = await pdf.render({
678
- document: proposalDocument,
679
- storage: { bucket: 'acquisition', path: 'proposals/deal-123/proposal.pdf' },
680
- })
681
- console.log(result.pdfUrl)
682
-
683
- // Render to buffer (for inline processing)
684
- const { buffer } = await pdf.renderToBuffer({
685
- document: proposalDocument,
686
- })
687
- ```
688
-
689
- ---
690
-
691
- ## Approval Adapter
692
-
693
- Singleton -- 2 methods for creating and managing human-in-the-loop (HITL) approval tasks. No credential required.
694
-
695
- ```typescript
696
- import { approval } from '@elevasis/sdk/worker'
697
- ```
698
-
699
- ### Methods
700
-
701
- | Method | Params | Returns |
702
- |--------|--------|---------|
703
- | `create` | `{ actions, context, description?, priority?, humanCheckpoint?, metadata?, expiresAt? }` | `{ id }` |
704
- | `deleteByMetadata` | `{ metadata, status? }` | `{ deleted }` |
705
-
706
- ### Example
707
-
708
- ```typescript
709
- // Create an approval gate
710
- const task = await approval.create({
711
- actions: [
712
- { id: 'approve', label: 'Approve', type: 'primary' },
713
- { id: 'reject', label: 'Reject', type: 'danger' },
714
- ],
715
- context: { dealId: 'deal-123', proposalUrl: 'https://...' },
716
- description: 'Review proposal for Acme Corp',
717
- humanCheckpoint: 'proposal-review',
718
- metadata: { dealId: 'deal-123' },
719
- })
720
-
721
- // Clean up stale tasks
722
- await approval.deleteByMetadata({
723
- metadata: { dealId: 'deal-123' },
724
- status: 'pending',
725
- })
726
- ```
727
-
728
- ---
729
-
730
- ## Execution Adapter
731
-
732
- Singleton -- 1 method for triggering another resource (workflow or agent) as a nested child execution. No credential required.
733
-
734
- ```typescript
735
- import { execution } from '@elevasis/sdk/worker'
736
- ```
737
-
738
- ### Method
739
-
740
- | Method | Params | Returns |
741
- |--------|--------|---------|
742
- | `trigger` | `{ resourceId, input? }` | `{ success, executionId, output, error? }` |
743
-
744
- ### Example
745
-
746
- ```typescript
747
- const result = await execution.trigger({
748
- resourceId: 'send-welcome-sequence',
749
- input: { userId: newUser.id, email: newUser.email },
750
- })
751
-
752
- if (!result.success) {
753
- throw new Error(`Child workflow failed: ${result.error}`)
754
- }
755
- ```
756
-
757
- Nested executions are tracked with depth up to a maximum of 5 levels.
758
-
759
- ---
760
-
761
- ## Email Adapter
762
-
763
- Singleton -- 1 method for sending platform emails to organization members (from `notifications@elevasis.io`). For client-facing emails, use the Resend or Instantly integration adapters instead.
764
-
765
- ```typescript
766
- import { email } from '@elevasis/sdk/worker'
767
- ```
768
-
769
- ### Method
770
-
771
- | Method | Params | Returns |
772
- |--------|--------|---------|
773
- | `send` | `{ subject, html?, text?, userIds?, targetRole?, targetAll?, replyTo?, tags? }` | `{ sent, failed, errors? }` |
774
-
775
- ### Example
776
-
777
- ```typescript
778
- // Notify all org members
779
- await email.send({
780
- subject: 'New deal closed',
781
- text: 'Acme Corp has signed the contract.',
782
- targetAll: true,
783
- })
784
-
785
- // Notify specific users
786
- await email.send({
787
- subject: 'Action required',
788
- html: '\<p\>Please review the proposal.\</p\>',
789
- userIds: ['user-abc', 'user-def'],
790
- })
791
- ```
792
-
793
- ---
794
-
795
- ## Custom Adapters with `createAdapter`
796
-
797
- The generic `createAdapter` factory is exported for building adapters for any tool. Define a type map and call the factory.
798
-
799
- ```typescript
800
- import { createAdapter } from '@elevasis/sdk/worker'
801
-
802
- // 1. Define a type map
803
- type MyToolMap = {
804
- doSomething: { params: { input: string }; result: { output: string } }
805
- listItems: { params: { limit?: number }; result: { items: string[] } }
806
- getStatus: { params: Record<string, never>; result: { healthy: boolean } }
807
- }
808
-
809
- // 2. Create the adapter
810
- const myTool = createAdapter<MyToolMap>('my-tool', [
811
- 'doSomething', 'listItems', 'getStatus',
812
- ], 'my-credential') // credential is optional
813
-
814
- // 3. Use it (fully typed)
815
- const result = await myTool.doSomething({ input: 'hello' })
816
- // ^-- { output: string }
817
- const status = await myTool.getStatus()
818
- // ^-- { healthy: boolean } (zero-arg because params is Record<string, never>)
819
- ```
820
-
821
- Method names are compile-time checked against the type map keys -- misspelling a method name is a compile error.
822
-
823
- ---
824
-
825
- ## Why `platform.call()` Must Stay
826
-
827
- The postMessage boundary between worker and main thread is load-bearing. Adapters wrap it -- they cannot replace it.
828
-
829
- - **Multi-tenancy enforcement** -- The main thread overwrites `organizationId` on every tool call from parent-owned JWT context. Workers cannot forge a different org's identity.
830
- - **Secrets isolation** -- API keys, decrypted OAuth tokens, and Supabase service-role credentials never cross the postMessage boundary. Workers only receive `NODE_ENV`.
831
- - **Service lifecycle** -- `toolServicesRegistry` is initialized once at API startup. Workers don't have access to `initialize()` or the singleton services.
832
- - **Bundle boundary** -- Org bundles only import `@elevasis/sdk/worker`. Server-side code is not in the dependency graph.
833
- - **Crash isolation** -- 256MB heap cap per worker means a runaway workflow kills only its worker, not the API process.
834
-
835
- Adapters are a pure DX layer on top of this boundary.
836
-
837
- ---
838
-
839
- ## Architecture
840
-
841
- ### Call Flow
842
-
843
- ```
844
- Developer Code
845
- |
846
- v
847
- attio.listRecords(params) <-- typed adapter (SDK-side)
848
- |
849
- v
850
- createAdapter<AttioToolMap>(...) <-- generic factory (SDK-side)
851
- |
852
- v
853
- platform.call({ tool, method, params, credential }) <-- existing generic proxy
854
- |
855
- v
856
- postMessage boundary <-- unchanged
857
- |
858
- v
859
- tool-dispatcher.ts <-- routing
860
- |
861
- v
862
- integrationService.call() <-- unchanged execution
863
- |
864
- v
865
- AttioAdapter.call() <-- unchanged server adapter
866
- ```
867
-
868
- ### ToolMap Type System
869
-
870
- Per-tool method maps are defined once in `@repo/core` as pure types (zero runtime code, browser-safe). A generic `createAdapter` factory generates the full typed adapter from these maps.
871
-
872
- **Before (hand-written, per-method boilerplate):**
873
-
874
- ```typescript
875
- export function createAttioAdapter(credential: string) {
876
- return {
877
- createRecord: (params: CreateRecordParams) =>
878
- platform.call({ tool: 'attio', method: 'createRecord', params, credential }) as Promise<CreateRecordResult>,
879
- // ... 12 methods, each with manual type import + literal strings + as cast
880
- }
881
- }
882
- ```
883
-
884
- **After (ToolMap + factory):**
885
-
886
- ```typescript
887
- import { createAdapter } from './create-adapter.js'
888
- import type { AttioToolMap } from '../../types/index.js'
889
-
890
- export function createAttioAdapter(credential: string) {
891
- return createAdapter<AttioToolMap>('attio', METHODS, credential)
892
- }
893
- ```
894
-
895
- The `methods` array is constrained to `(keyof TMap & string)[]` -- misspelling a method name is a compile error.
896
-
897
- ### Type Sharing Strategy
898
-
899
- Types are defined once in `@repo/core` and shared across SDK and server:
900
-
901
- ```
902
- packages/core/src/execution/engine/tools/
903
- tool-maps.ts <-- 16 ToolMap type definitions (browser-safe)
904
- integration/types/
905
- attio.ts, stripe.ts, notion.ts, google-sheets.ts
906
- instantly.ts, signature-api.ts, resend.ts, dropbox.ts
907
- apify.ts, gmail.ts, mailso.ts
908
- index.ts <-- barrel export (all 12 integrations)
909
- platform/storage/types.ts <-- Storage Zod schemas and inferred types
910
- ```
911
-
912
- **Export chain:**
913
-
914
- 1. Types defined in `@repo/core` (browser-safe locations)
915
- 2. ToolMaps reference these types in `tools/tool-maps.ts`
916
- 3. Exported via `packages/core/src/execution/engine/index.ts`
917
- 4. Available at `@repo/core/execution` subpath
918
- 5. `packages/sdk/src/types/index.ts` re-exports named types
919
- 6. SDK adapters import from `../../types/index.js`
920
-
921
- ---
922
-
923
- ## Design Decisions
924
-
925
- ### ToolMap + Generic Factory
926
-
927
- All adapters (except LLM) use `createAdapter<TMap>()` instead of hand-written method objects:
928
-
929
- - Method names are compile-time checked against ToolMap keys (misspelling = compile error)
930
- - No manual `as Promise<T>` casts -- `TypedAdapter` mapped type handles it
931
- - Adding a method = one entry in the ToolMap type + one string in the methods array
932
-
933
- ### Factory with Credential Binding
934
-
935
- Integration adapters use `createXxxAdapter(credential)` pattern:
936
-
937
- - Credential is bound once, not repeated on every call
938
- - Mirrors how `createIntegrationTool(credentialName)` works server-side
939
- - Multiple adapters for same integration with different credentials work simultaneously
940
-
941
- ### LLM Adapter is Hand-Written
942
-
943
- The LLM adapter stays hand-written because its `generate()` method uses a generic `<T>` for typed output that cannot be expressed in a static ToolMap:
944
-
945
- ```typescript
946
- generate: <T = unknown>(params: Omit<LLMGenerateRequest, 'signal'>) =>
947
- platform.call({ tool: 'llm', method: 'generate', params }) as Promise<LLMGenerateResponse<T>>
948
- ```
949
-
950
- ### Name Collision and Browser Safety
951
-
952
- - **Gmail/Resend prefix**: Both integrations have `SendEmailParams`/`SendEmailResult`. Gmail types use `Gmail*` prefix, Resend types use `Resend*` prefix.
953
- - **Mailso prefix**: `Mailso*` prefix for namespace clarity.
954
- - **Browser safety**: `UploadFileParams.contents` and `DownloadDocumentResult.content` use `Uint8Array` instead of Node.js `Buffer`. Server adapters still use `Buffer` internally.
955
- - **Snake\_case preservation**: Instantly types preserve snake\_case field names (`lead_email`, `campaign_id`) because the server adapter sends them as-is to the Instantly API.
956
- - **No server-side changes**: All 11 server adapters keep their local type definitions. Shared types are additive.
957
-
958
- ### `NotificationSDKInput` Derived from Core
959
-
960
- `NotificationSDKInput` is `Omit<CreateNotificationParams, 'userId' | 'organizationId'>` -- derived from the canonical core type, not a local duplicate. Re-exported as `NotificationInput` for backward compatibility.
961
-
962
- ---
963
-
964
- ## Known Issues
965
-
966
- ### Attio `listRecords` Type Mismatch (Server-Side Only)
967
-
968
- The server-side `attio-adapter.ts` defines a local `ListRecordsParams` with `filter?: Record<string, unknown>`, which is weaker than the shared `QueryRecordsParams` with `filter?: FilterExpression`. The SDK adapter correctly uses `QueryRecordsParams`. Updating `attio-adapter.ts` to use shared types is separate refactoring work.
969
-
970
- ### Dispatcher Still Untyped
971
-
972
- `tool-dispatcher.ts` still uses `eslint-disable @typescript-eslint/no-explicit-any`. The same ToolMap types could be used server-side to type the dispatcher's switch cases. Future improvement.
973
-
974
- ### Intentionally Not Migrated
975
-
976
- - `full-diagnostic.ts` and `integration-test.ts` -- use generic `platform.call()` loops for connectivity testing
977
- - All `supabase`, `session-memory`, `hitl`, `status`, `http` calls -- no typed adapters exist (platform-internal tools)
978
-
979
- ---
980
-
981
- ## Official SDK Comparison
982
-
983
- | Adapter | Official SDK | Strategy Used | Mirror Accuracy |
984
- |---------|-------------|---------------|-----------------|
985
- | Attio | None exists | Raw HTTP fetch | Exact -- endpoints and body shapes match Attio v2 REST API |
986
- | Stripe | `stripe` (official) | Official SDK | Exact -- uses `stripe.paymentLinks.*`, `stripe.checkout.sessions.*` |
987
- | Notion | `@notionhq/client` (official) | Official SDK | High -- uses `notion.pages.*`, `notion.blocks.*`; custom method names |
988
- | Google Sheets | `@googleapis/sheets` (official) | Official SDK + helpers | High -- adds 7 workflow-friendly helpers on top of raw SDK |
989
- | Apify | `apify-client` exists, not used | Raw HTTP fetch | N/A -- custom polling loop |
990
- | Resend | `resend` exists, not used | Raw HTTP fetch | High -- matches REST API surface |
991
- | Gmail | `@googleapis/gmail` (official) | Official SDK | High -- parallel to Sheets pattern |
992
-
993
- ---
994
-
995
- ## When to Use Adapters vs `platform.call()`
996
-
997
- | Scenario | Use |
998
- |----------|-----|
999
- | Tool has a typed adapter (all integrations + platform tools) | Adapter |
1000
- | Need autocomplete and type safety | Adapter |
1001
- | New dispatcher method added server-side, no SDK update yet | `platform.call()` |
1002
- | Tool without a typed adapter (supabase, session-memory, status, http) | `platform.call()` |
1003
-
1004
- `platform.call()` remains the universal escape hatch. Every adapter call compiles down to it. Both approaches work simultaneously with no conflicts.
1005
-
1006
- ---
1007
-
1008
- ## Available Platform Tools (Full Inventory)
1009
-
1010
- Not all platform tools have typed adapters. Here is the complete list of tools accessible via `platform.call()`:
1011
-
1012
- ### Integration Adapters (credential required)
1013
-
1014
- | Tool | Typed Adapter | Methods | Credential Shape |
1015
- |------|--------------|---------|-----------------|
1016
- | `attio` | `createAttioAdapter` | 12 (CRUD + schema + notes) | `{ apiKey }` |
1017
- | `google-sheets` | `createGoogleSheetsAdapter` | 13 (read/write/filter/upsert) | OAuth2 / service account |
1018
- | `notion` | `createNotionAdapter` | 8 (pages + blocks) | `{ token }` |
1019
- | `stripe` | `createStripeAdapter` | 6 (payment links + checkout) | `{ secretKey }` |
1020
- | `instantly` | `createInstantlyAdapter` | 5 (email campaigns) | `{ apiKey }` |
1021
- | `signature-api` | `createSignatureApiAdapter` | 4 (envelopes) | `{ apiKey }` |
1022
- | `resend` | `createResendAdapter` | 2 (send/get email) | `{ apiKey }` |
1023
- | `dropbox` | `createDropboxAdapter` | 2 (upload/folder) | `{ accessToken }` |
1024
- | `apify` | `createApifyAdapter` | 1 (run actor) | `{ token }` |
1025
- | `gmail` | `createGmailAdapter` | 1 (send email) | OAuth2 / service account |
1026
- | `mailso` | `createMailsoAdapter` | 1 (verify email) | `{ apiKey }` |
1027
- | `supabase` | -- | 7 (insert/select/update/delete/upsert/rpc/count) | `{ url, serviceRoleKey }` |
1028
-
1029
- ### Platform Services (no credential)
1030
-
1031
- | Tool | Typed Adapter | Methods |
1032
- |------|--------------|---------|
1033
- | `scheduler` | `scheduler` | `createSchedule`, `updateAnchor`, `deleteSchedule`, `findByIdempotencyKey`, `deleteScheduleByIdempotencyKey`, `listSchedules`, `getSchedule`, `cancelSchedule`, `cancelSchedulesByMetadata` |
1034
- | `storage` | `storage` | `upload`, `download`, `createSignedUrl`, `delete`, `list` |
1035
- | `notification` | `notifications` | `create` |
1036
- | `llm` | `llm` | `generate` |
1037
- | `lead` | `lead` | 35 methods (lists, companies, contacts, deals, deal-sync) |
1038
- | `email` | `email` | `send` |
1039
- | `pdf` | `pdf` | `render`, `renderToBuffer` |
1040
- | `approval` | `approval` | `create`, `deleteByMetadata` |
1041
- | `execution` | `execution` | `trigger` |
1042
- | `http` | -- | Raw HTTP with server-side credential injection |
1043
- | `status` | -- | `overview` |
1044
- | `session-memory` | -- | `load`, `save` |
1045
-
1046
- ---
1047
-
1048
- ## Error Handling
1049
-
1050
- All adapter calls throw `PlatformToolError` on failure, with a `code` field and `retryable` flag:
1051
-
1052
- ```typescript
1053
- import { PlatformToolError } from '@elevasis/sdk/worker'
1054
-
1055
- try {
1056
- await attio.listRecords({ object: 'deals' })
1057
- } catch (err) {
1058
- if (err instanceof PlatformToolError) {
1059
- console.log(err.message) // Human-readable error
1060
- console.log(err.code) // e.g., 'rate_limit_exceeded', 'credentials_missing'
1061
- console.log(err.retryable) // true for transient errors
1062
- }
1063
- }
1064
- ```
1065
-
1066
- Retryable error codes: `rate_limit_exceeded`, `network_error`, `timeout_error`, `api_error`, `service_unavailable`, `server_unavailable`.
1067
-
1068
- Timeouts: LLM calls have a 120s timeout. All other tool calls have a 60s timeout.
1069
-
1070
- ---
1071
-
1072
- **Last Updated:** 2026-03-05
1
+ ---
2
+ title: Typed Adapters
3
+ description: Type-safe wrappers over platform.call() for all integrations and platform tools -- Attio, Stripe, Notion, Google Sheets, and more -- with full autocomplete, compile-time checking, and zero boilerplate
4
+ loadWhen: "Using typed adapters instead of raw platform.call(), or needs to know what adapter methods are available"
5
+ ---
6
+
7
+ Typed adapters are ergonomic wrappers over `platform.call()` that provide full TypeScript autocomplete and compile-time type checking. They eliminate stringly-typed method names, manual `as Promise<T>` casts, and repeated credential passing.
8
+
9
+ Both patterns work simultaneously -- adapters compile down to `platform.call()` with zero runtime overhead.
10
+
11
+ **Coverage:** 11 integration adapters (55 methods) + 9 platform tool adapters (57 methods) = 112 total typed methods.
12
+
13
+ ```typescript
14
+ // Before: raw platform.call()
15
+ const records = await platform.call({
16
+ tool: 'attio',
17
+ method: 'listRecords',
18
+ credential: 'my-attio',
19
+ params: { object: 'deals' }
20
+ }) as unknown as QueryRecordsResult
21
+
22
+ // After: typed adapter
23
+ const attio = createAttioAdapter('my-attio')
24
+ const records = await attio.listRecords({ object: 'deals' })
25
+ // ^-- QueryRecordsResult (fully typed, autocomplete on params)
26
+ ```
27
+
28
+ All adapters are imported from `@elevasis/sdk/worker` -- the same subpath as `platform`.
29
+
30
+ ---
31
+
32
+ ## Quick Reference
33
+
34
+ ### Integration Adapters (factory pattern, credential required)
35
+
36
+ | Import | Methods |
37
+ | --------------------------------- | ------- |
38
+ | `createAttioAdapter(cred)` | 12 |
39
+ | `createStripeAdapter(cred)` | 6 |
40
+ | `createNotionAdapter(cred)` | 8 |
41
+ | `createGoogleSheetsAdapter(cred)` | 13 |
42
+ | `createInstantlyAdapter(cred)` | 5 |
43
+ | `createSignatureApiAdapter(cred)` | 4 |
44
+ | `createResendAdapter(cred)` | 2 |
45
+ | `createDropboxAdapter(cred)` | 2 |
46
+ | `createApifyAdapter(cred)` | 1 |
47
+ | `createGmailAdapter(cred)` | 1 |
48
+ | `createMailsoAdapter(cred)` | 1 |
49
+
50
+ ### Platform Adapters (singletons, no credential)
51
+
52
+ | Import | Methods |
53
+ | --------------- | ------- |
54
+ | `lead` | 35 |
55
+ | `scheduler` | 9 |
56
+ | `storage` | 5 |
57
+ | `pdf` | 2 |
58
+ | `approval` | 2 |
59
+ | `notifications` | 1 |
60
+ | `llm` | 1 |
61
+ | `execution` | 1 |
62
+ | `email` | 1 |
63
+
64
+ ### Generic Factory
65
+
66
+ | Import | Description |
67
+ | --------------------------------------------- | ---------------------------------- |
68
+ | `createAdapter<TMap>(tool, methods, cred?)` | Build custom adapters for any tool |
69
+
70
+ ```typescript
71
+ import {
72
+ // Integration adapters
73
+ createAttioAdapter, createStripeAdapter, createNotionAdapter,
74
+ createGoogleSheetsAdapter, createInstantlyAdapter,
75
+ createSignatureApiAdapter, createResendAdapter, createDropboxAdapter,
76
+ createApifyAdapter, createGmailAdapter, createMailsoAdapter,
77
+ // Platform adapters
78
+ lead, scheduler, storage, pdf, approval, notifications, llm, execution, email,
79
+ // Generic factory
80
+ createAdapter,
81
+ } from '@elevasis/sdk/worker'
82
+ ```
83
+
84
+ ---
85
+
86
+ ## Attio CRM Adapter
87
+
88
+ Factory pattern -- bind a credential once, use 12 typed methods.
89
+
90
+ ```typescript
91
+ const attio = createAttioAdapter('my-attio-credential')
92
+ ```
93
+
94
+ ### Methods
95
+
96
+ | Method | Params | Returns |
97
+ | ----------------- | ----------------------- | ----------------------- |
98
+ | `createRecord` | `CreateRecordParams` | `CreateRecordResult` |
99
+ | `updateRecord` | `UpdateRecordParams` | `UpdateRecordResult` |
100
+ | `listRecords` | `QueryRecordsParams` | `QueryRecordsResult` |
101
+ | `getRecord` | `GetRecordParams` | `GetRecordResult` |
102
+ | `deleteRecord` | `DeleteRecordParams` | `DeleteRecordResult` |
103
+ | `listObjects` | none | `ListObjectsResult` |
104
+ | `listAttributes` | `ListAttributesParams` | `ListAttributesResult` |
105
+ | `createAttribute` | `CreateAttributeParams` | `CreateAttributeResult` |
106
+ | `updateAttribute` | `UpdateAttributeParams` | `UpdateAttributeResult` |
107
+ | `createNote` | `CreateNoteParams` | `CreateNoteResult` |
108
+ | `listNotes` | `ListNotesParams` | `ListNotesResult` |
109
+ | `deleteNote` | `DeleteNoteParams` | `DeleteNoteResult` |
110
+
111
+ ### Examples
112
+
113
+ ```typescript
114
+ // Create a company record
115
+ const company = await attio.createRecord({
116
+ object: 'companies',
117
+ values: {
118
+ name: [{ value: 'Acme Corp' }],
119
+ domains: [{ domain: 'acme.com' }],
120
+ },
121
+ })
122
+
123
+ // Query deals with filters
124
+ const deals = await attio.listRecords({
125
+ object: 'deals',
126
+ filter: {
127
+ operator: 'and',
128
+ filters: [
129
+ { field: 'stage', operator: 'equals', value: 'proposal' },
130
+ ],
131
+ },
132
+ sorts: [{ field: 'created_at', direction: 'desc' }],
133
+ limit: 20,
134
+ })
135
+
136
+ // Get a specific record
137
+ const record = await attio.getRecord({
138
+ object: 'people',
139
+ recordId: 'rec_abc123',
140
+ })
141
+
142
+ // List all objects (no params)
143
+ const objects = await attio.listObjects()
144
+
145
+ // Create a note on a record
146
+ await attio.createNote({
147
+ parentObject: 'deals',
148
+ parentRecordId: 'rec_abc123',
149
+ title: 'Discovery call notes',
150
+ content: 'Key takeaways from the call...',
151
+ })
152
+ ```
153
+
154
+ Multiple adapters with different credentials work simultaneously:
155
+
156
+ ```typescript
157
+ const prodAttio = createAttioAdapter('prod-attio')
158
+ const testAttio = createAttioAdapter('test-attio')
159
+ ```
160
+
161
+ ---
162
+
163
+ ## Stripe Adapter
164
+
165
+ Factory pattern -- 6 methods for payment links and checkout sessions.
166
+
167
+ ```typescript
168
+ const stripe = createStripeAdapter('my-stripe-credential')
169
+ ```
170
+
171
+ ### Methods
172
+
173
+ | Method | Params | Returns |
174
+ | ----------------------- | ----------------------------- | ----------------------------- |
175
+ | `createPaymentLink` | `CreatePaymentLinkParams` | `CreatePaymentLinkResult` |
176
+ | `getPaymentLink` | `GetPaymentLinkParams` | `GetPaymentLinkResult` |
177
+ | `updatePaymentLink` | `UpdatePaymentLinkParams` | `UpdatePaymentLinkResult` |
178
+ | `listPaymentLinks` | `ListPaymentLinksParams` | `ListPaymentLinksResult` |
179
+ | `createAutoPaymentLink` | `CreateAutoPaymentLinkParams` | `CreateAutoPaymentLinkResult` |
180
+ | `createCheckoutSession` | `CreateCheckoutSessionParams` | `CreateCheckoutSessionResult` |
181
+
182
+ ---
183
+
184
+ ## Notion Adapter
185
+
186
+ Factory pattern -- 8 methods for page and block operations.
187
+
188
+ ```typescript
189
+ const notion = createNotionAdapter('my-notion-credential')
190
+ ```
191
+
192
+ ### Methods
193
+
194
+ | Method | Params | Returns |
195
+ | ----------------- | ----------------------- | ----------------------- |
196
+ | `listAllPages` | none | `ListAllPagesResult` |
197
+ | `readPage` | `ReadPageParams` | `ReadPageResult` |
198
+ | `createPage` | `CreatePageParams` | `CreatePageResult` |
199
+ | `updatePageTitle` | `UpdatePageTitleParams` | `UpdatePageTitleResult` |
200
+ | `appendBlocks` | `AppendBlocksParams` | `AppendBlocksResult` |
201
+ | `updateBlocks` | `UpdateBlocksParams` | `UpdateBlocksResult` |
202
+ | `deletePage` | `DeletePageParams` | `DeletePageResult` |
203
+ | `deleteBlocks` | `DeleteBlocksParams` | `DeleteBlocksResult` |
204
+
205
+ ---
206
+
207
+ ## Google Sheets Adapter
208
+
209
+ Factory pattern -- 13 methods including workflow-friendly helpers (getRowByValue, upsertRow, filterRows).
210
+
211
+ ```typescript
212
+ const sheets = createGoogleSheetsAdapter('my-google-credential')
213
+ ```
214
+
215
+ ### Methods
216
+
217
+ | Method | Params | Returns |
218
+ | ------------------------ | ------------------------------ | ------------------------------ |
219
+ | `readSheet` | `ReadSheetParams` | `ReadSheetResult` |
220
+ | `writeSheet` | `WriteSheetParams` | `WriteSheetResult` |
221
+ | `appendRows` | `AppendRowsParams` | `AppendRowsResult` |
222
+ | `clearRange` | `ClearRangeParams` | `ClearRangeResult` |
223
+ | `getSpreadsheetMetadata` | `GetSpreadsheetMetadataParams` | `GetSpreadsheetMetadataResult` |
224
+ | `batchUpdate` | `BatchUpdateParams` | `BatchUpdateResult` |
225
+ | `getHeaders` | `GetHeadersParams` | `GetHeadersResult` |
226
+ | `getLastRow` | `GetLastRowParams` | `GetLastRowResult` |
227
+ | `getRowByValue` | `GetRowByValueParams` | `GetRowByValueResult` |
228
+ | `updateRowByValue` | `UpdateRowByValueParams` | `UpdateRowByValueResult` |
229
+ | `upsertRow` | `UpsertRowParams` | `UpsertRowResult` |
230
+ | `filterRows` | `FilterRowsParams` | `FilterRowsResult` |
231
+ | `deleteRowByValue` | `DeleteRowByValueParams` | `DeleteRowByValueResult` |
232
+
233
+ ---
234
+
235
+ ## Instantly Adapter
236
+
237
+ Factory pattern -- 5 methods for email campaign management.
238
+
239
+ ```typescript
240
+ const instantly = createInstantlyAdapter('my-instantly-credential')
241
+ ```
242
+
243
+ ### Methods
244
+
245
+ | Method | Params | Returns |
246
+ | ----------------------- | ----------------------------- | ----------------------------- |
247
+ | `sendReply` | `SendReplyParams` | `SendReplyResult` |
248
+ | `removeFromSubsequence` | `RemoveFromSubsequenceParams` | `RemoveFromSubsequenceResult` |
249
+ | `getEmails` | `GetEmailsParams` | `GetEmailsResult` |
250
+ | `updateInterestStatus` | `UpdateInterestStatusParams` | `UpdateInterestStatusResult` |
251
+ | `addToCampaign` | `AddToCampaignParams` | `AddToCampaignResult` |
252
+
253
+ ---
254
+
255
+ ## SignatureAPI Adapter
256
+
257
+ Factory pattern -- 4 methods for eSignature envelope operations.
258
+
259
+ ```typescript
260
+ const signatureApi = createSignatureApiAdapter('my-signatureapi-credential')
261
+ ```
262
+
263
+ ### Methods
264
+
265
+ | Method | Params | Returns |
266
+ | ------------------ | ------------------------ | ------------------------ |
267
+ | `createEnvelope` | `CreateEnvelopeParams` | `CreateEnvelopeResult` |
268
+ | `voidEnvelope` | `VoidEnvelopeParams` | `VoidEnvelopeResult` |
269
+ | `downloadDocument` | `DownloadDocumentParams` | `DownloadDocumentResult` |
270
+ | `getEnvelope` | `GetEnvelopeParams` | `GetEnvelopeResult` |
271
+
272
+ ---
273
+
274
+ ## Resend Adapter
275
+
276
+ Factory pattern -- 2 methods for transactional email.
277
+
278
+ ```typescript
279
+ const resend = createResendAdapter('my-resend-credential')
280
+ ```
281
+
282
+ ### Methods
283
+
284
+ | Method | Params | Returns |
285
+ | ----------- | ----------------------- | ----------------------- |
286
+ | `sendEmail` | `ResendSendEmailParams` | `ResendSendEmailResult` |
287
+ | `getEmail` | `ResendGetEmailParams` | `ResendGetEmailResult` |
288
+
289
+ ---
290
+
291
+ ## Dropbox Adapter
292
+
293
+ Factory pattern -- 2 methods for file operations.
294
+
295
+ ```typescript
296
+ const dropbox = createDropboxAdapter('my-dropbox-credential')
297
+ ```
298
+
299
+ ### Methods
300
+
301
+ | Method | Params | Returns |
302
+ | -------------- | -------------------- | -------------------- |
303
+ | `uploadFile` | `UploadFileParams` | `UploadFileResult` |
304
+ | `createFolder` | `CreateFolderParams` | `CreateFolderResult` |
305
+
306
+ ---
307
+
308
+ ## Apify Adapter
309
+
310
+ Factory pattern -- 1 method for running web scraping actors.
311
+
312
+ ```typescript
313
+ const apify = createApifyAdapter('my-apify-credential')
314
+ ```
315
+
316
+ ### Methods
317
+
318
+ | Method | Params | Returns |
319
+ | ---------- | ---------------- | ---------------- |
320
+ | `runActor` | `RunActorParams` | `RunActorResult` |
321
+
322
+ ---
323
+
324
+ ## Gmail Adapter
325
+
326
+ Factory pattern -- 1 method for sending email via Gmail API.
327
+
328
+ ```typescript
329
+ const gmail = createGmailAdapter('my-gmail-credential')
330
+ ```
331
+
332
+ ### Methods
333
+
334
+ | Method | Params | Returns |
335
+ | ----------- | ---------------------- | ---------------------- |
336
+ | `sendEmail` | `GmailSendEmailParams` | `GmailSendEmailResult` |
337
+
338
+ ---
339
+
340
+ ## Mailso Adapter
341
+
342
+ Factory pattern -- 1 method for email verification.
343
+
344
+ ```typescript
345
+ const mailso = createMailsoAdapter('my-mailso-credential')
346
+ ```
347
+
348
+ ### Methods
349
+
350
+ | Method | Params | Returns |
351
+ | ------------- | ------------------------- | ------------------------- |
352
+ | `verifyEmail` | `MailsoVerifyEmailParams` | `MailsoVerifyEmailResult` |
353
+
354
+ ---
355
+
356
+ ## Scheduler Adapter
357
+
358
+ Singleton -- 9 methods for task schedule management.
359
+
360
+ ```typescript
361
+ import { scheduler } from '@elevasis/sdk/worker'
362
+ ```
363
+
364
+ ### Methods
365
+
366
+ | Method | Params | Returns |
367
+ | -------------------------------- | ------------------------------ | ---------------------- |
368
+ | `createSchedule` | `CreateScheduleInput` | `TaskSchedule` |
369
+ | `updateAnchor` | `{ scheduleId, anchorAt }` | `TaskSchedule` |
370
+ | `deleteSchedule` | `{ scheduleId }` | `void` |
371
+ | `findByIdempotencyKey` | `{ idempotencyKey }` | `TaskSchedule | null` |
372
+ | `deleteScheduleByIdempotencyKey` | `{ idempotencyKey }` | `void` |
373
+ | `listSchedules` | `{ status?, limit?, offset? }` | `TaskSchedule[]` |
374
+ | `getSchedule` | `{ scheduleId }` | `TaskSchedule` |
375
+ | `cancelSchedule` | `{ scheduleId }` | `void` |
376
+ | `cancelSchedulesByMetadata` | `{ metadata }` | `{ cancelledCount }` |
377
+
378
+ ### Examples
379
+
380
+ ```typescript
381
+ // Create a relative schedule (follow-up sequence)
382
+ const schedule = await scheduler.createSchedule({
383
+ organizationId: context.organizationId,
384
+ name: 'Proposal follow-up',
385
+ target: { resourceType: 'workflow', resourceId: 'send-followup' },
386
+ scheduleConfig: {
387
+ type: 'relative',
388
+ anchorAt: '2026-03-15T10:00:00Z',
389
+ anchorLabel: 'proposal_sent_date',
390
+ items: [
391
+ { offset: '+3d', payload: { step: 'first-followup' }, label: 'First follow-up' },
392
+ { offset: '+7d', payload: { step: 'second-followup' }, label: 'Second follow-up' },
393
+ ],
394
+ },
395
+ metadata: { dealId: 'deal-123', contactEmail: 'jane@acme.com' },
396
+ idempotencyKey: 'proposal-followup-deal-123',
397
+ })
398
+
399
+ // Reschedule (update anchor)
400
+ await scheduler.updateAnchor({
401
+ scheduleId: schedule.id,
402
+ anchorAt: '2026-03-20T10:00:00Z',
403
+ })
404
+
405
+ // Find by idempotency key (check if already created)
406
+ const existing = await scheduler.findByIdempotencyKey({
407
+ idempotencyKey: 'proposal-followup-deal-123',
408
+ })
409
+
410
+ // Cancel all schedules matching metadata
411
+ await scheduler.cancelSchedulesByMetadata({
412
+ metadata: { dealId: 'deal-123' },
413
+ })
414
+
415
+ // List active schedules
416
+ const schedules = await scheduler.listSchedules({
417
+ status: 'active',
418
+ limit: 50,
419
+ })
420
+ ```
421
+
422
+ ---
423
+
424
+ ## Storage Adapter
425
+
426
+ Singleton -- 5 methods for file storage operations.
427
+
428
+ ```typescript
429
+ import { storage } from '@elevasis/sdk/worker'
430
+ ```
431
+
432
+ All paths are relative to the organization's storage prefix. The platform injects the organization prefix server-side.
433
+
434
+ ### Methods
435
+
436
+ | Method | Params | Returns |
437
+ | ----------------- | ----------------------- | ------------------------ |
438
+ | `upload` | `StorageUploadInput` | `StorageUploadOutput` |
439
+ | `download` | `StorageDownloadInput` | `StorageDownloadOutput` |
440
+ | `createSignedUrl` | `StorageSignedUrlInput` | `StorageSignedUrlOutput` |
441
+ | `delete` | `StorageDeleteInput` | `StorageDeleteOutput` |
442
+ | `list` | `StorageListInput` | `StorageListOutput` |
443
+
444
+ ### Examples
445
+
446
+ ```typescript
447
+ // Upload a PDF
448
+ await storage.upload({
449
+ bucket: 'acquisition',
450
+ path: 'proposals/deal-123/proposal.pdf',
451
+ content: base64PdfContent,
452
+ contentType: 'application/pdf',
453
+ })
454
+
455
+ // Generate a signed URL (temporary access)
456
+ const { signedUrl } = await storage.createSignedUrl({
457
+ bucket: 'acquisition',
458
+ path: 'proposals/deal-123/proposal.pdf',
459
+ })
460
+
461
+ // Download a file
462
+ const file = await storage.download({
463
+ bucket: 'acquisition',
464
+ path: 'proposals/deal-123/proposal.pdf',
465
+ })
466
+
467
+ // List files under a prefix
468
+ const files = await storage.list({
469
+ bucket: 'acquisition',
470
+ path: 'proposals/deal-123/',
471
+ })
472
+
473
+ // Delete a file
474
+ await storage.delete({
475
+ bucket: 'acquisition',
476
+ path: 'proposals/deal-123/old-proposal.pdf',
477
+ })
478
+ ```
479
+
480
+ ---
481
+
482
+ ## Notification Adapter
483
+
484
+ Singleton -- 1 method for sending in-app team notifications.
485
+
486
+ ```typescript
487
+ import { notifications } from '@elevasis/sdk/worker'
488
+ ```
489
+
490
+ `userId` and `organizationId` are injected server-side from the execution context -- you never pass them.
491
+
492
+ ### Method
493
+
494
+ | Method | Params | Returns |
495
+ | -------- | ---------------------- | ------- |
496
+ | `create` | `NotificationSDKInput` | `void` |
497
+
498
+ `NotificationSDKInput` fields: `category`, `title`, `message`, `actionUrl?`, `metadata?`
499
+
500
+ ### Example
501
+
502
+ ```typescript
503
+ await notifications.create({
504
+ category: 'acquisition',
505
+ title: 'New lead qualified',
506
+ message: 'Acme Corp has been qualified and moved to proposal stage.',
507
+ actionUrl: '/deals/deal-123',
508
+ })
509
+ ```
510
+
511
+ ---
512
+
513
+ ## LLM Adapter
514
+
515
+ Singleton -- 1 method with a generic `<T>` for typed structured output.
516
+
517
+ ```typescript
518
+ import { llm } from '@elevasis/sdk/worker'
519
+ ```
520
+
521
+ No API keys needed -- keys are resolved server-side from environment variables.
522
+
523
+ ### Method
524
+
525
+ | Method | Params | Returns |
526
+ | --------------- | -------------------------------------- | -------------------------- |
527
+ | `generate<T>` | `Omit<LLMGenerateRequest, 'signal'>` | `LLMGenerateResponse<T>` |
528
+
529
+ The `signal` property (AbortSignal) is not serializable over the postMessage boundary. Abort is handled at the worker level via the parent process sending an abort message.
530
+
531
+ ### Example
532
+
533
+ ```typescript
534
+ interface Classification {
535
+ category: 'interested' | 'not-interested' | 'bounced'
536
+ confidence: number
537
+ summary: string
538
+ }
539
+
540
+ const response = await llm.generate<Classification>({
541
+ provider: 'anthropic',
542
+ model: 'claude-sonnet-4-5',
543
+ messages: [
544
+ { role: 'system', content: 'Classify this email reply.' },
545
+ { role: 'user', content: emailText },
546
+ ],
547
+ responseSchema: {
548
+ type: 'object',
549
+ properties: {
550
+ category: { type: 'string', enum: ['interested', 'not-interested', 'bounced'] },
551
+ confidence: { type: 'number', minimum: 0, maximum: 1 },
552
+ summary: { type: 'string' },
553
+ },
554
+ required: ['category', 'confidence', 'summary'],
555
+ },
556
+ temperature: 0.1,
557
+ })
558
+
559
+ // response.output is typed as Classification
560
+ const { category, confidence, summary } = response.output
561
+ ```
562
+
563
+ ---
564
+
565
+ ## Lead Adapter
566
+
567
+ Singleton -- 35 methods for acquisition lead management (lists, companies, contacts, deals, deal-sync). `organizationId` is injected server-side -- never pass it.
568
+
569
+ ```typescript
570
+ import { lead } from '@elevasis/sdk/worker'
571
+ ```
572
+
573
+ ### Methods
574
+
575
+ **List operations:**
576
+
577
+ | Method | Params | Returns |
578
+ | ------------ | -------------------------------------------- | ----------- |
579
+ | `listLists` | none | `AcqList[]` |
580
+ | `createList` | `Omit<CreateListParams, 'organizationId'>` | `AcqList` |
581
+ | `updateList` | `{ id } & UpdateListParams` | `AcqList` |
582
+ | `deleteList` | `{ id }` | `void` |
583
+
584
+ **Company operations:**
585
+
586
+ | Method | Params | Returns |
587
+ | --------------- | ----------------------------------------------- | -------------------- |
588
+ | `createCompany` | `Omit<CreateCompanyParams, 'organizationId'>` | `AcqCompany` |
589
+ | `upsertCompany` | `Omit<UpsertCompanyParams, 'organizationId'>` | `AcqCompany` |
590
+ | `updateCompany` | `{ id } & UpdateCompanyParams` | `AcqCompany` |
591
+ | `getCompany` | `{ id }` | `AcqCompany | null` |
592
+ | `listCompanies` | `{ filters? }` | `AcqCompany[]` |
593
+ | `deleteCompany` | `{ id }` | `void` |
594
+
595
+ **Contact operations:**
596
+
597
+ | Method | Params | Returns |
598
+ | -------------------- | ----------------------------------------------- | ------------------------------- |
599
+ | `createContact` | `Omit<CreateContactParams, 'organizationId'>` | `AcqContact` |
600
+ | `upsertContact` | `Omit<UpsertContactParams, 'organizationId'>` | `AcqContact` |
601
+ | `updateContact` | `{ id } & UpdateContactParams` | `AcqContact` |
602
+ | `getContact` | `{ id }` | `AcqContact | null` |
603
+ | `getContactByEmail` | `{ email }` | `AcqContact | null` |
604
+ | `listContacts` | `{ filters?, pagination? }` | `PaginatedResult<AcqContact>` |
605
+ | `deleteContact` | `{ id }` | `void` |
606
+ | `bulkImportContacts` | `Omit<BulkImportParams, 'organizationId'>` | `BulkImportResult` |
607
+
608
+ **Deal operations:**
609
+
610
+ | Method | Params | Returns |
611
+ | ---------------------- | -------------------------------------------- | ----------------- |
612
+ | `upsertDeal` | `Omit<UpsertDealParams, 'organizationId'>` | `AcqDeal` |
613
+ | `getDealByEmail` | `{ email }` | `AcqDeal | null` |
614
+ | `getDealByEnvelopeId` | `{ envelopeId }` | `AcqDeal | null` |
615
+ | `updateDealEnvelopeId` | `{ attioDealId, envelopeId }` | `AcqDeal | null` |
616
+ | `getDealByAttioId` | `{ attioDealId }` | `AcqDeal | null` |
617
+
618
+ **Deal-sync operations:**
619
+
620
+ | Method | Params | Returns |
621
+ | ------------------------------- | ------------------------------------------------------- | ------------------------------------- |
622
+ | `updateDiscoveryData` | `Omit<UpdateDiscoveryDataParams, 'organizationId'>` | `void` |
623
+ | `updateProposalData` | `Omit<UpdateProposalDataParams, 'organizationId'>` | `void` |
624
+ | `markProposalSent` | `Omit<MarkProposalSentParams, 'organizationId'>` | `void` |
625
+ | `markProposalReviewed` | `Omit<MarkProposalReviewedParams, 'organizationId'>` | `void` |
626
+ | `updateCloseLostReason` | `Omit<UpdateCloseLostReasonParams, 'organizationId'>` | `void` |
627
+ | `updateFees` | `Omit<UpdateFeesParams, 'organizationId'>` | `void` |
628
+ | `syncDealStage` | `Omit<SyncDealStageParams, 'organizationId'>` | `void` |
629
+ | `setContactNurture` | `Omit<SetContactNurtureParams, 'organizationId'>` | `void` |
630
+ | `cancelSchedulesAndHitlByEmail` | `Omit<..., 'organizationId'>` | `{ schedulesCancelled, hitlDeleted }` |
631
+ | `cancelHitlByDealId` | `Omit<..., 'organizationId'>` | `{ hitlDeleted }` |
632
+ | `clearDealFields` | `Omit<ClearDealFieldsParams, 'organizationId'>` | `void` |
633
+ | `deleteDeal` | `Omit<DeleteDealParams, 'organizationId'>` | `void` |
634
+
635
+ ### Example
636
+
637
+ ```typescript
638
+ const deal = await lead.getDealByEmail({ email: 'jane@acme.com' })
639
+ if (!deal) {
640
+ await lead.upsertDeal({
641
+ attioDealId: 'deal-123',
642
+ contactEmail: 'jane@acme.com',
643
+ })
644
+ }
645
+
646
+ // Bulk import contacts
647
+ await lead.bulkImportContacts({
648
+ listId: 'list-abc',
649
+ contacts: [
650
+ { email: 'a@example.com', firstName: 'Alice' },
651
+ { email: 'b@example.com', firstName: 'Bob' },
652
+ ],
653
+ })
654
+ ```
655
+
656
+ ---
657
+
658
+ ## PDF Adapter
659
+
660
+ Singleton -- 2 methods for rendering PDFDocument structures to files or buffers. No credential required.
661
+
662
+ ```typescript
663
+ import { pdf } from '@elevasis/sdk/worker'
664
+ ```
665
+
666
+ ### Methods
667
+
668
+ | Method | Params | Returns |
669
+ | ---------------- | ------------------------------------------------- | --------------------------- |
670
+ | `render` | `{ document, theme?, storage: { bucket, path } }` | `{ success, pdfUrl, size }` |
671
+ | `renderToBuffer` | `{ document, theme? }` | `{ buffer }` |
672
+
673
+ ### Example
674
+
675
+ ```typescript
676
+ // Render to storage (returns a URL)
677
+ const result = await pdf.render({
678
+ document: proposalDocument,
679
+ storage: { bucket: 'acquisition', path: 'proposals/deal-123/proposal.pdf' },
680
+ })
681
+ console.log(result.pdfUrl)
682
+
683
+ // Render to buffer (for inline processing)
684
+ const { buffer } = await pdf.renderToBuffer({
685
+ document: proposalDocument,
686
+ })
687
+ ```
688
+
689
+ ---
690
+
691
+ ## Approval Adapter
692
+
693
+ Singleton -- 2 methods for creating and managing human-in-the-loop (HITL) approval tasks. No credential required.
694
+
695
+ ```typescript
696
+ import { approval } from '@elevasis/sdk/worker'
697
+ ```
698
+
699
+ ### Methods
700
+
701
+ | Method | Params | Returns |
702
+ | ------------------ | ---------------------------------------------------------------------------------------- | ------------- |
703
+ | `create` | `{ actions, context, description?, priority?, humanCheckpoint?, metadata?, expiresAt? }` | `{ id }` |
704
+ | `deleteByMetadata` | `{ metadata, status? }` | `{ deleted }` |
705
+
706
+ ### Example
707
+
708
+ ```typescript
709
+ // Create an approval gate
710
+ const task = await approval.create({
711
+ actions: [
712
+ { id: 'approve', label: 'Approve', type: 'primary' },
713
+ { id: 'reject', label: 'Reject', type: 'danger' },
714
+ ],
715
+ context: { dealId: 'deal-123', proposalUrl: 'https://...' },
716
+ description: 'Review proposal for Acme Corp',
717
+ humanCheckpoint: 'proposal-review',
718
+ metadata: { dealId: 'deal-123' },
719
+ })
720
+
721
+ // Clean up stale tasks
722
+ await approval.deleteByMetadata({
723
+ metadata: { dealId: 'deal-123' },
724
+ status: 'pending',
725
+ })
726
+ ```
727
+
728
+ ---
729
+
730
+ ## Execution Adapter
731
+
732
+ Singleton -- 1 method for triggering another resource (workflow or agent) as a nested child execution. No credential required.
733
+
734
+ ```typescript
735
+ import { execution } from '@elevasis/sdk/worker'
736
+ ```
737
+
738
+ ### Method
739
+
740
+ | Method | Params | Returns |
741
+ | --------- | ------------------------ | ------------------------------------------ |
742
+ | `trigger` | `{ resourceId, input? }` | `{ success, executionId, output, error? }` |
743
+
744
+ ### Example
745
+
746
+ ```typescript
747
+ const result = await execution.trigger({
748
+ resourceId: 'send-welcome-sequence',
749
+ input: { userId: newUser.id, email: newUser.email },
750
+ })
751
+
752
+ if (!result.success) {
753
+ throw new Error(`Child workflow failed: ${result.error}`)
754
+ }
755
+ ```
756
+
757
+ Nested executions are tracked with depth up to a maximum of 5 levels.
758
+
759
+ ---
760
+
761
+ ## Email Adapter
762
+
763
+ Singleton -- 1 method for sending platform emails to organization members (from `notifications@elevasis.io`). For client-facing emails, use the Resend or Instantly integration adapters instead.
764
+
765
+ ```typescript
766
+ import { email } from '@elevasis/sdk/worker'
767
+ ```
768
+
769
+ ### Method
770
+
771
+ | Method | Params | Returns |
772
+ | ------ | ------------------------------------------------------------------------------- | --------------------------- |
773
+ | `send` | `{ subject, html?, text?, userIds?, targetRole?, targetAll?, replyTo?, tags? }` | `{ sent, failed, errors? }` |
774
+
775
+ ### Example
776
+
777
+ ```typescript
778
+ // Notify all org members
779
+ await email.send({
780
+ subject: 'New deal closed',
781
+ text: 'Acme Corp has signed the contract.',
782
+ targetAll: true,
783
+ })
784
+
785
+ // Notify specific users
786
+ await email.send({
787
+ subject: 'Action required',
788
+ html: '\<p\>Please review the proposal.\</p\>',
789
+ userIds: ['user-abc', 'user-def'],
790
+ })
791
+ ```
792
+
793
+ ---
794
+
795
+ ## Custom Adapters with `createAdapter`
796
+
797
+ The generic `createAdapter` factory is exported for building adapters for any tool. Define a type map and call the factory.
798
+
799
+ ```typescript
800
+ import { createAdapter } from '@elevasis/sdk/worker'
801
+
802
+ // 1. Define a type map
803
+ type MyToolMap = {
804
+ doSomething: { params: { input: string }; result: { output: string } }
805
+ listItems: { params: { limit?: number }; result: { items: string[] } }
806
+ getStatus: { params: Record<string, never>; result: { healthy: boolean } }
807
+ }
808
+
809
+ // 2. Create the adapter
810
+ const myTool = createAdapter<MyToolMap>('my-tool', [
811
+ 'doSomething', 'listItems', 'getStatus',
812
+ ], 'my-credential') // credential is optional
813
+
814
+ // 3. Use it (fully typed)
815
+ const result = await myTool.doSomething({ input: 'hello' })
816
+ // ^-- { output: string }
817
+ const status = await myTool.getStatus()
818
+ // ^-- { healthy: boolean } (zero-arg because params is Record<string, never>)
819
+ ```
820
+
821
+ Method names are compile-time checked against the type map keys -- misspelling a method name is a compile error.
822
+
823
+ ---
824
+
825
+ All typed adapters compile to `platform.call()` internally -- you can use either interchangeably.
826
+
827
+ ## When to Use Adapters vs `platform.call()`
828
+
829
+ | Scenario | Use |
830
+ | --------------------------------------------------------------------- | ----------------- |
831
+ | Tool has a typed adapter (all integrations + platform tools) | Adapter |
832
+ | Need autocomplete and type safety | Adapter |
833
+ | New dispatcher method added server-side, no SDK update yet | `platform.call()` |
834
+ | Tool without a typed adapter (supabase, session-memory, status, http) | `platform.call()` |
835
+
836
+ `platform.call()` remains the universal escape hatch. Every adapter call compiles down to it. Both approaches work simultaneously with no conflicts.
837
+
838
+ ---
839
+
840
+ For the full inventory of all platform tools (including those without typed adapters), see [Platform Tools](index.mdx).
841
+
842
+ ---
843
+
844
+ ## Error Handling
845
+
846
+ All adapter calls throw `PlatformToolError` on failure, with a `code` field and `retryable` flag:
847
+
848
+ ```typescript
849
+ import { PlatformToolError } from '@elevasis/sdk/worker'
850
+
851
+ try {
852
+ await attio.listRecords({ object: 'deals' })
853
+ } catch (err) {
854
+ if (err instanceof PlatformToolError) {
855
+ console.log(err.message) // Human-readable error
856
+ console.log(err.code) // e.g., 'rate_limit_exceeded', 'credentials_missing'
857
+ console.log(err.retryable) // true for transient errors
858
+ }
859
+ }
860
+ ```
861
+
862
+ Retryable error codes: `rate_limit_exceeded`, `network_error`, `timeout_error`, `api_error`, `service_unavailable`, `server_unavailable`.
863
+
864
+ Timeouts: LLM calls have a 120s timeout. All other tool calls have a 60s timeout.
865
+
866
+ ---
867
+
868
+ **Last Updated:** 2026-03-05