Solved! The solution required proper column aliasing in joins, API query payload structure with table aliases, and custom table schema review for best practices:
1. Column Aliasing in Joins: Use fully qualified column references with table aliases:
POST /api/v1/reports/ad-hoc/execute
{
"query": {
"from": [
{"table": "sales_orders", "alias": "so"},
{"table": "custom_order_attributes", "alias": "coa"}
],
"join": {
"type": "inner",
"on": "so.order_id = coa.order_id"
},
"select": [
"so.order_id",
"so.customer_name",
"coa.custom_attribute"
]
}
}
2. API Query Payload Structure: For complex joins with multiple custom tables:
POST /api/v1/reports/ad-hoc/execute
{
"query": {
"from": [
{"table": "sales_orders", "alias": "so"},
{"table": "custom_order_attributes", "alias": "coa"},
{"table": "custom_order_pricing", "alias": "cop"}
],
"joins": [
{
"type": "inner",
"table": "coa",
"on": "so.order_id = coa.order_id"
},
{
"type": "left",
"table": "cop",
"on": "so.order_id = cop.order_id"
}
],
"select": [
"so.order_id AS order_number",
"coa.custom_attribute",
"cop.custom_price"
]
}
}
Note the joins array (plural) for multiple joins - this is required in 7.0 API.
3. Custom Table Schema Review: Documented naming conventions for future custom tables:
- Prefix all custom table names with `custom_
- Use descriptive column names that include context (e.g.,
attribute_value instead of just value)
- Avoid reusing standard ERP column names unless intentionally matching for joins
- Document join keys in custom table metadata
The key was understanding that the ad-hoc reporting API requires explicit table aliases in the from array (not just table names) and SQL-style join conditions with full qualification. The "select" array supports AS aliases for output column naming. Without proper aliasing, any column name appearing in multiple tables causes ambiguity errors. This structure works for any number of joined tables.