@alvera-ai/platform-sdk 0.21.1 → 0.21.2

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.
@@ -389,7 +389,7 @@ const { data } = await api.dataActivationClients.ingest(
389
389
  tenantSlug, datalakeSlug, activationClientSlug,
390
390
  { data: rowAsJson }, // a single row, shaped per the CSV/JSON the contracts expect
391
391
  )
392
- // data.batch_id — UUID; the row's batch identifier (use to find log rows)
392
+ // data.batch_id — UUID; the run's batch identifier (use to find the run's log rows)
393
393
  // data.key — storage key for the row's archived ndjson
394
394
  // data.jobs_count — number of downstream jobs the platform enqueued
395
395
  ```
@@ -400,8 +400,11 @@ been written, poll the logs (§6.4) or use the verification
400
400
  pattern (§6.6).
401
401
 
402
402
  The `batch_id` is the most important field on the response —
403
- every downstream log row and dataset row carries the same
404
- `batch_id`, so it's the join key for run-specific verification.
403
+ every log row the run writes carries it, and every dataset row the
404
+ run writes points at one of those log rows through
405
+ `alvera_data_activation_log_id`. A dataset row carries no
406
+ `batch_id` of its own, so the batch reaches the rows through the
407
+ log rows: that is the join for run-specific verification.
405
408
 
406
409
  ### 6.2 Bulk file ingestion — `.ingestFile`
407
410
 
@@ -490,8 +493,9 @@ batch_id UUID — the run's batch identifier
490
493
  dataset_table string — the target dataset/table name
491
494
  rows_ingested number — count of top-level rows the contract emitted
492
495
  (cascade rows carry 0)
493
- dataset_updated number — count of dataset rows whose batch_id this run wrote
494
- (load-bearing: see Gotcha 7)
496
+ dataset_updated number — count of dataset rows that point at this log row
497
+ (each row the run writes carries its log row's id in
498
+ alvera_data_activation_log_id; load-bearing: see Gotcha 7)
495
499
  output_files { object_key, mode }[] — merged ndjson archive refs, one per
496
500
  bucket; mode is 'raw' | 'tokenized' | 'redacted'
497
501
  (empty until the post-batch merge worker commits).
@@ -639,29 +643,32 @@ spends its full timeout and then reports nothing about the cause.
639
643
 
640
644
  ### 6.6 Verifying ingestion: the two-step dataset search
641
645
 
642
- > **Simplest landing check — `executeSql`.** Ingestion is async (the row
643
- > commits a moment after the `202`). When you just need to know a row
644
- > *landed*, poll `datalakes.executeSql` with a read-only `SELECT … WHERE
645
- > batch_id = '…'`: **each call is a fresh read, so there is no stale
646
- > snapshot to recover from** — unlike a `createUserSearch` created before
647
- > the row commits, which captures nothing and never updates. Gate on it,
648
- > then run the two-step `createUserSearch` below **once** (race-free) only
649
- > if you need the dataset's alias-join + tokenization semantics.
646
+ > **Simplest landing check — the run's own log rows.** Ingestion is async
647
+ > (the row commits a moment after the `202`). When you just need to know
648
+ > rows *landed*, read the run's log rows (§6.4) filtered by its
649
+ > `batch_id`. Every dataset row the run writes points at the log row for
650
+ > its table, and that log row's `dataset_updated` counts it — so the log
651
+ > rows answer the question with no query on the dataset table and no
652
+ > table name to know. A dataset row carries no `batch_id` of its own to
653
+ > filter on. **Each call is a fresh read, so there is no stale snapshot to
654
+ > recover from** — unlike a `createUserSearch` created before the row
655
+ > commits, which captures nothing and never updates. Gate on it, then run
656
+ > the two-step `createUserSearch` below **once** (race-free) only if you
657
+ > need the dataset's alias-join + tokenization semantics.
650
658
  >
651
659
  > ```typescript
652
- > const { data: probe } = await api.datalakes.executeSql(tenantSlug, datalakeSlug, {
653
- > sql: `SELECT 1 FROM ${tableName} WHERE batch_id = '${batchId}' LIMIT 1`,
654
- > mode: 'raw',
655
- > })
656
- > // `data` is an array-of-arrays (a `string` only for { format: 'csv' });
657
- > // a non-empty page proves the batch landed. The table is named the same
658
- > // in every lake — `mode` selects which copy answers, not which name.
659
- > const landed = typeof probe !== 'string' && probe.data.length > 0
660
+ > const { data: logs } = await api.dataActivationClients.logs.list(
661
+ > tenantSlug, datalakeSlug, activationClientSlug,
662
+ > { filters: [{ field: 'batch_id', op: '==', value: batchId }] },
663
+ > )
664
+ > // One log row per dataset table the run wrote into; each counts the rows
665
+ > // that point at it. A sum above zero proves the batch landed.
666
+ > const landed = (logs.data ?? []).reduce((n, log) => n + (log.dataset_updated ?? 0), 0) > 0
660
667
  > ```
661
668
  >
662
669
  > `createUserSearch` is the heavier, workflow-oriented surface — reach for
663
- > it when you need its alias joins (`ri.value`, tokenized read-back), not
664
- > for a plain "did it land" check.
670
+ > it when you need the rows themselves: its alias joins, tokenized
671
+ > read-back. Not for a plain "did it land" check.
665
672
 
666
673
  The canonical way to **prove a row landed in the datalake** is
667
674
  to query the dataset directly. This is a two-step pattern:
@@ -683,7 +690,8 @@ const { data: search } = await api.datasets.createUserSearch(
683
690
  'patient', // dataset name
684
691
  {
685
692
  search_query: `ri.value = '${externalPatientId}'` // SQL WHERE-clause fragment
686
- + ` AND le.batch_id = '${batchId}'`,
693
+ + ` AND le.alvera_data_activation_log_id IN`
694
+ + ` (SELECT id FROM data_activation_logs WHERE batch_id = '${batchId}')`,
687
695
  },
688
696
  )
689
697
  // search.id — UUID of the materialised result set
@@ -736,8 +744,46 @@ a patient or an appointment is now a generic table you declare, plus
736
744
  the legal entity its rows resolve to**; there is no dataset of that
737
745
  name to search. The four cookbooks all take this shape.
738
746
 
739
- The `<alias>.batch_id` predicate scopes a search to one run, which is
740
- the usual reason to reach for this surface at all.
747
+ Scoping a search to one run is the usual reason to reach for this
748
+ surface at all, and it goes **through the log row**. A row carries no
749
+ `batch_id`, `client_id`, `user_id` or `source_uri` of its own: it
750
+ carries `alvera_data_activation_log_id`, the `data_activation_logs` row
751
+ of the ingest that wrote it, and the batch lives there. So the
752
+ predicate is
753
+
754
+ ```
755
+ <alias>.alvera_data_activation_log_id IN
756
+ (SELECT id FROM data_activation_logs WHERE batch_id = '<batch_id>')
757
+ ```
758
+
759
+ The search runs with read access across the whole lake, so
760
+ `data_activation_logs` is readable from inside it. The pointer is null
761
+ on a row nothing ingested (a row an app wrote itself), so such a row
762
+ never matches a batch predicate.
763
+
764
+ #### Where a row came from, coming back
765
+
766
+ A searched row carries the pointer **and** the log row it points at,
767
+ loaded. Measured on a live run — one legal entity, one generic-table row
768
+ — the platform columns that come back are:
769
+
770
+ ```
771
+ id string
772
+ inserted_at / updated_at string
773
+ checksum string
774
+ checksum_changed? boolean (the key has the question mark)
775
+ legal_entity_id string (generic-table rows)
776
+ alvera_data_activation_log_id string (null on a row no ingest wrote)
777
+ alvera_data_activation_log object — the log row itself:
778
+ id, batch_id, client_id, user_id, source_uri, dataset_table,
779
+ rows_ingested, dataset_updated, status, error, input_files,
780
+ output_files, inserted_at, updated_at
781
+ ```
782
+
783
+ So "which batch wrote this row?" is `row.alvera_data_activation_log.batch_id`,
784
+ and "where did it come from?" is `row.alvera_data_activation_log.source_uri` —
785
+ there is no top-level `batch_id`, `client_id`, `user_id` or `source_uri` on
786
+ the row unless the table declared a column of that name itself.
741
787
 
742
788
  #### What a masked column looks like coming back
743
789
 
@@ -807,13 +853,13 @@ screen. Pick the mode by which of those two readers you are serving.
807
853
  pattern in §6.6) before treating the row as ingested.
808
854
 
809
855
  7. **`dataset_updated > 0` is the load-bearing freshness
810
- gate.** `rows_ingested` flips as soon as the per-row job
811
- completes, but the dataset row's `batch_id` column is
812
- updated by a downstream trigger that can lag. Filtering a
813
- `.createUserSearch` by `<base_alias>.batch_id = '<this>'`
814
- BEFORE that trigger fires returns an empty result set,
815
- and there's no way to refresh the materialised result —
816
- you'd have to POST a new search. Poll for
856
+ gate.** `rows_ingested` counts rows the contract emitted —
857
+ received, not persisted. `dataset_updated` counts the dataset
858
+ rows that point at the log row, so it moves only when a row
859
+ has been written. A `.createUserSearch` scoped to the batch
860
+ (§6.6) issued BEFORE the rows are written returns an empty
861
+ result set, and there's no way to refresh the materialised
862
+ result — you'd have to POST a new search. Poll for
817
863
  `dataset_updated > 0` before issuing the search.
818
864
 
819
865
  8. **`output_files` empty ≠ batch failed.** A log row with
@@ -168,6 +168,7 @@ On a `none` column it is documentation and costs nothing.
168
168
  datetime timestamp with timezone
169
169
  time time of day (no date component)
170
170
  jsonb arbitrary JSON document
171
+ uuid identifier; reads back as a uuid string, not raw bytes
171
172
  ```
172
173
 
173
174
  `decimal` and `float` are distinct on purpose: `decimal` emits a
@@ -297,21 +298,49 @@ data activation clients use as the upsert key.
297
298
 
298
299
  ### Reserved column names
299
300
 
300
- The platform reserves certain column names you cannot redeclare
301
- in `columns[N].name`:
301
+ The platform creates every generic table with the same seven base
302
+ columns, and they are yours to read but never to declare in
303
+ `columns[N].name`:
302
304
 
303
- The platform creates every generic table with the same base columns,
304
- and they are yours to read but never to declare:
305
+ ```
306
+ id uuid primary key
307
+ inserted_at timestamp
308
+ updated_at timestamp
309
+ checksum the row's content fingerprint
310
+ checksum_changed whether the last write changed it
311
+ legal_entity_id the subject the contract's MDM resolve stamped
312
+ alvera_data_activation_log_id the data_activation_logs row of the
313
+ ingest that wrote the row
314
+ ```
315
+
316
+ **Where the batch, the client, the user and the source went.** A row
317
+ does not carry them. It carries `alvera_data_activation_log_id`, a
318
+ pointer to the `data_activation_logs` row of the ingest that wrote it,
319
+ and the batch, the client, the user and the source live on that log
320
+ row. The pointer is **null on a row nothing ingested** — a row an app
321
+ wrote straight into the table. To pick out one ingest's rows in SQL, go
322
+ through the log row:
305
323
 
306
324
  ```
307
- id uuid primary key
308
- inserted_at timestamp
309
- updated_at timestamp
310
- batch_id the ingest run that wrote the row
311
- client_id the activation client that wrote it
312
- legal_entity_id the subject the contract's MDM resolve stamped
325
+ <table>.alvera_data_activation_log_id IN
326
+ (SELECT id FROM data_activation_logs WHERE batch_id = '<batch_id>')
313
327
  ```
314
328
 
329
+ Because those four are no longer platform columns, they are no longer
330
+ reserved: a table may declare its own `batch_id`, `client_id`,
331
+ `user_id` or `source_uri`, and it is plain data like any other column.
332
+
333
+ **`id` may be named once: inside the unique combination.** A
334
+ definition must name a unique combination, and a table is sometimes
335
+ unique only together with its id — say `(organization_id, id)`.
336
+ Declaring `id` with `is_unique: true` puts it in that combination and
337
+ adds no column (every table already has one). Declared any other way,
338
+ `id` is a reserved name like the rest. And it may join the combination,
339
+ never be the whole of it: create accepts a table whose only unique
340
+ column is `id`, but the column sync drops reserved names before it
341
+ reads the unique columns, finds none, and fails every migrate of the
342
+ lake, which stays at `new`. Mark a column of the table's own unique.
343
+
315
344
  **`legal_entity_id` is the one that catches people.** It is not a
316
345
  column you declare and fill — a contract's `mdm_input_config`
317
346
  resolves the subject and the platform stamps it. Declaring a column
@@ -298,8 +298,8 @@ in the datalake and there is nothing to search afterwards. The
298
298
  `upsert_into_datalake` step (the DB insert, routed by the
299
299
  contract's `resource_type` destination) runs only when the DAC
300
300
  worker ingests a row — i.e. on `dataActivationClients.ingest`, not
301
- on `.run`. To prove a row *landed*, ingest and then query by
302
- `batch_id` (see `data_activation_clients.md` §6.6).
301
+ on `.run`. To prove a row *landed*, ingest and then read the run's
302
+ log rows for its `batch_id` (see `data_activation_clients.md` §6.6).
303
303
 
304
304
  ```typescript
305
305
  const { data: result } = await api.interoperabilityContracts.run(
@@ -592,8 +592,8 @@ surface, and `workflows.md` for the workflow side of the same join.
592
592
  `'checking'` PASSES `.run(slug, row)` — the sandbox renders
593
593
  JSON without casting the enum — but is rejected at live
594
594
  ingest: the batch reports `rows_ingested` yet the row never
595
- persists (`dataset_updated` stays `0`; a `batch_id` query
596
- returns nothing). Sandbox-run (§7.4) proves the shape, not the
595
+ persists (`dataset_updated` stays `0`; no row points at the
596
+ batch's log row). Sandbox-run (§7.4) proves the shape, not the
597
597
  enum domain — ground every enum value against the platform
598
598
  schema or a known-good fixture, never intuition.
599
599
 
@@ -14,9 +14,11 @@ third-party APIs the platform integrates with — Athenahealth, Twilio, Mailgun,
14
14
  Stripe, SendGrid, HubSpot, QuickBooks, Airtable, Atomic FI, the LLM providers,
15
15
  and a small set of AWS endpoints MiniStack cannot serve.
16
16
 
17
- **MiniStack** serves AWS itself: `s3,lambda,sns,ssm,sqs,sts,logs,secretsmanager,cloudformation`
17
+ **MiniStack** serves AWS itself: `s3,lambda,sns,ssm,sqs,sts,logs,secretsmanager,cloudformation,rds,ses`
18
18
  (`local-dependencies.yml`). See [Datalakes](datalakes.md) for how datalake
19
- provisioning uses it.
19
+ provisioning uses it. An SES email tool whose endpoint URL is `http://localhost:4566`
20
+ sends into MiniStack, which lists every message it received at
21
+ `http://localhost:4566/_ministack/ses/messages`.
20
22
 
21
23
  The division is not arbitrary. No AWS emulator here serves
22
24
  `pinpoint-sms-voice-v2` — LocalStack Community answered 501 (Ultimate tier
@@ -90,7 +90,12 @@ trying to add to the catalog.
90
90
  sub-directories under that prefix ship the same basename (e.g.
91
91
  two vendors under `data_activation/interoperability/`),
92
92
  `metadataDetails` returns `409 Conflict` with the candidate
93
- paths; a missing basename returns `404`.
93
+ paths; a missing basename returns `404`. This bites status
94
+ templates: Mailgun and SES both ship an `email_message_status`
95
+ under `status_poller`, so the bare `email_message_status`
96
+ answers 409 — name the provider's path
97
+ (`status_poller/aws_ses/email_message_status`), as the bare
98
+ `sms_message_status` already required.
94
99
 
95
100
  2. **`output_schema` may be `null`.** Some templates render plain
96
101
  strings without structured output. Test for null before
package/.agent/tools.md CHANGED
@@ -92,8 +92,9 @@ the discriminator. Each branch is its own TypeScript type:
92
92
  tool_body_type = 'email' → dedicated email-provider body
93
93
  (Mailgun, SendGrid, SES,
94
94
  SMTP-bridge; optional
95
- endpoint_url overrides the
96
- provider base URL)
95
+ endpoint_url: for SES the
96
+ whole request URL, for
97
+ Mailgun the API base URL)
97
98
  — alternative to using
98
99
  rest_api for email intents
99
100
  ```
@@ -274,6 +275,15 @@ Each branch has its own required-list. Common patterns:
274
275
  probe; blank ⇒ each service resolves to
275
276
  its own regional AWS host)
276
277
 
278
+ email body, provider ses from_email, region, access_key_id +
279
+ secret_access_key (a long-lived IAM key:
280
+ there is no session-token field, so
281
+ temporary credentials cannot send);
282
+ optional endpoint_url — the WHOLE request
283
+ URL, e.g. a local AWS stand-in at
284
+ http://localhost:4566; blank ⇒
285
+ https://email.<region>.amazonaws.com
286
+
277
287
  sql_database body db_type, db_host, db_port, db_name,
278
288
  auth_method, db_username,
279
289
  db_password (or iam_role)
@@ -788,12 +798,10 @@ usable as a proof — three things to get right:
788
798
  asynchronously, so the response carries the PREVIOUS run's stamps.
789
799
  Capture `last_run_at` as a baseline BEFORE calling it, then poll the
790
800
  row until `last_run_at` advances past that baseline.
791
- - **`last_run_status` has three values.** `'ok'` means the run completed
792
- and read its whole time window; `'partial'` means the provider fetch was
793
- truncated, so the newest events may be missing and `last_run_error`
794
- carries the detail; `'error'` means the run failed. Only
795
- `last_run_status === 'ok'` proves a complete reconciliation — a
796
- `!== 'error'` check silently accepts a truncated `partial`.
801
+ - **`last_run_status` has two values.** `'ok'` means the run read its
802
+ whole time window; `'error'` means it stopped, and `last_run_error`
803
+ says why. There is no partial run: every page of the window is read,
804
+ one job per page, with no page cap.
797
805
  `last_run_events_found` counts the current run's events, and because
798
806
  `refresh` is async an early read can show an in-progress figure.
799
807
  - **`status` is the halt flag.** It is `'active'` normally and the
@@ -441,6 +441,17 @@ variable. Field paths match the columns of the workflow's
441
441
  For generic-table-bound workflows, `event_dataset` carries the
442
442
  table's columns directly (no separate `row` indirection).
443
443
 
444
+ **Where the row came from is on its log row, not on the row.** A row
445
+ carries no `batch_id`, `client_id`, `user_id` or `source_uri` of its
446
+ own: it points at the `data_activation_logs` row of the ingest that
447
+ wrote it, and templates read that log row loaded on the event —
448
+ `event_dataset.alvera_data_activation_log.source_uri`,
449
+ `event_dataset.alvera_data_activation_log.batch_id`. It is null when
450
+ the row came from no ingest (an app wrote it straight into its table),
451
+ so guard a template that reads it. A generic table may declare its
452
+ own column called `source_uri` or `batch_id`; then
453
+ `event_dataset.source_uri` is that column, plain data.
454
+
444
455
  ### `skip_mdm_resolution` is a deliberate choice, not a generic-table default
445
456
 
446
457
  `skip_mdm_resolution` (default `false`) opts the workflow out of the
@@ -566,8 +577,12 @@ reads as "leave it alone" on the wire and as "remove it" in the author's head.
566
577
  Caller-supplied fields **inside each `actions[*]`** row:
567
578
 
568
579
  ```
569
- action_type required enum — 'sms' | 'email' | 'voice' | 'data_exchange'
570
- tool_id required UUID — tool.intent must match action_type
580
+ action_type required enum — 'sms' | 'mms' | 'email' | 'voice'
581
+ | 'data_exchange' | 'data_activation'
582
+ tool_id UUID — tool.intent must match action_type. Required
583
+ for every action type EXCEPT
584
+ data_activation, which refuses one
585
+ (its client carries its own tool)
571
586
  decision_key required string — unique within the workflow
572
587
  trigger_template required string — schedule; 'now'/'' = immediate,
573
588
  else local ISO 8601 in datalake tz (§2)
@@ -585,6 +600,49 @@ Inside `tool_call`, the `sms_request`, `mms_request` and `email_request`
585
600
  variants also take `link_prefix` (**required**) and `link_validity_days`
586
601
  (optional, default 365) — see §7.
587
602
 
603
+ ### The `data_activation` action runs a client for the row
604
+
605
+ An action of type `data_activation` sends nothing. It runs a data
606
+ activation client — the one `client_slug` names, in the workflow's own
607
+ datalake — for the row that started the workflow. Its tool call is
608
+ `run_data_activation_client`, and it names no tool: the client carries
609
+ its own, so an action with a `tool_id` is refused.
610
+
611
+ ```typescript
612
+ import { ActionType } from '@alvera-ai/platform-sdk'
613
+
614
+ const fetchMemberAction = {
615
+ action_type: ActionType.DATA_ACTIVATION, // no tool_id: the client carries its own tool
616
+ decision_key: 'fetch_member',
617
+ trigger_template: 'now',
618
+ idempotency_template: '{{ event_dataset.message_id }}-fetch',
619
+ tool_call: {
620
+ tool_call_type: 'run_data_activation_client',
621
+ client_slug: 'fetch-member',
622
+ tool_call_override_template:
623
+ `{"tool_call_type": "sql_query", "query": {"type": "custom", "body": "SELECT * FROM inbound_messages WHERE message_id = '{{ event_dataset.message_id }}'"}}`,
624
+ },
625
+ }
626
+ ```
627
+
628
+ The questions a builder asks about it:
629
+
630
+ - **What does the override do?** `tool_call_override_template` is
631
+ Liquid rendered against the row (`event_dataset`) to the client's
632
+ WHOLE tool call as JSON — the same shape `.runManually` takes as its
633
+ one-shot override (`data_activation_clients.md` §6.3). An override of
634
+ the same `tool_call_type` as the client's stored call is merged over
635
+ it; one of another type replaces it.
636
+ - **Does it change the client?** No. The override applies to that one
637
+ run; the client's stored tool call — a scheduled query, say — is
638
+ untouched.
639
+ - **Two rows at once?** Each row's action is its own run of the client,
640
+ with its own rendered override, and runs beside any scheduled run
641
+ rather than merging into it.
642
+ - **How do I see what it did?** The client's run is an ordinary data
643
+ activation run: read it back through the client's runs and log rows
644
+ (`data_activation_clients.md` §6.4–§6.5).
645
+
588
646
  **Write-only (Request-only).** None.
589
647
 
590
648
  ## 5. Lifecycle
package/dist/index.d.mts CHANGED
@@ -679,7 +679,7 @@ type ActionStatusUpdaterResponse = {
679
679
  */
680
680
  readonly last_run_at?: string | null;
681
681
  /**
682
- * Why the last poll run failed, or why a `partial` run was truncated; null when the run completed and read its whole window
682
+ * Why the last poll run failed; null when the run completed and read its whole window
683
683
  */
684
684
  readonly last_run_error?: string | null;
685
685
  /**
@@ -687,9 +687,9 @@ type ActionStatusUpdaterResponse = {
687
687
  */
688
688
  readonly last_run_events_found?: number | null;
689
689
  /**
690
- * Outcome of the last poll run. `partial` means the run completed but its provider fetch was truncated, so the window was not fully read and the newest events may be missing — read `last_run_error` for detail.
690
+ * Outcome of the last poll run: `ok` when the run read its whole window, `error` when it stopped — read `last_run_error` for why. A run is never partial: every page of the window is read, one job per page.
691
691
  */
692
- readonly last_run_status?: 'ok' | 'partial' | 'error';
692
+ readonly last_run_status?: 'ok' | 'error';
693
693
  message_config: SimpleTemplateConfigResponse;
694
694
  /**
695
695
  * Updater name
@@ -990,6 +990,7 @@ declare enum ActionType {
990
990
  EMAIL = "email",
991
991
  VOICE = "voice",
992
992
  DATA_EXCHANGE = "data_exchange",
993
+ DATA_ACTIVATION = "data_activation",
993
994
  }
994
995
  /**
995
996
  * MDMVerifyRequest
@@ -1185,11 +1186,13 @@ type ActionResponse = {
1185
1186
  tool_call_type: 'aws_lambda_request';
1186
1187
  } & ActionAwsLambdaCallResponse) | ({
1187
1188
  tool_call_type: 'manual_upload';
1188
- } & ActionManualUploadCallResponse);
1189
+ } & ActionManualUploadCallResponse) | ({
1190
+ tool_call_type: 'run_data_activation_client';
1191
+ } & ActionRunDataActivationClientCallResponse);
1189
1192
  /**
1190
- * Tool that executes this action
1193
+ * Tool that executes this action. Required for every action type except data_activation, which refuses one: its client carries its own tool.
1191
1194
  */
1192
- tool_id: string;
1195
+ tool_id?: string | null;
1193
1196
  /**
1194
1197
  * Liquid template that determines when this action executes
1195
1198
  */
@@ -1663,6 +1666,21 @@ type SystemTemplateListResponse = {
1663
1666
  type DataActivationClientS3CallResponse = S3CallResponse & {
1664
1667
  tool_call_type: 's3_request';
1665
1668
  };
1669
+ /**
1670
+ * RunDataActivationClientCallRequest
1671
+ *
1672
+ * Runs a data activation client for the row that started the workflow, with the tool call the template renders for that row in place of the client's stored one. Request
1673
+ */
1674
+ type RunDataActivationClientCallRequest = {
1675
+ /**
1676
+ * Slug of the data activation client to run, in the workflow's datalake
1677
+ */
1678
+ client_slug: string;
1679
+ /**
1680
+ * Liquid template rendered against the row that started the workflow, to the client's whole tool call as JSON — the shape the run-manually endpoint takes. It replaces the client's stored tool call for this run only.
1681
+ */
1682
+ tool_call_override_template: string;
1683
+ };
1666
1684
  /**
1667
1685
  * EmailCallResponse
1668
1686
  *
@@ -1786,10 +1804,10 @@ type GenericTableListResponse = {
1786
1804
  /**
1787
1805
  * MMSCallResponse
1788
1806
  *
1789
- * MMS tool-call config — Liquid-templated recipient and body, plain public media URL, and the prefix its links go out on.
1807
+ * MMS tool-call config — Liquid-templated recipient, optional caption, plain public media URL, and the prefix its links go out on.
1790
1808
  */
1791
1809
  type MmsCallResponse = {
1792
- body: SimpleTemplateConfigResponse;
1810
+ body?: SimpleTemplateConfigResponse;
1793
1811
  /**
1794
1812
  * Address of the host that serves this action's links. Every URL in the body goes out as <link_prefix>/<token>. An absolute http or https URL, path allowed, with no query, fragment or trailing slash.
1795
1813
  */
@@ -2232,7 +2250,7 @@ type GenericTableColumnResponse = {
2232
2250
  /**
2233
2251
  * Column data type
2234
2252
  */
2235
- type: 'string' | 'integer' | 'float' | 'boolean' | 'date' | 'datetime' | 'time' | 'decimal' | 'jsonb';
2253
+ type: 'string' | 'integer' | 'float' | 'boolean' | 'date' | 'datetime' | 'time' | 'decimal' | 'jsonb' | 'uuid';
2236
2254
  };
2237
2255
  /**
2238
2256
  * DatasetSearchResponse
@@ -2373,6 +2391,12 @@ type AuthenticatedLinkUpdateMessageTrackingRequest = {
2373
2391
  type DataActivationClientSftpCallResponse = SftpCallResponse & {
2374
2392
  tool_call_type: 'sftp_request';
2375
2393
  };
2394
+ /**
2395
+ * ActionRunDataActivationClientCallResponse
2396
+ */
2397
+ type ActionRunDataActivationClientCallResponse = RunDataActivationClientCallResponse & {
2398
+ tool_call_type: 'run_data_activation_client';
2399
+ };
2376
2400
  /**
2377
2401
  * DataSourceRequest
2378
2402
  *
@@ -2615,6 +2639,21 @@ type AlveraApiError = {
2615
2639
  */
2616
2640
  mode?: string | null;
2617
2641
  };
2642
+ /**
2643
+ * RunDataActivationClientCallResponse
2644
+ *
2645
+ * Runs a data activation client for the row that started the workflow, with the tool call the template renders for that row in place of the client's stored one.
2646
+ */
2647
+ type RunDataActivationClientCallResponse = {
2648
+ /**
2649
+ * Slug of the data activation client to run, in the workflow's datalake
2650
+ */
2651
+ client_slug: string;
2652
+ /**
2653
+ * Liquid template rendered against the row that started the workflow, to the client's whole tool call as JSON — the shape the run-manually endpoint takes. It replaces the client's stored tool call for this run only.
2654
+ */
2655
+ tool_call_override_template: string;
2656
+ };
2618
2657
  /**
2619
2658
  * CloudStorageAwsResponse
2620
2659
  *
@@ -3438,7 +3477,7 @@ type EmailResponse = {
3438
3477
  */
3439
3478
  domain?: string;
3440
3479
  /**
3441
- * Custom Mailgun API base URL (e.g., https://api.eu.mailgun.net/v3 for EU domains, or a WireMock endpoint for integration tests); leave blank for real Mailgun
3480
+ * Custom provider endpoint; leave blank for the real provider. For SES, the whole request URL (e.g., http://localhost:4566 for MiniStack); blank means https://email.<region>.amazonaws.com. For Mailgun, the API base URL (e.g., https://api.eu.mailgun.net/v3 for EU domains, or a WireMock endpoint for integration tests)
3442
3481
  */
3443
3482
  endpoint_url?: string | null;
3444
3483
  /**
@@ -4205,7 +4244,7 @@ type GenericTableColumnRequest = {
4205
4244
  /**
4206
4245
  * Column data type
4207
4246
  */
4208
- type: 'string' | 'integer' | 'float' | 'boolean' | 'date' | 'datetime' | 'time' | 'decimal' | 'jsonb';
4247
+ type: 'string' | 'integer' | 'float' | 'boolean' | 'date' | 'datetime' | 'time' | 'decimal' | 'jsonb' | 'uuid';
4209
4248
  };
4210
4249
  /**
4211
4250
  * CloudWatchQueryResponse
@@ -4738,10 +4777,10 @@ type InteroperabilityContractAiAgentRequestWritable = {
4738
4777
  /**
4739
4778
  * MMSCallRequest
4740
4779
  *
4741
- * MMS tool-call config — Liquid-templated recipient and body, plain public media URL, and the prefix its links go out on. Request
4780
+ * MMS tool-call config — Liquid-templated recipient, optional caption, plain public media URL, and the prefix its links go out on. Request
4742
4781
  */
4743
4782
  type MmsCallRequestWritable = {
4744
- body: SimpleTemplateConfigRequest;
4783
+ body?: SimpleTemplateConfigRequest;
4745
4784
  /**
4746
4785
  * Address of the host that serves this action's links. Every URL in the body goes out as <link_prefix>/<token>. An absolute http or https URL, path allowed, with no query, fragment or trailing slash.
4747
4786
  */
@@ -4973,11 +5012,13 @@ type ActionRequestWritable = {
4973
5012
  tool_call_type: 'aws_lambda_request';
4974
5013
  } & ActionAwsLambdaCallRequestWritable) | ({
4975
5014
  tool_call_type: 'manual_upload';
4976
- } & ActionManualUploadCallRequest);
5015
+ } & ActionManualUploadCallRequest) | ({
5016
+ tool_call_type: 'run_data_activation_client';
5017
+ } & ActionRunDataActivationClientCallRequestWritable);
4977
5018
  /**
4978
- * Tool that executes this action
5019
+ * Tool that executes this action. Required for every action type except data_activation, which refuses one: its client carries its own tool.
4979
5020
  */
4980
- tool_id: string;
5021
+ tool_id?: string | null;
4981
5022
  /**
4982
5023
  * Liquid template that determines when this action executes
4983
5024
  */
@@ -5098,7 +5139,7 @@ type EmailRequestWritable = {
5098
5139
  */
5099
5140
  domain?: string;
5100
5141
  /**
5101
- * Custom Mailgun API base URL (e.g., https://api.eu.mailgun.net/v3 for EU domains, or a WireMock endpoint for integration tests); leave blank for real Mailgun
5142
+ * Custom provider endpoint; leave blank for the real provider. For SES, the whole request URL (e.g., http://localhost:4566 for MiniStack); blank means https://email.<region>.amazonaws.com. For Mailgun, the API base URL (e.g., https://api.eu.mailgun.net/v3 for EU domains, or a WireMock endpoint for integration tests)
5102
5143
  */
5103
5144
  endpoint_url?: string | null;
5104
5145
  /**
@@ -5794,6 +5835,12 @@ type SftpRequestWritable = {
5794
5835
  */
5795
5836
  user_name: string;
5796
5837
  };
5838
+ /**
5839
+ * ActionRunDataActivationClientCallRequest
5840
+ */
5841
+ type ActionRunDataActivationClientCallRequestWritable = RunDataActivationClientCallRequest & {
5842
+ tool_call_type: 'run_data_activation_client';
5843
+ };
5797
5844
  /**
5798
5845
  * CloudStorageCustomRequest
5799
5846
  *