@superblocksteam/sdk-api 2.0.105 → 2.0.106-next.1

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.
Files changed (182) hide show
  1. package/README.md +439 -89
  2. package/dist/api/definition.d.ts +11 -6
  3. package/dist/api/definition.d.ts.map +1 -1
  4. package/dist/api/definition.js +19 -12
  5. package/dist/api/definition.js.map +1 -1
  6. package/dist/api/definition.test.js +39 -15
  7. package/dist/api/definition.test.js.map +1 -1
  8. package/dist/errors.d.ts +1 -1
  9. package/dist/errors.js +1 -1
  10. package/dist/index.d.ts +10 -11
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +9 -5
  13. package/dist/index.js.map +1 -1
  14. package/dist/integrations/base/index.d.ts +2 -1
  15. package/dist/integrations/base/index.d.ts.map +1 -1
  16. package/dist/integrations/base/index.js +1 -0
  17. package/dist/integrations/base/index.js.map +1 -1
  18. package/dist/integrations/base/rest-api-client-base.d.ts +48 -0
  19. package/dist/integrations/base/rest-api-client-base.d.ts.map +1 -0
  20. package/dist/integrations/base/rest-api-client-base.js +98 -0
  21. package/dist/integrations/base/rest-api-client-base.js.map +1 -0
  22. package/dist/integrations/base/rest-api-integration-client.d.ts +10 -20
  23. package/dist/integrations/base/rest-api-integration-client.d.ts.map +1 -1
  24. package/dist/integrations/base/rest-api-integration-client.js +10 -65
  25. package/dist/integrations/base/rest-api-integration-client.js.map +1 -1
  26. package/dist/integrations/box/types.d.ts +1 -1
  27. package/dist/integrations/declarations.d.ts +5 -73
  28. package/dist/integrations/declarations.d.ts.map +1 -1
  29. package/dist/integrations/declarations.js +5 -68
  30. package/dist/integrations/declarations.js.map +1 -1
  31. package/dist/integrations/documentation.test.js +0 -2
  32. package/dist/integrations/documentation.test.js.map +1 -1
  33. package/dist/integrations/googledrive/types.d.ts +1 -1
  34. package/dist/integrations/index.d.ts +1 -11
  35. package/dist/integrations/index.d.ts.map +1 -1
  36. package/dist/integrations/index.js +1 -7
  37. package/dist/integrations/index.js.map +1 -1
  38. package/dist/integrations/registry.d.ts +1 -11
  39. package/dist/integrations/registry.d.ts.map +1 -1
  40. package/dist/integrations/registry.js +0 -29
  41. package/dist/integrations/registry.js.map +1 -1
  42. package/dist/integrations/slack/client.d.ts +13 -9
  43. package/dist/integrations/slack/client.d.ts.map +1 -1
  44. package/dist/integrations/slack/client.js +60 -8
  45. package/dist/integrations/slack/client.js.map +1 -1
  46. package/dist/integrations/slack/client.test.d.ts +11 -0
  47. package/dist/integrations/slack/client.test.d.ts.map +1 -0
  48. package/dist/integrations/slack/client.test.js +368 -0
  49. package/dist/integrations/slack/client.test.js.map +1 -0
  50. package/dist/integrations/slack/index.d.ts +2 -1
  51. package/dist/integrations/slack/index.d.ts.map +1 -1
  52. package/dist/integrations/slack/index.js +1 -0
  53. package/dist/integrations/slack/index.js.map +1 -1
  54. package/dist/integrations/slack/types.d.ts +127 -28
  55. package/dist/integrations/slack/types.d.ts.map +1 -1
  56. package/dist/integrations/slack/types.js +27 -1
  57. package/dist/integrations/slack/types.js.map +1 -1
  58. package/dist/integrations/snowflake/client.d.ts +2 -2
  59. package/dist/integrations/snowflake/client.js +2 -2
  60. package/dist/runtime/context.d.ts +1 -1
  61. package/dist/runtime/executor.d.ts +2 -2
  62. package/dist/types.d.ts +15 -6
  63. package/dist/types.d.ts.map +1 -1
  64. package/package.json +2 -2
  65. package/src/api/definition.test.ts +40 -15
  66. package/src/api/definition.ts +19 -12
  67. package/src/errors.ts +1 -1
  68. package/src/index.ts +13 -33
  69. package/src/integrations/asana/README.md +12 -12
  70. package/src/integrations/base/index.ts +2 -1
  71. package/src/integrations/base/rest-api-client-base.ts +134 -0
  72. package/src/integrations/base/rest-api-integration-client.ts +12 -89
  73. package/src/integrations/bitbucket/README.md +19 -19
  74. package/src/integrations/box/README.md +24 -24
  75. package/src/integrations/box/types.ts +1 -1
  76. package/src/integrations/circleci/README.md +18 -18
  77. package/src/integrations/declarations.ts +5 -105
  78. package/src/integrations/documentation.test.ts +0 -2
  79. package/src/integrations/googledrive/README.md +25 -22
  80. package/src/integrations/googledrive/types.ts +1 -1
  81. package/src/integrations/graphql/README.md +2 -2
  82. package/src/integrations/groq/README.md +8 -8
  83. package/src/integrations/index.ts +0 -51
  84. package/src/integrations/mongodb/README.md +65 -12
  85. package/src/integrations/perplexity/README.md +39 -48
  86. package/src/integrations/registry.ts +1 -39
  87. package/src/integrations/salesforce/README.md +11 -9
  88. package/src/integrations/slack/README.md +62 -19
  89. package/src/integrations/slack/client.test.ts +553 -0
  90. package/src/integrations/slack/client.ts +92 -12
  91. package/src/integrations/slack/index.ts +6 -1
  92. package/src/integrations/slack/types.ts +142 -29
  93. package/src/integrations/snowflake/client.ts +2 -2
  94. package/src/integrations/zoom/README.md +15 -15
  95. package/src/runtime/context.ts +1 -1
  96. package/src/runtime/executor.ts +2 -2
  97. package/src/types.ts +15 -6
  98. package/dist/integrations/couchbase/client.d.ts +0 -36
  99. package/dist/integrations/couchbase/client.d.ts.map +0 -1
  100. package/dist/integrations/couchbase/client.js +0 -148
  101. package/dist/integrations/couchbase/client.js.map +0 -1
  102. package/dist/integrations/couchbase/index.d.ts +0 -8
  103. package/dist/integrations/couchbase/index.d.ts.map +0 -1
  104. package/dist/integrations/couchbase/index.js +0 -7
  105. package/dist/integrations/couchbase/index.js.map +0 -1
  106. package/dist/integrations/couchbase/types.d.ts +0 -100
  107. package/dist/integrations/couchbase/types.d.ts.map +0 -1
  108. package/dist/integrations/couchbase/types.js +0 -5
  109. package/dist/integrations/couchbase/types.js.map +0 -1
  110. package/dist/integrations/kafka/client.d.ts +0 -25
  111. package/dist/integrations/kafka/client.d.ts.map +0 -1
  112. package/dist/integrations/kafka/client.js +0 -124
  113. package/dist/integrations/kafka/client.js.map +0 -1
  114. package/dist/integrations/kafka/index.d.ts +0 -8
  115. package/dist/integrations/kafka/index.d.ts.map +0 -1
  116. package/dist/integrations/kafka/index.js +0 -7
  117. package/dist/integrations/kafka/index.js.map +0 -1
  118. package/dist/integrations/kafka/types.d.ts +0 -113
  119. package/dist/integrations/kafka/types.d.ts.map +0 -1
  120. package/dist/integrations/kafka/types.js +0 -5
  121. package/dist/integrations/kafka/types.js.map +0 -1
  122. package/dist/integrations/kinesis/client.d.ts +0 -31
  123. package/dist/integrations/kinesis/client.d.ts.map +0 -1
  124. package/dist/integrations/kinesis/client.js +0 -101
  125. package/dist/integrations/kinesis/client.js.map +0 -1
  126. package/dist/integrations/kinesis/index.d.ts +0 -8
  127. package/dist/integrations/kinesis/index.d.ts.map +0 -1
  128. package/dist/integrations/kinesis/index.js +0 -7
  129. package/dist/integrations/kinesis/index.js.map +0 -1
  130. package/dist/integrations/kinesis/types.d.ts +0 -97
  131. package/dist/integrations/kinesis/types.d.ts.map +0 -1
  132. package/dist/integrations/kinesis/types.js +0 -7
  133. package/dist/integrations/kinesis/types.js.map +0 -1
  134. package/dist/integrations/python/client.d.ts +0 -42
  135. package/dist/integrations/python/client.d.ts.map +0 -1
  136. package/dist/integrations/python/client.js +0 -89
  137. package/dist/integrations/python/client.js.map +0 -1
  138. package/dist/integrations/python/client.test.d.ts +0 -5
  139. package/dist/integrations/python/client.test.d.ts.map +0 -1
  140. package/dist/integrations/python/client.test.js +0 -214
  141. package/dist/integrations/python/client.test.js.map +0 -1
  142. package/dist/integrations/python/index.d.ts +0 -6
  143. package/dist/integrations/python/index.d.ts.map +0 -1
  144. package/dist/integrations/python/index.js +0 -5
  145. package/dist/integrations/python/index.js.map +0 -1
  146. package/dist/integrations/python/types.d.ts +0 -85
  147. package/dist/integrations/python/types.d.ts.map +0 -1
  148. package/dist/integrations/python/types.js +0 -5
  149. package/dist/integrations/python/types.js.map +0 -1
  150. package/dist/integrations/redis/client.d.ts +0 -43
  151. package/dist/integrations/redis/client.d.ts.map +0 -1
  152. package/dist/integrations/redis/client.js +0 -142
  153. package/dist/integrations/redis/client.js.map +0 -1
  154. package/dist/integrations/redis/index.d.ts +0 -8
  155. package/dist/integrations/redis/index.d.ts.map +0 -1
  156. package/dist/integrations/redis/index.js +0 -7
  157. package/dist/integrations/redis/index.js.map +0 -1
  158. package/dist/integrations/redis/types.d.ts +0 -137
  159. package/dist/integrations/redis/types.d.ts.map +0 -1
  160. package/dist/integrations/redis/types.js +0 -5
  161. package/dist/integrations/redis/types.js.map +0 -1
  162. package/src/integrations/couchbase/README.md +0 -138
  163. package/src/integrations/couchbase/client.ts +0 -225
  164. package/src/integrations/couchbase/index.ts +0 -8
  165. package/src/integrations/couchbase/types.ts +0 -126
  166. package/src/integrations/kafka/README.md +0 -144
  167. package/src/integrations/kafka/client.ts +0 -216
  168. package/src/integrations/kafka/index.ts +0 -14
  169. package/src/integrations/kafka/types.ts +0 -128
  170. package/src/integrations/kinesis/README.md +0 -153
  171. package/src/integrations/kinesis/client.ts +0 -146
  172. package/src/integrations/kinesis/index.ts +0 -14
  173. package/src/integrations/kinesis/types.ts +0 -114
  174. package/src/integrations/python/README.md +0 -566
  175. package/src/integrations/python/client.test.ts +0 -341
  176. package/src/integrations/python/client.ts +0 -136
  177. package/src/integrations/python/index.ts +0 -6
  178. package/src/integrations/python/types.ts +0 -92
  179. package/src/integrations/redis/README.md +0 -200
  180. package/src/integrations/redis/client.ts +0 -208
  181. package/src/integrations/redis/index.ts +0 -8
  182. package/src/integrations/redis/types.ts +0 -167
package/README.md CHANGED
@@ -183,7 +183,7 @@ interface ExecuteApiRequest {
183
183
  executeQuery: (
184
184
  integrationId: string,
185
185
  request: Record<string, unknown>, // Plugin-specific request matching protobuf schema
186
- bindings?: Record<string, unknown>, // For language plugins (Python/JavaScript)
186
+ bindings?: Record<string, unknown>, // For language plugins (JavaScript)
187
187
  ) => Promise<unknown>;
188
188
  }
189
189
  ```
@@ -276,6 +276,42 @@ export default api({
276
276
  });
277
277
  ```
278
278
 
279
+ ### Export style (default vs named exports)
280
+
281
+ **Default export** — one API per file. **Named exports** — several `api({ ... })` values in one module; import them by name into `server/apis/index.ts`. `useApi("X")` matches the **key** on the registry object; align `api({ name: "X" })` with that when practical.
282
+
283
+ ```typescript
284
+ // server/apis/users/pair.ts
285
+ import { api, z } from "@superblocksteam/sdk-api";
286
+
287
+ export const ListProfiles = api({
288
+ name: "ListProfiles",
289
+ input: z.object({ orgId: z.string() }),
290
+ output: z.object({ profiles: z.array(z.object({ id: z.string() })) }),
291
+ async run() {
292
+ return { profiles: [] };
293
+ },
294
+ });
295
+
296
+ export const UpdateProfile = api({
297
+ name: "UpdateProfile",
298
+ input: z.object({ userId: z.string() }),
299
+ output: z.object({ ok: z.literal(true) }),
300
+ async run() {
301
+ return { ok: true as const };
302
+ },
303
+ });
304
+ ```
305
+
306
+ ```typescript
307
+ // server/apis/index.ts
308
+ import { ListProfiles, UpdateProfile } from "./users/pair.js";
309
+
310
+ const apis = { ListProfiles, UpdateProfile } as const;
311
+ export default apis;
312
+ export type ApiRegistry = typeof apis;
313
+ ```
314
+
279
315
  ## API Reference
280
316
 
281
317
  ### `api(config)`
@@ -284,13 +320,14 @@ Defines a TypeScript-based API with input/output validation. Returns a complete
284
320
 
285
321
  #### Required Fields
286
322
 
287
- | Field | Type | Description |
288
- | -------------- | --------------------------------- | ---------------------------------------------- |
289
- | `name` | `string` | **Required.** Unique identifier for the API |
290
- | `input` | `z.ZodType` | **Required.** Zod schema for input validation |
291
- | `output` | `z.ZodType` | **Required.** Zod schema for output validation |
292
- | `run` | `(ctx, input) => Promise<Output>` | **Required.** The implementation function |
293
- | `integrations` | `Record<string, IntegrationRef>` | Optional. Integration declarations for the API |
323
+ | Field | Type | Description |
324
+ | -------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
325
+ | `name` | `string` | **Required.** Unique identifier for the API |
326
+ | `description` | `string` | Optional. Plain-language summary of what this API does and which integrations it uses. Auto-generated by the AI agent when creating or editing an API. |
327
+ | `input` | `z.ZodType` | **Required.** Zod schema for input validation |
328
+ | `output` | `z.ZodType` | **Required.** Zod schema for output validation |
329
+ | `run` | `(ctx, input) => Promise<Output>` | **Required.** The implementation function |
330
+ | `integrations` | `Record<string, IntegrationRef>` | Optional. Integration declarations for the API |
294
331
 
295
332
  ```typescript
296
333
  import { api, z } from "@superblocksteam/sdk-api";
@@ -325,7 +362,7 @@ const myApi = api({
325
362
 
326
363
  The `run` function receives two arguments:
327
364
 
328
- 1. **`ctx`** - Context object with integrations, logging, and environment
365
+ 1. **`ctx`** - Context object with integrations, logging, environment, and user information
329
366
  2. **`input`** - Typed, validated input data matching your input schema
330
367
 
331
368
  ```typescript
@@ -340,7 +377,7 @@ async run(ctx, { userId, options }) {
340
377
  Access typed integration clients via the **name (key)** you declared in the API config: `ctx.integrations.<name>`. For example, if you declared `integrations: { db: postgres(...), notifier: slack(...) }`, use `ctx.integrations.db` and `ctx.integrations.notifier` in your `run` function. Integrations must be declared upfront to enable type-safe access and pre-authentication.
341
378
 
342
379
  ```typescript
343
- import { api, z, postgres, slack, openai, anthropic, stripe, github, notion, python } from "@superblocksteam/sdk-api";
380
+ import { api, z, postgres, slack, openai, anthropic, stripe, github, notion } from "@superblocksteam/sdk-api";
344
381
 
345
382
  // Integration IDs from the integrations panel - store in constants
346
383
  const PROD_POSTGRES = "a1b2c3d4-5678-90ab-cdef-ghijklmnopqr";
@@ -350,7 +387,6 @@ const PROD_ANTHROPIC = "d4e5f6g7-8901-23ab-cdef-ghijklmnopqr";
350
387
  const PROD_STRIPE = "e5f6g7h8-9012-34ab-cdef-ghijklmnopqr";
351
388
  const PROD_GITHUB = "f6g7h8i9-0123-45ab-cdef-ghijklmnopqr";
352
389
  const PROD_NOTION = "g7h8i9j0-1234-56ab-cdef-ghijklmnopqr";
353
- const PYTHON_RUNTIME = "h8i9j0k1-2345-67ab-cdef-ghijklmnopqr";
354
390
 
355
391
  export default api({
356
392
  name: "ProcessUserWorkflow",
@@ -375,9 +411,6 @@ export default api({
375
411
 
376
412
  // Productivity
377
413
  wiki: notion(PROD_NOTION),
378
-
379
- // Code Execution
380
- runtime: python(PYTHON_RUNTIME),
381
414
  },
382
415
 
383
416
  input: z.object({ userId: z.string() }),
@@ -394,6 +427,22 @@ export default api({
394
427
  });
395
428
  ```
396
429
 
430
+ #### `ctx.user`
431
+
432
+ Access the current user's identity (from the Superblocks JWT). This is the server-side equivalent of the `Global` object available in the frontend.
433
+
434
+ ```typescript
435
+ async run(ctx, input) {
436
+ const userId = ctx.user.userId;
437
+ const email = ctx.user.email; // may be undefined
438
+ const name = ctx.user.name; // may be undefined
439
+ const groups = ctx.user.groups; // readonly string[]
440
+ const claims = ctx.user.customClaims; // custom JWT claims
441
+
442
+ ctx.log.info('Request from user', { userId, email });
443
+ }
444
+ ```
445
+
397
446
  #### `ctx.log`
398
447
 
399
448
  Structured logging utilities.
@@ -407,6 +456,248 @@ async run(ctx, { userId }) {
407
456
  }
408
457
  ```
409
458
 
459
+ #### `ctx.env`
460
+
461
+ Access environment variables configured for the application.
462
+
463
+ ```typescript
464
+ async run(ctx) {
465
+ const apiKey = ctx.env.EXTERNAL_API_KEY;
466
+ if (!apiKey) {
467
+ throw new Error("EXTERNAL_API_KEY environment variable is not configured");
468
+ }
469
+ const environment = ctx.env.NODE_ENV;
470
+ }
471
+ ```
472
+
473
+ #### `ctx.user`
474
+
475
+ User information extracted from the Superblocks JWT. This is the **secure, server-side** way to access the current user's identity in API implementations. Never pass user information from the frontend as API input — always use `ctx.user` instead.
476
+
477
+ ```typescript
478
+ interface ApiUser {
479
+ /** Unique user identifier from JWT */
480
+ readonly userId: string;
481
+
482
+ /** User's email address (if available) */
483
+ readonly email?: string;
484
+
485
+ /** User's display name (if available) */
486
+ readonly name?: string;
487
+
488
+ /** User's group memberships from JWT */
489
+ readonly groups: readonly string[];
490
+
491
+ /** Custom claims from JWT */
492
+ readonly customClaims: Readonly<Record<string, unknown>>;
493
+ }
494
+ ```
495
+
496
+ ##### Basic Usage
497
+
498
+ ```typescript
499
+ import { api, z, postgres } from "@superblocksteam/sdk-api";
500
+
501
+ const PROD_POSTGRES = "a1b2c3d4-5678-90ab-cdef-ghijklmnopqr";
502
+
503
+ export default api({
504
+ name: "GetMyProfile",
505
+ integrations: { db: postgres(PROD_POSTGRES) },
506
+ input: z.object({}),
507
+ output: z.object({
508
+ email: z.string(),
509
+ name: z.string(),
510
+ }),
511
+
512
+ async run(ctx) {
513
+ // Access user identity securely from the JWT — no frontend input needed
514
+ ctx.log.info("Fetching profile", { userId: ctx.user.userId });
515
+
516
+ const rows = await ctx.integrations.db.query(
517
+ "SELECT email, display_name FROM users WHERE id = $1",
518
+ z.object({ email: z.string(), display_name: z.string() }),
519
+ [ctx.user.userId],
520
+ );
521
+
522
+ if (rows.length === 0) {
523
+ throw new Error("User not found");
524
+ }
525
+
526
+ return { email: rows[0].email, name: rows[0].display_name };
527
+ },
528
+ });
529
+ ```
530
+
531
+ ##### Role-Based Access Control with Groups
532
+
533
+ Use `ctx.user.groups` to restrict API access based on the user's group memberships. Groups are populated from the user's identity provider (e.g., Okta, Azure AD) via the JWT.
534
+
535
+ ```typescript
536
+ import { api, z, postgres } from "@superblocksteam/sdk-api";
537
+
538
+ const PROD_POSTGRES = "a1b2c3d4-5678-90ab-cdef-ghijklmnopqr";
539
+
540
+ export default api({
541
+ name: "GetAdminDashboard",
542
+ integrations: { db: postgres(PROD_POSTGRES) },
543
+ input: z.object({}),
544
+ output: z.object({
545
+ stats: z.object({
546
+ totalUsers: z.number(),
547
+ activeUsers: z.number(),
548
+ }),
549
+ }),
550
+
551
+ async run(ctx) {
552
+ // Check group membership for authorization
553
+ if (!ctx.user.groups.includes("admin")) {
554
+ throw new Error("Access denied: admin group membership required");
555
+ }
556
+
557
+ const stats = await ctx.integrations.db.query(
558
+ `SELECT
559
+ COUNT(*) as total_users,
560
+ COUNT(*) FILTER (WHERE status = 'active') as active_users
561
+ FROM users`,
562
+ z.object({ total_users: z.number(), active_users: z.number() }),
563
+ );
564
+
565
+ return {
566
+ stats: {
567
+ totalUsers: stats[0].total_users,
568
+ activeUsers: stats[0].active_users,
569
+ },
570
+ };
571
+ },
572
+ });
573
+ ```
574
+
575
+ ##### Custom Claims
576
+
577
+ `ctx.user.customClaims` contains additional claims from the JWT, such as department, role, or tenant information configured in your identity provider.
578
+
579
+ **Important limitations:**
580
+
581
+ - Custom claims are only available when the user is in an **enterprise account that uses SSO**, OR when the application is **deployed and uses embed tokens with SSO claims**.
582
+ - In the editor, custom claims will be empty for non-SSO users.
583
+ - Always write defensive code that handles missing custom claims gracefully.
584
+
585
+ ```typescript
586
+ import { api, z, postgres } from "@superblocksteam/sdk-api";
587
+
588
+ const PROD_POSTGRES = "a1b2c3d4-5678-90ab-cdef-ghijklmnopqr";
589
+
590
+ export default api({
591
+ name: "GetTenantData",
592
+ integrations: { db: postgres(PROD_POSTGRES) },
593
+ input: z.object({}),
594
+ output: z.object({
595
+ tenantName: z.string(),
596
+ records: z.array(z.object({ id: z.string(), value: z.string() })),
597
+ }),
598
+
599
+ async run(ctx) {
600
+ // Access custom claims from the JWT — handle missing claims defensively
601
+ const tenantId = ctx.user.customClaims["tenant_id"];
602
+ if (typeof tenantId !== "string") {
603
+ throw new Error(
604
+ "Missing tenant_id claim. Custom claims require enterprise SSO or embed tokens with SSO claims.",
605
+ );
606
+ }
607
+
608
+ const department = ctx.user.customClaims["department"] as
609
+ | string
610
+ | undefined;
611
+
612
+ ctx.log.info("Fetching tenant data", {
613
+ userId: ctx.user.userId,
614
+ tenantId,
615
+ department: department ?? "unknown",
616
+ });
617
+
618
+ const records = await ctx.integrations.db.query(
619
+ "SELECT id, value FROM tenant_data WHERE tenant_id = $1",
620
+ z.object({ id: z.string(), value: z.string() }),
621
+ [tenantId],
622
+ );
623
+
624
+ const tenantNames = await ctx.integrations.db.query(
625
+ "SELECT name FROM tenants WHERE id = $1",
626
+ z.object({ name: z.string() }),
627
+ [tenantId],
628
+ );
629
+
630
+ return {
631
+ tenantName: tenantNames[0]?.name ?? "Unknown Tenant",
632
+ records,
633
+ };
634
+ },
635
+ });
636
+ ```
637
+
638
+ ##### Combining User Context for Row-Level Security
639
+
640
+ ```typescript
641
+ import { api, z, postgres } from "@superblocksteam/sdk-api";
642
+
643
+ const PROD_POSTGRES = "a1b2c3d4-5678-90ab-cdef-ghijklmnopqr";
644
+
645
+ export default api({
646
+ name: "GetMyOrders",
647
+ integrations: { db: postgres(PROD_POSTGRES) },
648
+ input: z.object({
649
+ status: z.enum(["pending", "completed", "cancelled"]).optional(),
650
+ }),
651
+ output: z.object({
652
+ orders: z.array(
653
+ z.object({
654
+ id: z.string(),
655
+ total: z.number(),
656
+ status: z.string(),
657
+ createdAt: z.string(),
658
+ }),
659
+ ),
660
+ }),
661
+
662
+ async run(ctx, { status }) {
663
+ // Use ctx.user for row-level security — users can only see their own orders
664
+ if (!ctx.user.email) {
665
+ throw new Error("User email is required to filter orders");
666
+ }
667
+ const params: unknown[] = [ctx.user.email];
668
+ let query =
669
+ "SELECT id, total, status, created_at FROM orders WHERE user_email = $1";
670
+
671
+ if (status) {
672
+ query += " AND status = $2";
673
+ params.push(status);
674
+ }
675
+
676
+ query += " ORDER BY created_at DESC LIMIT 50";
677
+
678
+ const orders = await ctx.integrations.db.query(
679
+ query,
680
+ z.object({
681
+ id: z.string(),
682
+ total: z.number(),
683
+ status: z.string(),
684
+ created_at: z.string(),
685
+ }),
686
+ params,
687
+ );
688
+
689
+ return {
690
+ orders: orders.map((o) => ({
691
+ id: o.id,
692
+ total: o.total,
693
+ status: o.status,
694
+ createdAt: o.created_at,
695
+ })),
696
+ };
697
+ },
698
+ });
699
+ ```
700
+
410
701
  ## Integration Clients
411
702
 
412
703
  ### Integration Client Methods Reference
@@ -415,48 +706,73 @@ async run(ctx, { userId }) {
415
706
 
416
707
  #### Method Availability by Client
417
708
 
418
- | Client | Available Methods | Notes |
419
- | ---------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------ |
420
- | **PostgresClient** | `query<T>(sql, schema, params?)`, `execute(sql, params?)` | Schema parameter is REQUIRED for query() |
421
- | **SnowflakeClient** | `query<T>(sql, schema, params?)`, `execute(sql, params?)` | Schema parameter is REQUIRED for query() |
422
- | **PythonClient** | `run<T>(code, schema, bindings?)` | Schema parameter is REQUIRED, bindings via {{}} syntax |
423
- | **AirtableClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
424
- | **AnthropicClient** | `apiRequest(options, schema)` | Claude AI models |
425
- | **AsanaClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
426
- | **BitbucketClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
427
- | **BoxClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
428
- | **CircleCIClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
429
- | **CohereClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
430
- | **ConfluenceClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
431
- | **DatadogClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
432
- | **DropboxClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
433
- | **ElasticSearchClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
434
- | **FireworksClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
435
- | **FrontClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
436
- | **GeminiClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
437
- | **GitHubClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest - no createIssue, etc. |
438
- | **GoogleAnalyticsClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
439
- | **GoogleDriveClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
440
- | **GraphQLClient** | `query(query, variables?, schema?)`, `mutation(mutation, variables?, schema?)` | GraphQL-specific methods |
441
- | **GroqClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
442
- | **HubSpotClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
443
- | **IntercomClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
444
- | **JiraClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
445
- | **LaunchDarklyClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
446
- | **MistralClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
447
- | **NotionClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest - no queryDatabase, etc. |
448
- | **OpenAIClient** | `apiRequest(options, schema)` | GPT AI models |
449
- | **PagerDutyClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
450
- | **PerplexityClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
451
- | **SegmentClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
452
- | **SendGridClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
453
- | **SlackClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest - no specialized methods |
454
- | **StabilityAIClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
455
- | **StripeClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest - no createCustomer, etc. |
456
- | **TwilioClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
457
- | **ZendeskClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
458
- | **ZoomClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
459
- | **RestApiIntegrationPluginClient** | `apiRequest(options, schema)` | Generic REST API Integration - works with any configured HTTP API. |
709
+ | Client | Available Methods | Notes |
710
+ | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
711
+ | **PostgresClient** | `query<T>(sql, schema, params?)`, `execute(sql, params?)` | Schema parameter is REQUIRED for query() |
712
+ | **SnowflakeClient** | `query<T>(sql, schema, params?)`, `execute(sql, params?)` | Schema parameter is REQUIRED for query() |
713
+ | **AirtableClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
714
+ | **AnthropicClient** | `apiRequest(options, schema)` | Claude AI models (see [Streaming note](#streaming--sse)) |
715
+ | **AsanaClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
716
+ | **BitbucketClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
717
+ | **BoxClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
718
+ | **CircleCIClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
719
+ | **CohereClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
720
+ | **ConfluenceClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
721
+ | **DatadogClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
722
+ | **DropboxClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
723
+ | **ElasticSearchClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
724
+ | **FireworksClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
725
+ | **FrontClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
726
+ | **GeminiClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
727
+ | **GitHubClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest - no createIssue, etc. |
728
+ | **GoogleAnalyticsClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
729
+ | **GoogleDriveClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
730
+ | **GraphQLClient** | `query<T>(query, schema, variables?, metadata?)`, `mutation<T>(mutation, schema, variables?, metadata?)` | GraphQL-specific methods |
731
+ | **GroqClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
732
+ | **HubSpotClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
733
+ | **IntercomClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
734
+ | **JiraClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
735
+ | **LaunchDarklyClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
736
+ | **MistralClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
737
+ | **NotionClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest - no queryDatabase, etc. |
738
+ | **OpenAIClient** | `apiRequest(options, schema)` | GPT AI models (see [Streaming note](#streaming--sse)) |
739
+ | **PagerDutyClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
740
+ | **PerplexityClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
741
+ | **SegmentClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
742
+ | **SendGridClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
743
+ | **SlackClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest - no specialized methods |
744
+ | **StabilityAIClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
745
+ | **StripeClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest - no createCustomer, etc. |
746
+ | **TwilioClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
747
+ | **ZendeskClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
748
+ | **ZoomClient** | `apiRequest(options, schema?)` | ONLY generic apiRequest |
749
+ | **MySQLClient** | `query<T>(sql, schema, params?)`, `execute(sql, params?)` | Schema parameter is REQUIRED for query() |
750
+ | **MariaDBClient** | `query<T>(sql, schema, params?)`, `execute(sql, params?)` | Schema parameter is REQUIRED for query() |
751
+ | **MSSQLClient** | `query<T>(sql, schema, params?)`, `execute(sql, params?)` | Uses `@PARAM_1` placeholder syntax |
752
+ | **CockroachDBClient** | `query<T>(sql, schema, params?)`, `execute(sql, params?)` | Schema parameter is REQUIRED for query() |
753
+ | **OracleDBClient** | `query<T>(sql, schema, params?)`, `execute(sql, params?)` | Uses `:PARAM_1` placeholder syntax |
754
+ | **RedshiftClient** | `query<T>(sql, schema, params?)`, `execute(sql, params?)` | Schema parameter is REQUIRED for query() |
755
+ | **AthenaClient** | `query<T>(sql, schema, params?)`, `execute(sql, params?)` | Uses `?` placeholder syntax |
756
+ | **DatabricksClient** | `query<T>(sql, schema, params?)`, `execute(sql, params?)` | Uses `:param` placeholder syntax |
757
+ | **BigQueryClient** | `query<T>(sql, schema, params?)`, `execute(sql, params?)` | Uses `@param` placeholder syntax |
758
+ | **LakebaseClient** | `query<T>(sql, schema, params?)`, `execute(sql, params?)` | Schema parameter is REQUIRED for query() |
759
+ | **SnowflakePostgresClient** | `query<T>(sql, schema, params?)`, `execute(sql, params?)` | Schema parameter is REQUIRED for query() |
760
+ | **MongoDBClient** | `run(collection, action, schema, params, metadata?)` | 13 actions (find, insertOne, updateOne, etc.) |
761
+ | **DynamoDBClient** | `query<T>(sql, schema)`, `getItem(params, schema)`, `putItem(params)`, `updateItem(params)`, `deleteItem(params)`, `scan(params, schema)`, `queryTable(params, schema)`, `batchWriteItem(params)`, `listTables(schema)`, `describeTable(params, schema)`, `deleteTable(params)` | 11 methods — supports both PartiQL and native CRUD |
762
+ | **CosmosDBClient** | `apiRequest(options, schema?)` | Azure Cosmos DB — use custom headers for queries |
763
+ | **CouchbaseClient** | `query<T>(bucket, sql, schema, params?)`, `insert(key, value, identifier)`, `get<T>(key, schema, identifier)`, `remove(key, identifier)` | N1QL queries + key-value operations |
764
+ | **S3Client** | `listBuckets(schema)`, `listObjects(params, schema)`, `getObject(params, schema)`, `deleteObject(params)`, `uploadObject(params)`, `uploadMultipleObjects(params)`, `generatePresignedUrl(params, schema)` | 7 methods for AWS S3 bucket/object operations |
765
+ | **GCSClient** | `listBuckets(schema)`, `listObjects(params, schema)`, `getObject(params, schema)`, `deleteObject(params)`, `uploadObject(params)`, `uploadMultipleObjects(params)`, `generateSignedUrl(params, schema)` | 7 methods for Google Cloud Storage operations |
766
+ | **SalesforceClient** | `query<T>(soql, schema)`, `create(params)`, `read(params, schema)`, `update(params)`, `remove(params)`, `bulkCreate(params)`, `bulkUpdate(params)`, `bulkDelete(params)`, `bulkUpsert(params)` | 9 methods — SOQL queries + CRUD + bulk operations |
767
+ | **KinesisClient** | `putRecord(params)`, `getRecords(params)` | AWS Kinesis stream put/get |
768
+ | **GoogleSheetsClient** | `run(action, schema, params, metadata?)` | 6 actions (READ_SPREADSHEET, APPEND, etc.) |
769
+ | **KafkaClient** | `consume(schema, params, metadata?)`, `produce(schema, params, metadata?)` | Kafka message consumption and production |
770
+ | **ConfluentClient** | `consume(schema, params, metadata?)`, `produce(schema, params, metadata?)` | Confluent Kafka — same API as KafkaClient |
771
+ | **RedpandaClient** | `consume(schema, params, metadata?)`, `produce(schema, params, metadata?)` | Redpanda — same API as KafkaClient |
772
+ | **SmtpClient** | `send(params)` | Send emails via SMTP |
773
+ | **SuperblocksOCRClient** | `apiRequest(options, schema?)` | OCR document processing |
774
+ | **SnowflakeCortexClient** | `apiRequest(options, schema?)` | Snowflake Cortex AI |
775
+ | **RestApiIntegrationPluginClient** | `apiRequest(options, schema)` | Generic REST API Integration - works with any configured HTTP API. |
460
776
 
461
777
  #### Common Mistakes to Avoid
462
778
 
@@ -478,7 +794,7 @@ await myApi.request({ ... }); // .request() does NOT exist on any client
478
794
  ```typescript
479
795
  // Use the generic apiRequest method
480
796
  await openai.apiRequest(
481
- { method: 'POST', path: '/v1/chat/completions', body: { ... } },
797
+ { method: 'POST', path: '/chat/completions', body: { ... } },
482
798
  { response: ResponseSchema }
483
799
  );
484
800
  ```
@@ -521,7 +837,18 @@ Each integration client has detailed documentation with examples and common pitf
521
837
  #### SQL Databases
522
838
 
523
839
  - [PostgreSQL](./src/integrations/postgres/README.md) - Query PostgreSQL databases with schema validation
840
+ - [MySQL](./src/integrations/mysql/README.md) - Query MySQL databases
841
+ - [MariaDB](./src/integrations/mariadb/README.md) - Query MariaDB databases
842
+ - [MSSQL](./src/integrations/mssql/README.md) - Query Microsoft SQL Server databases
843
+ - [CockroachDB](./src/integrations/cockroachdb/README.md) - Query CockroachDB databases
844
+ - [OracleDB](./src/integrations/oracledb/README.md) - Query Oracle databases
524
845
  - [Snowflake](./src/integrations/snowflake/README.md) - Query Snowflake data warehouses
846
+ - [Redshift](./src/integrations/redshift/README.md) - Query Amazon Redshift
847
+ - [BigQuery](./src/integrations/bigquery/README.md) - Query Google BigQuery
848
+ - [Databricks](./src/integrations/databricks/README.md) - Query Databricks SQL warehouses
849
+ - [Athena](./src/integrations/athena/README.md) - Query Amazon Athena
850
+ - [Lakebase](./src/integrations/lakebase/README.md) - Query Lakebase databases
851
+ - [SnowflakePostgres](./src/integrations/snowflakepostgres/README.md) - Query Snowflake via Postgres protocol
525
852
 
526
853
  #### GraphQL
527
854
 
@@ -575,24 +902,50 @@ Each integration client has detailed documentation with examples and common pitf
575
902
  - [Datadog](./src/integrations/datadog/README.md) - Metrics, monitors, and logs
576
903
  - [Segment](./src/integrations/segment/README.md) - Customer data platform
577
904
 
905
+ #### NoSQL Databases
906
+
907
+ - [MongoDB](./src/integrations/mongodb/README.md) - Document database with 13 actions (find, insertOne, updateOne, etc.)
908
+ - [DynamoDB](./src/integrations/dynamodb/README.md) - AWS DynamoDB with PartiQL and native CRUD
909
+ - [CosmosDB](./src/integrations/cosmosdb/README.md) - Azure Cosmos DB via apiRequest
910
+ - [Couchbase](./src/integrations/couchbase/README.md) - N1QL queries and key-value operations
911
+
578
912
  #### Cloud Storage
579
913
 
914
+ - [S3](./src/integrations/s3/README.md) - AWS S3 bucket and object operations
915
+ - [GCS](./src/integrations/gcs/README.md) - Google Cloud Storage operations
580
916
  - [Google Drive](./src/integrations/googledrive/README.md) - Files, folders, and sharing
581
917
  - [Dropbox](./src/integrations/dropbox/README.md) - Cloud file storage
582
918
  - [Box](./src/integrations/box/README.md) - Enterprise content management
583
919
 
584
- #### Code Execution
585
-
586
- - [Python](./src/integrations/python/README.md) - Execute Python scripts with type-safe inputs and outputs
587
-
588
920
  #### Generic HTTP
589
921
 
590
922
  - [REST API Integration](./src/integrations/restapiintegration/README.md) - Make HTTP requests to any configured REST API
591
923
 
924
+ #### CRM
925
+
926
+ - [Salesforce](./src/integrations/salesforce/README.md) - SOQL queries, CRUD, and bulk operations
927
+
928
+ #### Event Streaming
929
+
930
+ - [Kafka](./src/integrations/kafka/README.md) - Consume and produce Kafka messages
931
+ - [Kinesis](./src/integrations/kinesis/README.md) - AWS Kinesis stream put/get
932
+ - Confluent — same API as [Kafka](./src/integrations/kafka/README.md)
933
+ - Redpanda — same API as [Kafka](./src/integrations/kafka/README.md)
934
+
935
+ #### Email
936
+
937
+ - [SMTP](./src/integrations/smtp/README.md) - Send emails via SMTP
938
+
592
939
  #### Other
593
940
 
594
941
  - [Elasticsearch](./src/integrations/elasticsearch/README.md) - Search and analytics engine
595
942
  - [Stability AI](./src/integrations/stabilityai/README.md) - Image generation and editing
943
+ - [Superblocks OCR](./src/integrations/superblocks-ocr/README.md) - OCR document processing
944
+ - [Snowflake Cortex](./src/integrations/snowflakecortex/README.md) - Snowflake Cortex AI
945
+
946
+ ### Streaming / SSE
947
+
948
+ > **Note:** The SDK's `apiRequest()` method does **not** support streaming or Server-Sent Events (SSE) responses. All AI provider calls (OpenAI, Anthropic, Gemini, Groq, Mistral, Cohere, Perplexity, Fireworks) return complete responses — the SDK waits for the full response before returning. If you are building a chat interface that needs real-time token streaming, you will need to handle streaming at the frontend/UI layer rather than through the SDK.
596
949
 
597
950
  ## Generic API Requests
598
951
 
@@ -623,7 +976,6 @@ export default api({
623
976
  });
624
977
 
625
978
  const PostMessageResponseSchema = z.object({
626
- ok: z.boolean(),
627
979
  ts: z.string(),
628
980
  channel: z.string(),
629
981
  });
@@ -644,7 +996,11 @@ export default api({
644
996
  },
645
997
  );
646
998
 
647
- // result is fully typed as PostMessageResponse
999
+ if (!result.ok) {
1000
+ throw new Error(`Slack API error: ${result.error}`);
1001
+ }
1002
+
1003
+ // result is fully typed as { ok: true } & PostMessageResponseSchema
648
1004
  return { ts: result.ts };
649
1005
  },
650
1006
  });
@@ -711,7 +1067,7 @@ const result = await ctx.integrations.slack.apiRequest(
711
1067
  { label: "Send alert to Slack" },
712
1068
  );
713
1069
 
714
- // Other clients (MongoDB, Redis, S3, etc.)
1070
+ // Other clients (MongoDB, S3, etc.)
715
1071
  const doc = await ctx.integrations.mongo.run(
716
1072
  "users",
717
1073
  "findOne",
@@ -719,10 +1075,6 @@ const doc = await ctx.integrations.mongo.run(
719
1075
  { filter: { _id: userId } },
720
1076
  { label: "Look up user by ID" },
721
1077
  );
722
-
723
- await ctx.integrations.cache.get("session:" + sessionId, {
724
- label: "Check session cache",
725
- });
726
1078
  ```
727
1079
 
728
1080
  ### When to Use
@@ -746,6 +1098,7 @@ import {
746
1098
  QueryValidationError,
747
1099
  RestApiValidationError,
748
1100
  IntegrationNotFoundError,
1101
+ IntegrationError,
749
1102
  ExecutionError,
750
1103
  ErrorCode,
751
1104
  } from "@superblocksteam/sdk-api";
@@ -788,7 +1141,7 @@ Thrown when REST API request bodies or responses fail schema validation. Include
788
1141
  ```typescript
789
1142
  try {
790
1143
  const result = await ctx.integrations.ai.apiRequest(
791
- { method: 'POST', path: '/v1/chat/completions', body: { ... } },
1144
+ { method: 'POST', path: '/chat/completions', body: { ... } },
792
1145
  { body: RequestSchema, response: ResponseSchema }
793
1146
  );
794
1147
  } catch (error) {
@@ -1078,28 +1431,20 @@ The app template provides automatic type inference for API calls using a tRPC-st
1078
1431
 
1079
1432
  ### Adding a New API (2 Steps)
1080
1433
 
1081
- **Step 1**: Create your API file (e.g., `server/apis/GetUsers/api.ts`)
1082
-
1083
- **Step 2**: Add it to the registry at `server/apis/index.ts`:
1434
+ 1. Add a module under `server/apis/` (any layout; see [export styles](#export-style-default-vs-named-exports)).
1435
+ 2. Register it in `server/apis/index.ts` (`.js` specifiers for ESM):
1084
1436
 
1085
1437
  ```typescript
1086
- import GetUsers from "./GetUsers/api.js";
1087
- import CreateOrder from "./CreateOrder/api.js";
1438
+ import CreateOrder from "./orders/create-order.js";
1439
+ import GetUsers from "./users/get-users.js";
1440
+ import { ListProfiles, UpdateProfile } from "./users/pair.js";
1088
1441
 
1089
- const apis = {
1090
- GetUsers,
1091
- CreateOrder,
1092
- // Add new APIs here
1093
- } as const;
1442
+ const apis = { CreateOrder, GetUsers, ListProfiles, UpdateProfile } as const;
1094
1443
 
1095
1444
  export default apis;
1096
1445
  export type ApiRegistry = typeof apis;
1097
1446
  ```
1098
1447
 
1099
- That's it! Types automatically flow to the frontend.
1100
-
1101
- > **Note**: Always use `.js` extension in imports (required for ESM module resolution).
1102
-
1103
1448
  ### Calling APIs from React Components
1104
1449
 
1105
1450
  Import `useApi` from the template's pre-configured hook:
@@ -1142,13 +1487,15 @@ The hook uses `import type` to pull in only the type information from the regist
1142
1487
 
1143
1488
  ### Alternative: Explicit Type Parameter
1144
1489
 
1145
- For testing or when the registry isn't available:
1490
+ When you cannot use the registry hook, pass the compiled API type:
1146
1491
 
1147
1492
  ```typescript
1148
1493
  import { useApi } from "@superblocksteam/library";
1149
- import type GetUsersApi from "../../server/apis/GetUsers/api.js";
1494
+ import type GetUsersApi from "../../server/apis/users/get-users.js";
1495
+ import type { ListProfiles } from "../../server/apis/users/pair.js";
1150
1496
 
1151
- const { run } = useApi<typeof GetUsersApi>("GetUsers");
1497
+ const getUsers = useApi<typeof GetUsersApi>("GetUsers");
1498
+ const listProfiles = useApi<typeof ListProfiles>("ListProfiles");
1152
1499
  ```
1153
1500
 
1154
1501
  ## Complete Example
@@ -1193,7 +1540,6 @@ const CustomerSchema = z.object({
1193
1540
  });
1194
1541
 
1195
1542
  const SlackPostMessageResponseSchema = z.object({
1196
- ok: z.boolean(),
1197
1543
  ts: z.string().optional(),
1198
1544
  });
1199
1545
 
@@ -1250,7 +1596,7 @@ export default api({
1250
1596
  const orderId = `ord_${Date.now()}`;
1251
1597
 
1252
1598
  // Notify via Slack
1253
- await ctx.integrations.notifier.apiRequest(
1599
+ const slackResult = await ctx.integrations.notifier.apiRequest(
1254
1600
  {
1255
1601
  method: "POST",
1256
1602
  path: "/chat.postMessage",
@@ -1262,13 +1608,17 @@ export default api({
1262
1608
  { response: SlackPostMessageResponseSchema },
1263
1609
  );
1264
1610
 
1611
+ if (!slackResult.ok) {
1612
+ throw new Error(`Slack API error: ${slackResult.error}`);
1613
+ }
1614
+
1265
1615
  // Generate AI summary if requested
1266
1616
  let aiSummary: string | undefined;
1267
1617
  if (sendAiSummary) {
1268
1618
  const completion = await ctx.integrations.ai.apiRequest(
1269
1619
  {
1270
1620
  method: "POST",
1271
- path: "/v1/chat/completions",
1621
+ path: "/chat/completions",
1272
1622
  body: {
1273
1623
  model: "gpt-4",
1274
1624
  messages: [