Skip to main content

Holistics API (2.0)

Download OpenAPI specification:Download

Holistics Support: [email protected]

Introduction

Using these APIs, you are able to programmatically work with Holistics. For example:

  • You can programmatically retrieve CSV, XLSX and JSON data from any Holistics report.
  • You can use our API to trigger execution of an data schedule, transform or data import. etc...

This is useful especially if you need to pass live data to your other applications. For example, you may want to feed a live CSV endpoint of your user segmentation list into your email marketing application so that you can always get fresh data upon retrieving them.

NOTE: Please note that Holistics API version does not correlate with Holistics feature version. For example, Holistics API v2 can support operations on all Holistics 2.0, 2.7 and 3.0.

Data center regions

API data is limited to specific regions. For example, if your want to access data from the US region, you need to use the subdomain https://us.holistics.io. Below is the API subdomain corresponding with data center's region.

Region URL
Asia-Pacific (APAC) https://secure.holistics.io/api/v2
Europe (EU) https://eu.holistics.io/api/v2
United States (US) https://us.holistics.io/api/v2

If you do not know which data center your Holistics' account is currently on, check out this docs.

Authentication

Allow user API access

In order for a user to use API key, they must be allowed to use API access in User Management. Edit the User and tick the checkbox Allow API access:

API Key

The allowed user can then visit User Settings and generate a new API key.

Retrieve API key

Then, they can set the API key in the request header when calling Holistics APIs.

API Request Authorization

Set the HTTP header X-Holistics-Key to your API key. Example:

curl -X POST \
  -H "Accept:application/json" \
  -H "Content-Type:application/json" \
  -H "X-Holistics-Key:$your_api_key" \
  https://secure.holistics.io/api/v2/users/me

Errors

HTTP Code Description
200 - OK Everything worked as expected.
400 - Bad Request The request was unacceptable due to missing request parameter or wrong request structure.
401 - Unauthorized No valid API key provided.
403 - Forbidden The API Key owner does not have permission to perform the request.
404 - Not Found The requested resource does not exist or cannot found.
422 - Unprocessable Entity Cannot process the request parameter due to semantic errors. See the message for more detail.
429 - Too Many Requests Too many requests were sent.
500 - Internal Holistics Error Something went wrong on Holistics side (rarely).
Error type Description
BaseError Base schema for all types of errors.
RecordError Error when saving a record.
InvalidParameterError One or more parameters are missing or do not satisfy the endpoint's requirements.
AuthError Could not authorize current user, typically due to missing or expired authentication token.
MaintenanceError Your admins has set Holistics to Maintenance mode
SubscriptionError The subscription of the tenant does not have permission to access the resource.
PermissionDeniedError The API Key owner does not have permission to access a resource.
InternalHolisticsError Internal Holistics error. Please provide us the debug id in error message so we can investigate further.
InvalidOperationError Can not process your action due to system constraints.

Base Error (All errors inherit from this):

type
required
string
Enum: "BaseError" "RecordError" "InvalidParameterError" "AuthError" "MaintenanceError" "SubscriptionError" "PermissionDeniedError" "InternalHolisticsError" "RateLimitExceedError" "InvalidOperationError" "InvalidEmailsInvitationError"
message
required
string
object

Details about this error.

{
  • "type": "BaseError",
  • "message": "string",
  • "details": {
    }
}

Record Error:

type
required
string
Enum: "BaseError" "RecordError" "InvalidParameterError" "AuthError" "MaintenanceError" "SubscriptionError" "PermissionDeniedError" "InternalHolisticsError" "RateLimitExceedError" "InvalidOperationError" "InvalidEmailsInvitationError" "RecordError"
object

Detailed errors of the record being created/updated.

message
required
string
object

Details about this error.

{
  • "type": "BaseError",
  • "record": {
    },
  • "message": "string",
  • "details": {
    }
}

Rate-Limiting

Overview

To prevent DDoS attacks, every API request should go through our rate limit layer which will throttle the request if a user exceeds limit quotas. The rate limit is based on user and endpoint, quotas (per time frame) which maybe different for each endpoint are divided by levels as the table below:

Level Quota Note
Level 1 120 At least every API request is this level
Level 2 60 Request requires a lot of resource
Level 3 20 Request that heavily affect our server's resources

Return Header And Status Code

If a request is blocked because of it exceed the limit quota, status code is set to 429: Too Many Requests

Every API request returns header contains the following fields:

RateLimit-Limit: your_limit_quota_of_endpoint
RateLimit-Remaining: remaining_requests_until_reset
RateLimit-Reset: next_reset_time
Retry-After: next_reset_time(only available when status code is 429)

Tutorials

Get Reporting Data via API

Introduction

This short tutorial shows you how to use the Holistics APIs to get report data in raw tabular form (CSV, Excel).

This tutorial uses Ruby, but you can easily use any other language for it.

INFO

We'll be working on client libraries wrapper around our API. Once done, using the APIs will be simpler by just making a few function calls.

Mechanism

Since Holistics uses a async job queuing system to process job, you can't make a single API call to retrieve the results. We need to submit an 'export request', then wait for the export job to finish, and then make an API call to download the results.

API Endpoints used:

Steps

Let's go through the steps here.

1. Setting Up API Key

Please see guide to set up and retrieve your API key.

2. Initial code structure

To make things simple and reusable, we'll wrap our code around a HolisticsAPI class. We'll also use the httprb gem to handle making HTTP calls.

require 'http'
class HolisticsAPI
  def initialize(api_key, host: 'secure.holistics.io')
    @api_key = api_key
    @api_url = "https://#{host}/api/v2"
    @http = HTTP.headers({'X-Holistics-Key' => @api_key})
  end
end

3. (Optional) Get Filters ID for your Dashboard Export

If you want to include Filters in your export, you will need to get their Filter IDs. Please follow these steps to obtain them:

1. Get your Dashboard ID

The Dashboard ID can be retrieved by looking at its URL in the browser. In this sample URL below, the Dashboard ID would be 16076.

2. Get Filter ID

Supposed that your dashboard has a sets of filters like the one below. Let's get the ID of the Date filter to include it in our export.

We will use Get Dashboard API for this purpose. Let's call the API with the Dashboard ID from step 1.

curl --location --request GET 'https://secure.holistics.io/api/v2/dashboards/{your_dashboard_id}' \
--header 'X-Holistics-Key: your_API_key' \
--header 'Content-Type: application/json' \

The response would be quite lengthy, but you just need to find the dynamic_filters field to get the Filter ID.

You can then use this Filter ID in the next step.

4. Submit widget export request

Make sure you have the DashboardWidget ID in hand. The widget ID can be retrieved by opening up the widget in the dashboard, and look at the _e parameter in the URL. For example, 4175 is the widget ID of the below.

https://secure.holistics.io/dashboards/v3/12345-some-dashboard/?_e=4175

INFO

(Optional) Include filter conditions in your export If you wish to include a filter condition in your export, first refer to step 3 to get your desired Filter ID.

Then, append dashboard_filter_conditions in your request body.

  • dynamic_filter_id is the Filter ID from step 3.
  • condition:
    • operator: refer to Data Modeling Condition for all available operators.
    • values: is an array of strings or integers that go with the operator.

For example, assuming that you have completed step 3, to apply a Date Filter that filters data from 2 months ago to the export, simply include this snippet to your request.

{
  "dashboard_filter_conditions": [
    {
      "dynamic_filter_id": 2335,
      "condition": {
        "operator": "matches",
        "values": [
          "2 months ago"
        ]
      }
    }
  ]
}

Then we make the call to submit widget export:

class HolisticsAPI
  # ...

  # output: 'csv' or 'excel'
  def submit_report(widget_id, output: 'csv')
    url = @api_url + "/dashboard_widgets/" + widget_id.to_s + "/submit_export"

    response = @http.post(url, json: {output: output})
    res = JSON.parse(response.to_s)

    if response.code == 200
      res['job']['id']
    else
      raise StandardError.new(res['message'])
    end
  end
end

If successful, this method returns the job ID of the job created.

5. Waiting for job to complete

The job will the go to the queue system waiting to be processed. This method below will continuously poll the job's metadata until it is either success, or failure.

class HolisticsAPI
  # ...

  def wait_for_job_status(job_id)
    url = @api_url + "/jobs/" + job_id.to_s

    while true do
      response = @http.get(url)
      res = JSON.parse(response.to_s)

      raise StandardError.new(res['message']) if response.code != 200

      status = res['job']['status']
      puts "===> status: #{status}"

      unless ['created', 'running', 'queued'].include?(status)
        return status
      end

      # Wait a while before pinging again
      sleep 2
    end
  end
end

6. Downloading the results

Once the job finishes, we make one final API call to

class HolisticsAPI
  # ...

  def download_export(job_id)
    url = @api_url + "/exports/download"
    response = @http.follow.get(url, params: {job_id: job_id})

    raise StandardError.new(JSON.parse(response.to_s['message'])) if response.code != 200

    response.to_s
  end
end

7. Putting things together

Once the above class is defined, let's put in a short code to perform all 3 steps to get the data.

API_KEY = 'your_api_key'
WIDGET_ID = 1234 # your widget

api = HolisticsAPI.new(API_KEY)

job_id = api.submit_report(WIDGET_ID)
puts "===> job_id: #{job_id}"

job_status = api.wait_for_job_status(job_id)
puts "===> job_status: #{job_status}"

if job_status == 'success'
  csv_data = api.download_export(job_id)
  puts csv_data # your CSV-formatted data here!
end

7. Profit!

Save the data into CSV, convert them into array, or feed them to other applications. The potentials are limitless!

Create Data Alerts using API

Introduction


In Holistics, you can set up a system that sends you automatic notifications when data meets certain criteria, allowing you to make timely and strategic business decisions. This concept is called as Data Alert.

Holistics offers a set of public API endpoints allowing clients to work with Data Alert. This tutorial shows you how to programmatically create these data alerts using the API.

The Use case


Assuming you are an e-commerce company that wants to track the number and status of orders placed on your platform. You can set up data alerts that trigger notifications when certain conditions are met, such as:

  1. A number of orders placed in a specific time period exceed a certain threshold.
  2. A certain percentage of orders are canceled or marked as "canceled".

You can receive these alerts through email or slack, and they can use this information to quickly respond to any issues or to make informed decisions about adjusting inventory or shipping processes.

Let's walk through how we can create a data alert to track your number of orders using Holistics API.

High-level Approach


We will use the API to create email data alerts. Specifically, we'll send a POST request to Holistics create data alert API.

Before that, we need to prepare the following:

  • A dashboard widget with relevant report
  • Holistics API Key: We need to setup the API key. Refer to this guide for more info.

How to create a data alert via API


Step 1. Preparing the dashboard widget

You will need to create a dashboard widget containing the data of interest to you. In this example, I used this simple report that counts the total number of orders.

Untitled

Step 2: Making the API Call

We'll be using the following endpoint:

POST https://secure.holistics.io/api/v2/data_alerts

There are some notable configuration options in this request:

  • schedule: Specify how frequently the email schedule should be
  • viz_conditions: Specify what alert condition should be check on the report result. By configuring this, we can customize the alert for each alerting use case.
  • dest: Specify configurations regarding the alert delivery destination, e.g. the recipients' information.
  • dynamic_filter_presets: Specify what dynamic filters should be applied to the report. By configuring this, we can customize the report for each recipient.

Below is a sample request body. Refer to Holistics API docs for more information on these fields.

Untitled

Dest field

  • dest: Specify configurations regarding recipients' information
    • title (string): Note that Holistics support dynamic variables in email title. For example, the title Sales report for {{$today}} sent on 29/09/2021 will be rendered as Sales report for 2021-09-29 Wed.
    • type: This tells Holistics you want to receive notification via email: EmailDest or Slack: SlackDest

Viz conditions

  • viz_conditions: Coming to the core of the alert, the condition to set data alert
    • field_path: This is used to extract from a specific data model

      • field_name: field name of the field in the data model you want to apply the condition.
      • model_id: The id of the data model associated with the field_name
      • You can get the field_name and model_id by following these steps
        • From the widget id → Get a dashboard widget (include_report=true) then extract query_report.viz_settings.fields, you can find the field you want to add condition then extract path_hash to get model_id and field_name
    • aggregation: The aggregation that will be applied on the field when processing the condition.

    • transformation: The transformation that will be applied on the field when processing the condition.

    • condition: be applied on the Data Modeling layer to filter your data. Check out our document here for more details.

    • Example: you have a field id and want to check whether the number of it is greater than 35000

      {
        "field_path": {
          "field_name": "id",
          "model_id": 1
        },
        "aggregation": "count",
        "transformation": null,
        "condition": {
          "operator": "greater_than",
          "modifier": null,
          "values": [35000],
        },
      }
      

Schedule

schedule: This field is used to specify the schedule for your email schedule.

repeat (string): You will need to input a crontab expression to let Holistics know how frequently you want your email schedule to run.

Dashboard Filters

dynamic-filter-presets is where you define your filter presets.

dynamic_filter_id: To get your filter_id, send a request to Dashboards#Get API to get the dashboard info.

preset_conditions: Let's go through our example to better illustrate this field.

We want to set a value for the filter by setting a preset condition. A preset condition consists of an operator and a list of values. Holistics supports various operators and provides several examples here.

In this case, we want to use operator is and set values to customer_id. By this, we mean the filter's default value equals exactly to customer_id.

Finally, we also want to set up another dynamic filter preset to filter data to the whole of last week. Fortunately, Holistics allows a list of dynamic filter presets. For the Date Range filter, we similarly want to use operator is and set values to "last 7 days".

Our final dynamic_filter_presets object will look like this.

dynamic_filter_presets = [
  {
    "dynamic_filter_id": MY_CUSTOMER_FILTER_ID,
    "preset_condition": {
      "operator": "is",
      "values": [
        customer_id
      ]
    }
  },
  {
    "dynamic_filter_id": MY_DATE_RANGE_FILTER_ID,
    "preset_condition": {
      "operator": "is",
      "values": [
        "last 7 days"
      ]
    }
  }
]

Step 3: Verify to see if your data alert is created

The request should now be ready. After sending it to Holistics, the server will respond with a HTTP status response code to indicate whether the request was successful / failed.

Lastly, be sure to head over to the Holistics page to see if your data alert is created.

Untitled

Example script


An example script to send requests written in Ruby is available here:

# install the neccessary dependencies for this script to work
require "bundler/inline"
require "net/http"
require "json"

gemfile do
  source "https://rubygems.org"
  gem "net-http"
end

# this the Holisitics API endpoint that we need to send our POST request to
DATA_ALERT_ENDPOINT = URI("https://secure.holistics.io/api/v2/data_alerts")

# Step 1: Create a POST request to the endpoint below
request = Net::HTTP::Post.new(DATA_ALERT_ENDPOINT)

# Step 2: Provide your authentication information
request["X-Holistics-Key"] = "mysecretAPIkey"

# Step 2.1: This line helps the system know that we are sending a JSON request
request.content_type = "application/json"

# Step 3: Provide information for your data alert in the request body
request.body = {
  "data_alert": {
    "title": "Tracking number of orders",
    "source_id": 41,
    "source_type": "DashboardWidget",
    # Conditions for the Alert to be Triggered
    "viz_conditions": [
      {
        "field_path": {
          "field_name": "id",
          "model_id": 12
        },
        "aggregation": "count",
        "transformation": null,
        "condition": {
          "operator": "greater_than",
          "modifier": null,
          "values": [35000]
        }
      }
    ],
    # Destination for the Alert
    "dest": {
      "type": "EmailDest",
      "title": "The number of orders has just exceeded than 35000",
      "recipients": [
        "[email protected]"
      ],
      "options": {
        "body_text": null
      }
    },
    # Schedule for the Alert to be Triggered
    "schedule": {
      # Repeat Everyday at 7:00 AM
      "repeat": "0 7 * * *",
      "paused": false
    },
    # Dynamic Filter Presets for the Alert
    "dynamic_filter_presets": [
      {
        "preset_condition": {
          "operator": "is",
          "modifier": null,
          "values": ["VN", "SG"],
        },
        "dynamic_filter_id": 1
      },
      {
        "preset_condition": {
          "operator": "is",
          "modifier": null,
          "values": [
              "Male"
          ],
        },
        "dynamic_filter_id": 2
      }
    ]
  }
}.to_json

# Step 4: Send the request to Holistics!
response = Net::HTTP.start(
  DATA_ALERT_ENDPOINT.hostname,
  DATA_ALERT_ENDPOINT.port,
  :use_ssl => DATA_ALERT_ENDPOINT.scheme == "https",
) do |https|
  https.request(request)
end

# Optional: Output the result into the terminal
puts [response.code,response.message].join(' ')

Create Email Schedules using API

Introduction

In Holistics, you can set up schedules sending reports or dashboards to a group of recipients via email, Slack, SFTP, or Azure Blob. This concept is called Data Schedule.

Holistics offers a set of public API endpoints allowing clients to work with Data Schedule. This tutorial shows you how to use the API to create these data schedules programmatically.

The Use Case

Suppose you are a B2B SaaS company that has a feature to email usage statistics to your customers every week. You want to set it up such that:

  • When a new customer onboards to your platform, you want Holistics to send a weekly email report to that customer.
  • The weekly email reports should be customised to display information regarding the usage data of that customer only

Let's walk through how we can solve this problem using Holistics API.

High-level Approach

We will use the API to create email reports. Specifically, we'll send a POST request to Holistics' create email schedule API.

Before that, we need to prepare the following:

  • A dashboard with relevant reports. This dashboard will be emailed to the customer.
  • The dashboard should have a "Customer" filter to filter down the data for a particular customer.
  • Holistics API Key: We need to setup the API key. Refer to this guide for info.

Step 1: Preparing the Dashboard

You will need to create a master dashboard that contains the reports you want to send to the customers. In this example, we have a Customer Usage Data dashboard which showcases the statistics of jobs that Holistics customers execute in our platform. We also have two filters in this dashboard, Date Range and Customer.

  • The Customer ID filter accepts a customer ID, then passes it to the report within the dashboard. The report then replaces the ID with its placeholder in a prepared SQL query, reruns the query, and fetches only data that belongs to that customer. In other words, this filter helps filter down to the usage data of that customer only.
  • Similarly, the Date Range filter's value accepts "last 7 days", "last 14 days", "last 30 days", or "any time". This filter helps filter usage data by a date range. Note that this filter can be optional.

If you are not sure on how to create dashboards and filters, refer to Dashboard Docs.

Step 2: Making the API Call

We'll be using the following endpoint:

POST https://secure.holistics.io/api/v2/data_schedules

There are some notable configuration options in this request:

  • schedule: Specify how frequent the email schedule should be
  • dynamic_filter_presets: Specify what dynamic filters should be applied to the reports. By configuring this, we can customise the report for each recipient.
  • dest: Specify configurations regarding recipients' information.
    • Remember to set dest.type to "EmailDest"

Below is a sample request body. Refer to Holistics API docs for more information on these fields.

INFO

💡 Most of the fields are straightforward which means that you can fill them out by reading our API docs. Refer to this section below for notes on trickier fields.

Common fields

  • id (integer): This is used to uniquely identified your object. Holistics automatically generates an id for you if you don't specify this.
  • source_id (integer): This tells Holistics which dashboard you want to send email to. To determine this value, view your dashboard link. For example, the link https://secure.holistics.io/dashboards/v3/16076-demo-my-beautiful-dashboard will have the source_id of 16076.
  • dest: Specify the information regarding the recipients
    • title (string): Note that Holistics support dynamic variables in email title. For example, the title Sales report for {{$today}} sent on 29/09/2021 will be rendered as Sales report for 2021-09-29 Wed.

Schedule frequency

schedule is used to specify the schedule for your email schedule.

repeat (string): You will need to input a crontab expression here to let Holistics how frequent you want to your email schedule to run.

Dynamic filter values

dynamic-filter-presets is where you define your filter presets here.

dynamic_filter_id (integer): To get your filter_id, send a GET request to Dashboards#Get API to get the dashboard info.

preset_conditions (object): Let's go through our example to better illustrate this field.

We want to set a value for the filter by setting a preset condition. A preset condition consists of an operator and a list of values. Holistics supports various operators and provides several examples here.

In this case, we want to use operator is and set values to customer_id. By this, we mean the filter's default value equals exactly to customer_id.

Finally, we also want to set up another dynamic filter preset to filter data to the whole of last week. Fortunately, Holistics allows a list of dynamic filter presets. For the Date Range filter, we similarly want to use operator is and set values to "last 7 days".

Our final dynamic_filter_presets object will look like this.

dynamic_filter_presets = [
  {
    "dynamic_filter_id": MY_CUSTOMER_FILTER_ID,
      "preset_condition": {
        "operator": "is",
        "values": [
          customer_id
      ]
    }
  },
    {
    "dynamic_filter_id": MY_DATE_RANGE_FILTER_ID,
      "preset_condition": {
        "operator": "is",
        "values": [
          "last 7 days"
      ]
    }
  }
]

Step 3: Double-check to see if your email schedule is created

Example script

Step 3: Double-check to see if your email schedule is created

The request should now be ready. After sending it to Holistics, the server will respond with a HTTP status response code to indicate whether the request was successful / failed.

Lastly, be sure to head over to Holistics page to see if your email schedule is created.

Example script

An example script to send requests written in Ruby is available here:

# install the neccessary dependencies for this script to work
require "bundler/inline"
require "net/http"
require "json"

gemfile do
  source "https://rubygems.org"
  gem "net-http"
end

# this the Holisitics API endpoint that we need to send our POST request to
EMAIL_SCHEDULE_ENDPOINT = URI("https://secure.holistics.io/api/v2/data_schedules")

# Step 1: Create a POST request to the endpoint below
request = Net::HTTP::Post.new(EMAIL_SCHEDULE_ENDPOINT)

# Step 2: Provide your authentication information 
request["X-Holistics-Key"] = "mysecretAPIkey"

# Step 2.1: This line helps the system know that we are sending a JSON request
request.content_type = "application/json"

# Step 3: Provide information for your email schedule in the request body
request.body =
  {
    "data_schedule": {
      "source_type": "Dashboard",
      "source_id": 16076,
      "schedule": {
        "repeat": "* * * * *",
        "paused": false,
      },
      "dest": {
        "type": "EmailDest",
        "title": "Sale report for {{$today}}",
        "recipients": [
          "[email protected]",
        ],
      },
    },
  }.to_json

# Step 4: Send the request to Holistics!
response = Net::HTTP.start(
  EMAIL_SCHEDULE_ENDPOINT.hostname,
  EMAIL_SCHEDULE_ENDPOINT.port,
  :use_ssl => EMAIL_SCHEDULE_ENDPOINT.scheme == "https",
) do |https|
  https.request(request)
end

# Optional: Output the result into the terminal
puts [response.code,response.message].join(' ')

Holistics CLI

💡 NOTE

The current Holistics CLI version is: 0.4.0.

Requirements:

  • Ruby 2.7

Holistics CLI is an interface to Holistics Data Preparation module (like data transport, data transform, import).

Setting Up

Linux/Mac OSX

If you are using Linux/Mac OSX, we suggest you install Ruby with a Ruby Version Manager such as rbenv or asdf.

Please follow the instructions in your RVM of choice to install Ruby 2.7.4:

After you have Ruby installed on your computer, run:

gem install holistics -v 0.4.0

Windows

If you are using Windows, head over to: Official Ruby Download Page.

And download Ruby 2.7.4 installer. Remember to select the Devkit option for your installer so that you can run the gem install command in the next step from your command line.

After you have Ruby installed on your computer, run:

gem install holistics -v 0.4.0

Authentication (done once)

The next step is authentication. You first need to be granted permissions to have an API authentication token. If you are not sure how, visit: API Authentication.

Assuming that the above step is successful, you can access your token by going to ⚙️ My Account -> API Key. After that, run this in your command line to authenticate yourself.

$ holistics login <my_authentication_token>
Authenticating token...
Authentication successful. Info:
- ID: 1
- Email: admin@you.com

IMPORTANT - INVALID AUTHENTICATION TOKEN ERROR

By default, the CLI connects to the Asia-Pacific servers. If your account is in another server (EU or US), you will have to specify the HOLISTICS_HOST > environment variable in your authentication command.

👉 If you are not sure what server you are on, please follow this guide to find out.

Assuming you are in EU server (https://eu.holistics.io), the command will now become:

HOLISTICS_HOST=https://eu.holistics.io holistics login

You will need to prepend every command with HOLISTICS_HOST=https://eu.holistics.io.

Alternatively, specify it only once as a global variable by running:

export HOLISTICS_HOST=https://eu.holistics.io

And you can run the commands without prepending the host everytime.

CLI Commands

Help Command

For general help or a list of command, run:

$ holistics help

Here is a list of some Holistics CLI commands you can run: Holistics CLI commands

Migrate API V1 to V2

Introduction

This documentation will guide you through the process of transitioning from API V1 to the new API V2. It will also provide information about the migration plan and outline what you can expect in our new API version. Our primary goal is to assist you in achieving a smooth and successful transition to Holistics’s latest API version.

Why it is time to move to API V2

For the past few months, we have considered API V1 as our legacy API. While it is still functional, it is not actively maintained. Meanwhile, we have been developing API V2 as a more powerful, flexible, and well-structured version of V1.

Now that API V2 has surpassed its predecessor in both functionality and experience, we are deprecating V1 and are here to assist you in migrating your applications to V2.

Our deprecation plan for API V1

INFO

The entire API V1 will cease to function by November 20th, 2023.

This deprecation includes all functionalities available in these two V1 kits:

Be assured that we have supported all of these functionalities API in V2.

Next steps, you will need to manually update your applications that are utilizing V1 to switch them to V2.

Equivalent functions between API V1 & API V2

API V1 API V2 Changes
[User] Get all users in a tenant with full information GET - List users
  • Groups are in a different list, not included in each user
  • V2 applies pagination, you have to query page by page
  • V2 has user counter information
[User] Invite a new user to Holistics POST - Invite Multiple Users V2 allows inviting multiple emails with additional user settings, including API usage, group IDs, and data export permissions
[User] Resend invitation to user POST - Resend invitation to user
  • We introduced inviting multiple users API in API V2
  • In order to make invite multiple users and resend invitation user consistent, we return the job info of created job
[User] Soft-delete a user DEL - Delete a user
[User] Restore a deleted user POST - Restore a deleted user
[User] Allow/ Revoke a user's API access PUT - Update user By setting attribute: allow_authentication_token: boolean
[User] Revoke Authentication Token from a user

PUT - Update user (Upcoming)

[User] Check whether email address is already used for a user in Holistics GET - Check whether an email address is already used for a user in your Holistics workspace
[User] Change user role in Holistics PUT - Update user By setting attribute: “role”: enum remove_groups: passing empty group_ids
[Group] Get all groups in a tenant GET - List Groups
  • V2 applies pagination, you have to query page by page
  • V2 includes all users’s id instead of num_user in V1
  • V2 supports include_users
[Group] Create a new group POST - Create Group Response’s structure change
[Group] Update information of an existing group PUT - Update a Group Now included user_ids in groups
[Group] Delete an existing group DEL - Delete a Group
[Group] Add a user into a group POST - Add User to Group
[Group] Remove a user from a group POST - Remove User from Group

FAQ

How will the migration affect existing applications that are utilizing API V1?

  • We expect no interference in your automation workflow after the migration since all functionalities of V1 are completely covered by V2
  • However, since there will be some differences in the data schema (or response structure) between the two versions, you would need to follow our documents to set up precisely

Should I do anything prior to this migration?

We suggest you check which applications will be affected by such an update, and inform your team that they will not be available during that time.

What do I do if I encounter unexpected issues?

We are committed to providing support throughout the migration. If you encounter any problems, please reach out to us at support@holistics.io for assistance.

Jobs

Async (asynchronous) operations are operations that are executed within a background Job.
If an API endpoint triggers an async operation, it will return an AsyncResult response containing the background Job info.
You can use the bellow APIs to poll for the updated Job status.

Get a Job

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

Responses

Response samples

Content type
application/json
{
  • "job": {
    }
}

Get Job's logs

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

Responses

Response samples

Content type
application/json
{
  • "job_logs": [
    ]
}

List Job's queues

Authorizations:
ApiKeyAuthentication

Responses

Response samples

Content type
application/json
{
  • "job_queues": [
    ]
}

AML Studio

AML Studio APIs

Dashboards

Reporting Dashboards

List Dashboards

Authorizations:
ApiKeyAuthentication
query Parameters
before
string

Only get records before this cursor.

after
string

Only get records after this cursor.

limit
integer [ 1 .. 100 ]
Default: 20

Limit number of records per page.

sort
string
Default: "natural"
Enum: "natural" "id_asc" "id_desc"

Sort dashboards according to the specified sort order.

include_embed_info
boolean

Decide whether embed info is included or not.

Responses

Response samples

Content type
application/json
{
  • "dashboards": [
    ],
  • "cursors": {
    }
}

Get a Dashboard

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

Responses

Response samples

Content type
application/json
{
  • "dashboard": {
    }
}

Delete a Dashboard

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

Responses

Response samples

Content type
application/json
{
  • "message": "string"
}

Build a Dashboard URL with preset filter states

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

Request Body schema: application/json
Array of objects (DynamicFilterCondition)
Array
dynamic_filter_id
required
integer
required
object (DataModelingCondition)

Condition that can be applied on the Data Modeling layer to filter your data.

Examples

Responses

Request samples

Content type
application/json
{
  • "filter_states": [
    ]
}

Response samples

Content type
application/json
{
  • "dashboard_url": "string",
  • "fstate_hash": "string"
}

Preload a Dashboard

Preload a Dashboard by executing its widgets (using the submitted filter conditions, or default filter conditions if not submitted) and storing the widgets' results into Holistics cache.
Read more about Data Caching at https://docs.holistics.io/docs/data-caching.

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

Request Body schema: application/json
Array of objects (DynamicFilterCondition)
bust_cache
boolean
Default: false

If set to True, ignore existing cache and always execute the widgets. Otherwise, do not execute the widgets that are already cached.

Responses

Request samples

Content type
application/json
Example
{ }

Response samples

Content type
application/json
{
  • "job": {
    }
}

Dashboard Widgets

Reporting Dashboard Widgets

Get a Dashboard Widget

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

query Parameters
include_dashboard
boolean

Decide whether dashboard is included or not.

include_report
boolean

Decide whether report is included or not.

include_url
boolean

Decide whether dashboard url, widget url is included or not.

Responses

Response samples

Content type
application/json
{
  • "dashboard_widget": {
    }
}

Delete a Dashboard Widget

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

Responses

Response samples

Content type
application/json
{
  • "message": "string"
}

Export a Dashboard Widget

See more details on how to export a widget at Get Data. When the Job is finished, you can download the exported file using the download export API.

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

query Parameters
output
required
string
Enum: "csv" "xlsx" "pdf"

Output file type.

Request Body schema: application/json
Array of objects (DynamicFilterCondition)
Array
dynamic_filter_id
required
integer
required
object (DataModelingCondition)

Condition that can be applied on the Data Modeling layer to filter your data.

Examples

Responses

Request samples

Content type
application/json
{
  • "dashboard_filter_conditions": [
    ]
}

Response samples

Content type
application/json
{
  • "job": {
    }
}

Data Schedules

Data delivery using automated schedules

Create a Data Schedule

Authorizations:
ApiKeyAuthentication
Request Body schema: application/json
required
object (DataSchedule)
id
integer

Unique Data Schedule identifier.

source_type
required
string
Enum: "Dashboard" "QueryReport"
source_id
required
integer
required
object (Schedule)
Array of objects (DynamicFilterPreset)
required
object

Responses

Request samples

Content type
application/json
Example
{
  • "data_schedule": {
    }
}

Response samples

Content type
application/json
{
  • "data_schedule": {
    }
}

List Data Schedules

Authorizations:
ApiKeyAuthentication
query Parameters
before
string

Only get records before this cursor.

after
string

Only get records after this cursor.

limit
integer [ 1 .. 100 ]
Default: 20

Limit number of records per page.

dest_type
string
Enum: "EmailDest" "SlackDest" "Adls2Dest" "SftpDest"

Filter by destination type.

source_type
string
Enum: "QueryReport" "Dashboard"

Filter by source type.

source_id
integer

Filter by source id.

Responses

Response samples

Content type
application/json
{
  • "data_schedules": [
    ],
  • "cursors": {
    }
}

Get a Data Schedule

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

Responses

Response samples

Content type
application/json
{
  • "data_schedule": {
    }
}

Update a Data Schedule

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

Request Body schema: application/json
required
object (DataSchedule)
id
integer

Unique Data Schedule identifier.

source_type
required
string
Enum: "Dashboard" "QueryReport"
source_id
required
integer
required
object (Schedule)
Array of objects (DynamicFilterPreset)
required
object

Responses

Request samples

Content type
application/json
{
  • "data_schedule": {
    }
}

Response samples

Content type
application/json
{
  • "data_schedule": {
    }
}

Delete a Data Schedule

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

Responses

Response samples

Content type
application/json
{
  • "message": "string"
}

Execute a Data Schedule

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

Responses

Response samples

Content type
application/json
{
  • "job": {
    }
}

Data Alerts

Send automatic notification to you and your team when the data meets certain conditions

Create a Data Alert

Authorizations:
ApiKeyAuthentication
Request Body schema: application/json
required
object (DataAlert)
id
integer

Unique Data Schedule identifier.

title
string
source_id
required
integer
source_type
required
string
Value: "DashboardWidget"
required
string
required
Array of objects (DynamicFilterPreset)
Array of objects or null (VizCondition)

Alert conditions for Data Alert.

required
object (Schedule)

Responses

Request samples

Content type
application/json
{
  • "data_alert": {
    }
}

Response samples

Content type
application/json
{
  • "data_alert": {
    }
}

List Data Alerts

Authorizations:
ApiKeyAuthentication
query Parameters
before
string

Only get records before this cursor.

after
string

Only get records after this cursor.

limit
integer [ 1 .. 100 ]
Default: 20

Limit number of records per page.

search_term
string

Search records by its name, title...

dest_type
string
Enum: "EmailDest" "SlackDest"

Filter by destination type.

source_id
integer

Filter by source id.

source_type
string
Value: "DashboardWidget"

Filter by source type.

Responses

Response samples

Content type
application/json
{
  • "data_alerts": [
    ],
  • "cursors": {
    }
}

Get a Data Alert

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

Responses

Response samples

Content type
application/json
{
  • "data_alert": {
    }
}

Update a Data Alert

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

Request Body schema: application/json
required
object (DataAlert)
id
integer

Unique Data Schedule identifier.

title
string
source_id
required
integer
source_type
required
string
Value: "DashboardWidget"
required
string
required
Array of objects (DynamicFilterPreset)
Array of objects or null (VizCondition)

Alert conditions for Data Alert.

required
object (Schedule)

Responses

Request samples

Content type
application/json
{
  • "data_alert": {
    }
}

Response samples

Content type
application/json
{
  • "data_alert": {
    }
}

Delete a Data Alert

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

Responses

Response samples

Content type
application/json
{
  • "message": "string"
}

Execute a Test Data Alert

Authorizations:
ApiKeyAuthentication
Request Body schema: application/json
required
object (DataAlert)
id
integer

Unique Data Schedule identifier.

title
string
source_id
required
integer
source_type
required
string
Value: "DashboardWidget"
required
string
required
Array of objects (DynamicFilterPreset)
Array of objects or null (VizCondition)

Alert conditions for Data Alert.

required
object (Schedule)

Responses

Request samples

Content type
application/json
{
  • "test_data_alert": {
    }
}

Response samples

Content type
application/json
{
  • "job": {
    }
}

Execute a Data Alert

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

Responses

Response samples

Content type
application/json
{
  • "job": {
    }
}

Exports

Download an exported file

Authorizations:
ApiKeyAuthentication
query Parameters
job_id
required
integer

ID of the Job that has done the exporting.

Responses

Response samples

Content type
application/json
{
  • "type": "BaseError",
  • "message": "string",
  • "details": {
    }
}

Data Modeling

DataModelingCondition

Condition that can be applied on the Data Modeling layer to filter your data. See the examples in the Example panel.

required
Array of any
modifier
string or null
operator
required
string
Example
{
  • "operator": "is",
  • "values": [
    ],
  • "modifier": "string"
}

Dependencies

Downstream dependencies

Get ids of objects which are dependent on the object you input. The response schema varies based on the input type and its current dependants.

Get object's downstream dependencies

Authorizations:
ApiKeyAuthentication
query Parameters
id
required
integer

Object id.

type
required
string
Enum: "DataSource" "DataModel" "DataSet"

Object type.

Responses

Response samples

Content type
application/json
{
  • "dependants": {
    }
}

Users

Users management

List Users

Authorizations:
ApiKeyAuthentication
query Parameters
before
string

Only get records before this cursor.

after
string

Only get records after this cursor.

limit
integer [ 1 .. 100 ]
Default: 20

Limit number of records per page.

search_term
string

Search by User email, name, initials or Group names.

status
string
Enum: "active" "deleted" "pending"

Filter by User status.

role
string (UserRole)
Enum: "user" "explorer" "analyst" "admin" "growth_admin" "it_admin"

Filter by User role.

has_authentication_token
boolean

Whether or not to include users who have API key.

Note: the result also includes users even when their API access has been revoked.

exclude_deleted
boolean

Whether or not to exclude deleted Users.

sort
string
Default: "natural"
Enum: "natural" "id_asc" "id_desc" "name_asc" "name_desc" "initials_asc" "initials_desc"

Sort the Users according to the specified sort order.

If multiple records have the same value, the corresponding order of User id will be applied to assure consistent result.

Responses

Response samples

Content type
application/json
{
  • "counters": {
    },
  • "users": [
    ],
  • "groups": {
    },
  • "cursors": {
    }
}

Update a User

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

Request Body schema: application/json
object
email
string

User email.

password
string

User password.

name
string or null

User name.

title
string or null

User title.

job_title
string or null

User job title.

role
string
Enum: "user" "explorer" "analyst" "admin" "growth_admin" "it_admin"

User role.

allow_authentication_token
boolean

Allow User to have API access.

enable_export_data
group_ids
Array of integers

List of new Group ids that the User will be assigned to.

Responses

Request samples

Content type
application/json
{
  • "user": {
    }
}

Response samples

Content type
application/json
{
  • "id": 0,
  • "email": "string",
  • "name": "string",
  • "initials": "string",
  • "role": "user",
  • "is_deleted": true,
  • "is_activated": true,
  • "has_authentication_token": true,
  • "allow_authentication_token": true,
  • "enable_export_data": true,
  • "current_sign_in_at": "2019-08-24T14:15:22Z",
  • "last_sign_in_at": "2019-08-24T14:15:22Z",
  • "created_at": "2019-08-24T14:15:22Z",
  • "title": "string",
  • "job_title": "string"
}

Delete a User (soft-delete)

Revoke a User from accessing and using Holistics.
Note: The soft-deleted User will still retain ownership of their resources. You can contact support@holistics.io to request for an ownership transfer.

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

Responses

Response samples

Content type
application/json
{
  • "message": "string"
}

Invite Users

Authorizations:
ApiKeyAuthentication
Request Body schema: application/json
emails
required
Array of strings

List of User emails to invite.

role
required
string
Enum: "user" "explorer" "analyst" "admin" "growth_admin" "it_admin"

User role.

allow_authentication_token
boolean

Allow Users to have API access.

enable_export_data
group_ids
Array of integers

List of Group ids that the Users will be assigned to.

message
string

Greeting message that will be sent to all User emails.

Responses

Request samples

Content type
application/json
{
  • "emails": [
    ],
  • "role": "user",
  • "allow_authentication_token": true,
  • "enable_export_data": true,
  • "group_ids": [
    ],
  • "message": "string"
}

Response samples

Content type
application/json
{
  • "job": {
    }
}

Restore a Deleted User

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

Responses

Response samples

Content type
application/json
{
  • "message": "string"
}

Resend invitation to User

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

Responses

Response samples

Content type
application/json
{
  • "job": {
    }
}

Revoke User API key.

Admin can revoke all users' tokens. Users can revoke their tokens.

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

Responses

Response samples

Content type
application/json
{
  • "message": "string"
}

Check if email address is used

Check if the specified email address is already used for a User in your Holistics workspace.

Authorizations:
ApiKeyAuthentication
query Parameters
email
string

Query parameter for email attribute.

Responses

Response samples

Content type
application/json
{
  • "is_already_user": true
}

Get currently authorized User

Authorizations:
ApiKeyAuthentication

Responses

Response samples

Content type
application/json
{
  • "user": {
    }
}

Groups

Groups management

Create a Group

Authorizations:
ApiKeyAuthentication
Request Body schema: application/json
required
object
name
required
string

Responses

Request samples

Content type
application/json
{
  • "group": {
    }
}

Response samples

Content type
application/json
{
  • "group": {
    }
}

List Groups

Authorizations:
ApiKeyAuthentication
query Parameters
before
string

Only get records before this cursor.

after
string

Only get records after this cursor.

limit
integer [ 1 .. 100 ]
Default: 20

Limit number of records per page.

sort
string
Default: "natural"
Enum: "natural" "id_asc" "id_desc"

Sort groups according to the specified sort order.

include_users
boolean

Include users in the response.

Responses

Response samples

Content type
application/json
{
  • "groups": [
    ],
  • "users": {
    },
  • "cursors": {
    }
}

Get a Group

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

query Parameters
include_users
boolean

Whether to include Users information.

Responses

Response samples

Content type
application/json
{
  • "group": {
    }
}

Update a Group

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

Request Body schema: application/json
required
object
name
string

Responses

Request samples

Content type
application/json
{
  • "group": {
    }
}

Response samples

Content type
application/json
{
  • "group": {
    }
}

Delete a Group

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

Responses

Response samples

Content type
application/json
{
  • "message": "string"
}

Add a User to a Group

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

user_id
required
integer

User id.

Responses

Response samples

Content type
application/json
{
  • "message": "string"
}

Remove a User from a Group

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

user_id
required
integer

User id.

Responses

Response samples

Content type
application/json
{
  • "message": "string"
}

Data Sources

Get a Data Source

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

Responses

Response samples

Content type
application/json
{
  • "data_source": {
    }
}

Update a Data Source

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

Request Body schema: application/json
required
object
name
string (name)
object (settings)
object (dbconfig)

Responses

Request samples

Content type
application/json
{
  • "data_source": {
    }
}

Response samples

Content type
application/json
{
  • "data_source": {
    }
}

Delete a Data Source

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

Responses

Response samples

Content type
application/json
{
  • "message": "string"
}

Bust Exploration Cache (Beta)

This feature is currently in Beta.

Bust (invalidate) all exploration/report cache of the specified Data Source. After a successful busting, all explorations/reports running on the specified Data Source will return fresh data.

Please note that this does not bust or trigger the Data Source's Model Storages. Hence, even after calling this API, the explorations/reports can only be as fresh as the data persisted in the Model Storages.

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

Responses

Response samples

Content type
application/json
{
  • "job": {
    }
}

Data Sets

Get a Data Set

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

query Parameters
include_models
boolean

Decide whether data models are included or not.

include_reports
boolean

Decide whether query reports are included or not.

Responses

Response samples

Content type
application/json
{
  • "data_set": {
    }
}

Delete a Data Set.

We do not support deleting data set created from AML yet, try deleting it from AML Studio. Send your request to support@holistics.io if you have any concern.

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

Responses

Response samples

Content type
application/json
{
  • "message": "string"
}

Data Models

Get a Data Model

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

Responses

Response samples

Content type
application/json
{
  • "data_model": {
    }
}

Delete a Data Model

Will return async result when requesting to drop the import table of a data import model, otherwise, return the status of the action.

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

query Parameters
drop_import_table
boolean

Drop the relevant import table of this data model.

Responses

Response samples

Content type
application/json
Example
{
  • "job": {
    }
}

Query Reports

Get a Query Report

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

query Parameters
include_data_set
boolean

Decide whether Data Set is included or not.

Responses

Response samples

Content type
application/json
{
  • "query_report": {
    }
}

User Attributes (Beta)

Create a User Attribute

Authorizations:
ApiKeyAuthentication
Request Body schema: application/json
required
object (UserAttributeInput)
name
required
string
attribute_type
required
string
label
required
string
description
string

Responses

Request samples

Content type
application/json
{
  • "user_attribute": {
    }
}

Response samples

Content type
application/json
{
  • "user_attribute": {
    },
  • "status": "created"
}

List User Attributes

Authorizations:
ApiKeyAuthentication
query Parameters
before
string

Only get records before this cursor.

after
string

Only get records after this cursor.

limit
integer [ 1 .. 100 ]
Default: 20

Limit number of records per page.

Responses

Response samples

Content type
application/json
{
  • "user_attributes": [
    ],
  • "cursors": {
    }
}

Update a User Attribute

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

Request Body schema: application/json
required
object (UserAttributeInput)
name
required
string
attribute_type
required
string
label
required
string
description
string

Responses

Request samples

Content type
application/json
{
  • "user_attribute": {
    }
}

Response samples

Content type
application/json
{
  • "user_attribute": {
    }
}

Delete a User Attribute

Authorizations:
ApiKeyAuthentication
path Parameters
id
required
integer

Resource id.

Responses

Response samples

Content type
application/json
{
  • "message": "string"
}

List User Attribute Entries

Authorizations:
ApiKeyAuthentication
query Parameters
subject_type
required
string
Enum: "User" "Group"

Subject type.

subject_id
required
number

Subject Id.

Responses

Response samples

Content type
application/json
{
  • "user_attribute_entries": [
    ],
  • "cursors": {
    }
}

Compute User Attribute Values

Authorizations:
ApiKeyAuthentication
query Parameters
subject_type
required
string
Enum: "User" "Group"

Subject type.

subject_id
required
number

Subject Id.

Responses

Response samples

Content type
application/json
{
  • "user_attribute_values": {
    }
}

Upsert User Attribute Entries

Authorizations:
ApiKeyAuthentication
Request Body schema: application/json
required
Array of with User Attribute name (object) or with User Attribute ID (object)

The number of entries that can be processed at once is limited to 100.

Array
Any of
user_attribute_id
required
integer
input_type
string
Enum: "manual" "inherit" "all"
values
Array of strings
subject_type
required
string
Enum: "User" "Group"
subject_id
required
number

Responses

Request samples

Content type
application/json
{
  • "user_attribute_entries": [
    ]
}

Response samples

Content type
application/json
{
  • "user_attribute_entries": [
    ]
}