@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.
- package/.agent/AGENTS.md +499 -0
- package/.agent/account_management.md +456 -0
- package/.agent/action_status_updaters.md +264 -0
- package/.agent/ai_agents.md +462 -0
- package/.agent/ai_sandbox.md +265 -0
- package/.agent/async.md +112 -0
- package/.agent/connected_apps.md +408 -0
- package/.agent/cookbook/_fixtures/README.md +106 -0
- package/.agent/cookbook/_fixtures/accounts_receivable/_customers_accounts_receivable_customer.liquid +32 -0
- package/.agent/cookbook/_fixtures/accounts_receivable/_customers_accounts_receivable_mdm.liquid +20 -0
- package/.agent/cookbook/_fixtures/accounts_receivable/stripe_customers_batch1.csv +5 -0
- package/.agent/cookbook/_fixtures/foundation/_lead_submissions_foundation_generic_table.liquid +33 -0
- package/.agent/cookbook/_fixtures/foundation/_lead_submissions_foundation_legal_entity.liquid +88 -0
- package/.agent/cookbook/_fixtures/foundation/_lead_submissions_foundation_mdm.liquid +48 -0
- package/.agent/cookbook/_fixtures/healthcare/_cahps_appointments_healthcare_appointment.liquid +47 -0
- package/.agent/cookbook/_fixtures/healthcare/_cahps_appointments_healthcare_mdm.liquid +24 -0
- package/.agent/cookbook/_fixtures/healthcare/_cahps_appointments_healthcare_patient.liquid +38 -0
- package/.agent/cookbook/_fixtures/healthcare/memorandum-of-association-01.png +0 -0
- package/.agent/cookbook/_fixtures/healthcare/sample_two_page.pdf +43 -0
- package/.agent/cookbook/_fixtures/payment_risk/_compliance_screenings_payment_risk_compliance_screening.liquid +59 -0
- package/.agent/cookbook/_fixtures/payment_risk/_compliance_screenings_payment_risk_mdm.liquid +36 -0
- package/.agent/cookbook/_fixtures/payment_risk/_payment_accounts_payment_risk_mdm.liquid +30 -0
- package/.agent/cookbook/_fixtures/payment_risk/_payment_accounts_payment_risk_payment_account.liquid +55 -0
- package/.agent/cookbook/_setup/accounts_receivable.md +282 -0
- package/.agent/cookbook/_setup/foundation.md +277 -0
- package/.agent/cookbook/_setup/healthcare.md +279 -0
- package/.agent/cookbook/_setup/payment_risk.md +283 -0
- package/.agent/cookbook/action-status-updaters.md +212 -0
- package/.agent/cookbook/ai-agent-invoke.md +243 -0
- package/.agent/cookbook/appointment-review-sms-workflow.md +761 -0
- package/.agent/cookbook/birthday-greeting-sms-trigger.md +656 -0
- package/.agent/cookbook/bulk-ingest.md +254 -0
- package/.agent/cookbook/contact-us-triage-with-llm.md +622 -0
- package/.agent/cookbook/custom-tables.md +201 -0
- package/.agent/cookbook/dunning-sms-for-delinquent.md +619 -0
- package/.agent/cookbook/invite-team.md +194 -0
- package/.agent/cookbook/kyc-notification-on-account-activation.md +619 -0
- package/.agent/cookbook/rest-fetch.md +246 -0
- package/.agent/cookbook/sanctions-screening-with-agent-review.md +733 -0
- package/.agent/cookbook/score-leads-with-llm-categorization.md +624 -0
- package/.agent/cookbook/system-templates.md +129 -0
- package/.agent/cookbook/triage-prospects-by-priority.md +533 -0
- package/.agent/cookbook/welcome-sms-for-customers.md +607 -0
- package/.agent/data_activation_clients.md +559 -0
- package/.agent/data_sources.md +235 -0
- package/.agent/datalakes.md +714 -0
- package/.agent/debugging.md +137 -0
- package/.agent/errors.md +190 -0
- package/.agent/generic_tables.md +351 -0
- package/.agent/interoperability_contracts.md +417 -0
- package/.agent/mdm.md +293 -0
- package/.agent/mutations.md +126 -0
- package/.agent/templates.md +98 -0
- package/.agent/tool-call-configs.md +90 -0
- package/.agent/tools.md +547 -0
- package/.agent/type_naming.md +129 -0
- package/.agent/workflows.md +617 -0
- package/README.md +178 -0
- package/dist/bin/platform-sdk.d.mts +1 -0
- package/dist/bin/platform-sdk.mjs +106 -0
- package/dist/bin/platform-sdk.mjs.map +1 -0
- package/dist/index.d.mts +1310 -44116
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1194 -7344
- package/dist/index.mjs.map +1 -1
- 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
|