How to Set Up REST API Sync Protocol and Entity Routing
This guide explains how to orchestrate multi-step API workflows using endpoint chaining and data aggregation in the NAV-X Integration Framework.
What Is the Sync Protocol?
The sync protocol enables you to build multi-step API workflows where data from multiple related API endpoints is fetched and combined into a single import. The framework supports two patterns:
- Endpoint Chaining: A parent endpoint fetches a list of records, then for each parent record, child endpoints fetch related detail data. Parent and child rows are interleaved in the import buffer (e.g., fetch orders, then for each order fetch its line items).
- Data Aggregation: Multiple independent endpoints are fetched and merged horizontally by matching join keys (e.g., fetch customers from one endpoint and their account balances from another, then merge by customer ID).
When to Use Each Pattern
| Scenario | Use Chaining | Use Aggregation |
|---|---|---|
| Fetch headers and their line items from separate endpoints | Yes | - |
| Fetch a master record and its nested details via separate API calls | Yes | - |
| Combine data from two independent API sources by a shared key | - | Yes |
| Enrich primary data with supplementary data from a different API | - | Yes |
| Parent record ID is needed to construct the child endpoint URL | Yes | - |
| Both datasets use a common key but have no parent-child URL relationship | - | Yes |
Endpoint Chaining
How Chaining Works
Fetch all parent data from root endpoint (with pagination)
|
For each parent record:
|
+-- Write parent row to import buffer
|
+-- For each child endpoint (ordered by Chain Order):
|
+-- Resolve child URL using {{parent.<path>}} placeholders
|
+-- Fetch child data (with pagination)
|
+-- Write child rows to import buffer
|
Import pipeline processes interleaved parent + child rows
The result is an interleaved dataset where each parent row is immediately followed by its child rows, which maps naturally to Business Central's header/line document structure.
Configuring Endpoint Chaining
Step 1: Configure the Root (Parent) Endpoint
- Open the REST API Endpoint card for the parent endpoint
- Set the Connection Code, Resource Path, and Response Data Path as usual
- Leave Parent Endpoint Sort Order at 0 (this marks it as a root endpoint)
Step 2: Create a Child Endpoint
- Create a new REST API Endpoint for the child data
- Set the Parent Endpoint Sort Order to the Sorting Order of the root endpoint
- The Chain Configuration section becomes visible
Step 3: Configure the Chain Configuration
| Field | Description | Example |
|---|---|---|
| Parent Endpoint Sort Order | The Sorting Order of the parent endpoint. Non-zero values make this a child endpoint. | 1000 |
| Parent Key JSON Path | The JSON path in the parent response to extract the key value used for URL resolution. | id |
| Child URL Template | The URL template for child API calls. Use {{parent.<jsonpath>}} placeholders to insert values from the parent response. |
/orders/{{parent.id}}/lines |
| Chain Order | Execution order among sibling child endpoints. Lower numbers execute first. | 1 |
| Max Child Calls | Maximum number of child API calls to prevent runaway execution. Default is 500. | 500 |
Step 4: Set the Connection and Response Data Path
- Set the Connection Code for the child endpoint (can be the same or a different connection)
- Set the Response Data Path for the child endpoint response
Step 5: Run the Parent Integration
Run the import from the parent integration. The framework automatically:
- Fetches all parent data (handling pagination)
- For each parent record, resolves the child URL by replacing
{{parent.<path>}}placeholders with actual values - Fetches child data for each parent (handling pagination per child call)
- Interleaves parent and child rows in the import buffer
- Processes all rows through the standard import pipeline
Important: Child endpoints cannot be run directly. Always run the parent integration; it orchestrates all child fetches automatically.
Chaining Example: Orders and Line Items
Parent Endpoint (Sorting Order 1000):
| Field | Value |
|---|---|
| Connection Code | MYAPI |
| Resource Path | /api/v1/orders?status=open |
| Response Data Path | data |
| Parent Endpoint Sort Order | 0 (root) |
Child Endpoint (Sorting Order 1001):
| Field | Value |
|---|---|
| Connection Code | MYAPI |
| Parent Endpoint Sort Order | 1000 |
| Parent Key JSON Path | id |
| Child URL Template | /api/v1/orders/{{parent.id}}/lines |
| Response Data Path | data |
| Chain Order | 1 |
| Max Child Calls | 500 |
Result: For each order, the framework fetches its line items and produces an interleaved dataset suitable for Sales Header + Sales Line import.
Error Handling in Chains
When a child API call fails:
- The error is logged to telemetry
- The framework continues processing the remaining parent records by default
- You can subscribe to the
OnChildFetchErrorevent to customize error handling (e.g., stop processing on the first error) - If the Max Child Calls limit is reached for an endpoint, remaining parent rows skip that child endpoint and a warning is logged
Data Aggregation
How Aggregation Works
Fetch all primary endpoint data
|
For each aggregation source (ordered by Fetch Order):
|
+-- Fetch secondary endpoint data
|
+-- Index secondary data by Foreign Key
|
+-- For each primary record:
|
+-- Extract join key from primary record (Primary Key JSON Path)
|
+-- Look up matching secondary record by Foreign Key
|
+-- Merge secondary fields into primary record
|
Build import buffer from merged dataset
Configuring Data Aggregation
Step 1: Configure the Primary Endpoint
- Open the REST API Endpoint card for the primary integration
- Configure the endpoint normally (Connection Code, Resource Path, Response Data Path)
- Ensure Parent Endpoint Sort Order is 0 (root endpoint)
Step 2: Add Aggregation Sources
In the Data Aggregation section of the endpoint card, add one or more aggregation source records:
| Field | Description | Example |
|---|---|---|
| Source Endpoint Sorting Order | The Sorting Order of the secondary integration whose endpoint provides the data to merge. | 2000 |
| Join Type | Left Join keeps all primary records even without a match. Inner Join removes primary records that have no matching secondary record. | Left Join |
| Primary Key JSON Path | The JSON path in the primary record to extract the join key. | customerId |
| Foreign Key JSON Path | The JSON path in the secondary record to index by. | id |
| Fetch Order | The order in which secondary sources are fetched and merged. Lower numbers are processed first. | 1 |
Step 3: Configure the Secondary Integration
The secondary integration referenced by Source Endpoint Sorting Order must:
- Be a REST API type integration
- Have its own endpoint configured with Connection Code and Resource Path
- Not reference itself as an aggregation source
Step 4: Run the Primary Integration
Run the import from the primary integration. The framework fetches data from all sources and merges them.
Aggregation Example: Customers with Account Balances
Primary Endpoint (Integration 1000):
| Field | Value |
|---|---|
| Resource Path | /api/v1/customers |
| Response Data Path | customers |
Returns: [{"id": "C001", "name": "Contoso", "city": "Atlanta"}, ...]
Secondary Endpoint (Integration 2000):
| Field | Value |
|---|---|
| Resource Path | /api/v1/balances |
| Response Data Path | balances |
Returns: [{"customerId": "C001", "balance": 15000.00, "currency": "USD"}, ...]
Aggregation Source on Primary Endpoint:
| Field | Value |
|---|---|
| Source Endpoint Sorting Order | 2000 |
| Join Type | Left Join |
| Primary Key JSON Path | id |
| Foreign Key JSON Path | customerId |
| Fetch Order | 1 |
Result: Each customer record is enriched with balance and currency fields from the balances API. Customers without a matching balance record retain their original fields (Left Join behavior).
Join Types
| Join Type | Behavior |
|---|---|
| Left Join | All primary records are kept. If no matching secondary record is found, the primary record remains unchanged. |
| Inner Join | Only primary records with a matching secondary record are kept. Unmatched primary records are removed from the dataset. |
Field Name Conflicts
When merging secondary data into primary data, if a secondary field has the same name as an existing primary field, the secondary field is prefixed with _merged_ to avoid overwriting. For example, if both sources have a field called name, the secondary field becomes _merged_name in the merged dataset.
Use Cases
Multi-Entity Document Import
Fetch sales order headers from one endpoint and line items from another, interleaving them for document import:
- Parent endpoint:
/api/orders(maps to Sales Header) - Child endpoint:
/api/orders/{{parent.id}}/lines(maps to Sales Line)
Data Enrichment
Enrich a customer list with credit scores from a separate credit-check API:
- Primary endpoint:
/api/customers(customer master data) - Aggregation source:
/api/credit-scores(credit data keyed by customer ID)
Multi-Source Inventory Sync
Combine inventory levels from multiple warehouses into a single dataset:
- Primary endpoint:
/api/warehouse-a/inventory - Aggregation source 1:
/api/warehouse-b/inventory(merge by item SKU) - Aggregation source 2:
/api/warehouse-c/inventory(merge by item SKU)
Troubleshooting
"This endpoint is configured as a child endpoint and cannot be run directly"
You attempted to run a child endpoint directly. Run the parent integration instead; it orchestrates all child fetches.
"Cannot resolve placeholder {{parent.path}}" Error
The JSON path in the {{parent.<path>}} placeholder does not exist in the parent JSON object. Verify the path matches the parent response structure.
"Maximum child calls reached" Warning
The framework hit the Max Child Calls limit for a child endpoint. Increase the limit if you expect more parent records, or reduce the dataset with filters on the parent endpoint.
"Cannot reference the same integration as the primary endpoint"
You attempted to add the primary endpoint as its own aggregation source. The Source Endpoint Sorting Order must reference a different integration.