@alvera-ai/platform-sdk 0.17.0 → 0.18.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 (83) hide show
  1. package/.agent/AGENTS.md +82 -144
  2. package/.agent/account_management.md +2 -2
  3. package/.agent/action_logs.md +4 -4
  4. package/.agent/ai_agents.md +28 -21
  5. package/.agent/ai_sandbox.md +49 -39
  6. package/.agent/connected_apps.md +3 -3
  7. package/.agent/cookbook/_fixtures/README.md +1 -1
  8. package/.agent/cookbook/_fixtures/{foundation → organic-marketing}/_lead_submissions_foundation_generic_table.liquid +1 -1
  9. package/.agent/cookbook/_fixtures/organic-marketing/_lead_submissions_foundation_legal_entity.liquid +80 -0
  10. package/.agent/cookbook/_fixtures/{foundation → organic-marketing}/_lead_submissions_foundation_mdm.liquid +2 -1
  11. package/.agent/cookbook/_fixtures/payments-compliance/_compliance_screenings_generic_table.liquid +57 -0
  12. package/.agent/cookbook/_fixtures/payments-compliance/_compliance_screenings_legal_entity.liquid +30 -0
  13. package/.agent/cookbook/_fixtures/payments-compliance/_compliance_screenings_mdm.liquid +44 -0
  14. package/.agent/cookbook/_fixtures/payments-compliance/_payment_accounts_generic_table.liquid +57 -0
  15. package/.agent/cookbook/_fixtures/payments-compliance/_payment_accounts_legal_entity.liquid +36 -0
  16. package/.agent/cookbook/_fixtures/payments-compliance/_payment_accounts_mdm.liquid +41 -0
  17. package/.agent/cookbook/_fixtures/primary-care-feedback/_cahps_appointments_generic_table.liquid +70 -0
  18. package/.agent/cookbook/_fixtures/primary-care-feedback/_cahps_appointments_legal_entity.liquid +52 -0
  19. package/.agent/cookbook/_fixtures/primary-care-feedback/_cahps_appointments_mdm.liquid +42 -0
  20. package/.agent/cookbook/_fixtures/subscription-saas/_customers_subscription_generic_table.liquid +38 -0
  21. package/.agent/cookbook/_fixtures/subscription-saas/_customers_subscription_legal_entity.liquid +48 -0
  22. package/.agent/cookbook/_fixtures/subscription-saas/_customers_subscription_mdm.liquid +49 -0
  23. package/.agent/cookbook/organic-marketing.md +2801 -0
  24. package/.agent/cookbook/payments-compliance.md +2180 -0
  25. package/.agent/cookbook/primary-care.md +2175 -0
  26. package/.agent/cookbook/subscription-saas.md +2403 -0
  27. package/.agent/data_activation_clients.md +65 -52
  28. package/.agent/datalakes.md +338 -171
  29. package/.agent/errors.md +3 -3
  30. package/.agent/generic_tables.md +151 -62
  31. package/.agent/interoperability_contracts.md +57 -22
  32. package/.agent/mdm.md +136 -153
  33. package/.agent/messages.md +36 -34
  34. package/.agent/mock-services.md +1 -1
  35. package/.agent/mutations.md +2 -2
  36. package/.agent/templates.md +14 -13
  37. package/.agent/tools.md +63 -21
  38. package/.agent/type_naming.md +13 -13
  39. package/.agent/workflows.md +99 -53
  40. package/README.md +2 -2
  41. package/dist/bin/platform-sdk.mjs +33 -47
  42. package/dist/bin/platform-sdk.mjs.map +1 -1
  43. package/dist/index.d.mts +565 -379
  44. package/dist/index.d.mts.map +1 -1
  45. package/dist/index.mjs +494 -59
  46. package/dist/index.mjs.map +1 -1
  47. package/package.json +4 -3
  48. package/.agent/cookbook/_fixtures/foundation/_lead_submissions_foundation_legal_entity.liquid +0 -88
  49. package/.agent/cookbook/_fixtures/healthcare/_cahps_appointments_healthcare_appointment.liquid +0 -47
  50. package/.agent/cookbook/_fixtures/healthcare/_cahps_appointments_healthcare_mdm.liquid +0 -24
  51. package/.agent/cookbook/_fixtures/healthcare/_cahps_appointments_healthcare_patient.liquid +0 -38
  52. package/.agent/cookbook/_fixtures/payments/_compliance_screenings_payments_compliance_screening.liquid +0 -59
  53. package/.agent/cookbook/_fixtures/payments/_compliance_screenings_payments_mdm.liquid +0 -36
  54. package/.agent/cookbook/_fixtures/payments/_payment_accounts_payments_mdm.liquid +0 -30
  55. package/.agent/cookbook/_fixtures/payments/_payment_accounts_payments_payment_account.liquid +0 -55
  56. package/.agent/cookbook/_fixtures/subscription/_customers_subscription_mdm.liquid +0 -20
  57. package/.agent/cookbook/_setup/foundation.md +0 -359
  58. package/.agent/cookbook/_setup/healthcare.md +0 -361
  59. package/.agent/cookbook/_setup/payments.md +0 -365
  60. package/.agent/cookbook/_setup/subscription.md +0 -364
  61. package/.agent/cookbook/action-status-updaters.md +0 -278
  62. package/.agent/cookbook/ai-agent-invoke.md +0 -279
  63. package/.agent/cookbook/appointment-review-sms-workflow.md +0 -801
  64. package/.agent/cookbook/birthday-greeting-sms-trigger.md +0 -696
  65. package/.agent/cookbook/bulk-ingest.md +0 -302
  66. package/.agent/cookbook/contact-us-triage-with-llm.md +0 -663
  67. package/.agent/cookbook/dunning-sms-for-delinquent.md +0 -659
  68. package/.agent/cookbook/generic-tables.md +0 -244
  69. package/.agent/cookbook/invite-team.md +0 -200
  70. package/.agent/cookbook/kyc-notification-on-account-activation.md +0 -661
  71. package/.agent/cookbook/marketing-campaign-send.md +0 -1044
  72. package/.agent/cookbook/paginated-restapi-poller.md +0 -383
  73. package/.agent/cookbook/rest-fetch.md +0 -273
  74. package/.agent/cookbook/sanctions-screening-with-agent-review.md +0 -773
  75. package/.agent/cookbook/score-leads-with-llm-categorization.md +0 -665
  76. package/.agent/cookbook/system-templates.md +0 -165
  77. package/.agent/cookbook/talk-to-data.md +0 -178
  78. package/.agent/cookbook/triage-prospects-by-priority.md +0 -571
  79. package/.agent/cookbook/welcome-sms-for-customers.md +0 -647
  80. /package/.agent/cookbook/_fixtures/{healthcare → primary-care-feedback}/memorandum-of-association-01.png +0 -0
  81. /package/.agent/cookbook/_fixtures/{healthcare → primary-care-feedback}/sample_two_page.pdf +0 -0
  82. /package/.agent/cookbook/_fixtures/{subscription → subscription-saas}/_customers_subscription_customer.liquid +0 -0
  83. /package/.agent/cookbook/_fixtures/{subscription → subscription-saas}/stripe_customers_batch1.csv +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alvera-ai/platform-sdk",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "description": "Typed SDK for the Alvera platform API — manage data sources, tools, generic tables, AI agents, and action status updaters.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -31,11 +31,12 @@
31
31
  "codegen": "openapi-ts && tsx scripts/patch-generated.ts",
32
32
  "regen": "bun run fetch-metadata && bun run gen-environments && bun run codegen",
33
33
  "build": "tsdown",
34
- "test": "bun test scripts/cookbook-doctest test/contract.test.ts test/prism-surface.test.ts test/mms-media-url-and-action-log-config.test.ts test/eum-single-endpoint.test.ts test/workflow-tags-and-dac-log-fields.test.ts test/workflow-runs-and-twilio-sender.test.ts test/flop-lists.test.ts test/llm-export.test.ts test/auth-headers.test.ts",
34
+ "test": "bun test",
35
35
  "test:contract": "bun test test/contract.test.ts",
36
36
  "test:prism-surface": "bun test test/prism-surface.test.ts",
37
37
  "validate:cookbook": "tsx scripts/cookbook-doctest/run.ts",
38
- "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.test.json && tsc --noEmit -p scripts/tsconfig.json",
38
+ "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.test.json && tsc --noEmit -p scripts/tsconfig.json && bun run typecheck:guides",
39
+ "typecheck:guides": "tsx scripts/type-gate.ts",
39
40
  "clean": "rm -rf dist src/generated src/environments.generated.ts .cookbook-doctest .agent",
40
41
  "sync-agent-corpus": "tsx scripts/sync-agent-corpus.ts",
41
42
  "prepack": "bun run sync-agent-corpus",
@@ -1,88 +0,0 @@
1
- {% comment %}
2
- Foundation Airflow Lead Form → Foundation LegalEntity attrs.
3
-
4
- Maps a single Google-Sheet lead row into a Foundation
5
- `LegalEntity` upsert payload. Used by Contract A
6
- (`resource_type: "legal_entity"`, `mdm_input_type: :null`) — Contract A
7
- writes the LE directly because the row IS the MDM subject for Foundation.
8
- Identifier-based dedupe in `Foundation.LegalEntities.upsert_legal_entity`
9
- collapses repeat submissions sharing the same digital_identifier
10
- (verified email) onto a single LE row.
11
-
12
- ## Branches
13
- * `legal_entity_type: "individual"` when `company` is empty
14
- * `legal_entity_type: "business"` when `company` is non-empty
15
- (the company name becomes `business_name` and the submitter's name
16
- is dropped — businesses don't carry first/last on the LE row)
17
-
18
- ## Identifications
19
- * One identification per row: `id_type: "digital_identifier"`,
20
- `uri: <p.source_uri>` — the verifying platform's URI, which DAC
21
- injects from the configured `data_source.uri` (e.g. the Airflow
22
- form's host). `id_number: <email>`. Same triple is emitted on the
23
- regulated nested attrs so the raw plaintext id_number lives on the
24
- regulated mirror
25
- (`Foundation.LegalEntities.find_legal_entity_by_identifications/2`
26
- queries the regulated table — public id_number is tokenized).
27
- {% endcomment %}
28
- {% assign p = msg %}
29
- {% assign le_type = "individual" %}
30
- {% if p.company and p.company != "" %}{% assign le_type = "business" %}{% endif %}
31
- {% assign name_parts = p.name | default: "" | split: " " %}
32
- {% assign first_name = name_parts[0] | default: "" %}
33
- {% assign last_name_parts = name_parts | slice: 1, 9 %}
34
- {% assign last_name = last_name_parts | join: " " %}
35
- {
36
- "legal_entity_type": "{{ le_type }}",
37
- "role": "direct",
38
- {% if le_type == "business" %}"business_name": "{{ p.company | json_escape }}",{% endif %}
39
- {% if le_type == "individual" and first_name != "" %}"first_name": "{{ first_name | json_escape }}",{% endif %}
40
- {% if le_type == "individual" and last_name != "" %}"last_name": "{{ last_name | json_escape }}",{% endif %}
41
- {% comment %}
42
- Optional date_of_birth + phone_numbers — emitted at the TOP LEVEL
43
- because `Platform.Foundation.LegalEntities.upsert_legal_entity/5`
44
- feeds the SAME flat attrs map to both the public and the
45
- regulated half's changeset (see the @doc above the function: "Both
46
- halves consume the same flat `attrs` map — column names are
47
- identical on the public and regulated schemas, so each side's
48
- changeset reads the same keys"). Nesting these inside the
49
- `regulated_legal_entity` block here would render them invisible
50
- to the upsert (the regulated changeset reads `:date_of_birth` and
51
- `:phone_numbers` from the flat top-level, not from a nested
52
- object). The public LegalEntity schema does not declare
53
- `:date_of_birth` or `has_many :phone_numbers`, so the public
54
- changeset silently drops them; only the regulated mirror picks
55
- them up.
56
-
57
- `date_of_birth` is rendered as ISO 8601 YYYY-MM-DD; if the CSV
58
- column is absent, the {% if %} guard renders nothing and the
59
- regulated mirror's :date_of_birth stays nil.
60
-
61
- `phone_numbers` renders as a single-element list — matches the
62
- one-phone-per-lead shape of inbound lead-capture forms. Absent
63
- column leaves the has_many empty.
64
- {% endcomment %}
65
- {% if p.date_of_birth and p.date_of_birth != "" %}"date_of_birth": "{{ p.date_of_birth | json_escape }}",{% endif %}
66
- {% if p.phone and p.phone != "" %}"phone_numbers": [{ "phone_number": "{{ p.phone | json_escape }}" }],{% endif %}
67
- "identifications": [
68
- {
69
- "id_type": "digital_identifier",
70
- "uri": "{{ p.source_uri | json_escape }}",
71
- "id_number": "{{ p.email | json_escape }}"
72
- }
73
- ],
74
- "regulated_legal_entity": {
75
- "legal_entity_type": "{{ le_type }}",
76
- "role": "direct",
77
- {% if le_type == "business" %}"business_name": "{{ p.company | json_escape }}",{% endif %}
78
- {% if le_type == "individual" and first_name != "" %}"first_name": "{{ first_name | json_escape }}",{% endif %}
79
- {% if le_type == "individual" and last_name != "" %}"last_name": "{{ last_name | json_escape }}",{% endif %}
80
- "identifications": [
81
- {
82
- "id_type": "digital_identifier",
83
- "uri": "{{ p.source_uri | json_escape }}",
84
- "id_number": "{{ p.email | json_escape }}"
85
- }
86
- ]
87
- }
88
- }
@@ -1,47 +0,0 @@
1
- {% comment %}CAHPS Appointments → FHIR R4 Appointment attrs{% endcomment %}
2
- {% comment %}Parent appointment filtering is handled by filter_template{% endcomment %}
3
- {% assign a = msg %}
4
- {
5
- {% if a.appt_slot_status %}
6
- {% assign raw_status = a.appt_slot_status | downcase | strip %}
7
- {% if raw_status == "2 - checked in" %}
8
- "status": "arrived"
9
- {% elsif raw_status == "3 - checked out" or raw_status == "4 - charge entered" %}
10
- "status": "fulfilled"
11
- {% elsif raw_status == "f - filled" %}
12
- "status": "booked"
13
- {% elsif raw_status == "o - open slot" %}
14
- "status": "pending"
15
- {% elsif raw_status == "x - cancelled" %}
16
- "status": "cancelled"
17
- {% endif %}
18
- {% endif %},
19
- {% if a.appt_date %}"start": "{{ a.appt_date | parse_date: "{M}/{D}/{YYYY}" }}{% if a.appt_start_time %}T{{ a.appt_start_time | convert_time: "{h12}:{m} {am}" }}{% endif %}{{ a.timezone | tz_offset }}",{% endif %}
20
- {% if a.appt_slot_duration and a.appt_slot_duration != "" %}"minutes_duration": {{ a.appt_slot_duration }},{% endif %}
21
- {% if a.appt_type and a.appt_type != "" %}"description": "{{ a.appt_type }}",{% endif %}
22
- "identifier": [
23
- {
24
- "system": "urn:{{ a.source_uri }}:appointment-id",
25
- "value": "{{ a.appointment_id | default: "" }}"
26
- }
27
- ],
28
- "participants": [
29
- {% if a.svc_department and a.svc_department != "" %}
30
- {
31
- "actor_type": "location",
32
- "actor_id": "{{ a.svc_department }}",
33
- "required": "required",
34
- "status": "accepted"
35
- }{% if a.rndrng_provider_id and a.rndrng_provider_id != "" %},{% endif %}
36
- {% endif %}
37
- {% if a.rndrng_provider_id and a.rndrng_provider_id != "" %}
38
- {
39
- "actor_type": "practitioner",
40
- "actor_id": "{{ a.rndrng_provider_id }}",
41
- "required": "required",
42
- "status": "accepted"
43
- }
44
- {% endif %}
45
- ],
46
- "source_uri": "{{ a.source_uri | default: "" }}"
47
- }
@@ -1,24 +0,0 @@
1
- {% comment %}CAHPS Appointments → Healthcare MDM Input{% endcomment %}
2
- {% assign p = msg %}
3
- {
4
- "identifiers": [
5
- {
6
- "system": "urn:{{ p.source_uri }}:patient-id",
7
- "value": "{{ p.patient_id }}"
8
- }{% if p.enterprise_id and p.enterprise_id != "" %},
9
- {
10
- "system": "urn:{{ p.source_uri }}:enterprise-id",
11
- "value": "{{ p.enterprise_id }}"
12
- }{% endif %}
13
- ],
14
- {% assign space_parts = p.patient_name | split: " " %}
15
- {% if space_parts.size > 1 %}
16
- "given_name": "{{ space_parts | first }}",
17
- "family_name": "{{ space_parts | last }}",
18
- {% else %}
19
- "family_name": "{{ p.patient_name | strip }}",
20
- {% endif %}
21
- {% if p.patientdob and p.patientdob != "" %}"birth_date": "{{ p.patientdob | parse_date: "{M}/{D}/{YYYY}" }}",{% endif %}
22
- {% if p.patient_mobile_no and p.patient_mobile_no != "" %}"phone": "{{ p.patient_mobile_no }}",{% endif %}
23
- "gender": "unknown"
24
- }
@@ -1,38 +0,0 @@
1
- {% comment %}CAHPS Appointments → FHIR R4 Patient attrs{% endcomment %}
2
- {% comment %}Parent appointment filtering is handled by filter_template{% endcomment %}
3
- {% assign p = msg %}
4
- {
5
- "active": true,
6
- "gender": "unknown",
7
- {% if p.patientdob %}"birth_date": "{{ p.patientdob | parse_date: "{M}/{D}/{YYYY}" }}",{% endif %}
8
- "identifier": [
9
- {
10
- "system": "urn:{{ p.source_uri }}:patient-id",
11
- "value": "{{ p.patient_id | default: "" }}"
12
- }{% if p.enterprise_id and p.enterprise_id != "" %},
13
- {
14
- "system": "urn:{{ p.source_uri }}:enterprise-id",
15
- "value": "{{ p.enterprise_id }}"
16
- }{% endif %}
17
- ],
18
- {% if p.patient_name %}"name": [
19
- {
20
- "use": "official",
21
- {% assign space_parts = p.patient_name | split: " " %}
22
- {% if space_parts.size > 1 %}
23
- "given": ["{{ space_parts | first }}"],
24
- "family": "{{ space_parts | last }}"
25
- {% else %}
26
- "family": "{{ p.patient_name | strip }}"
27
- {% endif %}
28
- }
29
- ],{% endif %}
30
- {% if p.patient_mobile_no and p.patient_mobile_no != "" %}"telecom": [
31
- {
32
- "system": "phone",
33
- "value": "{{ p.patient_mobile_no }}",
34
- "use": "mobile"
35
- }
36
- ],{% endif %}
37
- "source_uri": "{{ p.source_uri | default: "" }}"
38
- }
@@ -1,59 +0,0 @@
1
- {% comment %}Atomic FI ComplianceScreenings → Payments ComplianceScreening attrs
2
-
3
- Source: GET /api/compliance-screenings on Atomic FI (paginated; the
4
- fetch pipeline extracts the `data` array via the response_extractor_key
5
- and streams one row per `msg`).
6
-
7
- The FK columns `legal_entity_id` and `account_holder_id` are NOT emitted
8
- here — `ComplianceScreening.upsert_changeset/9` `put_change`s them from
9
- the resolved MDMOutput after the MDM template's MDMInput drives
10
- `MDM.resolve/5` (which find-or-creates the LE+AH parent pair).
11
-
12
- Field handling on the unregulated side:
13
- * narrative + name fields (screened_entity_name, review_notes,
14
- pep_list_name) are persisted through TokenizedDataType — pass them
15
- as raw strings here; the changeset's `cast_tokenized_data/4` calls
16
- tokenize automatically before insert.
17
- * analytic numerics (aml_risk_score, aml_velocity_count,
18
- aml_high_risk_country) stay plaintext on both unreg + reg.
19
- * `sanctions_matches` is explicitly looped — Liquid's default
20
- rendering on a list produces a string, not JSON; the explicit
21
- {% for %} loop emits one JSON object per element so the CS
22
- upsert's `sync_sanctions_matches/6` can cast each child row.
23
- {% endcomment %}
24
- {% assign p = msg %}
25
- {
26
- "compliance_screening_number": "{{ p.compliance_screening_number | default: p.id | json_escape }}",
27
- "scope": "{{ p.scope | default: "account_holder" | json_escape }}",
28
- "screening_type": "{{ p.screening_type | default: "sanctions" | json_escape }}",
29
- "screening_status": "{{ p.screening_status | default: "pending" | json_escape }}",
30
- "screened_entity_type": "{{ p.screened_entity_type | default: "individual" | json_escape }}",
31
- {% if p.screening_score and p.screening_score != "" %}"screening_score": {{ p.screening_score }},{% endif %}
32
- {% if p.match_count and p.match_count != "" %}"match_count": {{ p.match_count }},{% endif %}
33
- {% if p.screened_at and p.screened_at != "" %}"screened_at": "{{ p.screened_at | json_escape }}",{% endif %}
34
- {% if p.sanctions_screening_status and p.sanctions_screening_status != "" %}"sanctions_screening_status": "{{ p.sanctions_screening_status | json_escape }}",{% endif %}
35
- {% if p.sanctions_screening_date and p.sanctions_screening_date != "" %}"sanctions_screening_date": "{{ p.sanctions_screening_date | json_escape }}",{% endif %}
36
- {% if p.manual_review_required %}"manual_review_required": {{ p.manual_review_required }},{% endif %}
37
- {% if p.screened_entity_name and p.screened_entity_name != "" %}"screened_entity_name": "{{ p.screened_entity_name | json_escape }}",{% endif %}
38
- {% if p.review_notes and p.review_notes != "" %}"review_notes": "{{ p.review_notes | json_escape }}",{% endif %}
39
- {% if p.pep_list_name and p.pep_list_name != "" %}"pep_list_name": "{{ p.pep_list_name | json_escape }}",{% endif %}
40
- {% if p.aml_risk_score and p.aml_risk_score != "" %}"aml_risk_score": {{ p.aml_risk_score }},{% endif %}
41
- {% if p.aml_velocity_count and p.aml_velocity_count != "" %}"aml_velocity_count": {{ p.aml_velocity_count }},{% endif %}
42
- {% if p.aml_high_risk_country and p.aml_high_risk_country != "" %}"aml_high_risk_country": "{{ p.aml_high_risk_country | json_escape }}",{% endif %}
43
- {% if p.sanctions_matches %}
44
- "sanctions_matches": [
45
- {% for m in p.sanctions_matches %}
46
- {
47
- "matched_name": "{{ m.matched_name | json_escape }}",
48
- {% if m.matched_entity_type and m.matched_entity_type != "" %}"matched_entity_type": "{{ m.matched_entity_type | json_escape }}",{% endif %}
49
- "match_score": {{ m.match_score }},
50
- "source_list": "{{ m.source_list | json_escape }}",
51
- {% if m.source_id and m.source_id != "" %}"source_id": "{{ m.source_id | json_escape }}",{% endif %}
52
- {% if m.false_positive_qualifier and m.false_positive_qualifier != "" %}"false_positive_qualifier": "{{ m.false_positive_qualifier | json_escape }}",{% endif %}
53
- {% if m.list_synced_at and m.list_synced_at != "" %}"list_synced_at": "{{ m.list_synced_at | json_escape }}"{% else %}"list_synced_at": null{% endif %}
54
- }{% unless forloop.last %},{% endunless %}
55
- {% endfor %}
56
- ],
57
- {% endif %}
58
- "source_uri": "{{ p.source_uri | default: "api.atomic.fi" | json_escape }}"
59
- }
@@ -1,36 +0,0 @@
1
- {% comment %}Atomic FI ComplianceScreenings → Payments MDM Input
2
-
3
- A CS row from Atomic FI references an account_holder UUID
4
- (`p.account_holder_id`). MDM-resolution side uses that as the external
5
- `account_holder_number` to find the LegalEntity + AccountHolder pair
6
- already ingested via the account-holders IOC.
7
-
8
- `legal_entity_id` on the resulting MDMOutput is what links the CS row's
9
- `legal_entity_id` (mandatory per the consistency trigger). The
10
- `account_number_uri + account_holder_number` pair is the canonical
11
- external identity used by `AccountHolders.match_account_holder/2`.
12
- {% endcomment %}
13
- {% assign p = msg %}
14
- {
15
- "owner_type": "account_holder",
16
- "account_holder_type": "individual",
17
- "account_holder_number": "{{ p.account_holder_id | json_escape }}",
18
- "account_number_uri": "{{ p.source_uri | default: "api.atomic.fi" | json_escape }}",
19
- "identifiers": [
20
- {
21
- "system": "urn:atomic.fi:account-holder-id",
22
- "value": "{{ p.account_holder_id | json_escape }}",
23
- "id_type": "digital_identifier"
24
- }
25
- ],
26
- {% if p.screened_entity_name and p.screened_entity_name != "" %}
27
- {% assign space_parts = p.screened_entity_name | split: " " %}
28
- {% if space_parts.size > 1 %}
29
- "given_name": "{{ space_parts | first | json_escape }}",
30
- "family_name": "{{ space_parts | last | json_escape }}",
31
- {% else %}
32
- "family_name": "{{ p.screened_entity_name | strip | json_escape }}",
33
- {% endif %}
34
- {% endif %}
35
- "gender": "unknown"
36
- }
@@ -1,30 +0,0 @@
1
- {% comment %}Atomic FI PaymentAccount → Payments MDM Input
2
-
3
- A PaymentAccount row from Atomic FI references an account_holder UUID
4
- (`p.account_holder_id`) — that's the canonical external identity used by
5
- `AccountHolders.match_account_holder/2` to find-or-create the
6
- LegalEntity + AccountHolder pair already ingested via the
7
- account-holders IOC.
8
-
9
- `legal_entity_id` on the resulting MDMOutput is what links the PA row's
10
- `legal_entity_id` (PA belongs_to LE — provides the address slice for
11
- sanctions-review override-agent context). The MDM scope is always
12
- `account_holder` for PA rows because the PA inherently serves an
13
- AccountHolder (per Atomic FI's PA schema invariant
14
- `validate_required([:account_type, :account_holder_id, :currency,
15
- :tenant_id])`).
16
- {% endcomment %}
17
- {% assign p = msg %}
18
- {
19
- "owner_type": "account_holder",
20
- "account_holder_type": "individual",
21
- "account_holder_number": "{{ p.account_holder_id | json_escape }}",
22
- "account_number_uri": "{{ p.source_uri | default: "api.atomic.fi" | json_escape }}",
23
- "identifiers": [
24
- {
25
- "system": "urn:atomic.fi:account-holder-id",
26
- "value": "{{ p.account_holder_id | json_escape }}",
27
- "id_type": "digital_identifier"
28
- }
29
- ]
30
- }
@@ -1,55 +0,0 @@
1
- {% comment %}Atomic FI PaymentAccount → Payments PaymentAccount attrs
2
-
3
- Source: GET /api/payment-accounts on Atomic FI (paginated; the fetch
4
- pipeline extracts the `data` array via the response_extractor_key and
5
- streams one row per `msg`).
6
-
7
- FK columns (`legal_entity_id`, `account_holder_id`, `counterparty_id`)
8
- are NOT emitted here — `PaymentAccount.upsert_changeset/7` `put_change`s
9
- them from the resolved MDMOutput after the MDM template's MDMInput
10
- drives `MDM.resolve/5`.
11
-
12
- Field handling on the unregulated side:
13
- * PCI-DSS-sensitive fields (`account_number`, `iban`, `card_pan`,
14
- `wallet_address`) and narratives (`purpose_of_payment_detail`,
15
- `business_relationship_description`) are persisted through
16
- `TokenizedDataType` — pass them as raw strings here; the
17
- changeset's `cast_tokenized_data/4` calls tokenize automatically
18
- before insert.
19
- * `enabled_regimes` is rendered as a JSON array; Liquid's `{{ x }}`
20
- on a string array would produce a stringified Elixir list, not
21
- JSON — the explicit `{% for %}` loop emits one quoted element per
22
- regime so the changeset's array cast accepts it.
23
-
24
- `destination_country` is derived here from `swift_bic` (chars 5-6 per
25
- ISO 9362) or `iban` (chars 1-2 per ISO 13616), with BIC preferred when
26
- both are present. Downstream agents (sanctions-review override) read
27
- this field directly without re-parsing.
28
- {% endcomment %}
29
- {% assign p = msg %}
30
- {% if p.swift_bic and p.swift_bic != "" %}
31
- {% assign derived_country = p.swift_bic | slice: 4, 2 | upcase %}
32
- {% elsif p.iban and p.iban != "" %}
33
- {% assign derived_country = p.iban | slice: 0, 2 | upcase %}
34
- {% else %}
35
- {% assign derived_country = nil %}
36
- {% endif %}
37
- {
38
- {% if p.payment_account_external_id and p.payment_account_external_id != "" %}"payment_account_external_id": "{{ p.payment_account_external_id | json_escape }}",{% endif %}
39
- {% if p.payment_account_number and p.payment_account_number != "" %}"payment_account_number": "{{ p.payment_account_number | json_escape }}",{% endif %}
40
- {% if p.status and p.status != "" %}"status": "{{ p.status | json_escape }}",{% endif %}
41
- {% if p.account_type and p.account_type != "" %}"account_type": "{{ p.account_type | json_escape }}",{% endif %}
42
- {% if p.currency and p.currency != "" %}"currency": "{{ p.currency | json_escape }}",{% endif %}
43
- {% if p.routing_number and p.routing_number != "" %}"routing_number": "{{ p.routing_number | json_escape }}",{% endif %}
44
- {% if p.swift_bic and p.swift_bic != "" %}"swift_bic": "{{ p.swift_bic | json_escape }}",{% endif %}
45
- {% if p.bank_name and p.bank_name != "" %}"bank_name": "{{ p.bank_name | json_escape }}",{% endif %}
46
- {% if p.wallet_chain and p.wallet_chain != "" %}"wallet_chain": "{{ p.wallet_chain | json_escape }}",{% endif %}
47
- {% if p.account_number and p.account_number != "" %}"account_number": "{{ p.account_number | json_escape }}",{% endif %}
48
- {% if p.iban and p.iban != "" %}"iban": "{{ p.iban | json_escape }}",{% endif %}
49
- {% if p.card_pan and p.card_pan != "" %}"card_pan": "{{ p.card_pan | json_escape }}",{% endif %}
50
- {% if p.wallet_address and p.wallet_address != "" %}"wallet_address": "{{ p.wallet_address | json_escape }}",{% endif %}
51
- {% if p.purpose_of_payment_detail and p.purpose_of_payment_detail != "" %}"purpose_of_payment_detail": "{{ p.purpose_of_payment_detail | json_escape }}",{% endif %}
52
- {% if p.business_relationship_description and p.business_relationship_description != "" %}"business_relationship_description": "{{ p.business_relationship_description | json_escape }}",{% endif %}
53
- {% if derived_country and derived_country != "" %}"destination_country": "{{ derived_country | json_escape }}",{% endif %}
54
- "enabled_regimes": [{% for r in p.enabled_regimes %}"{{ r | json_escape }}"{% unless forloop.last %},{% endunless %}{% endfor %}]
55
- }
@@ -1,20 +0,0 @@
1
- {% comment %}Stripe Customers → Subscription MDM Input{% endcomment %}
2
- {% assign p = msg %}
3
- {
4
- "identifiers": [
5
- {
6
- "system": "urn:{{ p.source_uri | json_escape }}:customer-number",
7
- "value": "{{ p.customer_number | json_escape }}"
8
- }
9
- ],
10
- {% assign space_parts = p.name | split: " " %}
11
- {% if space_parts.size > 1 %}
12
- "given_name": "{{ space_parts | first | json_escape }}",
13
- "family_name": "{{ space_parts | last | json_escape }}",
14
- {% else %}
15
- "family_name": "{{ p.name | strip | json_escape }}",
16
- {% endif %}
17
- {% if p.phone and p.phone != "" %}"phone": "{{ p.phone | json_escape }}",{% endif %}
18
- {% if p.email and p.email != "" %}"email": "{{ p.email | json_escape }}",{% endif %}
19
- "gender": "unknown"
20
- }