Summary
This article explains how to authenticate and consume the SMARTFENSE API from Microsoft Power BI Desktop using OAuth 2.0 (Client Credentials), including a pagination example to unify results from multiple pages into a single table.
Introduction
SMARTFENSE exposes an API that allows you to integrate data from your instance with tools such as Microsoft Power BI for reporting and analysis. In this example, the OAuth 2.0 Client Credentials flow is used, designed for server-to-server integrations (without a user present), typical for an administrator or partner who needs to query campaign and module information.
Base URL
All requests to the SMARTFENSE API must be made using the following base URL:
https://tenant.takesecurity.com/
| Note: Replace tenant with the subdomain corresponding to your SMARTFENSE instance. |
Authentication
The SMARTFENSE API uses OAuth 2.0, supporting the following flows:
- Client Credentials
- Authorization Code
For Power BI integrations, the Client Credentials flow is used, as it is designed for server-to-server interactions, without end-user involvement.
Important rules
- All requests must be made via HTTPS.
- Each request must include a Bearer Token in the Authorization header.
- Credentials must be stored securely.
Obtaining credentials
- Log in to the SMARTFENSE platform.
- Go to Settings → Integrations → API
-
Click Register new application.
- Configure the necessary scopes according to the endpoints to be consumed
(documentation available at:
https://tenant.takesecurity.com/api/schema/redoc/)
- At the end, you will obtain:
- client_id
- client_secret
| Important: The client_secret grants direct access to your instance’s information. It should not be shared or exposed publicly. |
Recommended structure in Power BI
For integration, it is recommended to create the following functions:
-
fnGetToken
Obtains the OAuth 2.0 token. -
fnGetPagedResults
Obtains and unifies paginated results from the API. -
Function per endpoint
Example="82"fnGetCampaignTrainings for Interactive Modules.
Authentication is handled within the M code, so in Power BI the source is set as Anonymous.
| Note: Authentication actually occurs within the M code, using the Authorization header and the fnGetToken function. Therefore, at the Power BI level, the source is set as “Anonymous”. |
Create function to obtain the token
Start Power BI. Go to the Get data from other sources section
In the Get Data window, select Other from the left menu and select Blank Query, then click Connect.
- fnGetToken and then go to Advanced Editor.
In Advanced Editor paste the following code:
let
fnGetToken = () as text =>
let
// Datos de la aplicación OAuth
// Reemplazar con los valores reales
client_id = "client_id de la plataforma SMARTFENSE",
client_secret = "client_secret de la plataforma SMARTFENSE",
base_url = "https://instancia.takesecurity.com",
// Tiempo máximo de espera en minutos para las llamadas HTTP
timeoutMinutes = 120,
// "client_id:client_secret" en Base64 para Authorization: Basic
TextToEncode = client_id & ":" & client_secret,
Encoded = Binary.ToText(Text.ToBinary(TextToEncode), BinaryEncoding.Base64),
// Cuerpo para grant_type=client_credentials
Body = "grant_type=client_credentials",
TokenResponse = Json.Document(
Web.Contents(
base_url & "/oauth/token/",
[
Content = Text.ToBinary(Body),
Headers = [
#"Content-Type" = "application/x-www-form-urlencoded",
#"Cache-Control" = "no-cache",
Authorization = "Basic " & Encoded
],
Timeout = #duration(0, 0, timeoutMinutes, 0)
]
)
),
AccessToken = TokenResponse[access_token]
in
AccessToken
in
fnGetToken
In the section that says "// Reemplazar con los valores reales" enter the data obtained when registering the application on the SMARTFENSE platform and click the Done button.
The URL "akesecurity.com" must have the subdomain of the SMARTFENSE instance.
Create function to obtain paginated data (fnGetPagedResults)
-
In the Home->New Source menu select "Blank Query".
-
Change the query name to fnGetPagedResults and then go to Advanced Editor.
In Advanced Editor paste the following code:
let
fnGetPagedResults = (initialPath as text) as list =>
let
// URL base de la instancia (host)
base_url = "https://instancia.takesecurity.com",
// Timeout máximo por request (en minutos)
timeoutMinutes = 120,
// Obtener token usando la función anterior (se usa en el header Authorization)
token = fnGetToken(),
// Armar la primera URL absoluta del endpoint
FirstUrl = base_url & initialPath,
// Función local para obtener una página (request + parseo JSON)
GetPage = (url as text) as record =>
let
Response =
Json.Document(
Web.Contents(
url,
[
Headers = [ Authorization = "Bearer " & token ],
Timeout = #duration(0, 0, timeoutMinutes, 0)
]
)
)
in
Response,
// Extraer la lista de items de una página, soportando ambos formatos:
// - {"results": [...]}
// - {"campaigns": [...]}
GetItems = (page as record) as list =>
let
items =
if Record.HasFields(page, "results") then page[results]
else if Record.HasFields(page, "campaigns") then page[campaigns]
else {}
in
if items is list then items else {},
// Recorrer todas las páginas mientras exista el campo "next"
Pages = List.Generate(
// Estado inicial: primera URL y primera página descargada
() => [ Url = FirstUrl, Page = GetPage(FirstUrl) ],
// Condición: seguimos mientras Page no sea null
each [Page] <> null,
// Siguiente estado: leer "next" y traer la siguiente página si existe
each
let
NextUrl = try [Page][next] otherwise null
in
[
Url = NextUrl,
Page = if NextUrl <> null then GetPage(NextUrl) else null
],
// De cada página, devolver la lista detectada (results o campaigns)
each try GetItems([Page]) otherwise {}
),
// Unir todas las listas en una sola lista final
AllResults = List.Combine(Pages)
in
AllResults
in
fnGetPagedResults Click the "Done" button.
Create function for Interactive Modules (example)
-
In the Home->New Source menu select "Blank Query".
Change the query name to fnGetCampaignTrainings and then go to Advanced Editor.
In Advanced Editor paste the following code:
let
initial_path = "/api/v1/users/campaigns/trainings",
ResultsList = fnGetPagedResults(initial_path)
in
ResultsList
Click the "Done" button.
Configure credentials in Power BI Desktop
-
Click Edit credentials.
Find the source: https://nombreinstancia.takesecurity.com and click Connect.
-
To specify privacy data click Continue.
Privacy level: Organizational.
Save and close.
Create the final table for the model
-
If the connection was successful, the Power Query Editor will open showing the following information.
Click the "To Table" button in the upper left corner and then the "OK" button.
-
Then click the expand button to expand the information you want to bring in.
-
Then select the fields you want to see in the table and click "OK".
-
With the bottom bar, move to the right until you see the results.related_campaigns column and click the expand button in the column.
-
="241"Expand to new rows.
-
Click the expand button in the column again to see the data it will bring, and there you can select all or just the ones you want to see. Click OK.
-
This way you will see all the data from the campaign results of the component in different columns.
-
Then you can go to the top menu and Close and apply to proceed with creating the queries you want.
| Note: If you use this data for dashboards shared with your support team, it is recommended to limit fields to only those necessary to improve performance and relevance in searches (for example, for Copilot or internal analysis). |
Final considerations
- Data refresh is manual.
- When you click Refresh in Power BI, it queries the current data from the instance.
- There is no scheduled automatic update from the API.
- It is possible to reuse this structure for any endpoint documented in the SMARTFENSE API.
Best practices
Protect the client_secret: store it in a safe place and avoid exposing it in shared files or public repositories, especially if access is managed by an administrator or a partner.
Validate scopes before building reports: confirm permissions and available endpoints at tenant.takesecurity.com/api/schema/redoc/ to avoid errors and rework.
Reduce volume in Power Query: expand only the necessary columns (for example, avoid expanding complete lists if they do not add value to the model).
Standardize the base_url per instance: if you manage multiple instances, define a clear criterion to maintain consistency and avoid querying incorrect data.
Important: You must replace the word instancia in each API URL with the subdomain of the corresponding instance.