Example implementation of dashboard delivery:
-
We built a UI page thats called reports - this allows users to select an “Embeddable” to display. On that page we built a Nav with a button called “Export”. Clicking this button will open a dialog that supports the ability to add in some emails / cadence time that is recored in our DB. e.g john@gmail.com/Twice a week/3pm.
-
We wrote a client in the backend that will query Embeddable API’s using
/web-component/v1/query
and builds up a payload - see below a shortapi-documentation
that shows how we achieved this for one component - but the logic is repeatable for what ever components you want to choose. To query the api you can just follow the documentation to get your security token and then build your CSV from the attached .md -
We then pop that payload into a Pub/Sub - Scheduler - Cron job ( How ever you want to achieve it ) and then hit what ever SMTP service provider you want to use to send the email
Web Component Query API Documentation
Step 1: Retrieve Widget Information
API Endpoint
- URL:
https://app-api.eu.embeddable.com/web-component/v1/embeddable
- Method: GET
curl Command
curl 'https://app-api.eu.embeddable.com/web-component/v1/embeddable' \
-H 'accept: */*' \
-H 'accept-language: en-GB,en-US;q=0.9,en;q=0.8' \
-H 'authorization: Bearer [YOUR_ACCESS_TOKEN]' \
-H 'origin: [YOUR_ORIGIN]' \
-H 'referer: [YOUR_REFERER]' \
-H 'sec-ch-ua: "Not;A=Brand";v="24", "Chromium";v="128"' \
-H 'sec-ch-ua-mobile: ?0' \
-H 'sec-ch-ua-platform: "macOS"' \
-H 'sec-fetch-dest: empty' \
-H 'sec-fetch-mode: cors' \
-H 'sec-fetch-site: cross-site' \
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36'
Response Structure
{
"embeddable": {
"widgets": [
{
"id": "component_id",
"inputConfiguration": {
// ... other properties ...
}
}
]
}
}
Step 2: Extract Required Data
From the response, extract the following information for each widget:
componentId
:widgets[i].id
limit
:widgets[i].inputConfiguration.limit.value
datasetId
:widgets[i].inputConfiguration.ds.value.datasetId
inputName
:widgets[i].inputConfiguration.columns.inputMeta.config.dataset
dimensions
: Extract allname
properties fromwidgets[i].inputConfiguration.columns.value
order
:- Start with
widgets[i].inputConfiguration.defaultSort.value.name
as the first item - Add remaining dimensions in the order they appear in
widgets[i].inputConfiguration.columns.value
- Start with
Example Python code to extract dimensions:
dimensions = [item['name'] for item in widget['inputConfiguration']['columns']['value']]
Explanation of the order
parameter
The order
parameter is an array of arrays, where each inner array contains two elements:
- The dimension name
- The sort direction (“desc” for descending or “asc” for ascending)
The first item in the order
array should always be the default sort dimension, which is specified by inputConfiguration.defaultSort.value.name
. The remaining items should be the other dimensions in the order they appear in inputConfiguration.columns.value
.
For example, if balances.account_id
is the default sort, the order
array would look like this:
[
["balances.account_id", "desc"],
["balances.account_number", "desc"],
["balances.country", "desc"],
["balances.account_code", "desc"],
["balances.amount", "desc"],
["balances.currency", "desc"],
["balances.balance_date", "desc"]
]
Example to construct this order
array programmatically:
-
Start with the default sort dimension:
default_sort = widget['inputConfiguration']['defaultSort']['value']['name'] order = [[default_sort, "desc"]]
-
Add the remaining dimensions:
dimensions = [item['name'] for item in widget['inputConfiguration']['columns']['value']] for dim in dimensions: if dim != default_sort: order.append([dim, "desc"])
Step 3: Construct Query Payload
Using the extracted data, construct a payload for the query API:
{
"inputName": "<extracted_input_name>",
"datasetId": "<extracted_dataset_id>",
"dimensions": ["<dimension1>", "<dimension2>", ...],
"order": [
["<default_sort_dimension>", "desc"],
["<dimension1>", "desc"],
["<dimension2>", "desc"],
...
],
"offset": 0,
"limit": <extracted_limit>,
"componentId": "<extracted_component_id>",
"timezone": "Europe/Amsterdam"
}
Step 4: Query Data
API Endpoint
- URL:
https://app-api.eu.embeddable.com/web-component/v1/query
- Method: POST
curl Command
curl 'https://app-api.eu.embeddable.com/web-component/v1/query?' \
-H 'accept: */*' \
-H 'accept-language: en-GB,en-US;q=0.9,en;q=0.8' \
-H 'authorization: Bearer [YOUR_ACCESS_TOKEN]' \
-H 'content-type: application/json' \
-H 'origin: [YOUR_ORIGIN]' \
-H 'referer: [YOUR_REFERER]' \
-H 'sec-ch-ua: "Not;A=Brand";v="24", "Chromium";v="128"' \
-H 'sec-ch-ua-mobile: ?0' \
-H 'sec-ch-ua-platform: "macOS"' \
-H 'sec-fetch-dest: empty' \
-H 'sec-fetch-mode: cors' \
-H 'sec-fetch-site: cross-site' \
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36' \
--data-raw '{
"inputName": "ds",
"datasetId": "[DATASET_ID]",
"dimensions": [
"balances.account_number",
"balances.country",
"balances.account_code",
"balances.account_id",
"balances.amount",
"balances.currency",
"balances.balance_date"
],
"order": [
["balances.account_id", "desc"],
["balances.account_number", "desc"],
["balances.country", "desc"],
["balances.account_code", "desc"],
["balances.amount", "desc"],
["balances.currency", "desc"],
["balances.balance_date", "desc"]
],
"offset": 0,
"limit": 1000,
"componentId": "[COMPONENT_ID]",
"timezone": "Europe/Amsterdam"
}'
Example Payload
{
"inputName": "ds",
"datasetId": "[DATASET_ID]",
"dimensions": [
"balances.account_number",
"balances.country",
"balances.account_code",
"balances.account_id",
"balances.amount",
"balances.currency",
"balances.balance_date"
],
"order": [
["balances.account_id", "desc"],
["balances.account_number", "desc"],
["balances.country", "desc"],
["balances.account_code", "desc"],
["balances.amount", "desc"],
["balances.currency", "desc"],
["balances.balance_date", "desc"]
],
"offset": 0,
"limit": 1000,
"componentId": "[COMPONENT_ID]",
"timezone": "Europe/Amsterdam"
}
Explanation of the Example Payload:
inputName
: Set to “ds” as specified in the original data.datasetId
: Taken from theinputConfiguration.ds.value.datasetId
.dimensions
: List of all dimension names frominputConfiguration.columns.value
.order
:- The first item is “balances.account_id”, assuming it was the
defaultSort.value.name
. - The remaining items are all other dimensions, in the order they appear in
dimensions
. - All are set to “desc” (descending) order.
- The first item is “balances.account_id”, assuming it was the
offset
: Set to 0 to start from the beginning of the dataset.limit
: Set to 1000, as specified in the originalinputConfiguration.limit.value
.componentId
: Taken from theid
field of the widget.timezone
: Set to “Europe/Amsterdam” as a default value.
Notes
- Filters are automatically applied by the API and don’t need to be included in the query payload - the data returned will be filtered automatically.
- The
timezone
andvariableValues
fields in the query payload are optional. - Replace all placeholder values (marked with [BRACKETS]) with your actual values.