We’re integrating our corporate travel booking system (Concur) with D365 Travel Management module via middleware. The integration runs successfully, but several critical expense fields aren’t populating in D365.
The external booking system sends complete JSON payloads with hotel, flight, and meal expenses. When we check the imported travel expenses in D365, fields like expense category, merchant name, and tax amounts are blank even though they’re present in the API payload.
We’ve verified the middleware logs show all required fields in the request, but somehow they’re not mapping to D365. Has anyone dealt with field mapping issues between external booking systems and D365 Travel Management? The expenses import but incomplete data is causing approval delays.
Yes, always check OData metadata! The API docs sometimes lag behind actual implementation. Go to your D365 instance and navigate to: https://yourinstance.dynamics.com/data/$metadata
Search for TravelExpense entity and you’ll see the exact field names and types. I bet your middleware is using displayName instead of the actual API field name. For example, ‘Expense Category’ in the UI might be ‘ExpenseCategoryId’ in the API, and it expects a GUID reference, not a text value.
What’s your middleware platform? Some integration tools have pre-built connectors for D365 that handle field mapping automatically. If you’re using custom code, you need to match D365’s data entity structure exactly. The Travel Expense entity has required vs optional fields - missing required fields causes the entire record to fail silently in some versions.
Another thing to check - are you using the correct data entity? D365 has multiple entities for travel expenses: TravelExpenseHeader, TravelExpenseLines, and TravelExpenseCategories. If your booking system sends everything in one payload, you might need to split it across multiple entity calls. Header fields go to one entity, line items to another. The merchant name specifically goes on the line level, not header.
I worked through this exact scenario last month. Here’s what was happening with our external booking system integration:
Issue Root Cause:
The API payload from your booking system contains all required fields, but the field mapping in Logic Apps wasn’t handling the nested JSON structure correctly. D365 Travel Management uses a hierarchical data model that requires proper entity relationships.
Solution - Address All Three Focus Areas:
- External Booking System Payload Structure:
Your Concur system sends flat JSON, but D365 expects hierarchical entities. You need to parse the payload and route different sections to different entities:
// Split this flat structure:
{"expenseType":"Hotel","merchant":"Marriott","amount":250.00,"tax":25.00}
// Into header + lines with proper references
- API Payload Transformation:
In Logic Apps, add a Parse JSON action before the D365 connector. Map fields using OData metadata names (not display names):
- Concur ‘expenseType’ → D365 ‘ExpenseCategoryId’ (lookup to Categories entity)
- Concur ‘merchant’ → D365 ‘MerchantName’ (on TravelExpenseLines, not Header)
- Concur ‘tax’ → D365 ‘TaxAmount’ (decimal format, not string)
- Required Fields Validation:
D365 silently drops records missing required fields. Add these mandatory fields to your transformation:
- LegalEntity (your company code)
- Worker (employee reference - use WorkerId from personnel number)
- TransactionDate (ISO 8601 format)
- CurrencyCode (must match your org’s default or booking currency)
The merchant name issue specifically: This field is on TravelExpenseLines entity, not the header. Your middleware probably posts to TravelExpenseHeader only. You need two API calls:
- POST to TravelExpenseHeader (creates expense report)
- POST to TravelExpenseLines with HeaderId reference (adds line items with merchant details)
Implementation Steps:
- Update Logic Apps to use Parse JSON action
- Add Compose action to build proper entity structure
- Split into two D365 connector actions (header then lines)
- Add error handling to catch validation failures
- Enable detailed logging to capture field-level errors
Test with a single expense first. Once merchant name and categories populate correctly, batch processing will work. The tax amount issue is usually a data type mismatch - ensure you’re sending decimal, not string.
I’ve seen this with external booking integrations. The issue is usually in how the middleware transforms the payload structure. D365 Travel Management expects specific field names and data types that might not match your booking system’s format.
Check your middleware transformation layer - are you mapping expenseCategory vs ExpenseType? D365 is case-sensitive and uses specific naming conventions. Also verify the merchant name field isn’t exceeding character limits, which causes silent truncation.
We’re using Azure Logic Apps as middleware. The connector is Microsoft’s official D365 connector. I checked the transformation step and we’re mapping to what we thought were correct field names. Could there be a mismatch between the API documentation and actual entity field names? Should I be looking at the OData metadata to verify exact field names?