@alvera-ai/platform-sdk 0.10.0-rc.8 → 0.11.0

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 (66) hide show
  1. package/.agent/AGENTS.md +499 -0
  2. package/.agent/account_management.md +456 -0
  3. package/.agent/action_status_updaters.md +264 -0
  4. package/.agent/ai_agents.md +462 -0
  5. package/.agent/ai_sandbox.md +265 -0
  6. package/.agent/async.md +112 -0
  7. package/.agent/connected_apps.md +408 -0
  8. package/.agent/cookbook/_fixtures/README.md +106 -0
  9. package/.agent/cookbook/_fixtures/accounts_receivable/_customers_accounts_receivable_customer.liquid +32 -0
  10. package/.agent/cookbook/_fixtures/accounts_receivable/_customers_accounts_receivable_mdm.liquid +20 -0
  11. package/.agent/cookbook/_fixtures/accounts_receivable/stripe_customers_batch1.csv +5 -0
  12. package/.agent/cookbook/_fixtures/foundation/_lead_submissions_foundation_generic_table.liquid +33 -0
  13. package/.agent/cookbook/_fixtures/foundation/_lead_submissions_foundation_legal_entity.liquid +88 -0
  14. package/.agent/cookbook/_fixtures/foundation/_lead_submissions_foundation_mdm.liquid +48 -0
  15. package/.agent/cookbook/_fixtures/healthcare/_cahps_appointments_healthcare_appointment.liquid +47 -0
  16. package/.agent/cookbook/_fixtures/healthcare/_cahps_appointments_healthcare_mdm.liquid +24 -0
  17. package/.agent/cookbook/_fixtures/healthcare/_cahps_appointments_healthcare_patient.liquid +38 -0
  18. package/.agent/cookbook/_fixtures/healthcare/memorandum-of-association-01.png +0 -0
  19. package/.agent/cookbook/_fixtures/healthcare/sample_two_page.pdf +43 -0
  20. package/.agent/cookbook/_fixtures/payment_risk/_compliance_screenings_payment_risk_compliance_screening.liquid +59 -0
  21. package/.agent/cookbook/_fixtures/payment_risk/_compliance_screenings_payment_risk_mdm.liquid +36 -0
  22. package/.agent/cookbook/_fixtures/payment_risk/_payment_accounts_payment_risk_mdm.liquid +30 -0
  23. package/.agent/cookbook/_fixtures/payment_risk/_payment_accounts_payment_risk_payment_account.liquid +55 -0
  24. package/.agent/cookbook/_setup/accounts_receivable.md +282 -0
  25. package/.agent/cookbook/_setup/foundation.md +277 -0
  26. package/.agent/cookbook/_setup/healthcare.md +279 -0
  27. package/.agent/cookbook/_setup/payment_risk.md +283 -0
  28. package/.agent/cookbook/action-status-updaters.md +212 -0
  29. package/.agent/cookbook/ai-agent-invoke.md +243 -0
  30. package/.agent/cookbook/appointment-review-sms-workflow.md +761 -0
  31. package/.agent/cookbook/birthday-greeting-sms-trigger.md +656 -0
  32. package/.agent/cookbook/bulk-ingest.md +254 -0
  33. package/.agent/cookbook/contact-us-triage-with-llm.md +622 -0
  34. package/.agent/cookbook/custom-tables.md +201 -0
  35. package/.agent/cookbook/dunning-sms-for-delinquent.md +619 -0
  36. package/.agent/cookbook/invite-team.md +194 -0
  37. package/.agent/cookbook/kyc-notification-on-account-activation.md +619 -0
  38. package/.agent/cookbook/rest-fetch.md +246 -0
  39. package/.agent/cookbook/sanctions-screening-with-agent-review.md +733 -0
  40. package/.agent/cookbook/score-leads-with-llm-categorization.md +624 -0
  41. package/.agent/cookbook/system-templates.md +129 -0
  42. package/.agent/cookbook/triage-prospects-by-priority.md +533 -0
  43. package/.agent/cookbook/welcome-sms-for-customers.md +607 -0
  44. package/.agent/data_activation_clients.md +559 -0
  45. package/.agent/data_sources.md +235 -0
  46. package/.agent/datalakes.md +714 -0
  47. package/.agent/debugging.md +137 -0
  48. package/.agent/errors.md +190 -0
  49. package/.agent/generic_tables.md +351 -0
  50. package/.agent/interoperability_contracts.md +417 -0
  51. package/.agent/mdm.md +293 -0
  52. package/.agent/mutations.md +126 -0
  53. package/.agent/templates.md +98 -0
  54. package/.agent/tool-call-configs.md +90 -0
  55. package/.agent/tools.md +547 -0
  56. package/.agent/type_naming.md +129 -0
  57. package/.agent/workflows.md +617 -0
  58. package/README.md +178 -0
  59. package/dist/bin/platform-sdk.d.mts +1 -0
  60. package/dist/bin/platform-sdk.mjs +106 -0
  61. package/dist/bin/platform-sdk.mjs.map +1 -0
  62. package/dist/index.d.mts +1310 -44116
  63. package/dist/index.d.mts.map +1 -1
  64. package/dist/index.mjs +1194 -7344
  65. package/dist/index.mjs.map +1 -1
  66. package/package.json +19 -10
@@ -0,0 +1,246 @@
1
+ ---
2
+ title: "Capability: pull records from a third-party REST API on demand"
3
+ summary: A capability walk for pull-based ingestion. Register a REST-API tool with its auth (Bearer here; an OAuth2 refresh-token variant is shown too), bind it to a contract and a Data Activation Client whose `tool_call` names the path to fetch, trigger a fetch (`api.dataActivationClients.runManually`), and verify the rows landed. The platform calls the API for you — no file to upload.
4
+ industry: accounts_receivable
5
+ slug: rest-fetch
6
+ vitest_source:
7
+ - integration-tests/tests/accounts_receivable/run-dac-fetch.test.ts
8
+ - integration-tests/tests/foundation/oauth2-dac-fetch.test.ts
9
+ - integration-tests/tests/accounts_receivable/bootstrap.test.ts
10
+ status: green
11
+ ---
12
+
13
+ # Capability
14
+
15
+ **What you get:** records pulled straight from a third-party REST API into your
16
+ dataset on demand, with the platform handling auth and the HTTP call — no file
17
+ to export and upload.
18
+
19
+ A **pull-based** Data Activation Client fetches instead of receiving:
20
+
21
+ - the **tool** holds the API's base URL + auth (`bearer`, or `oauth2` with a
22
+ refresh-token grant the platform exchanges server-side before each call),
23
+ - the **DAC's `tool_call`** names the HTTP `method` + `path` to hit and how to
24
+ paginate; a `response_extractor` unwraps the API's envelope into a row array,
25
+ - `dataActivationClients.runManually(...)` triggers a fetch and returns a
26
+ `batch_id`.
27
+
28
+ This is **global** — only the API and its auth differ. See
29
+ `data_activation_clients.md` §6.3 (`.runManually`) and `tools.md` (`rest_api`
30
+ body) for the reference.
31
+
32
+ # Walkthrough
33
+
34
+ The `_setup/accounts_receivable.md` bootstrap left `api`, `tenantSlug`,
35
+ `datalakeSlug`, and `ctx.datalakeId` populated. The fetch targets a mock Stripe
36
+ API; locally that is the integration stack's WireMock on `localhost:8080`.
37
+
38
+ ## 001 — create the data source
39
+
40
+ ```typescript
41
+ const { data: ds } = await api.dataSources.create(tenantSlug, datalakeSlug, {
42
+ name: `REST Stripe Source ${runSuffix}`,
43
+ uri: 'api.stripe.com',
44
+ description: 'Stripe REST API — origin of the pulled customer rows.',
45
+ status: 'active',
46
+ is_default: false,
47
+ })
48
+ dataSourceId = ds.id!
49
+ ```
50
+
51
+ ## 002 — create the REST-API tool (Bearer auth)
52
+
53
+ The tool holds the API's `base_url` and credentials. `auth_method: 'bearer'`
54
+ with a `bearer_token` is the simplest case; `status: 'active'` is required (a
55
+ draft tool is skipped at fetch time — see Gotchas).
56
+
57
+ ```typescript
58
+ const { data: tool } = await api.tools.create(tenantSlug, datalakeSlug, {
59
+ name: `Stripe REST Tool ${runSuffix}`,
60
+ description: 'WireMock-mocked Stripe REST API (AR customer fetch).',
61
+ intent: 'data_exchange',
62
+ status: 'active',
63
+ datalake_id: ctx.datalakeId,
64
+ data_source_id: dataSourceId,
65
+ body: {
66
+ tool_body_type: 'rest_api',
67
+ auth_method: 'bearer',
68
+ base_url: 'http://localhost:8080/stripe',
69
+ bearer_token: 'sk_test_vitest_stripe_token',
70
+ request_type: 'json',
71
+ response_type: 'json',
72
+ timeout_ms: 30_000,
73
+ },
74
+ })
75
+ ctx.restToolId = tool.id!
76
+ ```
77
+
78
+ ## 003 — create the customer interoperability contract
79
+
80
+ The contract shapes each fetched customer into a `Customer` upsert. Reuse the
81
+ vendored production Stripe customer + MDM templates.
82
+
83
+ ```typescript
84
+ const { readFileSync } = await import('node:fs')
85
+ const { join } = await import('node:path')
86
+ const customerTemplate = readFileSync(
87
+ join(process.env.COOKBOOK_FIXTURES_DIR!, 'accounts_receivable/_customers_accounts_receivable_customer.liquid'),
88
+ 'utf8',
89
+ )
90
+ const mdmTemplate = readFileSync(
91
+ join(process.env.COOKBOOK_FIXTURES_DIR!, 'accounts_receivable/_customers_accounts_receivable_mdm.liquid'),
92
+ 'utf8',
93
+ )
94
+
95
+ const { data: contract } = await api.interoperabilityContracts.create(tenantSlug, datalakeSlug, {
96
+ name: `Stripe REST Customer Contract ${runSuffix}`,
97
+ description: 'Stripe customer fetch → AccountsReceivable Customer.',
98
+ resource_type: 'customer',
99
+ template_config: { type: 'custom', body: customerTemplate },
100
+ mdm_input_config: { type: 'custom', body: mdmTemplate },
101
+ generic_table_id: null,
102
+ })
103
+ interopContractId = contract.id!
104
+ ```
105
+
106
+ ## 004 — create the fetch DAC
107
+
108
+ The Data Activation Client's `tool_call` selects the REST path: `restapi_request`
109
+ with the HTTP `method` and the `path` to hit (`/v1/customers`, appended to the
110
+ tool's `base_url`). `pagination_context_template` declares no further pages here.
111
+ A top-level `response_extractor` unwraps the API's `{ data: [...] }` envelope so
112
+ each element becomes one row.
113
+
114
+ ```typescript
115
+ const { data: dac } = await api.dataActivationClients.create(tenantSlug, datalakeSlug, {
116
+ name: `Stripe REST Fetch DAC ${runSuffix}`,
117
+ description: 'Pull-based DAC — fetches Stripe customers over REST.',
118
+ tool_id: ctx.restToolId,
119
+ data_source_id: dataSourceId,
120
+ tool_call: {
121
+ tool_call_type: 'restapi_request',
122
+ method: 'get',
123
+ path: { type: 'custom', body: '/v1/customers' },
124
+ pagination_context_template: { type: 'custom', body: '{"has_next": false}' },
125
+ },
126
+ response_extractor: { type: 'custom', body: '{{ msg.data | to_json }}' },
127
+ interoperability_contract_ids: [interopContractId],
128
+ })
129
+ dacId = dac.id!
130
+ ctx.dacSlug = dac.slug!
131
+ ```
132
+
133
+ ## 005 — trigger a fetch
134
+
135
+ `runManually` enqueues a fetch and returns the `batch_id` immediately; the actual
136
+ HTTP call + ingestion run in a background worker.
137
+
138
+ ```typescript
139
+ const { data: run } = await api.dataActivationClients.runManually(tenantSlug, datalakeSlug, ctx.dacSlug)
140
+ ctx.batchId = run.batch_id!
141
+ if (!ctx.batchId) {
142
+ throw new Error('runManually did not return a batch_id')
143
+ }
144
+ ```
145
+
146
+ ## 006 — verify the fetched rows landed
147
+
148
+ Poll the DAC logs until the batch shows a fully-merged ingest (the mock returns 3
149
+ customers), then search the `customer` dataset scoped to the batch.
150
+
151
+ ```typescript
152
+ const deadline = Date.now() + 90_000
153
+ let merged = false
154
+ while (Date.now() < deadline && !merged) {
155
+ const { data } = await api.dataActivationClients.logs.list(tenantSlug, datalakeSlug, ctx.dacSlug)
156
+ merged = (data.data ?? []).some((r) => {
157
+ const row = r as Record<string, unknown>
158
+ return row.batch_id === ctx.batchId &&
159
+ typeof row.rows_ingested === 'number' && row.rows_ingested >= 3 &&
160
+ Array.isArray(row.output_files) && row.output_files.length > 0
161
+ })
162
+ if (!merged) await new Promise((r) => setTimeout(r, 1_000))
163
+ }
164
+ if (!merged) {
165
+ throw new Error('REST fetch did not produce a merged batch within 90s')
166
+ }
167
+
168
+ const { data: userSearch } = await api.datasets.createUserSearch(tenantSlug, datalakeSlug, 'customer', {
169
+ search_query: `ra.batch_id = '${ctx.batchId}'`,
170
+ })
171
+ const searchDeadline = Date.now() + 60_000
172
+ let rows: Array<Record<string, unknown>> = []
173
+ while (Date.now() < searchDeadline && rows.length < 3) {
174
+ const { data: page } = await api.datasets.search(tenantSlug, datalakeSlug, 'customer', {
175
+ userSearchId: userSearch.id!,
176
+ dataAccessMode: 'unregulated',
177
+ })
178
+ rows = (page.data ?? []) as Array<Record<string, unknown>>
179
+ if (rows.length < 3) await new Promise((r) => setTimeout(r, 1_000))
180
+ }
181
+ if (rows.length < 3) {
182
+ throw new Error(`expected 3 fetched customers, found ${rows.length}`)
183
+ }
184
+ ```
185
+
186
+ ## 007 — variant: an OAuth2 refresh-token tool
187
+
188
+ When the API uses OAuth2, the tool carries the full `oauth2_*` grant config
189
+ instead of a static token. The platform exchanges the `oauth2_refresh_token` for
190
+ an access token **server-side before each fetch** — your code never handles the
191
+ token. This step creates such a tool to show the body shape (the fetch itself
192
+ runs the same `runManually` flow once bound to a DAC + contract for that API).
193
+
194
+ ```typescript
195
+ const { data: oauthTool } = await api.tools.create(tenantSlug, datalakeSlug, {
196
+ name: `OAuth2 REST Tool ${runSuffix}`,
197
+ description: 'REST tool authenticating via an OAuth2 refresh-token grant.',
198
+ intent: 'data_exchange',
199
+ status: 'active',
200
+ datalake_id: ctx.datalakeId,
201
+ data_source_id: dataSourceId,
202
+ body: {
203
+ tool_body_type: 'rest_api',
204
+ auth_method: 'oauth2',
205
+ base_url: 'http://localhost:8080/quickbooks',
206
+ oauth2_grant_type: 'authorization_code',
207
+ oauth2_client_id: 'test_client_id',
208
+ oauth2_client_secret: 'test_client_secret',
209
+ oauth2_token_url: 'http://localhost:8080/quickbooks/oauth2/v1/tokens/bearer',
210
+ oauth2_refresh_token: 'test_initial_refresh_token',
211
+ oauth2_scope: 'com.intuit.quickbooks.accounting',
212
+ oauth2_token_ttl: 3300,
213
+ request_type: 'json',
214
+ response_type: 'json',
215
+ timeout_ms: 30_000,
216
+ },
217
+ })
218
+ if (!oauthTool.id) {
219
+ throw new Error('OAuth2 tool create did not return an id')
220
+ }
221
+ ```
222
+
223
+ # Gotchas
224
+
225
+ - **A draft tool is silently skipped at fetch time.** The worker only fetches
226
+ through `status: 'active'` tools — a `draft` tool produces zero log rows (no
227
+ error, no data). If a fetch ingests nothing, check the tool status first.
228
+ - **The `response_extractor` unwraps the API envelope.** REST APIs wrap rows in
229
+ an envelope (`{ data: [...] }`); the extractor (`{{ msg.data | to_json }}`)
230
+ turns that into the row array the contract maps. Without it, the whole envelope
231
+ is treated as one row.
232
+ - **OAuth2 token exchange is server-side.** With `auth_method: 'oauth2'`, the
233
+ platform swaps the refresh token for an access token before each call — your
234
+ code never sees a token. Bad credentials fail the exchange and produce zero log
235
+ rows (same silent-skip shape as a draft tool).
236
+ - **`runManually` returns immediately.** The `batch_id` comes back before the
237
+ fetch runs; poll the logs to know when rows actually landed.
238
+
239
+ # See also
240
+
241
+ - `data_activation_clients.md` §6.3 — `.runManually` + the pull-based reference
242
+ - `tools.md` — the `rest_api` tool body, `bearer` vs `oauth2` auth
243
+ - `_setup/accounts_receivable.md` — the bootstrap this walk starts from
244
+ - `integration-tests/tests/accounts_receivable/run-dac-fetch.test.ts`,
245
+ `integration-tests/tests/foundation/oauth2-dac-fetch.test.ts` — the green tests
246
+ these calls are lifted from