Mappings API¶
The Mappings API provides endpoints for creating and managing column mappings that normalize data from different Excel file formats and servicers.
Overview¶
CalcBridge's mapping system enables:
- Column Normalization: Map varying column names to standard fields
- Servicer Profiles: Pre-built mappings for common data providers
- Custom Mappings: Create tenant-specific mapping configurations
- Validation: Ensure uploaded data matches expected schemas
Endpoints¶
Create Mapping¶
Create a new mapping configuration.
Request Body¶
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Mapping name (1-200 chars) |
description | string | No | Mapping description |
source_type | string | Yes | Source: clo_holdings, servicer_snapshot, custom |
profile_id | string | No | Base profile to extend |
mappings | object | Yes | Column mapping definitions |
transforms | array[Transform] | No | Data transformations |
validation_rules | array[Rule] | No | Validation rules |
Mappings Object¶
A dictionary mapping source column names to target field definitions:
{
"mappings": {
"source_column_name": {
"target": "standard_field_name",
"type": "string|number|date|boolean",
"required": true,
"default": null,
"aliases": ["alt_name_1", "alt_name_2"]
}
}
}
Transform Object¶
| Field | Type | Required | Description |
|---|---|---|---|
field | string | Yes | Field to transform |
operation | string | Yes | Transform operation |
parameters | object | No | Operation parameters |
Example Request¶
curl -X POST https://api.calcbridge.io/api/v1/mappings \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Custom CLO Holdings",
"description": "Mapping for ABC Servicer holdings format",
"source_type": "clo_holdings",
"profile_id": "standard_clo",
"mappings": {
"CUSIP/ISIN": {
"target": "cusip",
"type": "string",
"required": true,
"aliases": ["Security ID", "Identifier"]
},
"Issuer Name": {
"target": "borrower_name",
"type": "string",
"required": true,
"aliases": ["Borrower", "Company Name", "Obligor"]
},
"Face Value": {
"target": "par_value",
"type": "number",
"required": true,
"aliases": ["Par", "Principal", "Notional"]
},
"Current Price": {
"target": "price",
"type": "number",
"required": false,
"default": 100.0,
"aliases": ["Mark", "Market Price", "Bid Price"]
},
"Credit Rating": {
"target": "rating_composite",
"type": "string",
"required": false,
"aliases": ["Rating", "Moodys Rating", "S&P Rating"]
}
},
"transforms": [
{
"field": "par_value",
"operation": "multiply",
"parameters": {"factor": 1000}
},
{
"field": "price",
"operation": "divide",
"parameters": {"divisor": 100}
}
],
"validation_rules": [
{
"field": "par_value",
"rule": "positive",
"message": "Par value must be positive"
},
{
"field": "price",
"rule": "range",
"parameters": {"min": 0, "max": 200},
"message": "Price must be between 0 and 200"
}
]
}'
import requests
response = requests.post(
"https://api.calcbridge.io/api/v1/mappings",
headers={"Authorization": f"Bearer {token}"},
json={
"name": "Custom CLO Holdings",
"description": "Mapping for ABC Servicer holdings format",
"source_type": "clo_holdings",
"profile_id": "standard_clo",
"mappings": {
"CUSIP/ISIN": {
"target": "cusip",
"type": "string",
"required": True,
"aliases": ["Security ID", "Identifier"]
},
"Issuer Name": {
"target": "borrower_name",
"type": "string",
"required": True
},
"Face Value": {
"target": "par_value",
"type": "number",
"required": True
}
},
"transforms": [
{
"field": "par_value",
"operation": "multiply",
"parameters": {"factor": 1000}
}
]
}
)
mapping = response.json()
print(f"Created mapping: {mapping['id']}")
const response = await fetch('https://api.calcbridge.io/api/v1/mappings', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Custom CLO Holdings',
description: 'Mapping for ABC Servicer holdings format',
source_type: 'clo_holdings',
profile_id: 'standard_clo',
mappings: {
'CUSIP/ISIN': {
target: 'cusip',
type: 'string',
required: true,
aliases: ['Security ID', 'Identifier']
},
'Issuer Name': {
target: 'borrower_name',
type: 'string',
required: true
},
'Face Value': {
target: 'par_value',
type: 'number',
required: true
}
}
})
});
const mapping = await response.json();
console.log('Created mapping:', mapping.id);
Response¶
{
"id": "ff0e8400-e29b-41d4-a716-446655440070",
"name": "Custom CLO Holdings",
"description": "Mapping for ABC Servicer holdings format",
"source_type": "clo_holdings",
"profile_id": "standard_clo",
"mappings": {
"CUSIP/ISIN": {
"target": "cusip",
"type": "string",
"required": true,
"aliases": ["Security ID", "Identifier"]
},
"Issuer Name": {
"target": "borrower_name",
"type": "string",
"required": true,
"aliases": ["Borrower", "Company Name", "Obligor"]
},
"Face Value": {
"target": "par_value",
"type": "number",
"required": true,
"aliases": ["Par", "Principal", "Notional"]
},
"Current Price": {
"target": "price",
"type": "number",
"required": false,
"default": 100.0,
"aliases": ["Mark", "Market Price", "Bid Price"]
},
"Credit Rating": {
"target": "rating_composite",
"type": "string",
"required": false,
"aliases": ["Rating", "Moodys Rating", "S&P Rating"]
}
},
"transforms": [
{
"field": "par_value",
"operation": "multiply",
"parameters": {"factor": 1000}
},
{
"field": "price",
"operation": "divide",
"parameters": {"divisor": 100}
}
],
"validation_rules": [
{
"field": "par_value",
"rule": "positive",
"message": "Par value must be positive"
},
{
"field": "price",
"rule": "range",
"parameters": {"min": 0, "max": 200},
"message": "Price must be between 0 and 200"
}
],
"created_by": "user@example.com",
"created_at": "2026-01-25T10:30:00Z",
"updated_at": "2026-01-25T10:30:00Z"
}
Error Responses¶
| Status | Error | Description |
|---|---|---|
400 | Invalid source type | Unknown source_type value |
400 | Invalid mapping configuration | Mapping structure is invalid |
404 | Profile not found | Invalid profile_id |
409 | Mapping already exists | Duplicate mapping name |
List Mappings¶
Get all mapping configurations for the current tenant.
Query Parameters¶
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number |
page_size | integer | 20 | Items per page |
source_type | string | - | Filter by source type |
search | string | - | Search by name |
include_system | boolean | false | Include system/built-in mappings |
Example Request¶
Response¶
{
"items": [
{
"id": "standard_clo",
"name": "Standard CLO Holdings",
"description": "Default mapping for CLO holdings data",
"source_type": "clo_holdings",
"is_system": true,
"field_count": 25,
"created_at": "2025-01-01T00:00:00Z"
},
{
"id": "ff0e8400-e29b-41d4-a716-446655440070",
"name": "Custom CLO Holdings",
"description": "Mapping for ABC Servicer holdings format",
"source_type": "clo_holdings",
"is_system": false,
"profile_id": "standard_clo",
"field_count": 5,
"created_by": "user@example.com",
"created_at": "2026-01-25T10:30:00Z"
}
],
"total": 8,
"page": 1,
"page_size": 20,
"total_pages": 1
}
Get Mapping¶
Retrieve a specific mapping configuration.
Path Parameters¶
| Parameter | Type | Description |
|---|---|---|
mapping_id | string | Mapping identifier |
Query Parameters¶
| Parameter | Type | Default | Description |
|---|---|---|---|
resolve_profile | boolean | false | Merge with base profile mappings |
Example Request¶
const response = await fetch(
`https://api.calcbridge.io/api/v1/mappings/${mappingId}?resolve_profile=true`,
{ headers: { 'Authorization': `Bearer ${token}` } }
);
const mapping = await response.json();
console.log('Name:', mapping.name);
console.log('Fields:', Object.keys(mapping.mappings).length);
Response¶
{
"id": "ff0e8400-e29b-41d4-a716-446655440070",
"name": "Custom CLO Holdings",
"description": "Mapping for ABC Servicer holdings format",
"source_type": "clo_holdings",
"profile_id": "standard_clo",
"mappings": {
"CUSIP/ISIN": {
"target": "cusip",
"type": "string",
"required": true,
"aliases": ["Security ID", "Identifier"]
},
"Issuer Name": {
"target": "borrower_name",
"type": "string",
"required": true,
"aliases": ["Borrower", "Company Name", "Obligor"]
}
},
"resolved_mappings": {
"cusip": {
"sources": ["CUSIP/ISIN", "Security ID", "Identifier"],
"type": "string",
"required": true
},
"borrower_name": {
"sources": ["Issuer Name", "Borrower", "Company Name", "Obligor"],
"type": "string",
"required": true
},
"par_value": {
"sources": ["Face Value", "Par", "Principal", "Notional"],
"type": "number",
"required": true
}
},
"transforms": [],
"validation_rules": [],
"created_by": "user@example.com",
"created_at": "2026-01-25T10:30:00Z",
"updated_at": "2026-01-25T10:30:00Z"
}
Update Mapping¶
Update an existing mapping configuration.
Path Parameters¶
| Parameter | Type | Description |
|---|---|---|
mapping_id | string | Mapping identifier |
Request Body¶
All fields are optional. Only provided fields are updated.
| Field | Type | Description |
|---|---|---|
name | string | Mapping name |
description | string | Mapping description |
mappings | object | Column mappings |
transforms | array[Transform] | Data transformations |
validation_rules | array[Rule] | Validation rules |
Example Request¶
curl -X PATCH https://api.calcbridge.io/api/v1/mappings/ff0e8400-e29b-41d4-a716-446655440070 \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"mappings": {
"CUSIP/ISIN": {
"target": "cusip",
"type": "string",
"required": true,
"aliases": ["Security ID", "Identifier", "CUSIP"]
}
}
}'
import requests
response = requests.patch(
f"https://api.calcbridge.io/api/v1/mappings/{mapping_id}",
headers={"Authorization": f"Bearer {token}"},
json={
"mappings": {
"CUSIP/ISIN": {
"target": "cusip",
"type": "string",
"required": True,
"aliases": ["Security ID", "Identifier", "CUSIP"]
}
}
}
)
mapping = response.json()
print(f"Updated: {mapping['name']}")
const response = await fetch(
`https://api.calcbridge.io/api/v1/mappings/${mappingId}`,
{
method: 'PATCH',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
mappings: {
'CUSIP/ISIN': {
target: 'cusip',
type: 'string',
required: true,
aliases: ['Security ID', 'Identifier', 'CUSIP']
}
}
})
}
);
const mapping = await response.json();
console.log('Updated:', mapping.name);
Response¶
Returns the updated mapping object.
Delete Mapping¶
Delete a mapping configuration.
Path Parameters¶
| Parameter | Type | Description |
|---|---|---|
mapping_id | string | Mapping identifier |
Example Request¶
Response¶
Returns 204 No Content on success.
Error Responses¶
| Status | Error | Description |
|---|---|---|
403 | Cannot delete system mapping | System mappings are read-only |
404 | Mapping not found | Invalid mapping_id |
409 | Mapping in use | Mapping is referenced by workbooks |
Validate Mapping¶
Test a mapping against sample data.
Path Parameters¶
| Parameter | Type | Description |
|---|---|---|
mapping_id | string | Mapping identifier |
Request Body¶
| Field | Type | Required | Description |
|---|---|---|---|
sample_data | array[object] | Yes | Sample rows to validate |
strict | boolean | No | Fail on any warning (default: false) |
Example Request¶
curl -X POST https://api.calcbridge.io/api/v1/mappings/ff0e8400-e29b-41d4-a716-446655440070/validate \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"sample_data": [
{
"CUSIP/ISIN": "ABC123DEF",
"Issuer Name": "Acme Corp",
"Face Value": 500,
"Current Price": 99.5
},
{
"CUSIP/ISIN": "XYZ789GHI",
"Issuer Name": "Tech Industries",
"Face Value": 750,
"Current Price": 98.25
}
],
"strict": false
}'
Response¶
{
"valid": true,
"rows_processed": 2,
"rows_valid": 2,
"rows_invalid": 0,
"mapped_data": [
{
"cusip": "ABC123DEF",
"borrower_name": "Acme Corp",
"par_value": 500000,
"price": 0.995
},
{
"cusip": "XYZ789GHI",
"borrower_name": "Tech Industries",
"par_value": 750000,
"price": 0.9825
}
],
"warnings": [],
"errors": [],
"unmapped_columns": [],
"missing_required_columns": []
}
Source Types¶
| Type | Description | Standard Fields |
|---|---|---|
clo_holdings | CLO portfolio holdings | cusip, borrower_name, par_value, price, spread, rating |
servicer_snapshot | Servicer operational data | loan_id, principal_balance, interest_rate, days_open |
custom | Custom schema | User-defined |
Transform Operations¶
| Operation | Description | Parameters |
|---|---|---|
multiply | Multiply by factor | factor: number |
divide | Divide by divisor | divisor: number |
round | Round to decimals | decimals: number |
uppercase | Convert to uppercase | - |
lowercase | Convert to lowercase | - |
trim | Remove whitespace | - |
replace | Replace substring | pattern: string, replacement: string |
date_format | Parse/format date | input_format: string, output_format: string |
lookup | Map values | mapping: object |
Validation Rules¶
| Rule | Description | Parameters |
|---|---|---|
required | Field must have value | - |
positive | Value must be positive | - |
non_negative | Value must be >= 0 | - |
range | Value must be in range | min: number, max: number |
regex | Match pattern | pattern: string |
length | String length | min: number, max: number |
enum | Value must be in list | values: array |
unique | Value must be unique | - |
Built-in Profiles¶
standard_clo¶
Standard CLO holdings mapping with common field names.
everest_extended¶
Extended mapping for Everest servicer format.
hartford_trpa_holdings¶
Mapping for Hartford TRPA holdings format.
invesco_clo_holdings¶
Mapping for Invesco CLO holdings format.
Best Practices¶
- Extend existing profiles: Build on standard mappings rather than starting from scratch
- Use aliases generously: Cover all possible column name variations
- Validate before production: Test mappings with representative sample data
- Document transformations: Explain why transformations are needed
- Version your mappings: Use descriptive names when formats change
- Handle optional fields: Set sensible defaults for missing data