Scrydon
Authoring: Ontologies

Context-aware bindings

Model the same business concept across multiple source systems, standards, and jurisdictions — with provenance, transforms, and confidence on every projected value.

This is the authoring reference (how to build it with the SDK). For the concept — what a contextual ontology is and when you need one — start with Contextual ontologies. This guide also extends the Ontologies authoring guide: context-aware bindings are additive fields on the same defineBinding, defineObjectType, and defineOntology helpers.

Why context-aware bindings exist

A canonical ontology says Payment has a monetaryAmount property. But the same business concept looks different across every source system that feeds it:

What you call itWhere it livesDifference
amountCore banking (pacs.008, BE)Integer cents, outgoing, value-date semantics
trxAmtPayments hub (legacy ISO 8583)Decimal string, acquiring, settlement-date semantics
gross_amountFinance reporting (IFRS lens)Net + fees, accounting-date semantics

If you flatten all three into one monetaryAmount column without capturing that context, your AI, reporting, and compliance automation operate on ambiguous data. A compliance query for "all cross-border payments over €10 000" returns different answers depending on which binding ran last — and you cannot explain which one.

Context-aware bindings solve this by recording on every projected value:

  • Which source system and message standard produced it.
  • What transforms were applied (currency unit, timezone, divide-by-100, …).
  • Whether those transforms were lossless.
  • Confidence the mapping is correct.
  • A validity window (validFrom / validTo).

Reads return all of this as a provenance envelope alongside { id, properties }, so callers can see and explain divergence between sources rather than hiding it behind a silent golden record.


bindingContext — describing your source system

Add a bindingContext block to any defineBinding(...) call. All fields are optional — supply only what is meaningful for your source.

import { defineBinding } from '@scrydon/sdk-authoring/ontologies'

defineBinding({
  objectTypeSlug: 'Payment',
  kind: 'silver_table',
  source: { table: 'silver_payments_outgoing' },
  identitySpec: { keys: ['msg_id'] },
  columnMap: {
    monetary_amount: 'amount_cents',
    value_date: 'trade_ts_local',
    counterparty_lei: 'customer_id',
  },

  bindingContext: {
    sourceSystem: 'core_banking_x',
    integrationVersion: '3.2',
    standard: 'ISO20022',
    messageType: 'pacs.008',
    direction: 'outgoing',
    jurisdiction: 'BE',
    dataResidency: 'eu',
    businessProcess: 'credit_transfer',
    timestampSemantics: 'value_date',
    currencySemantics: 'transaction_currency',
    aggregationLevel: 'transaction_level',
    confidence: 0.96,
    validFrom: '2025-01-01',
    validTo: null,
  },
})

Field reference

FieldTypeNotes
sourceSystemstringSlug identifying the enterprise system (e.g. "core_banking_x", "salesforce_crm").
integrationVersionstringAPI or schema version of the source (e.g. "3.2", "v2024-01").
standardstringMessage standard, if applicable (e.g. "ISO20022", "FIX42", "HL7_FHIR").
messageTypestringStandard-specific message type (e.g. "pacs.008", "MT103", "Claim").
directionstringFlow direction — "inbound", "outgoing", or any free-form value.
jurisdictionstringISO country or region code (e.g. "BE", "EU", "US-NY").
dataResidencystringData-residency zone (e.g. "eu", "us", "on-prem").
businessProcessstringHigh-level business process (e.g. "credit_transfer", "claims_adjudication").
timestampSemanticsstringWhat timestamps mean — "value_date", "settlement_date", "accounting_date", or any free-form value.
currencySemanticsstringWhich currency view — "transaction_currency", "reporting_currency", etc.
aggregationLevelstringGrain of the data — "transaction_level", "position_level", "daily_snapshot", etc.
confidencenumber (0–1)Mapping confidence for this source. Flows into the provenance envelope on every projected instance.
validFromstringISO 8601 date string for when this mapping became valid.
validTostring | nullISO 8601 date string for when this mapping expires, or null for open-ended.

transformMap — per-property transforms

The flat columnMap maps property slugs to source column names. transformMap layers richer transform descriptors on top. When a property appears in transformMap, the projector uses entry.source as the source column and applies entry.transform; otherwise it falls back to columnMap. Both can coexist on the same binding.

defineBinding({
  objectTypeSlug: 'Payment',
  kind: 'silver_table',
  source: { table: 'silver_payments_outgoing' },
  identitySpec: { keys: ['msg_id'] },
  columnMap: {},  // transformMap entries take precedence

  bindingContext: {
    sourceSystem: 'core_banking_x',
    standard: 'ISO20022',
    messageType: 'pacs.008',
    jurisdiction: 'BE',
    confidence: 0.96,
  },

  transformMap: {
    monetary_amount: {
      source: 'amount_cents',
      transform: 'divide_by_100',
      unit: 'EUR',
      lossless: true,
    },
    value_date: {
      source: 'trade_ts_local',
      transform: 'timezone_normalize',
      tz: 'Europe/Brussels',
      lossless: false,
      notes: 'DST ambiguity on clock-change dates',
    },
    counterparty_lei: {
      source: 'customer_id',
      transform: 'resolve_via_mdm',
      lossless: false,
      confidence: 0.88,
    },
  },
})

Transform vocabulary

The allowed transforms are a closed, allow-listed set. Passing a string not in this list causes the property to project null and be marked as a lossy property — the projector never silently mis-types a value.

Transform IDWhat it doesLossless?
identityPasses the value through unchanged.Yes
divide_by_100Divides the numeric value by 100 (e.g. cents → currency unit).Yes
multiply_by_100Multiplies the numeric value by 100.Yes
regex_extractApplies a regex capture group to the string value. Use args: { pattern: "..." } to supply the expression.Depends
timezone_normalizeNormalises a local timestamp to UTC using the tz field.No (DST ambiguity)
currency_normalizeRecords the currency unit from unit. Value is passed through unchanged until an FX provider is configured.No
resolve_via_mdmLooks up the value in a Master Data Management system. Passes the value through until an MDM resolver is configured.No

currency_normalize and resolve_via_mdm are metadata-only stubs today — they record that the transform should occur and mark the property lossless: false, but they do not rewrite the value. When an FX provider or MDM resolver is configured on your deployment, the platform will automatically use it. Until then, treat the projected value as the raw source value.

TransformDescriptor field reference

FieldTypeNotes
sourcestringSource column expression (overrides columnMap[propertySlug] when present). Required.
transformTransformIdOne of the allow-listed transform IDs above. Optional — omit to pass the value through as-is.
unitstringCurrency code or other unit (e.g. "EUR", "USD"). Used by currency_normalize and informational for divide_by_100.
tzstringIANA timezone name (e.g. "Europe/Brussels"). Used by timezone_normalize.
losslessbooleanWhether the transform is lossless. When false, the property appears in provenance.lossyProperties.
confidencenumber (0–1)Per-property mapping confidence, overrides the binding-level confidence for this property.
notesstringFree-form note for documentation and audit purposes.
argsRecord<string, unknown>Additional per-transform arguments (e.g. { pattern: "^([A-Z]{2}\\d+)" } for regex_extract).

Standards anchors — linking to FIBO, ISO 20022, CDM, and LEI

Object Types and Properties can carry optional anchors that link a canonical concept to an external standard. Anchors are purely informational — they do not change how the platform stores or projects data, but they are surfaced in the ontology workbench and in audit provenance.

import {
  defineObjectType,
  defineProperty,
} from '@scrydon/sdk-authoring/ontologies'

defineObjectType({
  slug: 'Payment',
  displayName: 'Payment',
  kind: 'analytical',
  anchors: [
    {
      standard: 'FIBO',
      conceptId: 'fibo-fbc-fct-fse:Payment',
      uri: 'https://spec.edmcouncil.org/fibo/ontology/FBC/FunctionalEntities/FinancialServicesEntities/',
      label: 'Payment',
    },
    {
      standard: 'ISO20022',
      conceptId: 'pacs.008.CreditTransfer',
      label: 'Credit Transfer Initiation',
    },
  ],
  properties: [
    defineProperty({
      slug: 'monetary_amount',
      displayName: 'Monetary amount',
      scalarType: 'float',
      anchors: [
        { standard: 'ISO20022', conceptId: 'Pacs.008.IntrBkSttlmAmt', label: 'Interbank Settlement Amount' },
        { standard: 'FINOS_CDM', conceptId: 'rosetta:Money', label: 'Money' },
      ],
    }),
    defineProperty({
      slug: 'value_date',
      displayName: 'Value date',
      scalarType: 'date',
      anchors: [
        { standard: 'ISO20022', conceptId: 'Pacs.008.IntrBkSttlmDt', label: 'Interbank Settlement Date' },
      ],
    }),
  ],
})

Supported standards

ValueDescription
FIBOEDMC Financial Industry Business Ontology (OWL 2 DL)
ISO20022ISO 20022 Business Model concepts and message components
FINOS_CDMFINOS Common Domain Model (Rosetta)
LEILegal Entity Identifier (GLEIF)

These are free-form strings — the platform validates that standard, conceptId, and optionally uri and label are present, but it does not resolve URIs or validate concept IDs against the external standards themselves. Local-language concept names take precedence; anchors are an optional layer for interoperability and audit traceability.


Context-scoped aliasesalias + context → concept

Different source systems and standards use different local terms for the same canonical concept. The aliases array at the top level of defineOntology(...) lets you register those term-to-concept mappings — scoped to a context so the same term can map to different concepts depending on where it appears.

Aliases are always context-scoped. There is no global alias table. The same local term ("Debtor") can map to Counterparty under one standard and to LegalEntity under another — by design.

Use defineAlias(...) from @scrydon/sdk-authoring/ontologies:

import {
  defineOntology,
  defineAlias,
} from '@scrydon/sdk-authoring/ontologies'

defineOntology({
  id: 'acme-payments',
  // ... objectTypes, bindings, etc.

  aliases: [
    // "Debtor" in pacs.008 outgoing payments → the canonical Counterparty object type
    defineAlias({
      alias: 'Debtor',
      objectTypeSlug: 'Counterparty',
      contextSelector: {
        standard: 'ISO20022',
        messageType: 'pacs.008',
        direction: 'outgoing',
      },
      confidence: 0.97,
    }),

    // "Beneficiary" in pacs.008 outgoing payments → also Counterparty (different role, same type)
    defineAlias({
      alias: 'Beneficiary',
      objectTypeSlug: 'Counterparty',
      contextSelector: {
        standard: 'ISO20022',
        messageType: 'pacs.008',
        direction: 'outgoing',
      },
      confidence: 0.95,
    }),

    // "Client" in a CRM context → Counterparty (context-free fallback, lowest rank)
    defineAlias({
      alias: 'Client',
      objectTypeSlug: 'Counterparty',
      // no contextSelector = context-free fallback
      confidence: 0.75,
    }),

    // Property alias: "amount_cents" in outgoing pacs.008 → the monetary_amount property
    defineAlias({
      alias: 'amount_cents',
      objectTypeSlug: 'Counterparty',
      propertyTypeSlug: 'monetary_amount',
      contextSelector: {
        standard: 'ISO20022',
        messageType: 'pacs.008',
        direction: 'outgoing',
      },
      confidence: 0.99,
    }),
  ],
})

ConceptAlias field reference

FieldTypeNotes
aliasstringThe local or source term (e.g. "Debtor", "Client", "Obligor").
objectTypeSlugSlugThe canonical Object Type this alias resolves to. Required.
propertyTypeSlugSlugWhen set, the alias targets a specific property on the object type rather than the type itself.
contextSelectorobjectA subset of context keys that must match for this alias to apply. Omit to create a context-free fallback (lowest resolution priority).
contextSelector.sourceSystemstringMatch by source system slug.
contextSelector.standardstringMatch by message standard (e.g. "ISO20022").
contextSelector.messageTypestringMatch by message type (e.g. "pacs.008").
contextSelector.directionstringMatch by direction ("inbound" / "outgoing").
contextSelector.jurisdictionstringMatch by jurisdiction code.
contextSelector.businessProcessstringMatch by business process slug.
confidencenumber (0–1)Confidence that this alias is correct in the given context. Candidates are ranked by context specificity first, then by confidence.

Resolution priority: aliases whose contextSelector matches more keys in the query context rank above looser matches. A context-free alias (no contextSelector) is always the lowest-priority fallback.


dataContract — enforceable upstream contract

A data contract documents the SLA and quality expectations between a binding and its upstream source. It is stored on the binding and surfaced in governance tooling.

defineBinding({
  objectTypeSlug: 'Payment',
  kind: 'silver_table',
  source: { table: 'silver_payments_outgoing' },
  identitySpec: { keys: ['msg_id'] },
  columnMap: { monetary_amount: 'amount_cents' },

  dataContract: {
    version: '1.0.0',
    owner: 'data-platform@example.com',
    requiredConcepts: ['monetary_amount', 'value_date', 'counterparty_lei'],
    qualityRules: [
      { kind: 'not_null', on: 'counterparty_lei' },
      { kind: 'range', on: 'monetary_amount', min: 0 },
    ],
    sla: { freshnessMinutes: 60 },
    lineage: { upstream: 'core_banking_x.payments.outgoing' },
  },
})
FieldTypeNotes
versionsemverContract version — increment when SLA or required concepts change.
ownerstringContact or team responsible for this contract.
requiredConceptsstring[]Property slugs that must project non-null for a row to satisfy the contract.
qualityRulesarrayEach rule has a kind ("not_null", "range", "regex", etc.) and an on field naming the property slug. Additional rule-specific fields are allowed.
sla.freshnessMinutesnumberMaximum acceptable age of the latest row in minutes.
lineage.upstreamstringUpstream source table or feed identifier (free-form, for documentation and audit).
sourceRefstringForward-compat pointer to a defineDataSource() definition. When the platform's declarative data-source work lands, the binding will be able to derive its contract from the data-source definition automatically.

Provenance on reads

When your bindings include bindingContext and transformMap, every projected instance carries a provenance envelope alongside its properties:

{
  "id": "payment-abc123",
  "properties": {
    "monetary_amount": 1234.56,
    "value_date": "2025-04-15T00:00:00Z",
    "counterparty_lei": "213800ZOHLGEF2K5DX57"
  },
  "provenance": {
    "bindingId": "...",
    "bindingVersion": 3,
    "sourceRef": "silver_payments_outgoing",
    "materializedAt": "2025-04-15T08:12:00Z",
    "confidence": 0.96,
    "bindingContext": {
      "sourceSystem": "core_banking_x",
      "standard": "ISO20022",
      "messageType": "pacs.008",
      "direction": "outgoing",
      "jurisdiction": "BE"
    },
    "lossyProperties": ["value_date", "counterparty_lei"],
    "dlpLabels": []
  }
}

lossyProperties lists the slugs of any properties whose transform was flagged lossless: false or whose transform was unknown (failed closed to null). Callers that need to trust a value can check whether it appears in lossyProperties before acting on it.

When two source systems project instances of the same canonical type and disagree on a value, both instances are returned with their individual provenance — the platform does not silently pick a winner. If you want a survivorship policy, configure identityRule.survivorship: "highest_confidence" on the Object Type; this is opt-in and uses the confidence values from the provenance envelope.


Complete example

A payment ontology pack that uses all of the above:

import {
  defineOntology,
  defineObjectType,
  defineProperty,
  defineBinding,
  defineIdentityRule,
  defineAlias,
} from '@scrydon/sdk-authoring/ontologies'

export default defineOntology({
  id: 'acme-iso20022-payments',
  version: '1.0.0',
  displayName: 'ISO 20022 Payments',
  description: 'Payment ontology grounded in ISO 20022 pacs.008 — context-aware bindings from two core-banking feeds.',
  maturity: 'preview',
  author: { name: 'Acme Data Platform Team' },

  objectTypes: [
    defineObjectType({
      slug: 'Counterparty',
      displayName: 'Counterparty',
      kind: 'analytical',
      anchors: [
        { standard: 'FIBO', conceptId: 'fibo-fbc-fct-fse:Counterparty', label: 'Counterparty' },
        { standard: 'ISO20022', conceptId: 'pacs.008.Cdtr', label: 'Creditor' },
      ],
      properties: [
        defineProperty({ slug: 'name', displayName: 'Legal name', scalarType: 'string', cardinality: 'required' }),
        defineProperty({ slug: 'lei', displayName: 'LEI', scalarType: 'string', anchors: [
          { standard: 'LEI', conceptId: 'gleif:LEI', label: 'Legal Entity Identifier' },
        ]}),
      ],
    }),
    defineObjectType({
      slug: 'Payment',
      displayName: 'Payment',
      kind: 'analytical',
      anchors: [
        { standard: 'ISO20022', conceptId: 'pacs.008.CreditTransfer', label: 'Credit Transfer Initiation' },
        { standard: 'FINOS_CDM', conceptId: 'rosetta:Transfer', label: 'Transfer' },
      ],
      properties: [
        defineProperty({ slug: 'monetary_amount', displayName: 'Monetary amount', scalarType: 'float', cardinality: 'required',
          anchors: [{ standard: 'ISO20022', conceptId: 'Pacs.008.IntrBkSttlmAmt', label: 'Interbank Settlement Amount' }],
        }),
        defineProperty({ slug: 'value_date', displayName: 'Value date', scalarType: 'date',
          anchors: [{ standard: 'ISO20022', conceptId: 'Pacs.008.IntrBkSttlmDt', label: 'Settlement Date' }],
        }),
        defineProperty({ slug: 'currency', displayName: 'Currency', scalarType: 'string' }),
      ],
    }),
  ],

  linkTypes: [],
  actionTypes: [],

  identityRules: [
    defineIdentityRule({
      objectTypeSlug: 'Payment',
      deterministicKeys: ['msg_id'],
    }),
    defineIdentityRule({
      objectTypeSlug: 'Counterparty',
      deterministicKeys: ['lei'],
      probabilistic: {
        blockingKeys: ['name'],
        signals: [{ name: 'name_levenshtein', weight: 0.8, on: 'name' }],
        thresholds: { auto: 0.92, review: 0.75 },
      },
    }),
  ],

  bindings: [
    // Binding A — core banking X (ISO 20022 pacs.008, outgoing, Belgium)
    defineBinding({
      objectTypeSlug: 'Payment',
      kind: 'silver_table',
      source: { table: 'silver_payments_cbx_out' },
      identitySpec: { keys: ['msg_id'] },
      columnMap: { currency: 'ccy' },
      bindingContext: {
        sourceSystem: 'core_banking_x',
        integrationVersion: '3.2',
        standard: 'ISO20022',
        messageType: 'pacs.008',
        direction: 'outgoing',
        jurisdiction: 'BE',
        dataResidency: 'eu',
        businessProcess: 'credit_transfer',
        timestampSemantics: 'value_date',
        currencySemantics: 'transaction_currency',
        confidence: 0.96,
        validFrom: '2025-01-01',
      },
      transformMap: {
        monetary_amount: {
          source: 'amount_cents',
          transform: 'divide_by_100',
          unit: 'EUR',
          lossless: true,
        },
        value_date: {
          source: 'trade_ts_local',
          transform: 'timezone_normalize',
          tz: 'Europe/Brussels',
          lossless: false,
          notes: 'DST ambiguity on clock-change dates',
        },
      },
      dataContract: {
        version: '1.0.0',
        owner: 'data-platform@acme.example',
        requiredConcepts: ['monetary_amount', 'value_date', 'currency'],
        qualityRules: [
          { kind: 'not_null', on: 'currency' },
          { kind: 'range', on: 'monetary_amount', min: 0 },
        ],
        sla: { freshnessMinutes: 60 },
        lineage: { upstream: 'core_banking_x.payments.outgoing' },
      },
    }),
  ],

  aliases: [
    defineAlias({
      alias: 'Debtor',
      objectTypeSlug: 'Counterparty',
      contextSelector: { standard: 'ISO20022', messageType: 'pacs.008', direction: 'outgoing' },
      confidence: 0.97,
    }),
    defineAlias({
      alias: 'Beneficiary',
      objectTypeSlug: 'Counterparty',
      contextSelector: { standard: 'ISO20022', messageType: 'pacs.008', direction: 'outgoing' },
      confidence: 0.95,
    }),
    defineAlias({
      alias: 'Client',
      objectTypeSlug: 'Counterparty',
      confidence: 0.75,
    }),
  ],
})

Try it — the Contextual Payments example pack

Everything in this guide ships as a single downloadable pack you can install and click through. The Contextual Payments example bundles this exact ISO 20022 ontology plus its own demo data (two declarative data sources), so after install — with no CSV upload — you can watch the binding auto-resolve, inspect the transforms and confidence, and resolve "Counterparty" to a different concept per direction in the workbench Aliases tab.


Where to next

On this page

On this page