Skip to content

Field Mappings

Integration

A field mapping is the instruction "values in this source column belong to this canonical dimension". Without one, RefData Hub has no way to know that employees.marital_status should be matched against the marital_status dimension, and therefore no way to score it.

Field mappings are scoped to a connection. Every endpoint below is nested under /api/source/connections/{connection_id}/.

The record

{
  "source_table": "employees",
  "source_field": "marital_status",
  "ref_dimension": "marital_status",
  "description": "HR feed, refreshed nightly"
}
Field Meaning
source_table Table or view name as it appears in the source catalog
source_field Column name
ref_dimension Dimension code — must match dimension.code, not its label
description Free text for reviewer context

ref_dimension takes a code, not a label

The dimension picker in the UI fills in the code. If you call the API directly, ref_dimension: "Marital Status" will create a mapping that never scores anything, because matching joins on dimension.code.

Endpoints

Method Endpoint Notes
GET /api/source/connections/{id}/mappings Lists this connection's mappings
POST /api/source/connections/{id}/mappings Returns 201
PUT /api/source/connections/{id}/mappings/{mapping_id} Partial update
DELETE /api/source/connections/{id}/mappings/{mapping_id} Returns 204
curl -X POST http://localhost:8000/api/source/connections/1/mappings \
  -H 'Content-Type: application/json' \
  -d '{"source_table": "employees", "source_field": "marital_status", "ref_dimension": "marital_status"}'

Like the Source Connections workspace, the Field Mappings page shows the existing mapping grid above the creation form. The mappings are the context for the decision you are about to make, so they stay in view.

Capturing samples

Once a mapping exists, you can pull the distinct values straight out of the upstream database:

curl -X POST http://localhost:8000/api/source/connections/1/mappings/7/capture \
  -H 'Content-Type: application/json'

POST .../mappings/{mapping_id}/capture returns 201 and writes the observed values into sourcesample with an occurrence_count per distinct value. This is how you get real data into the review queue without hand-copying values.

Rapid ingestion without a live database

If you do not have upstream access — during modelling, or when testing the review flow — post samples directly:

curl -X POST http://localhost:8000/api/source/connections/1/samples \
  -H 'Content-Type: application/json' \
  -d '{
        "source_table": "employees",
        "source_field": "marital_status",
        "values": [
          {"raw_value": "M", "occurrence_count": 412},
          {"raw_value": "Married", "occurrence_count": 2890},
          {"raw_value": "S", "occurrence_count": 377}
        ]
      }'

occurrence_count defaults to 1 and must be >= 0. Samples are upserted per (connection, table, field, raw_value), so re-posting the same value adds to its count rather than duplicating the row. That is why repeated captures collapse to distinct values with accurate totals.

Read them back with optional filters:

curl "http://localhost:8000/api/source/connections/1/samples?source_table=employees&source_field=marital_status"

Why a mapping is not the same as a value mapping

This trips people up, so it is worth stating plainly:

Field mapping Value mapping
Answers "Which dimension owns this column?" "Does M equal Married?"
Granularity One per column One per distinct raw value
Table sourcefieldmapping valuemapping
Created by You, deliberately You, or by approving a suggestion

A field mapping is the scope. Value mappings are the content. Match statistics are computed per field mapping, because that is the unit you would actually fix.

Coverage reporting

curl http://localhost:8000/api/source/connections/1/match-stats

Each entry in the response is a FieldMatchStats:

{
  "mapping_id": 7,
  "source_table": "employees",
  "source_field": "marital_status",
  "ref_dimension": "marital_status",
  "total_values": 3679,
  "matched_values": 3120,
  "unmatched_values": 559,
  "match_rate": 0.848,
  "top_unmatched": [ /* UnmatchedValuePreview[] */ ],
  "top_matched":   [ /* MatchedValuePreview[] */ ]
}

Match rate is per mapping, not global

A mapping with no captured samples reports zero values and is filtered out of the dashboard rather than shown as a misleading 0/0. If a field you expect to see is missing from Match Insights, it has no samples yet — go capture them.

Next steps