Report Builder

Overview of the reporting builder syntax.

Zylo allows for users to build their own reports by combining multiple different resources such as Applications, Payments, etc.

Reports are built by creating JSON objects that specify:

  • Which resources should be combined.
  • How the fields should be displayed/aggregated
  • How the results should be sorted, filtered, and limited.

Difference between Query and Reporting

The biggest difference between Query and Reporting builders is that Reporting can join multiple resources together into a single report. Due to this, Reports must be submitted as a job and then downloaded, while Queries can be an HTTP request.

Another difference is minor syntax rules, such as that the primary resource in an Reporting Builder cannot have a op type field if it has joined resources.

Reporting Syntax

Below is an example of a report JSON object to get the "Top 10 Most Expensive Applications". Details on the report syntax are beneath the example.

{
    "builder": {
        "applications": {
            "fields": [
                {
                    "name": "app_label",
                    "as": "label"
                }
            ],
            "with": {
                "payments": {
                    "fields": [
                        {
                            "name": "amount",
                            "op": "sum",
                            "as": "total_amount"
                        }
                    ]
                }
            }
        }
    },
    "sort": "+total_amount",
    "limit": 10,
    "skip": 0
}

Example of a CSV response from the above report:

"label","total_amount"
"OpenAI",20020
"Salesforce",1072
"Facebook",501

Let's break down what is happening in the report object above.

Top-Level Structure

Every report object follows the same structure:

{
    "builder": {
        ...
    },
    "sort": "+total_amount",
    "limit": 10,
    "skip": 0
}
  • builder - This field is used to tell Zylo what resources you want to combine to make your report. Each report needs to have the builder field to work.
  • sort - Used to sort the report. In the above example, it will sort the report by the "total_amount" field.
  • limit - Used to set the limit of returned rows in the report. If not provided, it will default to the API limit (50 results).
  • skip - Used for paginating the results. If not provided, it will default to the API limit (0).

Builder Syntax

The builder object defines your primary resource and any related resource.

Primary Resource

{
    "builder": {
        "applications": {
            ...
        }
    },
    "sort": "+total_amount",
    "limit": 10,
    "skip": 0
}
  • applications - This is the primary resource for your report. Any other resources in the builder will be combined to the primary resource. It can be any resource with a relationship (see @TODO link to relations) for all possible resources.

Resource Fields

{
    "builder": {
        "applications": {
            "fields": [
                {
                    "name": "app_label",
                    "as": "label"
                }
            ],
            ...
        }
    },
    "sort": "+total_amount",
    "limit": 10,
    "skip": 0
}
  • fields - These are the fields from the resource that are brought into the report. In the above example, the report is retrieving the "app_label" field from the applications resource with the name parameter. The as parameter is an alias, which means the app_label will come back as label in the report.

Combining Resources

{
    "builder": {
        "applications": {
            "fields": [
                {
                    "name": "app_label",
                    "as": "label"
                }
            ]
            "with": {
                ...
            }
        }
    },
    "sort": "+total_amount",
    "limit": 10,
    "skip": 0
}
  • with - This field is used to combine other resources to your primary resource. Any resource under the with field will be joined to the primary resource. To see all the resources that can be joined to your primary resource, check out the relations page (@todo relation page)

Related Resources

{
    "builder": {
        "applications": {
            "fields": [
                {
                    "name": "app_label",
                    "as": "label"
                }
            ]
            "with": {
                "payments": {
                    ...
                }
            }
        }
    },
    "sort": "+total_amount",
    "limit": 10,
    "skip": 0
}
  • payments - This is a resource that will be combined with the primary resource applications. On the relationships page (@todo relationship page), there is a relationship between applications and payments.
{
    "builder": {
        "applications": {
            "fields": [
                {
                    "name": "app_label",
                    "as": "label"
                }
            ]
            "with": {
                "payments": {
                    "fields": [
                        {
                            "name": "amount",
                            "op": "sum",
                            "as": "total_amount
                        }
                    ],
                }
            }
        }
    },
    "sort": "+total_amount",
    "limit": 10,
    "skip": 0
}
  • fields - These are the fields from the resource that are brought into the report. In the above example, the report is retrieving the "amount" field from the payments resource with the name parameter. The as parameter is an alias, which means the amount will come back as total_amount in the report. The op parameter indicates how the field should be aggregated to primary resource. Since the relationship between applications and payments is "One-to-"Many", we must aggregate payments.

The list of available op operations are:

  • sum - Sum up the values.
  • max - Retrieve the max value.
  • min - Retrieve the min value.
  • avg - Calculate the average value.
  • count - Count the occurrences of the value.

How op Works with Different Relationships

If the relationship between the primary resource and the related resource is "One-to-Many", then all the fields within the related resource need to have an op field.

If the relationship between the primary resource and the related source is "Many-to-One", then none of the fields within the related resource can have an op field.

The op field is used to aggregate results, so the relationship between the resources needs to be able to be grouped and aggregated.

Filtering

One advantage to using the report syntax is that you can filter down the results on multiple levels.

The filter syntax for the report is similar to the API filter syntax (@todo filtering syntax).

  • Filter down the entire report results set.
  • Filter down on a resource level.
  • Filter down on a field-level.

Below are use cases for each filtering type.

Report-Level Filtering

Report-level filtering are filters applied to the aggregated result of the report.

Using the above example report, a filter can be added to only retrieve labels that are equal to "Salesforce".

{
    "builder": {
        "applications": {
            "fields": [
                {
                    "name": "app_label",
                    "as": "label"
                }
            ]
            "with": {
                "payments": {
                    "fields": [
                        {
                            "name": "amount",
                            "op": "sum",
                            "as": "total_amount
                        }
                    ],
                }
            }
        }
    },
    "filters": {
        "label": "Salesforce"
    },
    "sort": "+total_amount",
    "limit": 10,
    "skip": 0
}

Take note where the filters value is positioned. It is on the top-level similar to the builder value. This indicates a report-level filter.

Resource-Level Filtering

Resouce-level filtering are filters that are applied to a single resource in the report.

Using the above example report, a filter can be added to only retrieve payments that are between 2025-01-01 and 2025-06-30.

{
    "builder": {
        "applications": {
            "fields": [
                {
                    "name": "app_label",
                    "as": "label"
                }
            ]
            "with": {
                "payments": {
                    "fields": [
                        {
                            "name": "amount",
                            "op": "sum",
                            "as": "total_amount
                        }
                    ],
                    "filters": {
                        "payment_date": "2025-01-01,gte,2025-06-30,lte"
                    }
                }
            }
        }
    },
    "sort": "+total_amount",
    "limit": 10,
    "skip": 0
}

Take note where the filters value is positioned. It is on the same level as fields under the payments resource. This indicates a resource-level filter.

Field-Level Filtering

Field-level filtering are filters that are applied to a single field within a resource.

Using the above example report, a filter can be added to only retrieve amount on payment that are payment_type of "AP".

{
    "builder": {
        "applications": {
            "fields": [
                {
                    "name": "app_label",
                    "as": "label"
                }
            ]
            "with": {
                "payments": {
                    "fields": [
                        "name": "amount",
                        "filters": {
                            "payment_type": "AP"
                        },
                        "op": "sum",
                        "as": "ap_amount"
                    ]
                }
            }
        }
    },
    "sort": "+ap_amount",
    "limit": 10,
    "skip": 0
}

The benefit to field-level filtering is that the filters can be applied on the same field to create multiple values.

For example, another field can be added for amount and another filter can be applied to only retrieve payment_type of "Expense".

{
    "builder": {
        "applications": {
            "fields": [
                {
                    "name": "app_label",
                    "as": "label"
                }
            ]
            "with": {
                "payments": {
                    "fields": [
                        {
                            "name": "amount",
                            "filters": {
                                "payment_type": "AP"
                            },
                            "op": "sum",
                            "as": "ap_amount"
                        },
                        {
                            "name": "amount",
                            "filters": {
                                "payment_type": "Expense",
                            },
                            "op": "sum",
                            "as": "expense_amount"
                        }
                    ]
                }
            }
        }
    },
    "sort": "+ap_amount",
    "limit": 10,
    "skip": 0
}

Take note where the filters value is positioned. It is on the same level as name for each of the fields. This indicates a field-level filter.