@alvera-ai/platform-sdk 0.15.0 → 0.16.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.
- package/.agent/account_management.md +10 -0
- package/.agent/ai_agents.md +1 -0
- package/.agent/cookbook/_setup/foundation.md +62 -0
- package/.agent/cookbook/_setup/healthcare.md +62 -0
- package/.agent/cookbook/_setup/payments.md +62 -0
- package/.agent/cookbook/_setup/subscription.md +62 -0
- package/.agent/cookbook/appointment-review-sms-workflow.md +16 -8
- package/.agent/cookbook/birthday-greeting-sms-trigger.md +11 -5
- package/.agent/cookbook/contact-us-triage-with-llm.md +11 -5
- package/.agent/cookbook/dunning-sms-for-delinquent.md +16 -8
- package/.agent/cookbook/kyc-notification-on-account-activation.md +16 -8
- package/.agent/cookbook/marketing-campaign-send.md +13 -7
- package/.agent/cookbook/sanctions-screening-with-agent-review.md +11 -5
- package/.agent/cookbook/score-leads-with-llm-categorization.md +11 -5
- package/.agent/cookbook/triage-prospects-by-priority.md +11 -5
- package/.agent/cookbook/welcome-sms-for-customers.md +16 -8
- package/.agent/errors.md +7 -1
- package/.agent/interoperability_contracts.md +58 -0
- package/.agent/tools.md +140 -4
- package/.agent/workflows.md +137 -38
- package/dist/index.d.mts +974 -151
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +138 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/.agent/workflows.md
CHANGED
|
@@ -49,6 +49,7 @@ const { data: created } = await api.workflows.create(
|
|
|
49
49
|
description: 'Send a review-request SMS after a fulfilled appointment',
|
|
50
50
|
dataset_type: 'appointment', // the dataset this workflow listens on
|
|
51
51
|
status: 'live', // 'live' | 'draft' | 'manual' (see §5)
|
|
52
|
+
tags: ['appointments', 'sms'], // REQUIRED on every write — [] if untagged (see §2)
|
|
52
53
|
filter_config: {
|
|
53
54
|
type: 'custom',
|
|
54
55
|
body: `{% if appointment.source_uri == "12345.example.com" %}true{% endif %}`,
|
|
@@ -138,6 +139,25 @@ authors are Complex) — don't assume every `{ type, body }`-shaped config
|
|
|
138
139
|
also carries a request-writable `output_schema`; check whether the field
|
|
139
140
|
is pinned or caller-authored for that specific config before authoring one.
|
|
140
141
|
|
|
142
|
+
### `tags` is required on every write, and has no default
|
|
143
|
+
|
|
144
|
+
`tags` is a `string[]` on both the request and the response, and it is
|
|
145
|
+
in `required:`. **Every `create()` / `update()` without it is a 422.**
|
|
146
|
+
Send `tags: []` for an untagged workflow — the empty array is how you
|
|
147
|
+
say "no tags", not something you can omit.
|
|
148
|
+
|
|
149
|
+
The missing default is deliberate. `update()` is full-replacement (there
|
|
150
|
+
is no PATCH, and no add-tag / remove-tag endpoint), so an omitted key
|
|
151
|
+
cannot mean "leave tags alone". With a default, omitting it would have
|
|
152
|
+
silently emptied a workflow's tags and returned 200. A 422 beats losing
|
|
153
|
+
state the caller never mentioned.
|
|
154
|
+
|
|
155
|
+
The labels are free text — no taxonomy, no shared vocabulary, and the
|
|
156
|
+
execution pipeline does not read them. But they **do** participate in the
|
|
157
|
+
workflow checksum, so retagging shifts the drift fingerprint. Re-tag and
|
|
158
|
+
`checksum()` returns a different value even though nothing executable
|
|
159
|
+
changed.
|
|
160
|
+
|
|
141
161
|
### `update()` replaces inline arrays whole
|
|
142
162
|
|
|
143
163
|
`actions` and `context_datasets` are replaced whole by `update()`.
|
|
@@ -164,17 +184,34 @@ The placeholder names depend on the workflow's `dataset_type`
|
|
|
164
184
|
appointment-driven workflow, customer_id + invoice.id for an
|
|
165
185
|
invoice-driven workflow, etc.).
|
|
166
186
|
|
|
167
|
-
###
|
|
187
|
+
### Two scheduling levers, and they answer different questions
|
|
188
|
+
|
|
189
|
+
There are **two**, and picking the wrong one is the most common mistake
|
|
190
|
+
here:
|
|
191
|
+
|
|
192
|
+
| lever | lives on | answers |
|
|
193
|
+
|---|---|---|
|
|
194
|
+
| `scheduled_at` | the **run** (`.run` body, §5) | *when is this send going out?* |
|
|
195
|
+
| `trigger_template` | each **action** (below) | *when does this action fire, relative to the run?* |
|
|
196
|
+
|
|
197
|
+
**`scheduled_at` is the one you want for a campaign.** A send time is
|
|
198
|
+
per-send, not per-workflow: "this campaign goes Tuesday 9am" is a
|
|
199
|
+
property of *this* send, and the next one goes at a different time. Pass
|
|
200
|
+
`scheduled_at` on the run and leave the workflow definition alone.
|
|
168
201
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
202
|
+
**`trigger_template` is a property of the workflow itself** — "the
|
|
203
|
+
reminder goes 24h before the appointment", "the follow-up goes 3 days
|
|
204
|
+
after signup". It is relative to the run and belongs in the definition
|
|
205
|
+
because it is true of every run.
|
|
206
|
+
|
|
207
|
+
Use `trigger_template` for a campaign send time and you are rewriting the
|
|
208
|
+
workflow before every send — a full `update()` per campaign, since
|
|
209
|
+
`update()` replaces the resource whole. That was the only option before
|
|
210
|
+
runs became a resource, and it is no longer the right answer.
|
|
211
|
+
|
|
212
|
+
Neither lever is a guarantee of the instant a message leaves: the
|
|
213
|
+
action window / quiet hours (below) can push an action later, and a
|
|
214
|
+
`runtime_filter` can drop it entirely.
|
|
178
215
|
|
|
179
216
|
### `trigger_template` controls scheduling
|
|
180
217
|
|
|
@@ -322,6 +359,7 @@ are removed, and re-supplied ones are repositioned.
|
|
|
322
359
|
await api.workflows.create(tenantSlug, datalakeSlug, {
|
|
323
360
|
name: 'Contact-Us Triage',
|
|
324
361
|
dataset_type: 'generic_table',
|
|
362
|
+
tags: [],
|
|
325
363
|
// …other workflow fields…
|
|
326
364
|
workflow_ai_agents: [
|
|
327
365
|
{
|
|
@@ -412,6 +450,8 @@ name required string
|
|
|
412
450
|
description optional string
|
|
413
451
|
dataset_type required string — the dataset this listens on
|
|
414
452
|
status required enum — 'live' | 'draft' | 'manual' (§5 Create)
|
|
453
|
+
tags required array — string[]; NO default, send [] if
|
|
454
|
+
untagged; feeds the checksum (§2)
|
|
415
455
|
generic_table_id optional UUID — required iff dataset_type='generic_table'
|
|
416
456
|
skip_mdm_resolution optional bool — default false; a deliberate
|
|
417
457
|
choice, NOT a generic-table default (§3)
|
|
@@ -486,39 +526,95 @@ are all `workflow_slug`-keyed). Passing a slug to `.get` 422s with
|
|
|
486
526
|
|
|
487
527
|
### Run
|
|
488
528
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
`trigger_template` (§2): `.run` fires the pipeline now; the actions it
|
|
493
|
-
matches wait for their own trigger instants.
|
|
529
|
+
**`.run` does not run the workflow. It records a run and returns.**
|
|
530
|
+
The work happens when that run *fires* — immediately if you did not ask
|
|
531
|
+
for a time, or at `scheduled_at` if you did.
|
|
494
532
|
|
|
495
533
|
```typescript
|
|
496
534
|
const { data: run } = await api.workflows.run(
|
|
497
|
-
tenantSlug, workflowSlug,
|
|
535
|
+
tenantSlug, datalakeSlug, workflowSlug,
|
|
498
536
|
{
|
|
499
537
|
sql_where_clause: `ra.id = '${appointmentId}'`, // run against the regulated tables
|
|
500
538
|
mode: 'live', // 'live' | 'dry_run'
|
|
501
539
|
manual_override: false, // see below
|
|
540
|
+
scheduled_at: '2026-08-12T09:00:00+01:00', // OPTIONAL — see below
|
|
502
541
|
},
|
|
503
542
|
)
|
|
504
|
-
// run.
|
|
505
|
-
//
|
|
506
|
-
//
|
|
507
|
-
//
|
|
508
|
-
// run.
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
543
|
+
// run.workflow_run_id — UUID of the run. This is the handle for
|
|
544
|
+
// everything else: get, cancel, and the ids below.
|
|
545
|
+
// run.status — 'scheduled' | 'processing' | 'completed'
|
|
546
|
+
// | 'cancelled' | 'failed'
|
|
547
|
+
// run.scheduled_at — when it will fire (always present; "now" is a
|
|
548
|
+
// run scheduled for now, not a second code path)
|
|
549
|
+
// run.matched_count — nullable PREVIEW, see the warning below
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
**`batch_id`, `workflow_run_log_id` and `enqueued_count` are NOT on this
|
|
553
|
+
response.** They described work that had not happened yet: the segment is
|
|
554
|
+
resolved when the run fires, not when it is scheduled, so a run scheduled
|
|
555
|
+
for Friday reaches Friday's rows. `batch_id` and `workflow_run_log_id`
|
|
556
|
+
become readable from `workflowRuns.get` once the run has fired (below);
|
|
557
|
+
`enqueued_count` has no replacement — count the rows the fan-out actually
|
|
558
|
+
produced via `workflowLogs`, because a count column can claim three while
|
|
559
|
+
nothing was inserted, and rows cannot.
|
|
560
|
+
|
|
561
|
+
**`matched_count` is a preview, not the audience.** It is what the clause
|
|
562
|
+
matched at schedule time, for sanity-checking the clause you just wrote.
|
|
563
|
+
The segment is resolved *again* at send time with suppressed records
|
|
564
|
+
excluded, so the number that actually goes out can be smaller. Never
|
|
565
|
+
report `matched_count` to a user as "this many people will receive it".
|
|
566
|
+
|
|
567
|
+
#### Scheduling a run
|
|
568
|
+
|
|
569
|
+
`scheduled_at` is an ISO-8601 timestamp **with an offset**
|
|
570
|
+
(`2026-08-12T09:00:00+01:00`). Omit it, or pass `null`, and the run fires
|
|
571
|
+
as soon as a worker picks it up.
|
|
572
|
+
|
|
573
|
+
It is **not** a guarantee of send time. Each action still passes through
|
|
574
|
+
the workflow's action window and its own `trigger_template` (§2), so an
|
|
575
|
+
action may execute later than the run fires — `scheduled_at` says when the
|
|
576
|
+
segment is resolved and the fan-out begins, nothing more.
|
|
577
|
+
|
|
578
|
+
#### Reading a run back — and the trap in it
|
|
579
|
+
|
|
580
|
+
```typescript
|
|
581
|
+
const { data: state } = await api.workflowRuns.get(tenantSlug, datalakeSlug, runId)
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
Poll until **`workflow_run_log_id` is a string** — *not* until `status`
|
|
585
|
+
leaves `'scheduled'`. Those are different moments: the run reaches
|
|
586
|
+
`processing` first and writes `workflow_run_log_id` and `batch_id` a beat
|
|
587
|
+
later. A predicate on status alone releases you to read a `null`, and
|
|
588
|
+
because `typeof null === 'object'` in JavaScript the symptom is a baffling
|
|
589
|
+
*"expected string, got object"* rather than an obvious nil.
|
|
590
|
+
|
|
591
|
+
Raise on `status === 'failed'` carrying `failure_reason` instead of
|
|
592
|
+
polling to your deadline — a caller blocked on a run that will never fire
|
|
593
|
+
should learn why on the first read, not thirty seconds later behind a
|
|
594
|
+
generic timeout.
|
|
595
|
+
|
|
596
|
+
**The HTTP 200 from `.run` means the run was *accepted*, not that you know
|
|
597
|
+
its outcome.** For the actual truth, poll the run to get its
|
|
598
|
+
`workflow_run_log_id`, then read `batchLogs` + `workflowLogs` and the
|
|
599
|
+
action's own status. Never branch logic on the immediate response body.
|
|
600
|
+
|
|
601
|
+
#### Listing and cancelling
|
|
602
|
+
|
|
603
|
+
```typescript
|
|
604
|
+
await api.workflowRuns.list(tenantSlug, datalakeSlug, { page: 1, page_size: 50 })
|
|
605
|
+
await api.workflowRuns.cancel(tenantSlug, datalakeSlug, runId)
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
`list` is **datalake-scoped, not workflow-scoped** — it answers "what is
|
|
609
|
+
going out from this datalake", across every workflow, which is the question
|
|
610
|
+
a campaign screen actually asks. It takes the full Flop query surface
|
|
611
|
+
(`page`, `page_size`, `order_by`, `order_directions`, `filters`).
|
|
612
|
+
|
|
613
|
+
`cancel` is **refused once a run reaches `processing`**: the fan-out has
|
|
614
|
+
begun and a job already executing cannot be stopped. Cancelling races the
|
|
615
|
+
worker — both sides contend on one conditional update, so exactly one wins
|
|
616
|
+
and the loser is told. Treat a refusal as a normal outcome to surface, not
|
|
617
|
+
an error to retry.
|
|
522
618
|
|
|
523
619
|
`sql_where_clause` accepts a fragment against the dataset's
|
|
524
620
|
regulated tables (`ra` for appointments, `rp` for patients,
|
|
@@ -536,9 +632,11 @@ rejects stays rejected. (The platform pipeline discards the flag before
|
|
|
536
632
|
filter and decision evaluation — override is not a "force through"
|
|
537
633
|
switch.)
|
|
538
634
|
|
|
539
|
-
A zero-match `sql_where_clause` still returns 200 and still
|
|
540
|
-
|
|
541
|
-
`workflow_run_log_id` uniformly, no special empty case.
|
|
635
|
+
A zero-match `sql_where_clause` still returns 200 and still records a run
|
|
636
|
+
that fires and writes a batch log row — poll `workflowRuns.get` for the
|
|
637
|
+
`workflow_run_log_id` uniformly, no special empty case. At schedule time a
|
|
638
|
+
zero-match clause reports `matched_count: 0`, but see the warning above:
|
|
639
|
+
that is a preview of the clause, not proof that nothing was sent.
|
|
542
640
|
|
|
543
641
|
### Execute (per-row, per-action)
|
|
544
642
|
|
|
@@ -822,7 +920,8 @@ the datalake (live dispatch only):
|
|
|
822
920
|
| enrichment | runs | runs |
|
|
823
921
|
| action `runtime_filter` | applies | applies |
|
|
824
922
|
| `manual_override` | dedupe/idempotency bypass only | same |
|
|
825
|
-
| answers | `
|
|
923
|
+
| answers | `workflow_run_id`, `status`, `scheduled_at`, `matched_count` (the ids follow from `workflowRuns.get` once it fires) | `workflow_execution_log_id`, always `status: 'pending'` |
|
|
924
|
+
| runs when | the run fires — now, or at `scheduled_at` | immediately |
|
|
826
925
|
|
|
827
926
|
### The three states a send can be in
|
|
828
927
|
|