# BigID Developer Portal - Full Documentation
> This file contains the complete, concatenated markdown documentation for the BigID Developer Portal. It is specifically formatted for AI agents, LLMs, and RAG systems to easily ingest the entire context.
--------------------------------------------------------------------------------
# 404
import NotFoundSearch from '../../components/NotFoundSearch.tsx';
import NotFoundAnimation from '../../components/NotFoundAnimation.tsx';
--------------------------------------------------------------------------------
# BigID API Overview
import { Card, CardGrid, Aside } from '@astrojs/starlight/components';
BigID was designed as an open platform. Any action that can be performed
in our UI can also be done programmatically. This allows you to connect
anything and everything to the data discovery capabilities of your BigID
system. Whether you are automating a script, integrating a portal, or running a headless engine, the API is here and open for you to use.
## Authentication
Every API request to BigID must be authenticated. Learn how to securely manage sessions and tokens.
[Learn how to authenticate using a standard Username and Password.](/api/bigid-api-user-authentication/)
[Learn how to authenticate using long-lived API tokens.](/api/bigid-api-token-authentication/)
## Tutorials & Workflows
Learn how to use BigID programmatically through a series of step-by-step tutorials covering commonly automated tasks.
[Learn how to create and manage data sources automatically.](/api/bigid-api-add-data-source-tutorial/)
[Configure and execute system scans via API.](/api/scan-profiles-api-tutorial/)
[Trigger and retrieve Data Subject Access Requests.](/api/bigid-api-dsar-tutorial/)
[Export classification and object metadata to external tools.](/api/bigid-api-metadata-export-tutorial/)
[Analyze streaming data from tools like Kafka and Kinesis.](/api/bigid-api-scan-data-in-motion-tutorial/)
[Identify and eliminate redundant files across your ecosystem.](/api/bigid-api-duplicate-data-tutorial/)
--------------------------------------------------------------------------------
# Add Data Source Tutorial
import { TabItem, Tabs, Aside } from '@astrojs/starlight/components';
import ApiExplorer from '../../../components/ApiExplorer.astro';
In this tutorial, we'll use SAMPLE as our session token. This is unique
to the training sandbox and will not work in other environments. See
BigID API/Tutorial for information on
authenticating with BigID.
## Discovering Data Sources
You can see what data source connectors are installed in your
environment through the BigID UI, but since we're focused on the API
(and because all actions in the UI can be performed in the API), we are
going to use the API to retrieve them.
Press Send on the request below to get a listing of the data
source connectors installed on our test BigID system.
You'll see our test system has around 70 different data source
connectors installed. If you don't see a data source you want to use,
you can develop your own or it
might already exist, just not on our system. [See the BigID docs for an
exhaustive list.](https://docs.bigid.com)
## Getting Data Source Parameters
Each type of data source has different parameters needed to connect to
it. These parameters can be as simple as a username and password or as
complex as rate limiting information. BigID uses templates to display
these fields to the user in the UI. We can use those same templates to
determine what we need to supply when adding a data source via the API.
We're going to add a MySQL database. **Use the below request to get the
template for an rdb-mysql data source.**
As you can see in the request above, there's a ton of different options
to customize how we connect to a MySQL database. For our purposes, we're
going to go with just the most basic options as seen below.
```json
{
"name": "rdb-mysql",
...
"fields": [
{
"type": "string",
"name": "name",
"apiName": "name",
"displayName": "Data Source Name",
"placeholder": "Type data source name",
"mandatory": true,
"mandatoryForTest": true,
"validation": [
{
"regex": "^[\\w\\-\\s\\(\\):]+$",
"errorText": "Invalid value. Please use alphanumeric characters, spaces, underscore, dash and parentheses."
}
],
"section": "connection",
"order": 0,
"enabled": true
},
{
"type": "stringSelect",
"name": "enabled",
"apiName": "enabled",
"displayName": "Status",
"defaultValue": "yes",
"options": [
{
"value": "yes",
"label": "Enabled"
},
{
"value": "no",
"label": "Disabled"
}
],
"section": "connection",
"order": 1,
"enabled": true
},
{
"type": "string",
"name": "dbUrl",
"apiName": "rdb_url",
"displayName": "DB URL",
"placeholder": ":",
"tooltipText": "Enter a connection string to the data source.",
"section": "connection",
"mandatoryForTest": true,
"order": 0,
"enabled": true
},
{
"type": "string",
"name": "dBSchemaName",
"apiName": "rdb_name",
"displayName": "DB/Schema Name",
"placeholder": ".",
"tooltipText": "Enter database or schema name. Note: this field may be case sensitive depending on the specific data source.",
"isSeparatorAfter": true,
"mandatoryForTest": false,
"section": "connection",
"order": 1,
"enabled": true
},
{
"type": "string",
"name": "userName",
"apiName": "username",
"displayName": "User Name",
"visibleIf": [
{
"field": "useCredentialOrNamePass",
"value": false
}
],
"enabledIf": [
{
"field": "useCredentialOrNamePass",
"value": false
}
],
"mandatoryForTest": true,
"section": "connection",
"order": 6,
"enabled": true
},
{
"type": "password",
"name": "password",
"displayName": "Password",
"apiName": "password",
"visibleIf": [
{
"field": "useCredentialOrNamePass",
"value": false
}
],
"enabledIf": [
{
"field": "useCredentialOrNamePass",
"value": false
}
],
"mandatoryForTest": true,
"isSeparatorAfter": true,
"nullifyIfNotChanged": true,
"section": "connection",
"order": 7,
"enabled": true
},
{
"type": "string",
"name": "type",
"apiName": "type",
"mandatory": true,
"hidden": true,
"defaultValue": "rdb-mysql",
"section": "connection",
"order": 9,
"enabled": true
}
...
]
}
```
## Testing a data source
In any organization, getting the correct information to access a data
source can be an arduous process. Especially after data source
credentials have gone through multiple levels of your organization to
make it to you. Because of this, we recommend testing any data source
credentials before you enter them into BigID. This will also ensure
BigID has proper network connectivity to the data source. You can do
this with the /ds-connection-test endpoint.
Just be sure you include 'isNewPassword' to the request, otherwise BigID
will attempt to test the existing data source in your system.
## Adding a data source
Now that we know what parameters to pass, let's create our data source.
We just need to send a POST to the /ds_conenctions endpoint with our
parameters. Let's connect to the BigID test data set:
- Type: rdb-mysql
- URL: sql.mybigid.com
- Username: bigid
- Password: bigid111
- rdb_name: rockstream
Every data source in BigID also needs a unique name. For your data
source, you should use the name RANDOMHERE so you don't
conflict with other users.
If we retrieve our data sources, now we should see a new data source
with the information we supplied above. Use CTRL+F (or CMD+F) in your
browser to find the data source you created in the request below.
## Code Samples
```python
# Add Data Source Tutorial
import requests
import json
# Base URL of the BigID API (training sandbox)
base_url = "https://developer.bigid.com/api/v1"
# Session token (replace SAMPLE with actual session token)
headers = {
"Authorization": "Bearer SAMPLE",
"Content-Type": "application/json"
}
# 1. Get list of available data source connectors
url = f"{base_url}/ds-templates"
response = requests.get(url, headers=headers)
print(response.json())
# 2. Get the rdb-mysql template to find required fields
url = f"{base_url}/ds-templates/rdb-mysql"
response = requests.get(url, headers=headers)
print(response.json())
# 3. Test the connection to the example MySQL data source
url = f"{base_url}/ds-connection-test"
payload = {
"ds_connection": {
"username": "bigid",
"password": "bigid111",
"rdb_url": "sql.mybigid.com",
"type": "rdb-mysql",
"enabled": "yes",
"rdb_name": "rockstream"
},
"isNewPassword": True
}
response = requests.post(url, headers=headers, json=payload)
print(response.json())
# 4. Add the example MySQL data source
url = f"{base_url}/ds_connections"
payload = {
"ds_connection": {
"name": "645", # Name for every data source should be unique
"type": "rdb-mysql",
"rdb_url": "sql.mybigid.com",
"username": "bigid",
"password": "bigid111",
"rdb_name": "rockstream"
}
}
response = requests.post(url, headers=headers, json=payload)
print(response.json())
```
```javascript
// Add Data Source tutorial
const BASE_URL = "https://developer.bigid.com/api/v1";
const HEADERS = {
"Authorization": "Bearer SAMPLE", // Replace SAMPLE with a actual session token
"Content-Type": "application/json"
};
// 1. Get list of available data source connectors
async function getDataSourceConnectors() {
const res = await fetch(`${BASE_URL}/ds-templates`, {
method: "GET",
headers: HEADERS
});
const data = await res.json();
console.log("Available connectors:", JSON.stringify(data, null, 2));
}
// 2. Get the rdb-mysql template
async function getMySQLTemplate() {
const res = await fetch(`${BASE_URL}/ds-templates/rdb-mysql`, {
method: "GET",
headers: HEADERS
});
const data = await res.json();
console.log("MySQL template:", JSON.stringify(data, null, 2));
}
// 3. Test the connection
async function testConnection() {
const body = {
ds_connection: {
username: "bigid",
password: "bigid111",
rdb_url: "sql.mybigid.com",
type: "rdb-mysql",
enabled: "yes",
rdb_name: "rockstream"
},
isNewPassword: true
};
const res = await fetch(`${BASE_URL}/ds-connection-test`, {
method: "POST",
headers: HEADERS,
body: JSON.stringify(body)
});
const data = await res.json();
console.log("Connection test result:", JSON.stringify(data, null, 2));
}
// 4. Add the data source
async function addMySQLDataSource() {
const body = {
ds_connection: {
name: "645",
type: "rdb-mysql",
rdb_url: "sql.mybigid.com",
username: "bigid",
password: "bigid111",
rdb_name: "rockstream"
}
};
const res = await fetch(`${BASE_URL}/ds_connections`, {
method: "POST",
headers: HEADERS,
body: JSON.stringify(body)
});
const data = await res.json();
console.log("Add data source response:", JSON.stringify(data, null, 2));
}
async function main() {
await getTemplates();
await getMySQLTemplate();
await testConnection();
await addDataSource();
}
main().catch(err => {
console.error("Error in tutorial flow:", err);
});
```
--------------------------------------------------------------------------------
# API Best Practices
The BigID API has hundreds of endpoints and even more data points. To
best utilize the API, keep the following best practices in mind:
## Start with the data you need
When developing an integration, first write down what data you need.
There's a lot of data inside of our API and it's easiest to search for
something when you know what you're looking for. Once you have your list
of what you need for your integration, search the [BigID
Docs](https://docs.bigid.com) for an API that provides that data.
## Every API has a reference example, the UI
The API powers our UI as well as your integrations. That means anything
you can see within the UI is accessible to you programmatically. If
you're struggling to find a piece of information in the API
documentation, find it in the user interface. The network activity of
that page will give you the API endpoint you're looking for. See how to
examine network activity
[here](https://developer.chrome.com/docs/devtools/network/).
## Copy, don't modify
If your integration is going to modify data it didn't create, copy
records instead of modifying them. Modifying data sources and other
sensitive objects could cause important processes like Subject Access
Requests to return the wrong data. Copy these resources then apply your
modifications to the copy. That way you aren't causing ripple effects in
the system.
## Ask before performing intensive operations
Some operations like scans are intensive. You should ask the user before
performing these tasks because they may cause significant database load
on an organization's operational systems. Ideally, confirm with the user
prior to performing any task that puts load on an external data source
(scans, SARs, previews).
--------------------------------------------------------------------------------
# Execute DSARs Tutorial
import { TabItem, Tabs, Aside } from '@astrojs/starlight/components';
import ApiExplorer from '../../../components/ApiExplorer.astro';
In this tutorial, we'll use SAMPLE as our session token. This is unique
to the training sandbox and will not work in other environments. See
BigID API/Tutorial for information on
authenticating with BigID.
## Getting DSAR Profiles
BigID uses DSAR profiles to specify what data sources we should use to
look for user data. You can create these using the APIs, but creating
them via the UI is preferred since the UI will provide suggestions as
you work. In our case, we already have a few profiles within our system
so we can use an already created profile.
From this API call we can see a list of DSAR profiles. This also gives
us insight into why organizations use DASR profiles. Different groups of
systems can have users with the same unique ID (employee number 1 and
customer number 1 are probably different people). Profiles allow us to
segment those user groups. Below we see a different profile for US
customers to illustrate that.
```json
{
"profiles": [
{
"_id": "5d93e8431810782ce9173ae0",
"name": "Default Profile",
"allEnabledEs": true,
"allEnabledDs": true,
"scopes": [
"root"
],
"isCustom": false
},
{
"_id": "614d7794dfa3fdf8bd71cacb",
"allEnabledDs": true,
"allEnabledEs": false,
"name": "ALL PI for US Customer",
"scopes": [
"root"
],
"shouldAttributesEnrichment": true,
"isCustom": true
}
]
}
```
In our case, we know a user with the email alex.bulis@gmail.com is
present in the default profile. However, we need to figure out what
attributes we can use to execute a DSAR with this profile so we get the
names correct.
We can see that there's an idsor_attribute named Email that has an
identifiability of 1. This means that 100% of the sampled users have a
different Email which is perfect for our DSAR. Let's initiate a DSAR on
this user using that Email attribute.
## Initiating a DSAR
Because the amount of data scanned as part of a DSAR can be well into
the petabytes, the DSAR API is asynchronous. The below request will
start a DSAR and return immediately with an ID you can use to track its
progress. Execute the below request to start your DSAR.
## Checking DSAR Status
Let's check the status of your DSAR. Replace REQUESTID in the URL of the
below request with the requestId you received above when you initiated
the DSAR.
When checking the status, we can see the state of our DSAR overall as
well as each individual data source. The sample below has 17/18 data
sources in the scan completed so the overall state is still "Started".
**Rerun the above request until you have a state of "Complete".**
```json
{
"status": "success",
"statusCode": 200,
"data": {
"requestId": "61784c9b1a1d3e0ea3b21a53",
"userId": "DSAR",
"state": "Started",
"statuses": {
"Completed": 17,
"Queued": 1,
"InProgress": 1
}
...
},
"message": null
}
```
## Retrieving DSAR Results
After your DSAR has completed, there's a variety of ways we can get and
display the results. For a full view of the options, visit [the BigID
Docs](https://www.docs.bigid.com/bigid/reference/reports-2#get_api-v1-sar-reports-requestid).
Some common reports are:
- Full report - Includes internal information like what data source
information was found in and column names. Can be JSON or CSV.
- Short Report - Includes attribute names and values. Respects profile
settings for hiding columns. Useful for returning to consumers. Can be
in JSON, CSV, or a PDF.
- Personal Info report - PDF report with information, consent records,
purposes of processing, and data source information. Formatted as a
PDF. Useful as a pretty report for consumers and is an expanded
version of the short report.
Since we want to automate what we would return to consumers, we're going
to retrieve the short report in JSON. You can also replace json in the
URL with csv to change the format. **Replace REQUESTID with the
requestId of your DSAR that can be found above.**
Now that we know the required APIs to perform a DSAR we can add this
into our account portal and give users self service access to their
data.
## Code Samples
```python
# DSAR Tutorial
import requests
import json
# Base URL of the BigID API
base_url = "https://developer.bigid.com/api/v1"
# Session token (replace SAMPLE with actual session token)
headers = {
"Authorization": "Bearer SAMPLE",
"Content-Type": "application/json"
}
# 1. Getting DSAR Profiles
url = f"{base_url}/sar/profiles"
response = requests.get(url, headers=headers)
profiles = response.json()
print(json.dumps(profiles, indent=2))
# Get available attributes for the selected profile, assuming we are using the Default Profile
default_profile_id = profiles['profiles'][0]['_id']
url = f"{base_url}/sar/attributes?profileId={default_profile_id}"
response = requests.get(url, headers=headers)
attributes = response.json()
print(json.dumps(attributes, indent=2))
# 2. Initiating a DSAR
url = f"{base_url}/sar/reports"
payload = {
"userDetails": {
"attributes": {
"Email": "alex.bulis@gmail.com"
},
"userId": "DSAR",
"displayName": "DSAR"
},
"profileId": default_profile_id
}
response = requests.post(url, headers=headers, json=payload)
init_result = response.json()
print(json.dumps(init_result, indent=2))
# Get request_id which will be used to check DSAR status
request_id = init_result.get("requestId") or init_result.get("data", {}).get("requestId")
# 3. Checking DSAR Status
url = f"{base_url}/sar/reports/{request_id}/status"
response = requests.get(url, headers=headers)
status_result = response.json()
print(json.dumps(status_result, indent=2))
# 4. Retrieving DSAR Results
url = f"{base_url}/sar/reports/{request_id}/short-report?format=json" # Retrieving a short report. See docs for other report options
response = requests.get(url, headers=headers)
report = response.json()
print(json.dumps(report, indent=2))
```
```javascript
// DSAR Tutorial
const baseUrl = "https://developer.bigid.com/api/v1"; // Base URL of the BigID API
const headers = {
"Authorization": "Bearer SAMPLE", // Replace SAMPLE with a actual session token
"Content-Type": "application/json"
};
// 1. Get DSAR Profiles
async function getDsarProfiles() {
const response = await fetch(`${baseUrl}/sar/profiles`, {
method: "GET",
headers
});
const data = await response.json();
console.log("Available DSAR Profiles:", JSON.stringify(data, null, 2));
return data;
}
// Get available attributes for the selected profile, assuming we are using the Default Profile
async function getDefaultProfileId(profiles) {
for (const profile of profiles.profiles || []) {
if (profile.name === "Default Profile") {
return profile._id;
}
}
throw new Error("Default Profile not found");
}
async function getDsarAttributes(profileId) {
console.log("Getting DSAR Attributes for the profile...");
const res = await fetch(`${baseUrl}/sar/attributes?profileId=${profileId}`, { headers });
const attributes = await res.json();
console.log(JSON.stringify(attributes, null, 2));
return attributes;
}
// 2. Initiate a DSAR
async function initiateDsar(profileId) {
const payload = {
userDetails: {
attributes: {
Email: "alex.bulis@gmail.com"
},
userId: "DSAR",
displayName: "DSAR"
},
profileId
};
const response = await fetch(`${baseUrl}/sar/reports`, {
method: "POST",
headers,
body: JSON.stringify(payload)
});
const data = await response.json();
console.log("DSAR Initiated:", JSON.stringify(data, null, 2));
return data.data.requestId;
}
// 3. Checking DSAR Status
async function checkDsarStatus(requestId) {
const response = await fetch(`${baseUrl}/sar/reports/${requestId}/status`, {
method: "GET",
headers
});
const data = await response.json();
console.log("DSAR Status:", JSON.stringify(data, null, 2));
return data;
}
// 4. Retrieving DSAR Results
async function getDsarShortReport(requestId) {
const response = await fetch(`${baseUrl}/sar/reports/${requestId}/short-report?format=json`, {
method: "GET",
headers
});
const data = await response.json();
console.log("DSAR Short Report:", JSON.stringify(data, null, 2));
return data;
}
```
--------------------------------------------------------------------------------
# Find Duplicate Data Tutorial
import { TabItem, Tabs, Aside } from '@astrojs/starlight/components';
import ApiExplorer from '../../../components/ApiExplorer.astro';
This means that both structured and unstructured data sources can have
multiple objects within them. In a production BigID system there can be
millions of objects so we need to filter.
## Using Duplicate Filters
Let's use the has_duplicates filter to request objects that contain
duplicate files. Because any number of objects in your estate could carry
duplicates, this sweep runs against the streaming endpoint.
The stream returns a flat JSON array rather than a `results` wrapper, and it
returns far fewer objects than an unfiltered sweep of the catalog. But what are
the duplicates? Each duplicate object has a duplicate_id that represents a
hash of the file. We can filter objects by this ID to find all the
duplicates. Since one hash only ever matches a small number of files, that
lookup is bounded, so the paginated `/data-catalog` endpoint is a good fit.
**Replace DUPLICATEID in the URL of the request below with
the duplicate_id of the first object above to find its duplicates.**
Now you have a list of the files that are duplicated, you can delete
some of your unneeded copies to save on data storage costs.
## Code Samples
```python
# Duplicate Data Tutorial
import requests
import json
base_url = "https://developer.bigid.com/api/v1"
headers = {
"Authorization": "Bearer SAMPLE",
"Content-Type": "application/json"
}
# The cursor is derived from the sort field, so it must not change between pages.
CATALOG_SORT = '[{"field":"fullyQualifiedName","order":"asc"}]'
def stream_catalog_objects(page_size=1000, filter_expr=None):
"""Yield catalog objects via the streaming endpoint, following the cursor."""
url = f"{base_url}/data-catalog/objects/stream"
params = {
"limit": page_size,
"sort": CATALOG_SORT,
"offsetKey": "", # empty string starts at the beginning
}
if filter_expr:
params["filter"] = filter_expr
while True:
res = requests.get(url, headers=headers, params=params, timeout=300)
res.raise_for_status()
page = res.json() # a flat JSON array, not an object
if not page:
return
yield from page
if len(page) < page_size:
return # short page means we reached the end
# The cursor for the next page lives on the last object returned.
next_offset_key = page[-1].get("nextOffsetKey")
if not next_offset_key:
return
params["offsetKey"] = next_offset_key
# 1. Walk every catalog object
total = sum(1 for _ in stream_catalog_objects())
print("Total objects in catalog:", total)
# 2. Stream only the objects that have duplicates
duplicates = list(stream_catalog_objects(filter_expr='has_duplicates="true"'))
print("Objects with duplicates:", len(duplicates))
# Get the duplicate_id of the first object (for example)
duplicate_id = duplicates[0].get("duplicate_id")
# 3. Get all objects that share the same duplicate_id.
# This is a small, bounded lookup, so the paginated endpoint is a good fit.
response = requests.get(
f"{base_url}/data-catalog",
headers=headers,
params={"filter": f'duplicate_id="{duplicate_id}"'}
)
data = response.json()
print("Objects with same duplicate_id:", json.dumps(data, indent=2))
```
```javascript
// Duplicate Data Tutorial
const baseUrl = "https://developer.bigid.com/api/v1";
const headers = {
"Authorization": "Bearer SAMPLE",
"Content-Type": "application/json"
};
// The cursor is derived from the sort field, so it must not change between pages.
const CATALOG_SORT = '[{"field":"fullyQualifiedName","order":"asc"}]';
// Yield catalog objects via the streaming endpoint, following the cursor.
async function* streamCatalogObjects({ pageSize = 1000, filter } = {}) {
let offsetKey = ""; // empty string starts at the beginning
while (true) {
const params = new URLSearchParams({
limit: String(pageSize),
sort: CATALOG_SORT,
offsetKey,
});
if (filter) params.set("filter", filter);
const res = await fetch(`${baseUrl}/data-catalog/objects/stream?${params}`, { headers });
if (!res.ok) throw new Error(`Catalog stream failed: ${res.status} ${res.statusText}`);
const page = await res.json(); // a flat JSON array, not an object
if (page.length === 0) return;
yield* page;
if (page.length < pageSize) return; // short page means we reached the end
// The cursor for the next page lives on the last object returned.
const { nextOffsetKey } = page[page.length - 1];
if (!nextOffsetKey) return;
offsetKey = nextOffsetKey;
}
}
// 1. Walk every catalog object
async function countAllCatalogObjects() {
let total = 0;
for await (const _ of streamCatalogObjects()) total++;
console.log("Total objects in catalog:", total);
return total;
}
// 2. Stream only the objects that have duplicates
async function getObjectsWithDuplicates() {
const duplicates = [];
for await (const obj of streamCatalogObjects({ filter: 'has_duplicates="true"' })) {
duplicates.push(obj);
}
console.log("Objects with duplicates:", duplicates.length);
return duplicates;
}
// 3. Get all objects that share the same duplicate_id.
// This is a small, bounded lookup, so the paginated endpoint is a good fit.
async function getObjectsByDuplicateId(duplicateId) { // Use duplicate id of desired object obtained above in step 2
console.log(`Fetching objects for duplicate_id: ${duplicateId}`);
const params = new URLSearchParams({ filter: `duplicate_id="${duplicateId}"` });
const res = await fetch(`${baseUrl}/data-catalog?${params}`, { headers });
const data = await res.json();
console.log("Objects with same duplicate_id:", JSON.stringify(data, null, 2));
return data;
}
// Run all 3 steps
(async () => {
await countAllCatalogObjects();
const duplicates = await getObjectsWithDuplicates();
if (duplicates.length > 0) {
await getObjectsByDuplicateId(duplicates[0].duplicate_id);
}
})();
```
--------------------------------------------------------------------------------
# Export Metadata Tutorial
import { TabItem, Tabs, Aside } from '@astrojs/starlight/components';
import ApiExplorer from '../../../components/ApiExplorer.astro';
## Listing Every Object in the Catalog
Because you are synchronizing the *whole* catalog, start with
`/data-catalog/objects/stream`. This endpoint streams a flat JSON array of
catalog objects and pages with a cursor, so it can walk a catalog of any size.
To page through the stream:
1. Send the first request with `offsetKey=` (an empty string) to start at the beginning.
2. Read `nextOffsetKey` from the **last object** in the returned array.
3. Pass that value as `offsetKey` on the next request.
4. Stop when the array is empty or holds fewer objects than your `limit`.
The `sort` field must be identical on every request, because the cursor is
derived from that field's value.
## Drilling Into a Single Object
For unstructured data, we discover information directly about and inside
the files. For example, we can see that there are phone numbers within a
file, or that a file matches a machine learning model for an invoice.
For structured data we discover data about the data inside of the
columns as well as the columns themselves. For example we would know
that a database *Users* has a column named State and that column
contains values of the type US State Abbreviation. This means that if we
want information about specific columns we will need to perform an extra
API request. The below API request shows how to retrieve the information
about a specific table. The column parameter follows the format of Data
Source Name.schema name.table name.
For unstructured data sources, you can use the information returned from
the catalog stream or use the attributes call to get more
detailed information like below:
By using these three API calls you can find out information about any
element being scanned by your BigID system.
## Code Samples
```python
# Metadata Export Tutorial
import requests
import json
base_url = "https://developer.bigid.com/api/v1"
headers = {
"Authorization": "Bearer SAMPLE",
"Content-Type": "application/json"
}
# The cursor is derived from the sort field, so it must not change between pages.
CATALOG_SORT = '[{"field":"fullyQualifiedName","order":"asc"}]'
# 1. Stream every object in the catalog, one page at a time
def stream_catalog_objects(page_size=1000, filter_expr=None):
"""Yield every catalog object using cursor-based pagination."""
url = f"{base_url}/data-catalog/objects/stream"
params = {
"limit": page_size,
"sort": CATALOG_SORT,
"offsetKey": "", # empty string starts at the beginning
}
if filter_expr:
params["filter"] = filter_expr
while True:
res = requests.get(url, headers=headers, params=params, timeout=300)
res.raise_for_status()
page = res.json() # a flat JSON array, not an object
if not page:
return
yield from page
if len(page) < page_size:
return # short page means we reached the end
# The cursor for the next page lives on the last object returned.
next_offset_key = page[-1].get("nextOffsetKey")
if not next_offset_key:
return
params["offsetKey"] = next_offset_key
count = 0
for obj in stream_catalog_objects():
count += 1
print(obj["fullyQualifiedName"])
print(f"Exported {count} catalog objects")
# 2. Retrieve information about a specific table
url = f"{base_url}/data-catalog/object-details/columns?object_name=DataSourceName.schemaName.tableName" # Replace "DataSourceName.schemaName.tableName with desired table information"
res = requests.get(url, headers=headers)
data = res.json()
print(json.dumps(data, indent=2))
# 3. Retrieve information about unstructured data source
url = f"{base_url}/data-catalog/object-details/attributes?object_name=Sales%20Drive.devops%40bigiddemo.com%2FEducation%2FProspects4-Restricted.docx" # Example using attribute object_name to fetch detailed information
res = requests.get(url, headers=headers)
data = res.json()
print(json.dumps(data, indent=2))
```
```javascript
// Metadata Export Tutorial
const baseUrl = "https://developer.bigid.com/api/v1";
const headers = {
"Authorization": "Bearer SAMPLE", // Replace with your token
"Content-Type": "application/json"
};
// The cursor is derived from the sort field, so it must not change between pages.
const CATALOG_SORT = '[{"field":"fullyQualifiedName","order":"asc"}]';
// 1. Stream every object in the catalog, one page at a time
async function* streamCatalogObjects({ pageSize = 1000, filter } = {}) {
let offsetKey = ""; // empty string starts at the beginning
while (true) {
const params = new URLSearchParams({
limit: String(pageSize),
sort: CATALOG_SORT,
offsetKey,
});
if (filter) params.set("filter", filter);
const res = await fetch(`${baseUrl}/data-catalog/objects/stream?${params}`, { headers });
if (!res.ok) throw new Error(`Catalog stream failed: ${res.status} ${res.statusText}`);
const page = await res.json(); // a flat JSON array, not an object
if (page.length === 0) return;
yield* page;
if (page.length < pageSize) return; // short page means we reached the end
// The cursor for the next page lives on the last object returned.
const { nextOffsetKey } = page[page.length - 1];
if (!nextOffsetKey) return;
offsetKey = nextOffsetKey;
}
}
async function exportAllCatalogObjects() {
let count = 0;
for await (const obj of streamCatalogObjects()) {
count++;
console.log(obj.fullyQualifiedName);
}
console.log(`Exported ${count} catalog objects`);
return count;
}
// 2. Retrieve information about a specific structured table
async function getTableMetadata() {
const tableFQN = "DataSourceName.schemaName.tableName"; // Replace this with your actual table name
const url = `${baseUrl}/data-catalog/object-details/columns?object_name=${encodeURIComponent(tableFQN)}`;
console.log(`Fetching metadata for table: ${tableFQN}`);
const res = await fetch(url, { headers });
const data = await res.json();
console.log("Structured Table Metadata:\n", JSON.stringify(data, null, 2));
return data;
}
// 3. Retrieve info about an unstructured object
async function getUnstructuredAttributes() {
const objectFQN = "Sales Drive.devops@bigiddemo.com/Education/Prospects4-Restricted.docx"; // Replace as needed
const url = `${baseUrl}/data-catalog/object-details/attributes?object_name=${encodeURIComponent(objectFQN)}`;
console.log(`Fetching metadata for unstructured object: ${objectFQN}`);
const res = await fetch(url, { headers });
const data = await res.json();
console.log("Unstructured Object Metadata:\n", JSON.stringify(data, null, 2));
return data;
}
// Run all 3 steps
(async () => {
await exportAllCatalogObjects();
await getTableMetadata();
await getUnstructuredAttributes();
})();
```
--------------------------------------------------------------------------------
# Scan Data in Motion Tutorial
import { Aside, Tabs, TabItem } from '@astrojs/starlight/components';
import ApiExplorer from '../../../components/ApiExplorer.astro';
Many organizations are receiving and processing data in real time. Where
there's data, there's bound to be personal information. In this
tutorial, we'll add an AWS Kinesis data source that BigID will scan in
real time.
Unlike traditional BigID scans that run weekly, monthly or quarterly,
data in motion scans run continuously. This means that under scan
details you'll see your data in motion scan at 0% with a status of "in
progress" while you're monitoring a data in motion data source.
First, we should check if data in motion is enabled in your environment.
## Discovering Data Source Options
You can see what data source connectors are installed in your
environment through the BigID UI, but since we're focused on the API
(and because all actions in the UI can be performed in the API), we are
going to use the API to retrieve them.
Press Send on the request below to get a listing of the data
source connectors installed on our test BigID system.
You'll see our test system has around 70 different data source
connectors installed. Use CTRL+F (CMD+F on Mac OS) to search for the
Kinesis connector.
import SandboxAction from "../../../components/SandboxAction";
## Adding the Data in Motion data source
Now that we know the Kinesis data source connector is enabled, lets add
our data source. Remember the three steps of adding a datasource:
- Populate the data source parameters.
- Test the data source connection.
- Save the data source.
### Populating Data Source Parameters
Kinesis data sources look like the following JSON object:
{
` "owners": [`
` ""`
` ],`
` "differential": false,`
` "rdb_is_sample_data": false,`
` "aws_key_id": "",`
` "aws_key_secret": "",`
` "isIamRoleAuth": true,`
` "region": "us-east-1",`
` "stream_name": "STREAMNAME",`
` "name": "Kinesis",`
` "type": "kinesis",`
` "security_tier": "1",`
` "ocr_languages": "eng",`
` "scanner_strategy": "SCAN_ALL",`
` "enabled": "yes",`
` "keyDeserializer": "String",`
` "valueDeserializer": "String"`
}
Notice how you have the option to either use an IAM role or an access
key and secret to connect to the data source. In our case we'll use an
IAM Role already applied to the sandbox that gives us access to a
Kinesis stream called "StockTradeStream".
Our parameters should look like the following:
` "owners": [`
` ""`
` ],`
` "differential": false,`
` "rdb_is_sample_data": false,`
` "aws_key_id": "",`
` "aws_key_secret": "",`
` "isIamRoleAuth": true,`
` "region": "us-east-1",`
` "stream_name": "StockTradeStream",`
` "name": "Kinesis",`
` "type": "kinesis",`
` "security_tier": "1",`
` "ocr_languages": "eng",`
` "scanner_strategy": "SCAN_ALL",`
` "enabled": "yes",`
` "keyDeserializer": "String",`
` "valueDeserializer": "String"`
}
### Test the data source connection
Let's make sure our BigID installation has the proper permissions and
network capabilities to access our data source by performing a test
connection. We can do this by sending our parameters to the
/ds-connection-test endpoint.
### Save the data source connection
Now that we know what parameters to pass, let's create our data source.
We just need to send a POST to the /ds_conenctions endpoint with our
parameters.
Every data source in BigID also needs a unique name. For your data
source, use RANDOMHERE as the name so you don't conflict
with other users.
If we retrieve our data sources, now we should see a new data source
with the information we supplied above. Use CTRL+F (or CMD+F) in your
browser to find the data source you created.
## Scanning a Data in Motion Data Source
### Creating a Scan Profile
Scans within BigID rely on scan profiles to know what data sources to
scan, when to scan them, and what classifiers to apply. To scan our new
data source we need to add a scan profile that targets it. The object
for a scan profile looks like the following:
```json
{
"scanType": "dataInMotion",
"allEnabledIdSor": true,
"allEnabledDs": false,
"skipIdScan": true,
"isClassificationsAsPiiFindings": false,
"labelFramework": {
"id": "mip",
"name": "Labels"
},
"dataSourceList": [
"DSNAME"
],
"name": "PROFILENAME",
"owners": [],
"isCustomScanProfile": true
}
```
Let's create one below. Be sure to use RANDOMHERE as your
profile and data source name.
### Starting a Scan
Now that we have our profile, we need to start our scan. Remember that
this scan will not complete. Data in motion scans run continuously to
scan data as it comes in. The request to start a scan won't have a
request body, but if we request a list of our scans we can see it.
## Code Sample
```python
import requests
BASE_URL = "https://sandbox.bigid.tools/api/v1"
TOKEN = "YOUR_SESSION_TOKEN"
headers = {"Authorization": TOKEN}
response = requests.get(f"{BASE_URL}/ds-connections-types", headers=headers)
connectors = response.json()["data"]["ds_connections_types"]
kinesis_available = any(c["type"] == "kinesis" for c in connectors)
if not kinesis_available:
print("Kinesis connector not enabled. Enable DIM_ENABLED flag first.")
exit(1)
test_payload = {
"ds_connection": {
"isIamRoleAuth": True,
"region": "us-east-1",
"stream_name": "StockTradeStream",
"type": "kinesis",
"scanner_strategy": "SCAN_ALL",
"keyDeserializer": "String",
"valueDeserializer": "String"
},
"isNewPassword": True
}
response = requests.post(
f"{BASE_URL}/ds-connection-test",
headers=headers,
json=test_payload
)
if response.status_code != 200:
print(f"Connection test failed: {response.text}")
exit(1)
print("Connection test successful!")
ds_name = "MyKinesisDataSource"
ds_payload = {
"ds_connection": {
"name": ds_name,
"isIamRoleAuth": True,
"region": "us-east-1",
"stream_name": "StockTradeStream",
"type": "kinesis",
"scanner_strategy": "SCAN_ALL",
"keyDeserializer": "String",
"valueDeserializer": "String"
}
}
response = requests.post(
f"{BASE_URL}/ds_connections",
headers=headers,
json=ds_payload
)
ds_id = response.json()["data"]["ds_connection"]["_id"]
print(f"Created data source with ID: {ds_id}")
profile_name = "MyKinesisScanProfile"
scan_profile_payload = {
"scanType": "dataInMotion",
"allEnabledIdSor": True,
"allEnabledDs": False,
"skipIdScan": True,
"isClassificationsAsPiiFindings": False,
"labelFramework": {"id": "mip", "name": "Labels"},
"dataSourceList": [ds_name],
"name": profile_name,
"owners": [],
"isCustomScanProfile": True
}
response = requests.post(
f"{BASE_URL}/scanProfiles",
headers=headers,
json=scan_profile_payload
)
print(f"Created scan profile: {profile_name}")
scan_payload = {
"scanType": "dataInMotion",
"scanProfileName": profile_name,
"scanOrigin": "Invoked manually via API"
}
response = requests.post(
f"{BASE_URL}/scans",
headers=headers,
json=scan_payload
)
print("Scan started! Data in motion scans run continuously.")
```
```js
const BASE_URL = "https://sandbox.bigid.tools/api/v1";
const TOKEN = "YOUR_SESSION_TOKEN";
const connectorsResponse = await fetch(`${BASE_URL}/ds-connections-types`, {
headers: { Authorization: TOKEN }
});
const connectorsData = await connectorsResponse.json();
const kinesisAvailable = connectorsData.data.ds_connections_types.some(
c => c.type === "kinesis"
);
if (!kinesisAvailable) {
console.log("Kinesis connector not enabled. Enable DIM_ENABLED flag first.");
process.exit(1);
}
const testPayload = {
ds_connection: {
isIamRoleAuth: true,
region: "us-east-1",
stream_name: "StockTradeStream",
type: "kinesis",
scanner_strategy: "SCAN_ALL",
keyDeserializer: "String",
valueDeserializer: "String"
},
isNewPassword: true
};
const testResponse = await fetch(`${BASE_URL}/ds-connection-test`, {
method: "POST",
headers: { Authorization: TOKEN, "Content-Type": "application/json" },
body: JSON.stringify(testPayload)
});
if (!testResponse.ok) {
console.log("Connection test failed");
process.exit(1);
}
console.log("Connection test successful!");
const dsName = "MyKinesisDataSource";
const dsPayload = {
ds_connection: {
name: dsName,
isIamRoleAuth: true,
region: "us-east-1",
stream_name: "StockTradeStream",
type: "kinesis",
scanner_strategy: "SCAN_ALL",
keyDeserializer: "String",
valueDeserializer: "String"
}
};
const dsResponse = await fetch(`${BASE_URL}/ds_connections`, {
method: "POST",
headers: { Authorization: TOKEN, "Content-Type": "application/json" },
body: JSON.stringify(dsPayload)
});
const dsData = await dsResponse.json();
console.log(`Created data source with ID: ${dsData.data.ds_connection._id}`);
const profileName = "MyKinesisScanProfile";
const scanProfilePayload = {
scanType: "dataInMotion",
allEnabledIdSor: true,
allEnabledDs: false,
skipIdScan: true,
isClassificationsAsPiiFindings: false,
labelFramework: { id: "mip", name: "Labels" },
dataSourceList: [dsName],
name: profileName,
owners: [],
isCustomScanProfile: true
};
await fetch(`${BASE_URL}/scanProfiles`, {
method: "POST",
headers: { Authorization: TOKEN, "Content-Type": "application/json" },
body: JSON.stringify(scanProfilePayload)
});
console.log(`Created scan profile: ${profileName}`);
const scanPayload = {
scanType: "dataInMotion",
scanProfileName: profileName,
scanOrigin: "Invoked manually via API"
};
await fetch(`${BASE_URL}/scans`, {
method: "POST",
headers: { Authorization: TOKEN, "Content-Type": "application/json" },
body: JSON.stringify(scanPayload)
});
console.log("Scan started! Data in motion scans run continuously.");
```
--------------------------------------------------------------------------------
# Token Authentication
import { Aside } from '@astrojs/starlight/components';
import ApiExplorer from '../../../components/ApiExplorer.astro';
In this tutorial we're going to authenticate with BigID using a user
token to retrieve a list of data sources.
First we'll need to create a user token for us to use through the BigID
UI.
### Generate a Token
To do this we need to navigate to the Access Management screen under
Administration -\> Access Management. On the Access Management screen,
select the user you want to create a token for from the System Users
List. Then press the Generate button to start the token
creation process.
Tokens can only be valid for up to 999 days. Since we're just using this
token for testing, let's set it to 30 days and then click
Generate like in the screenshot below.
On the next screen you'll see a name for the token as well as the token
value. Copy the token value by clicking the icon to the right of it then
close the dialog. You can't see the token value again so be sure you
have saved it someplace safe.
Finally, save the user so the token can take effect.
import SandboxAction from "../../../components/SandboxAction";
### Exchange a token for an session token
Now that we have a user token, we need to exchange it for a system token
that we can use to access API endpoints. This uses the
/api/v1/refresh-access-token endpoint like below. Replace the TOKEN HERE
in the request headers with your previously obtained user token and
click Send to get a session token.
In response to this request you'll get the following:
```json
{
"success": true,
"systemToken": "eyJhbGciOi..."
}
```
This session token can then get used on any BigID API.
### Calling an API
Now that you have a session token we can directly call BigID APIs.
Documentation for these APIs is available at
[https://www.docs.bigid.com/bigid/reference/api-getting-started](https://www.docs.bigid.com/bigid/reference/api-getting-started) . Since
we're just trying to perform a simple task, we don't need the docs here,
just to know that GET /ds-connections is the endpoint to retrieve a list
of data source connections.
Add a new header named "Authorization" and paste the session token you
got in the previous request to authenticate yourself.
In that API call, we can see a list of data sources and all the
information for each data source.
```json
{
"status": "success",
"statusCode": 200,
"data": {
"ds_connections": [
""
]
}
}
```
--------------------------------------------------------------------------------
# User Authentication
import { TabItem, Tabs } from '@astrojs/starlight/components';
import ApiExplorer from '../../../components/ApiExplorer.astro';
In this tutorial, we're going to authenticate with BigID using
Username/Password auth and retrieve a list of data sources.
### Getting a session token
Below you'll see the POST request we'll use to authenticate. The body of
the request contains our username and password and we're directing the
request to the sessions endpoint in our BigID Sandbox system. Press
Send to get a session token.
In the response, there's a bunch of information about the logged in
user. For our purposes, we just care about line 4, the auth_token. This
token is what we'll use the authenticate with the other BigID APIs.
We've placed a sample below with the auth token highlighted. **Copy the
auth token from the request you placed above. We'll need it in just a
second.**
```json
{
"success": true,
"message": "Enjoy your token!",
"auth_token": "eyJhbGciOiJ...",
"username": "bigid",
"firstName": "BigID Admin",
"permissions": [
"admin",
"permission.tasks.edit",
"permission.tasks.read_task_list",
...
```
### Calling an API
Now that you have a session token we can directly call BigID APIs.
Documentation for these APIs is available at
[https://www.docs.bigid.com/bigid/reference/api-getting-started](https://www.docs.bigid.com/bigid/reference/api-getting-started) . Since
we're just trying to perform a simple task, we don't need the docs here,
just to know that GET /ds-connections is the endpoint to retrieve a list
of data source connections.
Add a new header named "Authorization" and paste the session token you
got in the previous request to authenticate yourself.
In that API call, we can see a list of data sources and all the
information for each data source.
```json
{
"status": "success",
"statusCode": 200,
"data": {
"ds_connections": [
""
]
}
}
```
We now know the API calls we need and can use our programming language of choice to prepare our report. Below are some samples.
```python
import requests
credentials = {'username': 'bigid', 'password': 'learner'}
env = 'https://sandbox.bigid.tools/'
def getDataSources(credentials, env):
sessionRequest = requests.post(env + 'api/v1/sessions', json=credentials)
sessionData = sessionRequest.json()
dsRequest = requests.get(env + 'api/v1/ds-connections', headers={'Authorization': sessionData.get('auth_token')})
return dsRequest.json()
```
```javascript
const credentials = { username: "bigid", password: "learner" };
const env = "https://sandbox.bigid.tools/";
async function getDataSources(credentials, env) {
// Request API Key using user/pass authentication
const sessionResponse = await fetch(env + 'api/v1/sessions', {
method: 'POST',
body: JSON.stringify(credentials),
headers: { 'Content-Type': 'application/json' }
});
const sessionData = await sessionResponse.json();
const dsResponse = await fetch(env + 'api/v1/ds-connections', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': sessionData.auth_token
}
});
return await dsResponse.json();
}
```
--------------------------------------------------------------------------------
# Cluster Analysis Tutorial
import { TabItem, Tabs, Aside } from '@astrojs/starlight/components';
import ApiExplorer from '../../../components/ApiExplorer.astro';
In this tutorial, we'll use SAMPLE as our session token. This is unique
to the training sandbox and will not work in other environments. See
BigID API/Tutorial for information on
authenticating with BigID.
To view the **complete code** for all steps, see the section labelled
Code Samples.''
For more information on the API capabilities used in this tutorial,
check out the [**Data Catalog API
Docs**](https://api.bigid.com/doc/data-catalog/).
## 1. Authenticate Using Your API Key
All API requests require authentication using a valid API key. Refer to
[**BigID
Documentation**](https://developer.bigid.com/wiki/BigID_API/API_Tutorial)
to obtain your token. Then, define the Authorization header using the
format \`Authorization: Bearer YOUR_API_KEY\`. This header must be
included in every request to ensure proper authentication and access to
BigID’s API endpoints. Throughout the tutorial, we will be using SAMPLE
as our token.
## 2. Obtain Cluster ID
The easiest way to obtain the Cluster ID for the cluster you are
interested in is through the BigID UI. On the Cluster Analysis page, you
can find detailed information on all existing clusters for your
organization’s data.
However, if you prefer to handle everything programmatically, you can
use the GET /api/v1/clusters endpoint to retrieve the list of clusters
via API. You can filter the results to find exactly what you’re looking
for.
For example, the following request retrieves all clusters where the size
equals 20:
Your response will contain a variety of information for each cluster.
Once you have located the cluster you are interested in, you will need
the ID in order to proceed.
## 3. Get All Columns
Once you have the id of the cluster you are interested in, you can
review its columns using the GET
/api/v1/data-catalog/column/cluster/similar endpoint. This endpoint
returns a detailed list of every column that has been grouped into the
specified cluster by BigID’s similarity engine. These columns may come
from different tables, sources, or systems, but they share common
structural, content-based, or metadata characteristics that indicate a
high degree of similarity.
You can use this response to:
- Audit similar columns across your data environment
- Apply or verify tags (like sensitivity or classification labels)
- Identify redundant or duplicate data
- Export the columns for further analysis or reporting
### Query Parameters
Depending on your needs, you can customize your request using optional
query parameters to paginate results, apply filters, or limit the data
returned. The specific query options for this endpoint are outlined
below:
| **Parameter** | **Type** | **Description** | **Default** |
|----|----|----|----|
| **clusterId** | string | **Required.** The ID of the cluster to retrieve columns for. | N/A |
| **limit** | number | Maximum number of results to return. | 10000 |
| **skip** | number | Number of results to skip (used for pagination). | 0 |
| **filter** | string | Filter results based on column metadata. | "" (empty) |
| **searchText** | string | Search across column names and attributes. | "" (empty) |
| **sort** | string | Field to sort the results by. | _id |
| **offsetKey** | string | Used for advanced pagination. | "" |
| **ignoreLimit** | boolean | If true, ignores the limit parameter. | false |
| **sample** | number | Number of columns to randomly sample from the cluster. | null |
All of the above options can be used to control the API response;
however, they are all optional **except** for the *clusterId*. In the
below request, only the clusterId is provided. To test the endpoint,
replace the fake clusterId below with a real id.
A successful request will return a 200 OK response with a JSON payload
containing metadata for all columns in the specified cluster. For
example:
```json
{
"results": [
{
"fullyQualifiedName": "x.y.z",
"columnName": "abc",
"businessAttribute": "friendly name",
"tableName": "my-table",
"source": "my-sql-tables",
"isPrimary": true,
"dataType": "integer",
"attributes": [
{
"attribute_original_name": "SSN",
"attribute_name": "SSN",
"rank": "High",
"calc_confidence_level": 0.695781717492013,
"attribute_type": "IDSoR Attribute",
"attribute_id": "74b78d7040e97f1ab9ba3b69c8e372e3"
}
],
"tags": [
{
"tagId": "ba7a5426-64d6-47eb-995b-8fd8e1224de0",
"valueId": "ba7a5426-64d6-47eb-995b-8fd8e1224de1",
"isMutuallyExclusive": "true",
"properties": {
"hidden": false,
"applicationType": "sensitivityClassification"
},
"tagName": "sensitivity",
"tagValue": "high"
}
]
}
],
"totalCount": 5
}
```
## 4. Troubleshooting
If your request fails, here’s what the server might tell you, and how to
fix it:
| **Status Code** | **Example Response** | **What It Means** | **How to Fix It** |
|----|----|----|----|
| **200** | Successful response with scan data | Everything’s looking good! | Keep cruising. |
| **400** | `{ "error": "Scan ID is invalid" }` | Bad or malformed scan ID provided | Double-check the scan ID you’re using. |
| **404** | `{ "error": "Scan 1234 was not found" }` | Scan ID doesn’t exist | Make sure the ID is valid and fetched from the parent scans endpoint. |
| **401** | Unauthorized | API key missing or invalid | Verify your API key and authorization header. |
| **500** | `{ "status": "error", "message": "Server error", "errors": [{}] }` | BigID server hit a snag (internal error) | Wait a moment and retry. If it persists, reach out to support. |
## Code Samples
```python
# Cluster Analysis API Tutorial
import requests
import json
# --- 1. Setup and Authentication ---
# Base URL of the BigID API (training sandbox)
base_url = "https://developer.bigid.com/api/v1"
# Session token (replace SAMPLE with actual session token)
AUTH_TOKEN = "SAMPLE"
headers = {
"Authorization": f"Bearer {AUTH_TOKEN}",
"Content-Type": "application/json"
}
try:
# 2. Provide the Cluster ID once obtained
target_cluster_id = "cluster_id_here"
# 3. Get All Columns for the provided cluster
columns_url = f"{base_url}/data-catalog/column/cluster/similar"
# The clusterId is a required parameter for this endpoint.
# You can also add other optional parameters like 'limit'.
params = {
'clusterId': target_cluster_id,
'limit': 20 # Limit the results for this example
}
response_cols = requests.get(columns_url, headers=headers, params=params)
response_cols.raise_for_status()
columns_data = response_cols.json()
print("Successfully retrieved columns for the cluster.")
print(f"Total columns in cluster: {columns_data.get('totalCount')}")
print("Sample of retrieved columns:")
print(json.dumps(columns_data.get('results', []), indent=2))
except requests.exceptions.HTTPError as http_err:
print(f"HTTP error occurred: {http_err}")
if http_err.response:
print(f"Response content: {http_err.response.text}")
except Exception as err:
print(f"An other error occurred: {err}")
```
```javascript
// Cluster Analysis API Tutorial
// --- 1. Setup and Authentication ---
// Base URL of the BigID API (training sandbox)
const base_url = "https://developer.bigid.com/api/v1";
// Session token (replace SAMPLE with actual session token)
const AUTH_TOKEN = "SAMPLE";
const headers = {
"Authorization": `Bearer ${AUTH_TOKEN}`,
"Content-Type": "application/json"
};
async function runDataCatalogWorkflow() {
try {
// 2. Provide the Cluster ID from the BigID UI
const targetClusterId = "cluster_id_here";
// 3. Get All Columns for the provided cluster
const params = new URLSearchParams({
clusterId: targetClusterId,
limit: 20 // Limit the results for this example
});
const columnsUrl = `${base_url}/data-catalog/column/cluster/similar?${params}`;
const colsResponse = await fetch(columnsUrl, { headers });
if (!colsResponse.ok) {
throw new Error(`HTTP error fetching columns! Status: ${colsResponse.status}`);
}
const columnsData = await colsResponse.json();
console.log("Successfully retrieved columns for the cluster.");
console.log(`Total columns in cluster: ${columnsData.totalCount}`);
console.log("Sample of retrieved columns:");
console.log(JSON.stringify(columnsData.results || [], null, 2));
} catch (error) {
console.error("An error occurred during the workflow:", error.message);
}
}
// Run the entire workflow
runDataCatalogWorkflow();
```
## Summary
Congratulations! In this tutorial, you have learned how to authenticate
with BigID, get a cluster ID, and retrieve all columns in that cluster
using the API. Now you can easily review and manage similar columns
across your data environment.
--------------------------------------------------------------------------------
# Data Posture API Tutorial
import { TabItem, Tabs, Aside } from '@astrojs/starlight/components';
import ApiExplorer from '../../../components/ApiExplorer.astro';
In this tutorial, we'll use SAMPLE as our session token. This is unique
to the training sandbox and will not work in other environments. See
BigID API/Tutorial for information on
authenticating with BigID.
To view the **complete code** for all steps, see the section labelled
Code Samples.
For more information on the API capabilities used in this tutorial,
check out the [**Data Posture API
Docs**](https://api.bigid.com/doc/dspm/).
## 1. Authenticate Using Your API Key
All API requests require authentication using a valid API key. Refer to
[**BigID
Documentation**](https://developer.bigid.com/wiki/BigID_API/API_Tutorial)
to obtain your token. Then, define the Authorization header using the
format Authorization: Bearer YOUR_API_KEY. This header must be included
in every request to ensure proper authentication and access to BigID’s
API endpoints. Throughout the tutorial, we will be using SAMPLE as our
token.
## 2. Retrieve Security Issues
There are multiple endpoints available for fetching security cases, but
because our goal is to export these issues to a third party system, we
want to use the **GET** /api/v1/actionable-insights/all-cases. This will
allow us to fetch all existing security issues, as well as limit the
selection based on various parameters if necessary.
In the case that you do not want to fetch all cases, you can filter the
response based on several parameters, all of which are outlined in the
following table:
| **Parameter** | **Type** | **Description** |
|----|----|----|
| **skip** | integer | Number of records to skip (for pagination) |
| **limit** | integer | Number of records to return |
| **filter** | string | BigID Query Language filter to narrow results |
| **fields** | string | Comma-separated list of fields to return |
| **sort** | string | Field to sort by, plus optional ASC/DESC |
| **requireTotalCount** | boolean | Whether to include totalCount in response |
Once you have fetched the details for all of the security cases you are
interested in, you can proceed to the next step.
## 3. Export Issues to Your Third-Party System
Once you retrieve the list of open security issues, the next step is to
export them to your third-party system for remediation. This could be a
ticketing platform like Jira, ServiceNow, or a SOAR tool for automated
workflows.
For each issue, map the relevant fields to your third-party system’s
API. Common mappings might include:
| **BigID Field** | **Third-Party Field** |
|--------------------|-----------------------|
| **caseLabel** | Ticket title |
| **policyName** | Category or label |
| **severityLevel** | Priority |
| **assignee** | Assigned owner |
| **dataSourceName** | Affected resource |
## 4. Mark Issues as Resolved
After successfully exporting the issues, you’ll want to mark them as
resolved in BigID to avoid duplicate work and keep the backlog clean.
You have **two options** for marking issues as resolved using the API.
First, you can do so in bulk using the **PATCH**
/api/v1/actionable-insights/cases:{actionType} endpoint. In order to use
this endpoint, you should:
1. In the request, tell BigID which field to update (in this case,
caseStatus) and what the new value should be (set it to resolved).
2. Add filters to select the cases you want to update, like policyName
or caseStatus:open.
3. Run the request. BigID will return the list of case IDs that were
successfully updated.
For example, your request may be structured like the following:
```json
{
"type": "CasesDB",
"subType": "updateCases",
"additionalProperties": {
"field": "assignee",
"newValue": "qaext@bigid.com",
"casesFilters": [
{
"filterField": "policyName",
"filterValues": [
"policy1234"
]
},
{
"filterField": "caseStatus",
"filterValues": [
"remediated"
]
}
]
}
}
```
The following request uses the example request body above. To actually
test this endpoint, replace the fields with real values based on the
previous steps.
If you only need to update the status of specific cases, this can be
done using the **PATCH**
/api/v1/actionable-insights/case-status/{caseId} endpoint. To do so:
1. Replace {caseId} with the actual ID of the case you want to update.
2. In the request body, set caseStatus to resolved.
3. Add an auditReason so your team knows why the change was made (for
example, “Exported to Jira on July 14th”).
For example, your request may be structured like the following:
```json
{
"caseStatus": "resolved",
"auditReason": "Exported to Jira on July 14th"
}
```
In the request below, the *{caseId}* is currently represented by a fake
ID, 123abc. In order to test the endpoint, replace it with a case ID you
have addressed in the previous steps.
## 5. Troubleshooting
If your request fails, here’s what the server might tell you, and how to
fix it:
| **Status Code** | **Example Response** | **What It Means** | **How to Fix It** |
|----|----|----|----|
| **200** | Successful response with scan data | Everything’s looking good! | Keep cruising. |
| **400** | `{ "error": "Scan ID is invalid" }` | Bad or malformed scan ID provided | Double-check the scan ID you’re using. |
| **404** | `{ "error": "Scan 1234 was not found" }` | Scan ID doesn’t exist | Make sure the ID is valid and fetched from the parent scans endpoint. |
| **401** | Unauthorized | API key missing or invalid | Verify your API key and authorization header. |
| **500** | `{ "status": "error", "message": "Server error", "errors": [{}] }` | BigID server hit a snag (internal error) | Wait a moment and retry. If it persists, reach out to support. |
## Code Samples
```python
# Data Posture API Tutorial
import requests
import json
# --- 1. Setup and Authentication ---
# Base URL of the BigID API (training sandbox)
base_url = "https://developer.bigid.com/api/v1"
# Session token (replace SAMPLE with actual session token)
AUTH_TOKEN = "SAMPLE"
headers = {
"Authorization": f"Bearer {AUTH_TOKEN}",
"Content-Type": "application/json"
}
def get_issues(filters=None):
"""
Fetches security cases from the actionable-insights endpoint.
Args:
filters (dict): A dictionary of query parameters like {'limit': 10, 'filter': '...'}.
Returns:
list: A list of case objects, or an empty list if none are found.
"""
cases_url = f"{base_url}/actionable-insights/all-cases"
try:
response = requests.get(cases_url, headers=headers, params=filters)
response.raise_for_status()
issues = response.json()
print(f" -> Found {len(issues)} issues.")
return issues
except requests.exceptions.HTTPError as http_err:
print(f"HTTP error occurred while fetching issues: {http_err}")
return []
def move_issues_to_system(issues):
"""
A placeholder function to simulate exporting issues to a third-party system.
Args:
issues (list): A list of case objects.
Returns:
list: A list of case IDs that were "exported".
"""
exported_ids = []
for issue in issues:
case_id = issue.get("caseId")
# Example mapping
ticket_data = {
"title": issue.get("caseLabel"),
"priority": issue.get("severityLevel"),
"description": f"Policy: {issue.get('policyName')}, Source: {issue.get('dataSourceName')}",
"assignee": issue.get("assignee")
}
print(f" -> Creating ticket for Case ID {case_id}: {ticket_data['title']}")
exported_ids.append(case_id)
print(" -> Simulation complete.")
return exported_ids
def resolve_single_issue(case_id):
"""
Marks a single case as resolved.
"""
resolve_url = f"{base_url}/actionable-insights/case-status/{case_id}"
payload = {
"caseStatus": "resolved",
"auditReason": f"Remediated and moved on {json.dumps(requests.get('http://worldtimeapi.org/api/ip').json()['datetime'])}"
}
try:
response = requests.patch(resolve_url, headers=headers, json=payload)
response.raise_for_status()
print(f" -> Successfully marked case {case_id} as resolved.")
return True
except requests.exceptions.HTTPError as http_err:
print(f"HTTP error occurred while resolving case {case_id}: {http_err}")
return False
def resolve_issues_in_bulk(policy_name):
"""
Marks all open cases for a given policy name as resolved.
"""
bulk_resolve_url = f"{base_url}/actionable-insights/cases:update" # Assuming 'update' is the actionType
payload = {
"type": "CasesDB",
"subType": "updateCases",
"additionalProperties": {
"field": "caseStatus", # Field to update
"newValue": "resolved", # New value
"casesFilters": [
{
"filterField": "policyName",
"filterValues": [ policy_name ]
},
{
"filterField": "caseStatus",
"filterValues": [ "open" ] # Only target open cases
}
]
}
}
try:
response = requests.patch(bulk_resolve_url, headers=headers, json=payload)
response.raise_for_status()
updated_cases = response.json()
print(f" -> Successfully resolved {len(updated_cases.get('caseIds', []))} cases in bulk.")
return updated_cases
except requests.exceptions.HTTPError as http_err:
print(f"HTTP error occurred during bulk resolve: {http_err}")
return None
# --- Main Workflow ---
if __name__ == "__main__":
# 2. Retrieve the first 5 open issues for demonstration
open_issues = get_issues(filters={'limit': 5, 'filter': '{"caseStatus": "open"}'})
if open_issues:
# 3. Simulate exporting these issues
exported_case_ids = export_issues_to_system(open_issues)
# 4. Mark issues as resolved
if exported_case_ids:
# Option A: Resolve the first exported issue individually
resolve_single_issue(exported_case_ids[0])
# Option B: Resolve all issues for a specific policy in bulk
# We'll use the policy from the second issue as an example
if len(open_issues) > 1:
example_policy = open_issues[1].get("policyName")
if example_policy:
resolve_issues_in_bulk(example_policy)
```
```javascript
// Data Posture API Tutorial
// --- 1. Setup and Authentication ---
// Base URL of the BigID API (training sandbox)
const base_url = "https://developer.bigid.com/api/v1";
// Session token (replace SAMPLE with actual session token)
const AUTH_TOKEN = "SAMPLE";
const headers = {
"Authorization": `Bearer ${AUTH_TOKEN}`,
"Content-Type": "application/json"
};
/**
* Fetches security cases from the actionable-insights endpoint.
* @param {URLSearchParams} params - Query parameters for the request.
* @returns {Promise} A list of case objects, or an empty list if none are found.
*/
async function get_issues(params) {
const cases_url = `${base_url}/actionable-insights/all-cases?${params}`;
try {
const response = await fetch(cases_url, { headers });
if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
const issues = await response.json();
console.log(` -> Found ${issues.length} issues.`);
return issues;
} catch (error) {
console.error("HTTP error occurred while fetching issues:", error.message);
return [];
}
}
/**
* A placeholder function to simulate exporting issues to a third-party system.
* @param {Array} issues - A list of case objects.
* @returns {Array} A list of case IDs that were "exported".
*/
function move_issues_to_system(issues) {
const exported_ids = issues.map(issue => {
const case_id = issue.caseId;
// Example mapping
const ticket_data = {
title: issue.caseLabel,
priority: issue.severityLevel,
description: `Policy: ${issue.policyName}, Source: ${issue.dataSourceName}`,
assignee: issue.assignee
};
console.log(` -> Creating ticket for Case ID ${case_id}: ${ticket_data.title}`);
return case_id;
});
console.log(" -> Simulation complete.");
return exported_ids;
}
/**
* Marks a single case as resolved.
* @param {string} case_id - The ID of the case to resolve.
*/
async function resolve_single_issue(case_id) {
const resolve_url = `${base_url}/actionable-insights/case-status/${case_id}`;
const timeResponse = await fetch('http://worldtimeapi.org/api/ip');
const timeData = await timeResponse.json();
const payload = {
caseStatus: "resolved",
auditReason: `Remediated and moved on ${timeData.datetime}`
};
try {
const response = await fetch(resolve_url, { method: 'PATCH', headers, body: JSON.stringify(payload) });
if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
console.log(` -> Successfully marked case ${case_id} as resolved.`);
return true;
} catch (error) {
console.error(`HTTP error occurred while resolving case ${case_id}:`, error.message);
return false;
}
}
/**
* Marks all open cases for a given policy name as resolved.
* @param {string} policy_name - The name of the policy to filter by.
*/
async function resolve_issues_in_bulk(policy_name) {
const bulk_resolve_url = `${base_url}/actionable-insights/cases:update`; // Assuming 'update' is the actionType
const payload = {
"type": "CasesDB",
"subType": "updateCases",
"additionalProperties": {
"field": "caseStatus", // Field to update
"newValue": "resolved", // New value
"casesFilters": [
{ "filterField": "policyName", "filterValues": [policy_name] },
{ "filterField": "caseStatus", "filterValues": ["open"] } // Only target open cases
]
}
};
try {
const response = await fetch(bulk_resolve_url, { method: 'PATCH', headers, body: JSON.stringify(payload) });
if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
const updated_cases = await response.json();
console.log(` -> Successfully resolved ${updated_cases.caseIds?.length || 0} cases in bulk.`);
return updated_cases;
} catch (error) {
console.error("HTTP error occurred during bulk resolve:", error.message);
return null;
}
}
// --- Main Workflow ---
async function runWorkflow() {
// 2. Retrieve the first 5 open issues for demonstration
const params = new URLSearchParams({
limit: 5,
filter: JSON.stringify({ "caseStatus": "open" })
});
const open_issues = await get_issues(params);
if (open_issues.length > 0) {
// 3. Simulate exporting these issues
const exported_case_ids = move_issues_to_system(open_issues);
// 4. Mark issues as resolved
if (exported_case_ids.length > 0) {
// Option A: Resolve the first exported issue individually
await resolve_single_issue(exported_case_ids[0]);
// Option B: Resolve all issues for a specific policy in bulk
// We'll use the policy from the second issue as an example
const example_policy = open_issues[1]?.policyName;
if (example_policy) {
await resolve_issues_in_bulk(example_policy);
}
}
}
}
runWorkflow();
```
## Summary
Congratulations! In this tutorial, you have learned how to retrieve open
security issues from BigID, export those issues to a third-party system
for remediation, and mark the issues as resolved in BigID to keep your
backlog clean and accurate. By following these steps, you can streamline
your remediation process, reduce manual effort, and maintain a clear,
up-to-date view of your organization’s security posture.
--------------------------------------------------------------------------------
# Manage Data Sources
import { TabItem, Tabs, Aside } from '@astrojs/starlight/components';
import ApiExplorer from '../../../components/ApiExplorer.astro';
BigID system.
- How to import those data sources into the new BigID system using the
API.
- How to verify that all data sources were transferred successfully
In this tutorial, we'll use SAMPLE as our session token. This is unique
to the training sandbox and will not work in other environments. See
BigID API/Tutorial for information on
authenticating with BigID.
To view the **complete code** for all steps, see the section labelled
Code Samples.''
For more information on the API capabilities used in this tutorial,
check out the [**Data Source Connections API
Docs**](https://api.bigid.com/doc/data-sources/).
## 1. Authenticate Using Your API Key
All API requests require authentication using a valid API key. Refer to
[**BigID
Documentation**](https://developer.bigid.com/wiki/BigID_API/API_Tutorial)
to obtain your token. Then, define the Authorization header using the
format \`Authorization: Bearer YOUR_API_KEY\`. This header must be
included in every request to ensure proper authentication and access to
BigID’s API endpoints. Throughout the tutorial, we will be using SAMPLE
as our token.
## 2. Export All Existing Data Sources
Use the GET /ds-connections/file-download/export endpoint to export your
configured data sources into a JSON file. There is an optional ids
parameter that can be used to fetch specific data sources only, but
because we are transferring all the data, we do not need to include it.
The file returned will contain all the necessary configuration metadata
for each data source, including connection type, credentials (if
stored), scan options, and more.
## 3. Import Data Sources into the New BigID System
Now that you've exported your data sources as a JSON file in Step 2,
it's time to import them into the new BigID environment. BigID does not
support bulk importing via file upload, so each data source must be
created individually using the POST /ds_connections endpoint.
This endpoint accepts a ds_connection object with the configuration
values for the new data source. These should match the fields exported
from your old system, and they must include any required values as
defined in the data source’s template.
## 4. Verify Data Source are Properly Transferred
Once all data sources have been created in the new BigID environment,
it’s important to verify that the transfer was successful.
You can confirm this by using the GET /ds-connections endpoint on the
new system to retrieve the list of all configured data sources. Compare
this list to your original export to ensure that each data source has
been recreated accurately.
## 5. Troubleshooting
| **Status Code** | **Example Response** | **What It Means** | **How to Fix It** |
|----|----|----|----|
| **200** | Successful response with scan data | Everything’s looking good! | Keep cruising. |
| **400** | `{ "error": "Scan ID is invalid" }` | Bad or malformed scan ID provided | Double-check the scan ID you’re using. |
| **404** | `{ "error": "Scan 1234 was not found" }` | Scan ID doesn’t exist | Make sure the ID is valid and fetched from the parent scans endpoint. |
| **401** | Unauthorized | API key missing or invalid | Verify your API key and authorization header. |
| **500** | `{ "status": "error", "message": "Server error", "errors": [{}] }` | BigID server hit a snag (internal error) | Wait a moment and retry. If it persists, reach out to support. |
## Code Samples
```python
# Scan Insights API Tutorial
import requests
import json
API_TOKEN = "SAMPLE"
HEADERS = {
"Authorization": f"Bearer {API_TOKEN}",
"Content-Type": "application/json"
}
# 1. Export all data sources and return the contents of the response file
def export_data_sources():
url = "https://bigid-ui:9090/api/v1/ds-connections/file-download/export"
response = requests.get(url, headers=HEADERS)
if response.status_code == 200:
with open("exported_datasources.json", "w") as f:
f.write(response.text)
print("Data sources exported.")
return json.loads(response.text)
else:
print("Failed to export data sources.")
print(response.text)
return []
# 2. Import data sources by cycling through the provided list
def import_data_sources(data_sources):
url = "https://bigid-ui:9090/api/v1/ds_connections"
for ds in data_sources:
body = { "ds_connection": ds }
response = requests.post(url, headers=HEADERS, json=body)
if response.status_code == 200:
print(f"Imported: {ds.get('name')}")
else:
print(f"Failed to import: {ds.get('name')}")
print(response.text)
# 3. Verify successful migration
def verify_imported_sources():
url = "https://bigid-ui:9090/api/v1/ds-connections"
response = requests.get(url, headers=HEADERS)
if response.status_code == 200:
data = response.json()
print(f"Total data sources in new environment: {len(data.get('data', []))}")
else:
print("Failed to verify data sources.")
print(response.text)
# Run through the full process
data = export_data_sources()
if data:
import_data_sources(data)
verify_imported_sources()
```
```javascript
// Scan Insights API Tutorial
const API_TOKEN = 'SAMPLE';
const BASE_URL = 'https://bigid-ui:9090/api/v1';
const headers = {
'Authorization': `Bearer ${API_TOKEN}`,
'Content-Type': 'application/json'
};
// Step 1: Export all data sources and return the contents of the response file
async function exportDataSources() {
try {
const response = await fetch(`${BASE_URL}/ds-connections/file-download/export`, {
method: 'GET',
headers
});
if (!response.ok) throw new Error('Export failed.');
const blob = await response.blob();
const text = await blob.text();
const data = JSON.parse(text);
console.log('Exported data sources:', data);
return data;
} catch (err) {
console.error('Error exporting:', err.message);
}
}
// Step 2: Import data sources by cycling through the provided list
async function importDataSources(dataSources) {
for (const ds of dataSources) {
try {
const res = await fetch(`${BASE_URL}/ds_connections`, {
method: 'POST',
headers,
body: JSON.stringify({ ds_connection: ds })
});
if (!res.ok) {
const errText = await res.text();
console.error(`Failed to import ${ds.name}:`, errText);
} else {
const result = await res.json();
console.log(`Imported: ${result.name}`);
}
} catch (err) {
console.error(`Import error for ${ds.name}:`, err.message);
}
}
}
// Step 3: Verify successful transfer
async function verifyTransfer() {
try {
const response = await fetch(`${BASE_URL}/ds-connections`, {
method: 'GET',
headers
});
if (!response.ok) throw new Error('Verification failed.');
const data = await response.json();
console.log(`Verified ${data.data.length} data sources in the new system.`);
} catch (err) {
console.error('Error verifying transfer:', err.message);
}
}
// Function to run all steps in order
async function migrateDataSources() {
const exported = await exportDataSources();
if (exported && exported.length > 0) {
await importDataSources(exported);
await verifyTransfer();
}
}
migrateDataSources();
```
## Summary
Congratulations! In this tutorial, you have learned how to efficiently
export existing data sources from one BigID environment and import them
into another using the BigID API.
--------------------------------------------------------------------------------
# Scan Insights API Tutorial
import { TabItem, Tabs, Aside } from '@astrojs/starlight/components';
import ApiExplorer from '../../../components/ApiExplorer.astro';
In this tutorial, we'll use SAMPLE as our session token. This is unique
to the training sandbox and will not work in other environments. See
BigID API/Tutorial for information on
authenticating with BigID.
To view the **complete code** for all steps, see the section labelled
Code Samples.''
For more information on the API capabilities used in this tutorial,
check out the [**Scan Insights API
Docs**](https://api.bigid.com/doc/scan-insights/).
## 1. Authenticate Using Your API Key
All API requests require authentication using a valid API key. Refer to
[**BigID
Documentation**](https://developer.bigid.com/wiki/BigID_API/API_Tutorial)
to obtain your token. Then, define the Authorization header using the
format \`Authorization: Bearer YOUR_API_KEY\`. This header must be
included in every request to ensure proper authentication and access to
BigID’s API endpoints. Throughout the tutorial, we will be using SAMPLE
as our token.
## 2. Obtain the scan ID(s)
Depending on the information you're interested in, you can retrieve scan
IDs from one of two endpoints. Since our goal is to check the status of
this week’s scheduled scan, we’ll focus on the **Parent Scan endpoint**
(*/scans/parent-scans*), which provides a high-level overview of each
full scan execution.
### Querying
When making a GET request to this endpoint, you can customize the
response using query parameters like **sort** and **limit**. By default,
results are sorted in descending order based on **updated_at**, meaning
the most recent scans appear first. Since we're only interested in the
most recent scan (this week’s scan) we’ll set limit=1 to return just the
latest result. The final request will look like this:
Upon receiving a successful response, we can view various details about
each scan. However, our primary focus is on obtaining the **id**
attribute, which should be clearly labeled in the response data.
Focus on the \`_id\` attribute inside each object in the
\`scanChildren\` array. This \`_id\` uniquely identifies the scan and
is what you’ll use in the next steps.
```json
{
"status": "string",
"statusCode": 42.0,
"message": "string",
"data": {
"totalCount": 42.0,
"scanChildren": [
{
"_id": "string",
"name": "string"
// ... additional fields omitted for brevity
}
]
}
}
```
## 3. Check Scan Status
Once you have the scan ID for a particular scan, you can check its
current **status** to determine whether it's still running or has
completed. To do this, send a **GET** request to the
*/scans/{scan_id}/status* endpoint. In the below request, replace
**SAMPLE_SCAN_ID** with the ID you previously retrieved.
### Parse and Interpret Response
Upon receiving a successful (200) response, the **status** field will
indicate the scan’s current state.
- A value of **true** means the scan is currently active.
- A value of **false** means the scan has stopped, which likely means it
has completed (though you’ll want to confirm completion by checking
additional scan metadata if needed).
If you do not receive a successful response, check the
Troubleshooting section for common errors.
## 4. Troubleshooting
If your request fails, here’s what the server might tell you, and how to
fix it:
| **Status Code** | **Example Response** | **What It Means** | **How to Fix It** |
|----|----|----|----|
| **200** | Successful response with scan data | Everything’s looking good! | Keep cruising. |
| **400** | `{ "error": "Scan ID is invalid" }` | Bad or malformed scan ID provided | Double-check the scan ID you’re using. |
| **404** | `{ "error": "Scan 1234 was not found" }` | Scan ID doesn’t exist | Make sure the ID is valid and fetched from the parent scans endpoint. |
| **401** | Unauthorized | API key missing or invalid | Verify your API key and authorization header. |
| **500** | `{ "status": "error", "message": "Server error", "errors": [{}] }` | BigID server hit a snag (internal error) | Wait a moment and retry. If it persists, reach out to support. |
## Code Samples
```python
# Scan Insights API Tutorial
import requests
import json
# Base URL of the BigID API
base_url = "https://developer.bigid.com/api/v1"
# Session token (replace SAMPLE with actual session token)
headers = {
"Authorization": "Bearer SAMPLE",
"Content-Type": "application/json"
}
# Obtain the scan ID
url_scans = f"{base_url}/scans/parent-scans"
params = {"limit": 1}
response = requests.get(url_scans, headers=headers, params=params)
if response.status_code != 200:
print(f"Failed to get parent scans: {response.status_code} - {response.text}")
exit(1)
data = response.json()
scans = data.get("data", [])
if not scans:
print("No parent scans found.")
exit(1)
scan_id = scans[0]["id"]
print(f"Latest parent scan ID: {scan_id}")
# Check scan status using the scan ID
url_status = f"{base_url}/scans/{scan_id}/status"
response = requests.get(url_status, headers=headers)
if response.status_code != 200:
print(f"Failed to get scan status: {response.status_code} - {response.text}")
exit(1)
status_data = response.json()
status = status_data.get("status")
if status is True:
print("Scan is currently ACTIVE.")
elif status is False:
print("Scan has STOPPED (likely completed).")
else:
print("Scan status unknown or missing.")
```
```javascript
// Scan Insights API Tutorial
const baseUrl = "https://developer.bigid.com/api/v1"; // Base URL of the BigID API
const headers = {
"Authorization": "Bearer SAMPLE", // Replace SAMPLE with a actual session token
"Content-Type": "application/json"
};
// Obtain the scan ID
async function getLatestParentScanId() {
const url = `${baseUrl}/scans/parent-scans?limit=1`;
const response = await fetch(url, { headers });
if (!response.ok) {
throw new Error(`Failed to get parent scans: ${response.status} ${response.statusText}`);
}
const data = await response.json();
if (!data.data || data.data.length === 0) {
throw new Error("No parent scans found.");
}
return data.data[0].id;
}
// Check Scan Status
async function getScanStatus(scanId) {
const url = `${baseUrl}/scans/${scanId}/status`;
const response = await fetch(url, { headers });
if (!response.ok) {
throw new Error(`Failed to get scan status: ${response.status} ${response.statusText}`);
}
const data = await response.json();
return data.status;
}
async function main() {
try {
console.log("Fetching latest parent scan ID...");
const scanId = await getLatestParentScanId();
console.log(`Latest parent scan ID: ${scanId}`);
console.log("Checking scan status...");
const status = await getScanStatus(scanId);
if (status === true) {
console.log("Scan is currently ACTIVE.");
} else if (status === false) {
console.log("Scan has STOPPED (likely completed).");
} else {
console.log("Scan status unknown or missing.");
}
} catch (error) {
console.error("Oops! Something went wrong:", error.message);
}
}
main();
```
## Summary
Congratulations, you can now confidently explore and evaluate the status
of scans using the Scan Insights API! With these skills, you’re equipped
to monitor your organization’s data scans effectively, ensuring timely
detection and management of sensitive information.
--------------------------------------------------------------------------------
# Manage Scan Profiles
import { TabItem, Tabs, Aside } from '@astrojs/starlight/components';
import ApiExplorer from '../../../components/ApiExplorer.astro';
In this tutorial, we'll use SAMPLE as our session token. This is unique
to the training sandbox and will not work in other environments. See
BigID API/Tutorial for information on
authenticating with BigID.
To view the **complete code** for all steps, see the section labelled
Code Samples.''
For more information on the API capabilities used in this tutorial,
check out the [**Scan Profiles API
Docs**](https://api.bigid.com/doc/scan-profiles/) or the [**Data Sources
API Docs**](https://api.bigid.com/doc/data-sources/).
## 1. Authenticate Using Your API Key
All API requests require authentication using a valid API key. Refer to
[**BigID
Documentation**](https://developer.bigid.com/wiki/BigID_API/API_Tutorial)
to obtain your token. Then, define the Authorization header using the
format \`Authorization: Bearer YOUR_API_KEY\`. This header must be
included in every request to ensure proper authentication and access to
BigID’s API endpoints. Throughout the tutorial, we will be using SAMPLE
as our token.
## 2. Gather Data Source IDs
Before creating your scan profile, you need to gather the unique IDs of
the data sources you want to include in your scan. If you have not
obtained them already, whether by browsing the BigID UI or using the
API, this step ensures you have the correct identifiers to specify
exactly which sources to scan.
You can retrieve this information using the **GET**
*/api/v1/ds-connections* endpoint. This API returns the details of one
or more data sources in your BigID environment.
Depending on which data sources you're interested in, this endpoint
supports several optional query parameters to help you narrow down and
customize the results:
- **skip (integer)**: Number of data sources to skip for pagination.
- **limit (integer)**: Maximum number of data sources to return.
- **requireTotalCount (boolean)**: If true, returns the total count of
matching data sources.
- **sort (string)**: Sort results by a specified field and order.
- **filter (string)**: Filter results based on field values.
The response provides common fields for each data source. From this, you
can compile the _id values for all the data sources you want to include
in your scan profile. These IDs will be used in the dataSourceList field
when creating the scan profile in the next step.
## 3. Create a Scan Profile
Once you have a list of all the data sources you’d like to include in
the scan, you can proceed to create a new scan profile using the **Scan
Profiles API**. This profile defines what you want to scan, when you
want to scan it, and how it should behave.
Use the **POST** */api/v1/scanProfiles* endpoint to define the profile
details, including:
- A name and optional description
- A list of dataSourceList IDs (targeted sources only)
- A valid scanTemplateId
- A schedule for recurring scans or isSingleRunScan: true for a one-time
run
For example, to establish a scan profile named "Targeted Marketing Data
Scan" that runs a one-time scan on two specific data sources (e.g.,
marketing-related databases), you would make the request using the
following details:
```json
{
"name": "Targeted Marketing Data Scan",
"description": "Scans only marketing-related sources for compliance",
"dataSourceList": [
"64f7df00cc834f0001a44e85",
"64f7df00cc834f0001a44e86"
],
"scanTemplateId": "standard-privacy-template-001",
"isSingleRunScan": true
}
```
Once submitted, BigID will create the scan profile and queue the scan
based on the schedule you've defined (or start it right away for a
one-time scan). Upon success, the API response will return the newly
created scan profile, including its unique ID. Be sure to save this ID,
as you’ll need it in the next step to verify and manage the scan
profile.
## 4. Verify Scan Profile Creation
After creating your scan profile, you’ll want to double-check that BigID
received and saved it correctly. Using the id returned in the previous
step, you can retrieve the scan profile directly to confirm its details
and status.
Use the **GET** */api/v1/scanProfiles/{id}* endpoint, replacing *{id}*
with your unique scan profile *id* returned from the creation step, to
retrieve and verify the details of your scan profile.
At this step, you’re primarily confirming a successful API response and
that the scan profile exists with the expected ID. If the request
returns a valid profile object (status code 200), you know your scan
profile was created correctly and is ready to rock!
## 5. Troubleshooting
If your request fails, here’s what the server might tell you, and how to
fix it:
| **Status Code** | **Example Response** | **What It Means** | **How to Fix It** |
|----|----|----|----|
| **200** | Successful response with scan data | Everything’s looking good! | Keep cruising. |
| **400** | `{ "error": "Scan ID is invalid" }` | Bad or malformed scan ID provided | Double-check the scan ID you’re using. |
| **404** | `{ "error": "Scan 1234 was not found" }` | Scan ID doesn’t exist | Make sure the ID is valid and fetched from the parent scans endpoint. |
| **401** | Unauthorized | API key missing or invalid | Verify your API key and authorization header. |
| **500** | `{ "status": "error", "message": "Server error", "errors": [{}] }` | BigID server hit a snag (internal error) | Wait a moment and retry. If it persists, reach out to support. |
## Code Samples
```python
# Scan Profiles API Tutorial
import requests
import json
# --- 1. Setup and Authentication ---
# Base URL of the BigID API (training sandbox)
base_url = "https://developer.bigid.com/api/v1"
# Replace SAMPLE with your actual API key
AUTH_TOKEN = "SAMPLE"
headers = {
"Authorization": f"Bearer {AUTH_TOKEN}",
"Content-Type": "application/json"
}
# --- 2. Gather Data Source IDs ---
ds_connections_url = f"{base_url}/ds-connections"
try:
# We'll add a limit to get a small number of data sources for this example.
response = requests.get(f"{ds_connections_url}?limit=5", headers=headers)
response.raise_for_status() # Raise an error for bad status codes (4xx or 5xx)
data_sources = response.json()
# We will look for data sources of these types.
target_types = {'snowflake', 's3'}
target_data_source_ids = []
# Access the list via the 'data' object
connections_list = data_sources_response.get('data', {}).get('ds_connections', [])
for ds in connections_list:
if ds.get('type') in target_types:
print(f" -> Found matching source: {ds.get('name')} (ID: {ds.get('_id')})")
target_data_source_ids.append(ds.get('_id'))
if not target_data_source_ids:
print("Could not find any data sources with the specified types. Exiting.")
exit()
# --- 3. Create a Scan Profile ---
scan_profiles_url = f"{base_url}/scanProfiles"
# The payload defines the new scan profile.
new_profile_data = {
"name": "Dynamic Marketing Data Scan",
"description": "Scans all Snowflake and S3 marketing sources",
"dataSourceList": target_data_source_ids,
"scanTemplateId": "standard-privacy-template-001",
"isSingleRunScan": True
}
response_create = requests.post(scan_profiles_url, headers=headers, json=new_profile_data)
response_create.raise_for_status()
created_profile = response_create.json()
new_profile_id = created_profile.get("_id")
if not new_profile_id:
raise ValueError("Failed to get ID from the created profile response.")
# --- 4. Verify Scan Profile Creation ---
verify_url = f"{scan_profiles_url}/{new_profile_id}"
response_verify = requests.get(verify_url, headers=headers)
response_verify.raise_for_status()
verified_profile = response_verify.json()
print("Verification successful! The profile was created correctly.")
except requests.exceptions.HTTPError as http_err:
print(f"HTTP error occurred: {http_err}")
print(f"Response content: {http_err.response.text}")
except Exception as err:
print(f"An other error occurred: {err}")
```
```javascript
// Scan Profiles API Tutorial in progress
// --- 1. Setup and Authentication ---
// Base URL of the BigID API (training sandbox)
const base_url = "https://developer.bigid.com/api/v1";
const AUTH_TOKEN = "SAMPLE";
const headers = {
"Authorization": `Bearer ${AUTH_TOKEN}`,
"Content-Type": "application/json"
};
async function runBigIDWorkflow() {
try {
// --- 2. Gather and Parse Data Source IDs ---
const dsConnectionsUrl = `${base_url}/ds-connections`;
const dsResponse = await fetch(dsConnectionsUrl, { headers });
if (!dsResponse.ok) throw new Error(`HTTP error! Status: ${dsResponse.status}`);
const dataSourcesResponse = await dsResponse.json();
const targetTypes = ['snowflake', 's3'];
// Access the list via the 'data' object.
const connectionsList = (dataSourcesResponse.data && dataSourcesResponse.data.ds_connections) || [];
const targetDataSourceIds = connectionsList
.filter(ds => targetTypes.includes(ds.type))
.map(ds => {
console.log(` -> Found matching source: ${ds.name} (ID: ${ds._id})`);
return ds._id;
});
if (targetDataSourceIds.length === 0) {
console.log("Could not find any data sources with the specified types. Exiting.");
return;
}
// --- 3. Create a Scan Profile Using Dynamic IDs ---
const scanProfilesUrl = `${base_url}/scanProfiles`;
const newProfileData = {
"name": "Dynamic Marketing Data Scan",
"description": "Scans all Snowflake and S3 marketing sources",
"dataSourceList": targetDataSourceIds,
"scanTemplateId": "standard-privacy-template-001",
"isSingleRunScan": true
};
const createResponse = await fetch(scanProfilesUrl, {
method: 'POST',
headers: headers,
body: JSON.stringify(newProfileData)
});
if (!createResponse.ok) throw new Error(`HTTP error! Status: ${createResponse.status}`);
const createdProfile = await createResponse.json();
const newProfileId = createdProfile._id;
if (!newProfileId) throw new Error("Failed to get ID from created profile.");
// --- 4. Verify Scan Profile Creation ---
const verifyUrl = `${scanProfilesUrl}/${newProfileId}`;
const verifyResponse = await fetch(verifyUrl, { headers });
if (!verifyResponse.ok) throw new Error(`HTTP error! Status: ${verifyResponse.status}`);
const verifiedProfile = await verifyResponse.json();
console.log("Verification successful! The profile was created correctly.");
console.log(JSON.stringify(verifiedProfile, null, 2));
} catch (error) {
console.error("An error occurred during the workflow:", error);
}
}
runBigIDWorkflow();
```
## Summary
Congratulations! In this tutorial, you have learned how to create a
targeted scan profile in BigID by specifying the exact data sources you
want to include. You’ve mastered how to submit a scan profile via the
API, retrieve its unique ID, and verify that the profile was
successfully created and saved.
Now that you’ve set up and verified your scan profile, you can take it
further by monitoring or managing scan execution using the [**Scan
Insights
API**](https://developer.bigid.com/wiki/Scan_Insights_API_Tutorial).
--------------------------------------------------------------------------------
# App Development Best Practices
> Guidelines and best practices for building robust, scalable BigID Applications.
When developing custom BigID Applications, following best practices ensures that your app is secure, scalable, and behaves correctly across both BigID Cloud (multi-tenant) and On-Premise environments.
## Avoid Duplicating Settings
If a configuration setting or preference already exists within the core BigID platform, **do not** recreate a redundant option within your custom application's UI.
Instead, your application should utilize the [BigID API](/api/bigid-api/) to seamlessly retrieve the existing configuration. This guarantees a single source of truth for administrators and prevents conflicting states between the platform and your app.
## Utilize Native TPA Storage
**Do not** stand up your own database or establish direct external database connections for your application state unless absolutely necessary.
BigID provides native **TPA (Third Party App) Storage** specifically designed for custom applications. Relying on TPA storage ensures that:
- Your application natively inherits BigID's secure, multi-tenant isolation out of the box.
- Your application will function identically whether deployed in a BigID Cloud environment or within an On-Premise customer installation.
- You avoid the overhead of managing, scaling, and securing an external database connection.
You can interact with TPA storage directly through the BigID API using your App's token credentials.
## Secure Endpoint Validation
Because your custom application is an independent web service receiving POST requests from the BigID core, it is critical that your application validates that these requests are legitimately originating from BigID.
- **Always** parse and validate the `X-Signed-Token` header sent with action executions.
- **Always** fetch the public key from the BigID `api/v1/tpa/public-key` endpoint to cryptographically verify the signature payload.
- Ensure the `exp` (expiration) field within the signature payload is checked to prevent replay attacks using expired tokens.
## Build for Asynchronous Execution
When designing your app's actions, assume that tasks might take a significant amount of time to process, especially when dealing with large datasets or complex remediation workflows.
- Design actions to run asynchronously whenever possible. Avoid blocking the initial HTTP request with long-running operations.
- Instead, your `/execute` endpoint should immediately acknowledge the request and return an `IN_PROGRESS` status response.
- Once your application completes the background processing, utilize the `updateResultCallback` URL provided in the initial execution payload to send the final `SUCCESS` or `ERROR` status back to BigID.
## Stateless Design
Your BigID Application should be designed as a stateless microservice.
- Avoid storing session state or long-running execution context directly in memory on your application server.
- If your app crashes or scales horizontally across multiple instances, it should be able to pick up work or serve UI components without relying on local server state.
- Rely on the payload parameters sent by BigID on each request, and use TPA Storage for any necessary persistent configuration.
--------------------------------------------------------------------------------
# Role of BigID Apps
## The BigID Platform
BigID is implemented as a platform. This means the BigID core provides
four services: Correlation, Classification, Clustering, and Catalog. All
other capabilities are implemented as apps using the data from these
four services.
### Correlation
Correlation is the process of determining who a piece of information
belongs to. For example, in our DSAR application we use correlation to
report all of the information belonging to an individual. In our breach
response application, we can use a sample of breached data to discover
the individuals that need to be notified.
### Classification
Classification is the process of determining the type of a piece of
information. For example, classification can tell us that the string "+1
(917) 555-5555" is a phone number. It also can tell us that an image
looks like a receipt. BigID uses regular expressions, NLP, and NER
classifiers to determine the types of data and files. You can read more
about our classification methodology here:
[https://bigid.com/blog/what-is-data-classification/](https://bigid.com/blog/what-is-data-classification/)
### Clustering
Clustering is a commonly used machine learning technique. Google has a
good article about clustering in general at
[https://developers.google.com/machine-learning/clustering/overview](https://developers.google.com/machine-learning/clustering/overview).
For BigID, clustering is the process of combining like files together to
generalize about the group. For example, if you have hundreds of files
that look similar (similar types of data, layouts, text patterns, etc)
BigID can determine that files it encounters in the future are going to
have the same data elements.
### Catalog
The BigID Catalog takes the results of the previous 3 services and
allows them to be accessed and searched in one place. Along with that,
the catalog also indexes metadata about the objects being scanned like
access permissions and modification dates.
## Where does my app come in?
All apps have the same access to the four core services above. This
means your app can perform any use case that requires knowing the type,
metadata or owner of a piece of data.
--------------------------------------------------------------------------------
# Building a BigID App
import { Aside } from '@astrojs/starlight/components';
import { Vimeo } from '@astro-community/astro-embed-vimeo';
import ExerciseIframe from '@/components/ExerciseIframe.astro';
import Scenario from '@/components/Scenario.astro';
import ApiExplorer from '../../../components/ApiExplorer.astro';
# What is a BigID App
BigID Applications allow you to add your own business logic and UI to a
BigID system. This means that you can add dashboards, synchronize BigID
with an external system, or even add entire data governance
applications.
BigID applications are written as web applications. This means you can
use any programming language and development environment you want. We've
created samples in Typescript, Java, and Python to get you started. You
also can use our partner [Retool](https://retool.com) to create low-code
BigID apps.
## Common Use Cases
There are a variety of uses for BigID apps, but the most common are:
- Sending BigID's classification results to external systems (Alation
App, Informatica App, Wiz App)
- Adding intelligence from an external system into the BigID Data
Catalog (Okera App)
- Using third-party password stores inside of BigID (AWS Secrets Manager
App)
- Generating proprietary reports with BigID data (Sanctions.io)
- Configuring BigID with information from other systems (AWS
AutoDiscovery)
## How are BigID Apps implemented?
BigID apps are web applications. In their simplest form can be
implemented with 3 HTTP endpoints. More advanced applications provide
their own user interface which is also written as a web page.
This means you can use your programming environment of choice to make a
BigID app. As long as your programming language of choice can send and
receive HTTP requests it can be used to make a BigID app.
## The Simplest BigID App
BigID apps range from a few hundred lines to entire data governance
suites. Below is a sample of an minimal BigID app. It consists of the
manifest and an action. The specifics of manifests and actions are
covered more below, but this code is a good launching point.
```js
const app = express();
app.use(express.json());
app.get('/manifest', (req, res) => {
let manifest = {
app_name: "Training App",
description: "Test App",
vendor: "BigID",
category: "utility",
license_type: "FREE",
actions: [
{
description: "test",
params: [],
is_sync: true,
action_id: "Sync"
}
],
global_params: []
};
res.json(manifest)
});
app.post('/execute', (req, res) => {
let response = {
"statusEnum": "ERROR",
"executionId": req.body.executionId,
"progress": 0,
"message": ""
};
if (req.body.actionName !== "Sync") {
return res.json(response);
}
// Do something here
// Update status to success if we had a success
response.statusEnum = "SUCCESS";
response.progress = 1;
return res.json(response);
})
app.listen(3000);
```
## Knowledge Check
import Quiz from "../../../components/Quiz";
# Defining Your Application
The name, description, and capabilities of your application are defined
in a JSON file called the App Manifest. The App Manifest tells us two
types of information:
- Who your application is (metadata)
- What your application can do (actions)
A sample manifest is below:
```json
{
"app_name": "Training App",
"description": "This application is a training sample",
"category": "privacy",
"license_verification_key": "",
"license_type":"FREE",
"vendor": "BigID",
"is_interactive": true,
"actions": [],
"global_params": []
}
```
- app_name - The name that will appear within the apps page for your
application
- description - The description that will appear within the apps page
for your application
- category - The section of the apps page your app will be placed in
once installed. Options are "privacy", "protection", "perspective",
and "utility"
- license_verification_key - A key you receive from the after your app
has been reviewed and submitted. Without a key, a warning will be
presented to users when installing your application.
- license_type - Whether a paid license is required for your
application. Options are "paid" or "free"
- vendor - The name of the application author
- is_interactive - Boolean representing whether this application has a
custom UI component. True if a custom ui component exists.
- actions - One or more App Actions that your application can perform
- global_params - global settings for your application that are sent
with all action requests
## Exercise: Create an App Manifest
You want to create an application for your org to be able to synchronize
BigID with a home grown inventory system. The first step is creating
your application manifest. **Create an application manifest for an app
named Training App**
## Exposing Logs to BigID
Apps are most commonly installed within Kubernetes clusters. This makes
it difficult for users other than system administrators to know what's
going on in your application. To make this process easier you can
publish logs to the /logs endpoint as text. The contents of this
endpoint will then be accessible to users by navigating to the App
Activity Log within the BigID UI.
## Customizing Icons
Apps have two endpoints where they can customize icons displayed within
the UI, /assets/icon sets the icon shown within the app drawer.
/assets/sideBarIcon sets the icon shown within the sidebar while the app
is active or pinned to the sidebar. You can return jpg, png, and svg
icons at that endpoint, but we suggest svg to allow the icon to scale
with your user's screens.
## Defining App Actions
Actions are schedulable pieces of business logic that your application
allows BigID to access. They can synchronize between BigID and other
systems, modify BigID contents and more. They are defined in your
manifest under actions with the following format:
```json
{
"action_id": "dsConnections",
"description": "The action updates the data source connections in our training app",
"is_sync": true,
"action_params": [
{
"param_name": "save",
"param_type": "boolean",
"is_cleartext": true,
"param_description": "Should we save data source connections?",
"default_value": "true",
"param_priority": "primary",
"is_mandatory": true
}
]
}
```
This action will look like the following inside BigID:
Actions can have multiple configurations and schedules. For instance,
someone can run your action once a week with settings to modify all data
sources and once a day to modify a specific data source.
## Exercise: Defining App Actions
Your application needs an action to send data between your internal
system and BigID. **Add an action to your app manifest without
parameters called "Sync".**
## Implementing App Actions
All app actions will result in a POST request to your app's /execute
endpoint. This post request will have the following body:
```json
{
"actionName": "dsConnections",
"executionId": "5f0bd3bad10a2604246ad846",
"globalParams": [],
"actionParams": [
{
"paramName": "save",
"paramValue": "true"
}
],
"bigidToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJiaWdpZC13ZWIiLCJpc0FkbWluIjp0cnVlLCJyb2xlSWRzIjpbInN5c3RlbSJdLCJ0eXBlIjoiYWNjZXNzLXRva2VuIiwiaWF0IjoxNTk0NjEwNjE4LCJleHAiOjE1OTUyMTU0MTh9.0isHh5qJ1pa8rwJVLQD-wjf5Vik5-dwNtwBGM0EFQCw",
"updateResultCallback": "https://bigid.mybigid.com:443/api/v1/tpa/executions/5f0bd3bad10a2604246ad846",
"bigidBaseUrl": "https://bigid.mybigid.com:443/api/v1/",
"tpaId": "5f04b073292cf28c3bb756fb"
}
```
- actionName - the name of the action that you defined in your manifest
that is currently being executed
- executionId - the ID of this particular call to the action. Used in
BigID for auditing and tracking if an action was completed
successfully.
- globalParams/actionParams - parameters defined in the the manifest and
the values set for these parameters in the BigID UI.
- bigidToken - the API token that lets you query BigID APIs. You need
this to get data from BigID
- updateResultCallback - URL is where you can send status information
about long running tasks so BigID knows they are complete
- bigidBaseUrl - the URL of the BigID API so you know where to send API
calls
- tpaId - unique ID for our application from BigID
The bigidToken is a system token that can be used to access any of the
[BigID API endpoints](https://developer.bigid.com/wiki/BigID_API).
Your /execute endpoint should return its status with the following JSON:
```json
{
"statusEnum": "COMPLETED/ERROR/IN_PROGRESS",
"executionId": executionId,
"progress": 1,
"message": "Successfully imported data sources"
}
```
This status will be displayed within the application's activity page in
the BigID UI. Your app may have long running actions and need to
incrementally update the user with its status. You can do that using the
updateResultCallback provided in the initial request to your action.
```http
PUT https://bigidBaseUrl/api/v1/tpa/executions/executionId HTTP/1.1
Authorization: bigidToken
{
"statusEnum": "IN_PROGRESS",
"progress": 0.5,
"message": "Almost there!"
}
```
## Exercise: Implementing App Actions
Write the /execute endpoint for your action. Make sure if there's
invalid data being sent you return a status of ERROR. Call
inventory.update() to update your homegrown inventory. **Implement the
"Sync" action for the /execute endpoint.**
Now that you've created an basic action, you can use API token supplied
by BigID to your action to call any of the [BigID API
Endpoints](https://developer.bigid.com/wiki/BigID_API) to modify and
retrieve data in BigID.
# Creating an App Frontend
Apps can also have a user interface that's displayed within the BigID
UI. This UI takes the form of a website. The app framework provides an
[SDK for both Typescript and
Javascript](https://www.npmjs.com/package/@bigid/app-fw-ui-sdk) that
allows you to communicate with BigID from your app.
In your manifest, set the is_interactive property to true.
```json
{
"is_interactive": true,
}
```
When you install an app with this flag set equal to true, users will be
prompted for a UI URL during installation as can be seen in the below
app install walkthrough. By default this will be set to ``/ui,
so we suggest you use that endpoint to make installation easier for end
users.
This UI can either be served directly to your users or BigID can
function as a proxy to request the contents of the UI from a private IP
address only accessible to BigID.
## Exercise: Adding a UI
You've decided that instead of using actions you want your application
to take the form of a web app. **Implement a /ui endpoint that returns
the text "Hello World" and modify the manifest so BigID knows this is an
interactive application.**
## Framework Features
### Creating Remediation Actions
The app framework allows developers to create remediation actions that
integrate directly into BigID policies and the Security Posture console.
Once created, these actions are available for users to manage security
violations and enforce data policies.
#### How to Create a Remediation Action
To turn an application action into a BigID remediation action, you must:
1. Add the field **\`command_type\`** to the action definition in your
**app manifest**.
2. Ensure the action contains the **mandatory parameter** for its
\`command_type\`.
3. (Optional) Define additional parameters your action requires.
##### \`command_type\` Values
| Command Type | Mandatory Parameter | Type | Description |
|----|----|----|----|
| **objects** | \`objectList\` | \`string\[\]\` | A list of object identifiers (e.g., files). |
| **container** | \`containerName\` | \`string\` | A single container identifier. |
| **column** | \`columnList\` | \`string\[\]\` | A list of column identifiers. |
Remediation Action Types
##### Example: Remediation Action Manifest
Below is an example manifest snippet for an action configured as a
remediation action using the **\`objects\`** \`command_type\`:
```json
{
"ds_supported_types": [
"googledrive",
"gdrive-v2",
"sharepoint-online",
"sharepoint-online-v2",
"o365-onedrive",
"onedrive-v2",
"smb_v2"
],
"action_id": "revoke",
"command_type": "objects",
"description": "Revokes permission from file",
"is_sync": false,
"action_params": [
{
"param_name": "objectList",
"param_type": "Array",
"is_cleartext": true,
"param_description": "The list of file names",
"default_value": ["file1", "file2"],
"param_priority": "primary",
"is_mandatory": true
},
{
"param_name": "coaLabel",
"param_type": "String",
"is_cleartext": true,
"param_description": "The access label to revoke",
"default_value": "coaLabel",
"param_priority": "primary",
"is_mandatory": true,
"fetch_items": {
"api": "api/v1/aci/coa",
"pathToArray": "data",
"pathToValue": "label_name"
}
},
{
"param_name": "dataSource",
"param_type": "String",
"is_cleartext": true,
"param_description": "The data source name",
"default_value": "dataSource",
"param_priority": "primary",
"is_mandatory": true
}
]
}
```
#### Availability in BigID
1. **Policies**: Remediation actions automatically appear in the
**Available Remediation Actions** section of the policy
configuration page.
2. **Case Creation**: When a policy violation creates a **case** in the
Security Posture console, the remediation action will appear in the
**All Actions** dropdown list if the case is on a supported data
source.
3. **Execution**: The user can execute the action at the **case level**
(for all violations) or **object level** (for specific violations).
#### Developer Checklist
- Added \`command_type\` in the manifest.
- Verified inclusion of the correct **mandatory parameter**.
- Declared **supported data sources** (\`ds_supported_types\`).
- Tested execution at both case and object level in the Security Posture
console.
## Asymmetric Signing Without Using SDK
When Asymmetric Signing is enabled, all requests to Third Party
Applications will include a special header called X-Signed-Token. This
token is a base64 encoded JSON object that contains a signature created
using the RSA-SHA256 algorithm.
Example of X-Signed-Token
```json
{
"signaturePayload": {
"exp": 1672508987,
"kid": "key123"
},
"signature": "Base64 or Hex Encoded Signature"
}
```
Components
signaturePayload: Contains the following fields:
- exp: The expiration time of the token in UNIX timestamp format.
- kid: The key ID used for the signature.
signature: The actual signature, encoded in Base64 or Hex.
Steps to Validate the Signature
1. Fetch the Public Keys
To validate the signature, you will need to fetch the public keys from
our platform. These keys are available at:
GET BASE_PLATFORM_URL/api/v1/tpa/public-key
This endpoint returns two keys in JSON format:
```json
{
"keys": [
{
"kid": "keyId1",
"publicKey": ""
},
{
"kid": "keyId2",
"publicKey": ""
}
]
}
```
Decode the X-Signed-Token
Extract the X-Signed-Token from the request header and decode the Base64
encoded JSON object.
Validate the Signature
To validate the signature, you need to:
- Retrieve the kid from the signaturePayload.
- Find the corresponding public key from the fetched keys using the kid.
- Verify the signature using the public key and the RSA-SHA256
algorithm.
- Ensure the exp field in the signaturePayload is still valid (not
expired).
Notes
- Signature Algorithm: Ensure you use the RSA-SHA256 algorithm
for validation.
- Expiration Time
Always check the exp field to avoid accepting expired tokens.
## Storing Application Data
BigID applications should be multitenant. This allows your application
to be used by many BigID installations. To make this possible, you need
someplace to store configuration and application data that is unique to
each environment. BigID allows your application to store information
through a series of API calls named TPA Storage.
To store a value into TPA storage you can post the information to the
[TPA storage
endpoint](https://api.bigid.com/index-custom-dev.html#put-/-tpaId-/storage):
Then to retrieve the values you've stored, you can retrieve them with a
GET request like below:
If you want to retrieve just a single key like the test key we set
earlier you can use the /key endpoint like below:
Both actions and the BigID UI SDK provide you the context of the
environment you're in. To make your application properly store data
across environments, use the bigidBaseUrl provided by an action or the
getBffUrl() function in the UI SDK to determine the base url to send
requests to.
## Retrieve Data Source Credentials
If enabled in the application settings, your application can retrieve
data source credentials from BigID to act upon data sources without
prompting the user to enter credentials again.
To enable this feature, your app needs to do the following:
- Set the APPLICATION_CREDENTIALS_KEY environment variable in your BigID
installation to a SHA256 key.
- Enable "Allow Application to retrieve BigID data sources credentials"
within your application settings
- Retrieve encrypted data source credentials from
[tpa/{tpaid}/credentials/{datasource}](https://api.bigid.com/index-custom-dev.html#get-/tpa/-tpaId-/credentials/-dsName-)
- Decrypt Credentials
## Supply Data Source Credentials
Your application can supply credentials to BigID. This allows BigID to
interface with custom credentials stores like Amazon Secrets Manager,
Azure Credentials Manager, or a homegrown solution. To do this you need
to create an action in your application to receive credential requests.
When the scanner needs to contact a data source that your application is
providing credentials for it will execute the action. This action will
be look the same as other action executions, but will have an additional
parameter **credentialProviderCustomQuery** that is set by the user to
indicate which credential they want your app to provide. The scanner
request will look like the following:
```json
{
"actionName": "your-action",
"executionId": "executionid",
"globalParams": [],
"actionParams": [
{
"paramName": "credentialProviderCustomQuery",
"paramValue": "user set credential locator"
}
],
"bigidToken": "bigidjwt",
"updateResultCallback": "https://bigid.mybigid.com:443/api/v1/tpa/executions/executionid",
"bigidBaseUrl": "https://bigid.mybigid.com:443/api/v1/",
"tpaId": "appid"
}
```
In response to this your application should look up the credential and
supply the username and password in the additionalData field of the
response like below:
```json
{
"executionId": "executionid",
"statusEnum": "COMPLETED",
"progress": 1,
"message": "User found and deserialized",
"additionalData": {
"username": "admin",
"password": "password"
}
}
```
The scanner will then use that username and password to contact the data
source.
A sample password vault implementation is available at
[https://source.bigid.tools/training/credentials-vault](https://source.bigid.tools/training/credentials-vault)
### Credential Types
The above sample shows basic authentication, but there's several other
types of credentials that are available.
#### Basic Credential Type
This credential type supplies a username and password.
```json
{
"executionId": "executionid",
"statusEnum": "COMPLETED",
"progress": 1,
"message": "User found and deserialized",
"additionalData": {
"username": "admin",
"password": "password"
}
}
```
#### JSON Credential Type
This credential type supplies a JSON object.
```json
{
"executionId": "executionid",
"statusEnum": "COMPLETED",
"progress": 1,
"message": "User found and deserialized",
"additionalData": {
"content_enc": "{}"
}
}
```
#### Personal Access Token Credential Type
This credential type supplies an access token for a user.
```json
{
"executionId": "executionid",
"statusEnum": "COMPLETED",
"progress": 1,
"message": "User found and deserialized",
"additionalData": {
"personalAccessToken": "token"
}
}
```
#### Kerberos Principal Credential Type
This credential type supplies a Kerberos Principal username and
password.
```json
{
"executionId": "executionid",
"statusEnum": "COMPLETED",
"progress": 1,
"message": "User found and deserialized",
"additionalData": {
"principal": "principal",
"username": "user",
"password": "pass"
}
}
```
#### Key Credential Type
This credential type supplies a secret key.
```json
{
"executionId": "executionid",
"statusEnum": "COMPLETED",
"progress": 1,
"message": "User found and deserialized",
"additionalData": {
"authentication_key_enc": "secret"
}
}
```
#### Account Authentication Credential Type
This credential type supplies a user account and a secret key.
```json
{
"executionId": "executionid",
"statusEnum": "COMPLETED",
"progress": 1,
"message": "User found and deserialized",
"additionalData": {
"authentication_key_enc": "secret",
"accountName": "user"
}
}
```
#### OAuth2 Credential Type
This credential type supplies an OAuth2 client id, client secret and
token url. It is used for data sources that use the OAuth2 Client
Credentials flow.
```json
{
"executionId": "executionid",
"statusEnum": "COMPLETED",
"progress": 1,
"message": "User found and deserialized",
"additionalData": {
"url": "tokenurl.com/oauth2/token",
"clientId": "clientid",
"client_secret_enc": "secret"
}
}
```
#### Role Credential Type
This credential type supplies a role name and session name.
```json
{
"executionId": "executionid",
"statusEnum": "COMPLETED",
"progress": 1,
"message": "User found and deserialized",
"additionalData": {
"roleSessionName": "session",
"roleResourceName": "resource"
}
}
```
#### AAD Service Principal Credential Type
This credential type supplies a principal id and secret used for Azure
Active Directory.
```json
{
"executionId": "executionid",
"statusEnum": "COMPLETED",
"progress": 1,
"message": "User found and deserialized",
"additionalData": {
"principalId": "principal",
"principal_secret_enc": "secret"
}
}
```
#### Credential Credential Type
This credential type supplies an access key and secret.
```json
{
"executionId": "executionid",
"statusEnum": "COMPLETED",
"progress": 1,
"message": "User found and deserialized",
"additionalData": {
"accessKey": "key",
"secret_key_enc": "secret"
}
}
```
#### STS Credential Type
This credential type supplies an access key, secret, and session token
for use with AWS STS.
```json
{
"executionId": "executionid",
"statusEnum": "COMPLETED",
"progress": 1,
"message": "User found and deserialized",
"additionalData": {
"accessKey": "key",
"secret_key_enc": "secret",
"session_token_enc": "000000"
}
}
```
## Custom Permissions and Roles
Permissions in BigID allow you to control which users have access to
which parts of your system. You can create custom permissions to control
how users access your app. For example you could create a custom
permission to make your application read only for your report writers,
but give full access to admins.
Roles in BigID allow you to give a set of permissions to users. For
instance you might want all report writers to be able to do the same
thing. You also might want all users of your app to be able to do
certain things. That's where custom roles come in.
Both custom roles and custom permissions are defined in the manifest.
A custom permission has the following format:
```json
{
"action": "permission.action",
"label": "Label Displayed in UI",
"description": "Description about what this permissions allows the user to do"
}
```
A role is a collection of permissions and has the following format:
```json
{
"name": "test",
"permissions": ["permission.action1","permission.action2"],
}
```
So if we wanted to add a custom permission named app.deleteData and a
custom role named App Admin, our manifest would look like below:
```json
{
"app_name": "Training App",
"description": "This application is a training sample",
"category": "privacy",
"license_verification_key": "",
"license_type":"FREE",
"vendor": "BigID",
"is_interactive": true,
"actions": [],
"global_params": []
"permissions": [
{
"action": "app.DeleteData",
"label": "Allow this user to delete data",
"description": "This allows a user to delete data in sample app"
}
],
"custom_roles": [
{
"name": "App Admin",
"permissions": ["app.DeleteData"]
}
]
}
```
Your application can then retrieve the permissions assigned to a user by
doing a GET request to the /api/v1/roles/rbac/user-permissions endpoint
using the user's token like below:
Custom permissions will be name-spaced with the
permission.applications.{App Name} prefix
--------------------------------------------------------------------------------
# Writing a Java Connector
## SDK Structure and Data Model
In this module, we'll explore the core components of the BigID Connector
SDK and delve into the data model that underpins connector development.
Understanding these elements is crucial for building effective and
interoperable connectors.
### Deep Dive into SDK Modules
The BigID Connector SDK is comprised of three modules, each serving a
distinct purpose:
- **sdk-api** This module houses the interfaces that define the core
functionalities of a connector. These interfaces act as contracts,
specifying the methods that your connector must implement to interact
with BigID and the data source.
- **sdk-data:** This module provides the data model, which includes
classes and objects that represent data source entities in a
standardized format. This standardized representation ensures seamless
communication between your connector and the BigID platform.
- **sdk-utils:** This module offers a collection of utility classes
designed to simplify common development tasks. These utilities can
help with data object conversion, iterator management, and other
functionalities, streamlining your development process.
### Exploring the Data Object Hierarchy
The SDK's data model employs a hierarchical structure to represent data
source entities. This hierarchy consists of three main levels:
- A **Container** Represents the highest-level entry point in a data
source. Examples include a database, a cloud storage bucket, a root
folder, or a user account.
- A **Subcontainer** Represents a secondary entry point within a
container. Examples include a schema within a database, a folder
within a bucket, or a workspace within a user account.
- A **Leaf Object** Represents the lowest level in the hierarchy,
containing the actual data. Examples include tables in a database,
files in a folder, emails in a mailbox, or tickets in a system.
Understanding this hierarchy is crucial for effectively modeling data
source entities and ensuring that your connector can accurately
represent the structure of the data source to BigID.
```mermaid
graph TD
Container --> Subcontainer
Subcontainer --> LeafObject
```
This diagram visually represents the relationship between the three
levels of the data object hierarchy. The *Container* is the root, with
*Subcontainers* nested within it, and *LeafObjects* at the bottom,
containing the actual data.
### Understanding Data Object Types
The SDK supports four types of data sources:
- **Structured** Represents data objects with a defined schema, such as
tables in relational databases or collections in NoSQL databases.
- **Unstructured** Represents data objects without a predefined schema,
such as files in a file system or documents in cloud storage.
- **App** Represents data objects specific to applications, such as
emails, messages, or tickets. These objects often contain a
combination of structured and unstructured data.
Determining the appropriate data source type allows you to know what
interfaces you need to implement.
### Importance of Excluding Sensitive Data
It's crucial to remember that \`DataSourceObjects\` and \`DataLink\`
objects should only contain metadata or indexing information. \*\*Never
include sensitive data, such as passwords, personally identifiable
information (PII), or other confidential details, in these objects.\*\*
BigID provides mechanisms for handling sensitive data during the
scanning process, and including it in the metadata objects can pose
security risks.
This concludes the module on SDK Structure and Data Model. You now have
a deeper understanding of the SDK structure and the data model that
forms the foundation of connector development. In the upcoming modules,
we'll explore the various interfaces that enable your connector to
interact with BigID and data sources.
--------------------------------------------------------------------------------
# Installing a Connector
import { Aside } from '@astrojs/starlight/components';
First, you'll need to install your connector onto a server. Many people
have a dedicated server for Apps and Connectors, other people just
install them directly onto the BigID App server. Where you install the
connectors depends on the resources of your BigID App server. **Wherever
they are installed, BigID connectors must be network accessible from
your scanners.**
Since BigID connectors can be created in any language and with any
deployment methodology, installing the connector may differ. What won't
differ is where you find out how to install a connector. Connectors
obtained from the marketplace will have a Setup and Support guide that
details the steps to install them. Connectors obtained from the BigID
community will have a README.md file detailing this information.
Once you have your connector installed on a server, we can install it
within BigID.
1. Once logged into BigID, navigate to Administration \> Data Source
2. Select + New Data Source
3. If your connector is for an unstructured data source, select "GRA
Unstructured". Otherwise select, "Generic REST API"
4. Enter the server URL of your REST connector into the "Generic server
URL" field
5. Fill out any other parameters as described in your connector's
README.
6. Test your connection and save
--------------------------------------------------------------------------------
# What is a BigID Connector?
import { Aside } from '@astrojs/starlight/components';
## What is a BigID Connector
BigID Connectors allow your BigID system to provide insights about new
types of data. Whether that's a known data type like CSV from a new type
of data source or something completely new to the BigID ecosystem, a
connector will allow you to bring BigID's data discovery capabilities to
that system.
### Why do we need connectors?
Every data source has its own way of communicating with third parties.
Some data sources return information nicely organized, others return it
as a jumbled mess. In order for BigID to give you the insights you
expect, data needs to be fed to BigID in a consistent way. Connectors
work as translators between the multitude of formats that data sources
have adapted to the standard format BigID expects. Note that even if the
data format is the same (REST JSON, REST XML, GraphQL, etc) small
differences make it difficult to reuse connectors. Think of a connector
as a way to interface with a single system.
### How are connectors implemented?
Connectors can either be implemented as a REST API or as a Java JAR
file. REST connectors are bound by the limitations of HTTP connections
including timeouts, size limitations and more. Java connectors are well
suited for complex use cases especially those involving data sources
that stream data.
### BigID Scanning Process
While BigID has different scanning methods (snapshots, metadata scans,
Hyperscan), they all depend on scanners. Scanners allow BigID to contact
data sources and create the search maps that are used to power the BigID
system. Depending on your deployment model you may have scanners located
in the BigID cloud, on-premise, or in your organization's cloud provider
accounts. Scanners take the form of a Docker container and require only
outbound network access.
In a scan, the scanner will do the following:
- If correlation is enabled, load all correlation records in order to
find them within data sources.
- Scan table and file metadata to determine access permissions and
ownership
- Classify data streams
After a user starts a scan, the scanner will use the data in the scan
request to determine what type of connection to make. In the case of
REST API scans, the connector will reach out to your connector. This
means your REST connector must allow inbound network access from your
scanner, and your data source must allow inbound access from the
connector. For Java connectors, the scanner will directly communicate
with the data source.
### Connector Types
There are two different types of connectors supported within BigID.
Which type of connector you want to use to connect to your data source
will have broad implications on setup, network security settings, and
connector installation.
#### Internal (Java-based Connectors)
Most of the connectors you are familiar with are Java-based connectors.
These connectors are written in the Java programming language and
distributed as JAR files. To install a new Java-based connector, an
administrator must manually load the connector JAR file into the scanner
using the command-line. Thankfully, the 50+ BigID written internal
connectors are bundled in the scanner by default. The scanner directly
uses these connectors’ code to connect to your data sources.
These connectors allow large amounts of customization in the scanning
process and the connection to your data source. Due to the customization
options, they are more complicated to create and are not the recommended
connector development method for BigID customers.
#### External (Generic REST API Connectors)
External connectors allow you to create a connector in your favorite
programming language. The scanner will communicate with your connector
over HTTPS so as long as your programming language of choice can respond
to web requests, it can be used to create an external connector.
External connectors can be hosted on any server that has a network
connection to both your scanner and your data source.
There are two different types of external connectors that you can
create: unstructured and structured.
##### Unstructured External Connector
Unstructured connectors allow BigID to scan files from a given data
source. An example of an unstructured data source is Google Drive.
##### Structured External Connector
Structured connectors allow BigID to scan databases. An example of a
structured connector would be our MySQL connector.
### The Simplest BigID Connector
Below is sample code for the simplest REST connector you can make.
```js
const express = require('express')
const app = express()
const port = 3000;
// This is our fake data for the connector
const FAKE_DATA = {
Customers: [
{ Id: 1, Name: "Michael", Address: "100 Osceola Parkway, Kissimmee FL" },
{ Id: 2, Name: "Bob", Address: "1 Sand Lake Rd, Orlando FL" },
{ Id: 3, Name: "Stewart", Address: "1 Sand Lake Rde, Orlando FL" }
],
Orders: [
{ Id: 1, Item: "banana", customerId: 2, Price: 1 }
]
};
/**
* Describes all objects inside a data source
*
* @async
* @param {{ domain: string; user: string; pass: string; header: string; }} login login information from BigID
* @param {string} object name of object
*/
async function describeObjects(login) {
const objects = [];
// Create a new object for each table
for (let table in FAKE_DATA) {
let thisObject = { objectName: table, fields: [] };
// Get field definitions for the object using the first item in each object
for (let field in FAKE_DATA[table][0]) {
thisObject.fields.push({ fieldName: field, fieldType: typeof FAKE_DATA[table][0][field] });
}
objects.push(thisObject);
}
return {
status: 'success',
objects: objects
};
}
/**
* Describes a specific object in the data source.
*
* @async
* @param {{ domain: string; user: string; pass: string; header: string; }} login login information from BigID
* @param {string} object name of object
*/
async function describeObject(login, object) {
const objectFields = [];
// Get field definitions for the object using the first item in the object
for (let field in FAKE_DATA[object][0]) {
thisObject.fields.push({ fieldName: field, fieldType: typeof FAKE_DATA[table][0][field] });
}
objectFields.push(thisObject);
return {
status: "success",
objectName: object,
fields: objectFields
}
}
/**
* Get records from data source for a specific object
*
* @async
* @param {{ domain: string; user: string; pass: string; header: string; }} login login information from BigID
* @param {string} object
* @param {number} offset
* @returns {unknown}
*/
async function getRecords(login, object, offset, count) {
let records = [];
// an offset defines where we should start. Use that as the starting point
// TODO splice only elements needed
const recordWindow = FAKE_DATA[object];
for(let record of FAKE_DATA['object']) {
let thisResult = {
id: record.Id, // All records *must* have a unique ID for BigID to identify them
data: []
};
for(let field in record){
thisResult.data.push({fieldName: field, fieldType: typeof record[field], fieldValue: record[field]});
}
records.push(thisResult);
}
return {
status: "success",
records: records,
offset: records.length
}
}
/**
* Get a count of the number of records of a given object type
*
* @async
* @param {{ domain: string; user: string; pass: string; header: string; }} login login information from BigID
* @param {string} object object name
* @returns {unknown}
*/
async function countRecords(login, object) {
return {
status: "success",
count: FAKE_DATA[object].length
};
}
/**
* Search through objects
*
* @param {{ domain: string; user: string; pass: string; header: string; }} login
* @param {*} object
* @param {*} search
*/
function search(login, object, search) {
//TODO
}
/**
* Returns BASIC formatted auth data from the authorization header
*
* @param {*} req
* @returns {{ user: string; pass: string; header: string; }}
*/
function getAuthData(req) {
if (req.headers.authorization === undefined) {
console.log("No Login information supplied");
res.status(401).json({
status: 'error'
})
return null;
}
const b64auth = (req.headers.authorization || '').split(' ')[1] || ''
const [login, password] = Buffer.from(b64auth, 'base64').toString().split(':');
return {
header: 'Basic ' + Buffer.from(login + ':' + password).toString('base64')
}
}
app.get('/objects/', async (req, res) => {
console.log(`LISTOBJS()`);
const login = getAuthData(req);
if (login === null) { return; }
const obj = await describeObjects(login);
if (obj.status === "success") {
return res.json(obj)
}
return res.status(401).json(obj);
});
app.get('/objects/:object/describe', async (req, res) => {
console.log(`DESCRIBEOBJS(object=${req.params.object})`);
const login = getAuthData(req);
if (login === null) { return; }
let result = await describeObject(login, req.params.object);
if (result.status === "success") {
return res.json(result);
}
return res.status(401).json(result);
});
app.get('/objects/:object/records', async (req, res) => {
console.log(`GETRECORDS(offset=${offset},count=${count},object=${req.params.object})`);
const login = getAuthData(req);
if (login === null) { return; }
let offset = req.query.Offset || 0;
let count = req.query.Count || 25;
if (count > 200) { count = 200 }
return res.json(await getRecords(login, req.params.object, offset, count));
});
app.get('/objects/:object/count', async (req, res) => {
console.log(`COUNTRECORDS(object=${req.params.object})`);
const login = getAuthData(req);
if (login === null) { return; }
return res.json(await countRecords(login, req.params.object));
});
app.listen(port, () => {
console.log(`Sample Connector listening on port ${port}`)
});
```
### Knowledge Check
import Quiz from "../../../components/Quiz";
--------------------------------------------------------------------------------
# Writing a REST Connector
import { Aside } from '@astrojs/starlight/components';
import ExerciseIframe from '@/components/ExerciseIframe.astro';
import Scenario from '@/components/Scenario.astro';
## Connectors Overview
BigID Connectors act as translators between your custom data sources and the standardized format BigID expects. While BigID supports native Java-based internal connectors, **External REST Connectors** allow you to write integrations in any programming language. As long as your service can respond to HTTP requests in the format BigID expects, the BigID scanner will be able to map, catalog, and classify your custom data.
If you need a refresher on the types of connectors and how BigID scanning works, please read [What is a BigID Connector?](/connectors/what-is-a-bigid-connector/) before proceeding.
---
# Writing a Structured Connector
While your data source may store its data differently, you will need to
reorganize it into this structure for BigID to scan the data. Remember
that connectors are translators. **Improper translation from your data
source's format to BigID's is the largest issue when testing and
developing custom connectors.** See the below example of a JSON file for
a single user converted into this format:
```json
{
"user":"user@bigid.com",
"phone": "(321) 555-5555",
"information": [
{"key": "name", "value": "user"},
{"key": "favcolor", "value": "green"}
]
}
```
translates to
```json
{
"objectName": "User",
"fields": [
{
"fieldName": "Name",
"fieldType": "string"
},
{
"fieldName": "FavColor",
"fieldType": "string"
},
{
"fieldName": "Phone",
"fieldType": "string"
}
]
}
```
```json
{
"id": "user@bigid.com",
"fields": [
{
"fieldName": "phone",
"fieldValue": "(321) 555-5555"
},
{
"fieldName": "name",
"fieldValue": "user"
},
{
"fieldName": "favcolor",
"fieldValue": "green"
}
]
}
```
## Connector Endpoints
BigID will request and view your connector’s hierarchy through six
endpoints:
- List what **fields** an **object** has (GET
/objects/``/describe)
- List what **objects** exist (/objects)
- List what **records** are inside an **object** (GET
/objects/``/records)
- Count the number of **records** inside an **object** (GET
/objects/``/count)
- Return the **fields and values** for a given record ID (GET
/objects/``/``)
- Search for **records** in an **object** (POST
/objects/``/sar)
You are not required to implement every endpoint outlined here. If you
want a connector that only does DSAR requests you only need to implement
the following endpoints:
- List what **objects** exist (/objects)
- List what **fields** an **object** has (GET
/objects/``/describe)
- Search for **records** in an **object** (POST
/objects/``/sar)
### List what **fields** an **object** has
Since all records within an object must contain the same fields, BigID
uses an object's fields to tell users what type of information is inside
your data source. BigID expects the following response by your connector
to see what fields an item has:
```http
GET /objects/User/describe HTTP/1.1
```
```json
{
"status": "success",
"objectName": "User",
"fields": [
{
"fieldName": "Name",
"fieldType": "string"
},
{
"fieldName": "FavColor",
"fieldType": "string"
},
{
"fieldName": "Phone",
"fieldType": "string"
}
]
}
```
#### Exercise: List what **fields** an **object** has
You want to create a connector for your org to be able to scan for
sensitive data within an ecommerce system, ShopDB. **Create an endpoint
for your connector that allows BigID to list an object's fields**
### List what **objects** exist
Now that we have an object class created, we need to let BigID all of
the objects our connector has access to. Our object listing endpoint
needs to display the fields within an object that we implemented in the
previous exercise. As we implement these endpoints, many of them rely on
one another.
```http
GET /objects/ HTTP/1.1
```
```json
{
"status": "success",
"objects": [
{
"objectName": "User",
"fields": [
{
"fieldName": "Name",
"fieldType": "string"
},
{
"fieldName": "FavColor",
"fieldType": "string"
},
{
"fieldName": "Phone",
"fieldType": "string"
}
]
}
]
}
```
#### Exercise: List what **objects** exist
For your ShopDB connector to work, you need to tell BigID what objects
are inside the database. The code for this endpoint is similar to your
previous endpoint. **Create an endpoint for your connector that allows
BigID to list out all objects.**
### List what **records** are inside an object
Now that BigID knows what objects we have and what type of information
it can find inside those objects, we need to return the actual data
BigID will scan.
```http
GET /objects/User/records?Count=&Offset= HTTP/1.1
```
```json
{
"status": "success",
"records": [
{
"id": 1,
"data": [
{
"fieldName": "Name",
"fieldValue": "Michael",
"fieldType": "string"
},
{
"fieldName": "Phone",
"fieldValue": "3215555555",
"fieldType": "string"
}
]
}
],
"offset": 1
}
```
#### Exercise: List what **records** are inside an object
BigID has the structure of your connector, but scans are still failing
with 0 records found. **Create an endpoint for your connector that
allows BigID to list out records.**
### **Count** the number of records inside an object
In order for BigID to properly paginate through your data sources, we
need to know how many total records to expect.
```http
GET /objects/User/count HTTP/1.1
```
```json
{
"status": "success",
"count": 1
}
```
### Record Pagination
In a previous exercise, you returned all records inside of an object in
a single call. It's extremely common for providers not to allow all
records to be returned in a single call either do to performance, or
cost reasons. This is where the offset and count parameter come in.
Data providers typically meter their APIs in one of the following ways:
- Limit the number of requests per second/hour/day
- Limit the number of records returned in a single request
- Limit the amount of data in GB processed in a given time period
To handle these use cases, BigID has two properties on the records
endpoint: Count and Offset.
Count dictates the batch size. This is how many records your connector
should return in a single call. This allows BigID users to throttle your
connector based on how many records it returns per page.
Offset allows BigID to paginate requests.
A sample scan using the records endpoint looks like the following:
` sequenceDiagram`
` participant BigID`
` participant Connector`
` BigID->>Connector: Call /count`
` Connector-->>BigID: Total records = 40`
` BigID->>Connector: Call /records with offset = 0 and count = 20`
` Connector-->>BigID: 20 Records retrieved, offset = 20`
` BigID->>Connector: Call /records with offset = 21 and count = 20`
` Connector-->>BigID: 20 Records retrieved, offset = 40`
` BigID->>BigID: Stop as offset >= total (40 >= 40)`
` `
#### Exercise: Pagination
Your connector is constantly scanning and scans are not completing.
**Implement pagination so BigID knows when you are done scanning.**
### Return the fields and values for a given **record ID**
There are cases when we only want to look into an individual record.
This is especially true in cases where we already know the unique ID
like a DSAR or a record investigation.
```http
GET /objects/User/records/1 HTTP/1.1
```
```json
{
"status": "success",
"records": [
{
"id": 1,
"data": [
{
"fieldName": "Name",
"fieldValue": "Michael",
"fieldType": "string"
},
{
"fieldName": "Phone",
"fieldValue": "3215555555",
"fieldType": "string"
}
]
}
],
"offset": 1
}
```
#### Exercise: Return the fields and values for a given **record ID**
Your team has been trying to investigate sensitive data in your data
source using the "Investigate" button in the data inventory, but they
aren't getting results. **Create an endpoint for your connector that
allows BigID to lookup an individual record**
### **Search** for records in an object
Our final service that our connector needs to offer is the ability to
search for records. This is what allows our connector to do DSARs.
Depending on your data source, you may need to implement searching on
your own.
```http
POST /objects/User/records/sar HTTP/1.1
Content-Type: application/json
[{"fieldName": "Name", "fieldValue": "Michael", "isFullMatch": "false"}]
```
```json
{
"status": "success",
"records": [
{
"id": 1,
"data": [
{
"fieldName": "Name",
"fieldValue": "Michael",
"fieldType": "string"
},
{
"fieldName": "Phone",
"fieldValue": "3215555555",
"fieldType": "string"
}
]
}
],
"offset": 1
}
```
#### Exercise: **Search** for records in an object
Your team is using BigID to comply with GDPR DSAR requests. ShopDB
doesn't have any search functionality so you'll have to implement it
yourself. **Implement the SAR endpoint so BigID can properly query your
data source.**
### Authentication
The connector you've written is allowing anyone on the internet to see
the information inside of your data source which is no good. This
connector's database also has no username and password which is
extremely rare. Typically the data sources your connectors will access
will also require authentication of some type.
BigID has the ability to store and manage these credentials for you
inside of the BigID system or use credentials from external password
vaults. When a scan is started, BigID will send the credentials to your
connector.
Credentials sent to your connector will always take the form of a [Basic
authentication
header](https://en.wikipedia.org/wiki/Basic_access_authentication). This
header is not encrypted. For this reason, you must never install a BigID
connector without HTTPS.
While Basic auth credentials always take the form of a username and
password, not all systems use that mechanism for login. Below are some
examples of how you can format a username and password into the
credentials your application is expecting:
- OAuth Client Credentials - Username:
[https://tenanturl.com/,CLIENT_ID](https://tenanturl.com/,CLIENT_ID) Password: CLIENT_SECRET
- Multi Tenant SaaS application with API key - Username: tenantID
Password: API_KEY
- SaaS application with just an API key - Username: `` Password:
API_KEY
#### Exercise: Authentication
Your organization is writing a connector and wants to be sure the data
is only viewable to BigID. You've set up the appropriate firewalls, but
also want your connector to be protected by a username and password.
**Protect your records endpoint with username password auth**
# Writing an Unstructured Connector
## Connector Endpoints
BigID will request and view your connector’s hierarchy through six
endpoints:
- List what **Containers** exist (/objects)
- List what **Objects** exist (/objects/``?Offset=)
- Return the **Metadata** for a given object (GET
/objects/``/``/describe)
- Return the **InputStream** for a given object (GET
/objects/``/``/content-stream)
- **Search** for specific data inside objects (POST
/objects/``/``/sar?Offset=)
Just like for structured connectors, you are not required to implement
every endpoint outlined here. If you want a connector that only does
DSAR requests you only need to implement the following endpoints:
- List what **Containers** exist (/objects)
- List what **Objects** exist (/objects/``?Offset=)
- **Search** for specific data inside objects (POST
/objects/``/``/sar?Offset=)
### List what **containers** exist
The first step in BigID's scan of unstructured data sources will always
be to list out the containers that exist within a data source. This
allows BigID to know where to start the discovery process. Compared to
structured connectors, this endpoint is extremely simple. That's because
containers or folders don't really specify anything about the
information that can be inside them, just that they are a storage
location.
```http
GET /objects HTTP/1.1
```
```json
{
"status": "success",
"containers": [
{
"containerName": "Folder"
},
{
"containerName": "Folder2"
}
]
}
```
#### Exercise: List what **containers** exist
Your team has a set of CSV files they are looking to scan on a file
server. **Implement the containers endpoint to let BigID know what
containers are present on the file server**
### List what **objects** exist inside a container
Now that BigID knows what containers our connector has access to, we can
give it insights into the objects inside those containers. This endpoint
will also provide BigID with the metadata about the objects so we can do
things like HyperScan and enforce permissions policies.
You'll notice that certain fields are set directly on the object,
whereas others are set inside of custom fields. The field set in the
object are predetermined by the BigID connector specification and cannot
change. The custom fields object allows you to add your own!
```http
GET /objects/Folder?Offset= HTTP/1.1
```
```json
{
"status": "success",
"objects": [
{
"containerName": "Folder",
"objectName": "1.pdf",
"dateCreated": "26/02/2020 19:00:34",
"lastModified": "26/02/2020 19:00:34",
"owner": 501,
"sizeInBytes": 177924,
"schemaFields": "NULL",
"sarCapable": false,
"customFields": [
{
"fieldName": "file_type",
"fieldValue": "pdf"
}
]
}
],
"offset": 1
}
```
#### Exercise: List what **objects** exist inside a container
List the objects inside of the container and their metadata so BigID can
scan populate the catalog. **Implement the objects endpoint to let BigID
know what objects are present on the file server**
### Return the **metadata** for a given object
Just like we listed out the metadata for all objects, we need to be able
to list out the metadata for a single object so BigID can inspect single
objects without needing to go through potentially thousands of listings
in our container.
```http
GET /objects/Folder/File/describe HTTP/1.1
```
```json
{
"status": "success",
"containerName": "Folder",
"objectName": "1.pdf",
"dateCreated": "26/02/2020 19:00:34",
"lastModified": "26/02/2020 19:00:34",
"owner": 501,
"sizeInBytes": 177924,
"schemaFields": "NULL",
"sarCapable": false,
"customFields": [
{
"fieldName": "file_type",
"fieldValue": "pdf"
}
]
}
```
#### Exercise: Return the **metadata** for a given object
We need our users to be able to know the metadata of a single file. The
format is exactly the same as the container listing endpoint so feel
free to reuse your code. **Implement the object endpoint to let BigID
know a single object's metadata**
### Return the **inputStream** for a given object
In order for BigID to do classification on the contents of the file it
needs access to them. The inputStream endpoint is unique in that it just
returns the raw data from the file. There's no formatting required here,
just return the raw data.
```http
GET /objects/Folder/File/content-stream HTTP/1.1
utf-8 data here
```
#### Exercise: Return the **inputStream** for a given object
### **Search** for specific data inside objects
```http
POST /objects/Folder/File/sar?Offset= HTTP/1.1
Content-Type: application/json
[{"fieldName": "Name", "fieldValue": "Michael", "isFullMatch": "false"}]
```
```json
{
"status": "success",
"records": [
{
"id": "container/object",
"data": [
{
"searchedFieldName": "fieldName",
"searchedFieldValue": "fieldValue",
"fullObjectName": "container/object",
"offset": "12"
}
]
}
]
}
```
### Exercise: **Search** for specific data inside objects
# Distributing a BigID Connector
` graph TD`
` Cloud[BigID Cloud Environment]`
` OnPrem[BigID On-Premise Environment]`
` Connector[Connector]`
` Scanner[Scanner]`
` Target[Target Data Source]`
` `
` subgraph BigID Environment`
` Cloud --- |Outbound Access| Scanner`
` OnPrem --- |Outbound Access| Scanner`
` end`
` `
` Scanner -->|Inbound Access| Connector`
` Connector -->|Outbound Access| Target`
BigID connectors are distributed as Docker images. This allows them to
be run within the BigID app server Kubernetes cluster or on the
Kubernetes clusters and Docker compose servers running an organization's
scanners. As long as your scanner can access your connector and your
connector can access your data source there are no other deployment
requirements.
\n\n---\n\n## Pre-Flight Checklist\n\nBefore creating a connector for a given data source you'll need to collect the following information:\n\n- What is the type of data returned from the API?\n- Where is the API documentation? Do you have access to it?\n- What authentication modes does this API support?\n- Does your account have any kind of usage limits? (X requests per day, per hour, per minute?)\n- Is there a programmatic representation of all API endpoints? (OpenAPI, Swagger)\n- Is this a structured data source or an unstructured data source?\n- What are the credentials for a test environment of this data source?\n\nAfter you have obtained the above information, determine what data models from the data source you wish to include in your connector. There may be hundreds of data models, but only a few may contain PI. For each of these data models, you need to know the following information:\n\n- What are the fields for this data model? What are the types of those fields? (ex: name=string, age=number)\n- What endpoint gives the records of this data model? (ex: /animals/)\n- What endpoint allows you to lookup a single record of this data model given an id? (ex: /animals/1)\n- What endpoint allows you to search for records in this data model matching criteria? (ex: animals/search?type=cat)\n\nYou now have all the information required to build a connector.
--------------------------------------------------------------------------------
# Getting Started
import { Vimeo } from '@astro-community/astro-embed-vimeo';
import { Card, CardGrid, Aside } from '@astrojs/starlight/components';
Welcome to the BigID Developer Portal! With the BigID data intelligence platform, you can bring our data intelligence anywhere.
BigID has **four main integration options**: the REST API, BigID Apps, BigID Connectors, and Model Context Protocol (MCP) Servers. Each of these integration options is designed for a different use case.
## Integration Options
The BigID API allows you to do anything you can do through the BigID UI programmatically. That could be adding data sources, running DSAR requests from an external privacy portal, or even extracting insights to a data science tool like Tableau or R Studio.
The most common use case for the BigID API is to automate repetitive tasks.
[Go to API Docs →](/api/bigid-api/)
BigID Applications allow you to add your own business logic and UI to a BigID system. This means that you can add dashboards, synchronize BigID with an external system, or even add entire data governance applications.
[Learn about Apps →](/apps/building-a-bigid-app/)
Out of the box, BigID ships with 55+ connectors, but if you want insights using a non-standard or custom system, you have the option to create your own connector. Connectors are created with your programming language of choice as a REST API.
[Build a Connector →](/connectors/what-is-a-bigid-connector/)
Interact with BigID using the Model Context Protocol (MCP) and Large Language Models. MCP allows AI agents to securely query the BigID catalog and perform data intelligence actions autonomously.
[Explore MCP & LLMs →](/llms/llms/)
---
## Exploring the Options
### BigID Apps
BigID Applications are written in your programming language of choice as a web application. You also can use our partner [Retool](https://retool.com) to create low-code BigID apps.
There are two different types of BigID Apps: **Utility** and **Interactive**.
#### Utility Applications
Utility Applications allow you to run custom code at regular intervals or on-demand. Your custom code will be given a BigID API Token to access data from a BigID system.
#### Interactive Applications
Interactive Applications allow you to add additional screens to the BigID user interface. These applications get their authentication information using the [BigID UI SDK](https://www.npmjs.com/package/@bigid/app-fw-ui-sdk).
### REST API Resources
- Documentation for the BigID API is available on the BigID Docs site at [docs.bigid.com/bigid/reference/api-getting-started](https://www.docs.bigid.com/bigid/reference/api-getting-started).
- For a quick overview on how to create an API integration, follow our [BigID API Tutorial](/api/bigid-api-authentication-tutorial/).
### MCP Servers
The Model Context Protocol (MCP) provides a standardized way for Large Language Models to interact with the real world. By deploying the BigID MCP Server, you can give your AI agents and custom LLM applications the ability to autonomously query the BigID data catalog, investigate privacy findings, and execute intelligent workflows.
- The official BigID MCP Server repository and documentation are hosted on [GitHub](https://github.com/bigexchange/bigid-plugin-official).
- Learn how MCP architecture works and how to design Agentic workflows in our [LLMs & Agents Guide](/llms/llms/).
---
## Next Steps
Once you've decided on your integration path, check out our publishing guidelines!
- [Marketplace Publishing Guidelines](/guides/publish-marketplace-guidelines/)
--------------------------------------------------------------------------------
# BigID Developer Portal | Documentation & APIs
> Official developer documentation and wiki for the BigID platform. Learn how to build native and external Apps, integrate data with Connectors, use the BigID API, Privacy Portal API, and build LLM-driven agents.
import { Card, CardGrid } from "@astrojs/starlight/components";
import ClassificationDemo from "../../components/ClassificationDemo";
import DataAnimation from "../../components/DataAnimation";
import MCPAnnouncement from "../../components/MCPAnnouncement.astro";
#### What you can build
Manage BigID programmatically. Automate data source creation, trigger system scans, and retrieve catalog insights.
[View API Tutorials →](/api/bigid-api/)
Automate data subject rights (DSARs), verify user identities, and download secure personal data report bundles.
[Explore Privacy Tutorials →](/privacy-portal-api/authentication/)
Build custom logic and screens on top of the BigID platform using our Apps framework. Connect to external governance tools.
[Learn about Apps →](/apps/building-a-bigid-app/)
Write custom connectors to scan and catalog any un-supported data source in your ecosystem.
[Build a Connector →](/connectors/what-is-a-bigid-connector/)
Interact with BigID using the Model Context Protocol (MCP) and Large Language Models like Claude and ChatGPT.
[Explore LLMs →](/llms/llms/)
--------------------------------------------------------------------------------
# Apps Agent Instructions
# BigID Apps - Agent Instructions
This directory contains documentation for building BigID Applications. When generating code, answering questions, or assisting developers with BigID Apps, adhere to the following core concepts:
## Core Architecture
- BigID Apps are standalone web applications. They can be written in any programming language (Node.js/Express, Python/FastAPI, Java/Spring, etc.) as long as they can respond to HTTP requests.
- They do not run *inside* the BigID core codebase; they run alongside it and communicate via HTTP.
## App Types
1. **Utility Applications**: Run custom code at regular intervals or on-demand. They rely on the `actions` defined in the manifest.
2. **Interactive Applications**: Provide a user interface embedded within BigID. These require `is_interactive: true` in the manifest and typically serve HTML/JS at a `/ui` endpoint. They use the `@bigid/app-fw-ui-sdk` NPM package for client-side context.
## Mandatory Endpoints
Every BigID app MUST implement at least the following endpoints:
- `GET /manifest`: Returns a JSON object describing the app (`app_name`, `version`, `actions`, `permissions`, `global_params`).
- `POST /execute`: The main webhook endpoint. When a user triggers an app action in BigID, BigID sends a POST request here containing the `actionName`, `bigidToken`, and any parameters.
## Authentication & Security
- **Never hardcode credentials.**
- When BigID calls `/execute`, it provides a `bigidToken` in the JSON payload. The app must extract this token and use it as a `Bearer` token in the `Authorization` header to make subsequent calls back to the BigID API.
- For interactive apps, the UI SDK automatically handles token retrieval from the parent BigID window.
--------------------------------------------------------------------------------
# Connectors Agent Instructions
# BigID Connectors - Agent Instructions
This directory contains documentation for building BigID Connectors. When generating code, answering questions, or assisting developers with BigID Connectors, adhere to the following core concepts:
## Core Architecture
- Connectors allow BigID to scan and catalog data from custom or unsupported data sources.
- They are typically distributed as Docker containers and run alongside the BigID Scanners.
## Connector Types
1. **Internal Connectors (Java)**: Built directly into the scanner using the BigID Java Connector SDK. Highly performant but requires Java expertise.
2. **External Connectors (REST API)**: Standalone web services written in any language. The BigID scanner communicates with them over HTTP to stream data.
## REST Connector Structure
An external REST connector must implement specific endpoints depending on the data type:
- **Structured Data**: Needs endpoints to list objects (`/objects`), list fields within objects, and read tabular records (`/objects/{objectName}/records`).
- **Unstructured Data**: Needs endpoints to list containers (`/containers`), list objects/files within containers, and a content-stream endpoint to stream raw binary data back to BigID for classification.
## Pagination & Implementation Rules
- **Pagination is mandatory.** All endpoints that return lists of objects or records MUST support `limit` and `offset` query parameters to prevent memory exhaustion and timeout errors in the BigID scanner.
- **Connection Testing**: Every connector must implement a `/test` endpoint that validates the user-provided credentials against the target data source without running a full scan.
- **Security**: Connectors receive data source credentials over HTTP. Therefore, they should always be secured behind HTTPS/TLS in production.
--------------------------------------------------------------------------------
# About LLMs
import { Aside } from '@astrojs/starlight/components';
## LLM Fundamentals
Large Language Models (LLMs) are a type of Artificial Intelligence known
as Predictive Models. They are trained on vast amounts of text data,
allowing them to understand and generate human-like language by
predicting the most probable next word (or "token") in a sequence. A key
characteristic of a standard LLM is that its knowledge is "frozen" at
the time of its training. This means its knowledge is limited to the
data it was trained on and doesn't include any information or events
that occurred after that point.
```mermaid
graph TD
A[Training Data] --> B{Model};
C[Question] --> B;
B --> D[Token];
```
**Example Interaction:**
```text
User: To be, or not to be, that is the...
LLM: question.
```
### Retrieval Augmented Generation (RAG)
Retrieval Augmented Generation (RAG) is a technique designed to overcome
the "frozen knowledge" limitation of LLMs. It allows a model to access
current, external data that was not part of its original training set.
This is achieved by first searching an external knowledge base (like a
company's internal documents or a real-time database) for information
relevant to the user's query. This retrieved information is then
provided to the LLM as additional context along with the original
prompt, enabling it to generate a more informed, accurate, and
up-to-date answer.
```mermaid
graph TD
A[User's Question] --> B{RAG Process};
B --> C[(Search External Data)];
C --> D[Relevant Information];
A --> E{LLM};
D --> E;
E --> F[Generated Answer];
```
**The RAG process intercepts a user's question, searches for relevant
information, and provides that information to the LLM as context to
generate a better answer.**
**Example of RAG in action:**
```text
User's original question
User: What was our company's Q4 revenue?
Step 1: RAG system searches internal documents and finds the following text in "budget.xlsx":
"Q4 2025 revenue reported at $1,000,000"
Step 2: The system combines the user's question with the found context
Augmented Prompt for LLM:
Context: "Q4 2025 revenue reported at $1,000,000"
Question: What was our company's Q4 revenue?
Step 3: LLM provides an answer based on the new context
LLM: Our company's Q4 revenue was $1,000,000.
```
### Tool Calling
Tool calling significantly expands an LLM's capabilities beyond simple
information retrieval and text generation. It gives an LLM the ability
to interact with and take action on external systems. When an LLM is
configured with tools, it receives a description of what each tool does
(e.g., a function or an API) and what inputs it requires. Based on the
user's request, the model can then intelligently decide which tool to
call and with what arguments to achieve a specific goal. This transforms
the LLM from a passive text generator into an active system that can
execute tasks.
```mermaid
graph TD
A[User's Question] --> B{LLM};
C[Available Tools] --> B;
B --> D[Decide Tool to Call];
D --> E[Execute Tool];
E --> F[Tool Result];
F --> B;
B --> G[Final Answer];
```
**The LLM is provided with a set of tools. When asked a question, it
determines which tool to call, executes it, and uses the result to
formulate the final answer.**
**Example of the Tool Calling process:**
```json
// 1. User asks a question that requires an action
{ "user_prompt": "Please order a pizza for me." }
// 2. The LLM is given a description of available tools
{
"available_tools": [
{
"name": "place_order",
"description": "Places an order for a food item.",
"parameters": {
"item": "string",
"quantity": "integer"
}
}
]
}
// 3. The LLM decides which tool to use
{
"thought": "The user wants to order a pizza. I should use the 'place_order' tool.",
"tool_to_call": "place_order",
"parameters": {
"item": "pizza",
"quantity": 1
}
}
// 4. The system executes the tool and gets a result
{ "tool_response": { "status": "success", "order_id": "12345" } }
// 5. The LLM uses the result to answer the user
{
"llm_response": "I have successfully placed an order for one pizza. Your order ID is 12345."
}
```
## LLMs vs. Agents
With the ability to use tools, LLMs evolve from being simple models into
the core of **agents**. An LLM acts as the "brain" of an agent. The
agent framework provides the LLM with memory (to recall past
interactions) and access to a suite of tools. This combination enables
the agent to reason about a task, break it down into a sequence of
steps, and execute those steps by calling the appropriate tools. This
ability to autonomously plan and act on the real world is the key
differentiator between a basic LLM and an agent.
Agents are more than just predictive models; they can:
Act on their own
Remember their past interactions
Take action in the real world
## The Role of an MCP Server
A **Model Context Protocol (MCP) server** is a standardized bundle of
tools that an agent can connect to and use. Think of it as a universal
API gateway or a service directory for LLM agents. It exposes a
collection of tools in a consistent format, so that any compatible agent
can connect to it, understand the available capabilities, and start
using them without needing custom integration for each individual tool.
While the MCP standard is still evolving, it aims to create an
interoperable ecosystem of tools for AI agents.
Common installation methods for servers providing these tools include
npm, http, and Docker.
```mermaid
graph TD
subgraph MCPServer
direction LR
T1[Tool 1]
T2[Tool 2]
T3[Tool 3]
end
A1[Agent 1] --> MCPServer;
A2[Agent 2] --> MCPServer;
A3[Agent 3] --> MCPServer;
```
BigID provides a hosted MCP server that exposes the full BigID platform — data discovery, classification, access governance, privacy, and more — as a ready-to-use tool bundle for any compatible AI agent. Continue to the next page to learn how to connect to it.
--------------------------------------------------------------------------------
# BigID Cloud MCP Server Docs
import { LinkCard, Aside } from '@astrojs/starlight/components';
The BigID Cloud MCP Server repository and documentation have moved to GitHub.
To access the latest installation instructions, prerequisites, setup guides, and troubleshooting resources, please visit our official repository:
--------------------------------------------------------------------------------
# Authentication
import { Aside, Steps, TabItem, Tabs } from '@astrojs/starlight/components';
Every API request to the Privacy Portal Admin API must be authenticated. To authorize external scripts, developer portals, or orchestration tools, you must generate a secure API key and include it in your HTTP headers.
In this tutorial, you will learn how to obtain your API key via the BigID product integration and construct the necessary authentication headers.
---
## 1. Locating the Privacy Portal Integration
When the Privacy Portal is integrated into BigID, it is accessed as a custom application.
1. **Find the Requests Menu**
By default, the Privacy Portal is accessed via the BigID integration at the following URL pattern:
```text
https:///#/customApp/69a802316d9bfbd8e1fzzb0/request-manager
```
The left-hand sidebar displays the main navigation menu with the **Requests** item selected:
2. **Transition to the Users Settings Page**
API Key settings are located in the User Settings panel. Replace `/request-manager` at the end of the URL with `/settings/users` to jump directly to this screen:
```text
https:///#/customApp/69a802316d9bfbd8e1fzzb0/settings/users
```
---
## 2. Generating Your API Key
1. **Select Your User Profile**
On the **Users Settings** screen, select the user account for which you want to generate an API key. Scroll to the bottom of their profile to find the **API Keys** section.
2. **Launch the Generation Modal**
Click the **Generate** button in the top-right corner of the **API Keys** grid.
3. **Configure Expiration and Name**
In the configuration dialog:
- **API Key Name**: Enter a descriptive name (e.g., `Orchestration-Automation-Key`).
- **API Key expires after (days)**: Set the expiration duration. The maximum limit is **`3650` days** (10 years).
Click **Generate** to proceed.
4. **Copy the Key Value Immediately**
The next dialog displays your newly generated token value.
Click the copy icon (clipboard) to copy the key immediately.
---
## 3. Authenticating Requests
To authenticate requests to the Admin API, pass your copied key in the `X-API-Key` HTTP header.
### Request Headers
```http
X-API-Key: YOUR_GENERATED_API_KEY
Content-Type: application/json
```
### Code Examples
Select your preferred language to see how to authenticate your API calls:
```python
import requests
url = "https://bigidprivacy.cloud/api/prm/my-tenant/user-profiles"
headers = {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json"
}
response = requests.get(url, headers=headers)
print(response.json())
```
```javascript
const url = 'https://bigidprivacy.cloud/api/prm/my-tenant/user-profiles';
const headers = {
'X-API-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
};
async function getUserProfiles() {
try {
const response = await fetch(url, { headers });
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Error fetching user profiles:', error);
}
}
getUserProfiles();
```
--------------------------------------------------------------------------------
# Retrieve and Download Privacy Reports
import { Aside, Steps, Tabs, TabItem } from '@astrojs/starlight/components';
Once data collection, curation, and approval processes are complete, the Privacy Portal compiles all discovered personal data records into a secure report file. For Access requests, privacy professionals need to retrieve these reports programmatically to securely distribute them to customers or archive them for auditing.
In this tutorial, you will learn how to programmatically check if a report is ready and download the completed privacy report using the Admin API.
---
## 1. Verifying Request Completion
You must verify that a request's `processingStage` is **`COMPLETE`** or **`APPROVED`** before attempting to download the report file. If you query the report file endpoint for an active or open request that is still in the verification or collection phases, the server will return an error.
To check request status, use the [Retrieve and Filter Privacy Requests](/privacy-portal-api/get-requests/) endpoint to inspect the `processingStage` field:
```json
{
"id": "req_841203",
"status": "APPROVED",
"processingStage": "COMPLETE"
}
```
---
## 2. Retrieving the Report File
To download the compiled personal data report, execute a `GET` request against the report file endpoint.
### HTTP Endpoint
```http
GET /api/prm/{tenant}/requests/{requestId}/report/file
```
### Path Parameters
| Parameter | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **`tenant`** | String | Yes | Your organization's tenant slug. |
| **`requestId`** | String | Yes | The ID of the completed request (e.g., `req_841203`). |
### Request Headers
Include your programmatically generated key in the `X-API-Key` header (refer to the [Authentication Guide](/privacy-portal-api/authentication/) on how to obtain this key):
```http
X-API-Key:
Accept: application/octet-stream
```
---
## 3. Code Examples
Select your preferred language to see how to download and stream compiled report zip files programmatically:
```python
import requests
url = "https://bigidprivacy.cloud/api/prm/my-tenant/requests/req_841203/report/file"
headers = {
"X-API-Key": "YOUR_API_KEY",
"Accept": "application/octet-stream"
}
print("Retrieving report for request req_841203...")
response = requests.get(url, headers=headers, stream=True)
if response.status_code == 200:
with open("user_results_bundle.zip", "wb") as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
print("Report downloaded successfully and saved as user_results_bundle.zip")
elif response.status_code == 403:
print("Error: Request is not in a completed stage. Report is not ready.")
else:
print(f"Failed to download report. HTTP Status: {response.status_code}")
```
```javascript
const fs = require('fs');
const { Readable } = require('stream');
const url = 'https://bigidprivacy.cloud/api/prm/my-tenant/requests/req_841203/report/file';
const headers = {
'X-API-Key': 'YOUR_API_KEY',
'Accept': 'application/octet-stream'
};
async function downloadPrivacyReport(outputPath) {
try {
console.log('Retrieving report for request req_841203...');
const response = await fetch(url, { headers });
if (!response.ok) {
if (response.status === 403) {
console.error('Error: Request is not in a completed stage. Report is not ready.');
} else {
console.error(`Failed to download report. HTTP Status: ${response.status}`);
}
return;
}
const fileStream = fs.createWriteStream(outputPath);
const nodeStream = Readable.fromWeb(response.body);
nodeStream.pipe(fileStream);
fileStream.on('finish', () => {
console.log(`Report downloaded successfully and saved to: ${outputPath}`);
});
} catch (error) {
console.error('Failed to download report:', error);
}
}
downloadPrivacyReport('user_results_bundle.zip');
```
---
## Handling Common Errors
If you attempt to retrieve the report of a request that has not been approved or is still active, the server will block the download.
**Response (`403 Forbidden`):**
```json
{
"message": "The privacy report for request req_841203 is not compiled yet.",
"status": 403
}
```
**Response (`404 Not Found`):**
If no files or reports were compiled for this request:
```json
{
"message": "Report file not found for request req_841203",
"status": 404
}
```
--------------------------------------------------------------------------------
# Retrieve and Filter Privacy Requests
import { Aside, Steps, Tabs, TabItem } from '@astrojs/starlight/components';
To automate privacy operations, privacy engineers and DPOs often need to continuously poll for new data subject rights requests (DSARs, Deletion, or Opt-Outs). This allows external orchestration engines to trigger scans or notify downstream databases.
In this tutorial, you will learn how to programmatically search, filter, and retrieve incoming privacy requests using the Privacy Portal Admin API.
---
## 1. Navigating the UI for Context
Before writing search scripts, you can locate the request ID, status, and processing stage in the custom application interface within BigID. Under the **Requests** sidebar menu, you will see a live grid of all requests and their current processing status (e.g., `VERIFY`, `COLLECT`, `APPROVE`, `COMPLETE`).
---
## 2. Searching Requests via API
To retrieve user requests programmatically, perform a `POST` request to the search endpoint. Filtering options are passed in the request body, allowing you to build complex search queries.
### HTTP Endpoint
```http
POST /api/prm/{tenant}/requests/search
```
### Request Headers
Ensure you include your admin API key in the `X-API-Key` header (refer to the [Authentication Guide](/privacy-portal-api/authentication/) on how to obtain this key):
```http
X-API-Key:
Content-Type: application/json
```
### Request Body Parameters
The search body accepts a `FindRequestsOptionsDto` JSON object:
* **`filters`** *(array)*: A list of search filter criteria objects.
* **`name`** *(string)*: The field to filter on (e.g., `status`, `type`, `processingStage`).
* **`values`** *(array of strings)*: Values to match against the filtered field.
* **`limit`** *(query integer)*: Maximum number of records to return (defaults to `10`).
* **`skip`** *(query integer)*: Number of records to skip for pagination (defaults to `0`).
* **`showAttributes`** *(boolean)*: Set to `true` to include consumer attribute fields in the returned dataset.
---
## 3. Code Examples
Select your preferred language to see how to search and filter requests via the Admin API:
```python
import requests
url = "https://bigidprivacy.cloud/api/prm/my-tenant/requests/search?limit=5"
headers = {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json"
}
payload = {
"filters": [
{
"name": "status",
"values": ["SUBMITTED"]
}
],
"showAttributes": True
}
response = requests.post(url, headers=headers, json=payload)
print(response.json())
```
```javascript
const url = 'https://bigidprivacy.cloud/api/prm/my-tenant/requests/search?limit=5';
const headers = {
'X-API-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
};
const payload = {
filters: [
{
name: 'status',
values: ['SUBMITTED']
}
],
showAttributes: true
};
async function searchRequests() {
try {
const response = await fetch(url, {
method: 'POST',
headers: headers,
body: JSON.stringify(payload)
});
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Error searching requests:', error);
}
}
searchRequests();
```
---
## 4. Understanding the Response
The API returns a `PagingResponseDtoUserRequestDto` object containing a list of matching requests.
**Response (`200 OK`):**
```json
{
"data": [
{
"id": "req_841203",
"originalRequestId": "ext_9831a28d",
"type": "ACCESS",
"requestKey": "alex.bulis@gmail.com",
"requestKeyType": "EMAIL",
"userType": "CUSTOMER",
"dueDays": 29,
"dueDate": "2026-09-11T14:32:00.000Z",
"status": "SUBMITTED",
"processingStage": "VERIFY",
"regulation": "CCPA",
"closed": false,
"processingStartDate": "2026-08-12T10:00:00.000Z",
"issueDate": "2026-08-12T10:00:00.000Z"
}
]
}
```
### Key Response Fields to Parse
| Field Name | Type | Description |
| :--- | :--- | :--- |
| **`id`** | String | The unique identifier of the request in the Privacy Portal. |
| **`type`** | String | The request action type (`ACCESS`, `DELETE`, `OPT_OUT`, etc.). |
| **`requestKey`** | String | The primary identity lookup key used by the data subject (e.g., email or phone). |
| **`processingStage`** | String | The active workflow step (`VERIFY`, `COLLECT`, `REVIEW`, `CONFIRM`, `APPROVE`, `COMPLETE`). |
| **`dueDate`** | String | ISO 8601 date indicating when compliance must be achieved under the specified regulation. |
---
## Error Handling
If your API key is invalid or your session has expired, the API will respond with:
**Response (`401 Unauthorized`):**
```json
{
"message": "Neither valid API Key nor Authentication cookie is present",
"status": 401
}
```
--------------------------------------------------------------------------------
# Programmatically Validate and Verify Requests
import { Aside, Steps, Tabs, TabItem } from '@astrojs/starlight/components';
When a data subject submits a privacy request (such as an Access or Deletion request), regulations require organizations to verify the requestor's identity before retrieving or deleting personal data. This initial screening stage is referred to as the **Verification Stage** (`VERIFY`).
In this tutorial, you will learn how to programmatically validate a consumer's identity and transition a request from the verification stage to the data collection stage (`COLLECT`) using the Admin API.
---
## 1. The Verification Lifecycle
By default, newly submitted requests start in the **`VERIFY`** processing stage. Until the identity of the requestor is verified:
1. Data collection scanning is blocked to prevent accidental exposure of personal data.
2. Compliance timers (`dueDays`) continue to count down.
Once you have verified the identity out-of-band or via an external IDP, you invoke the `verify` action API to transition the request's `processingStage` to **`COLLECT`**.
---
## 2. Triggering Programmatic Verification
To verify a request, issue a `POST` request to the verification action endpoint.
### HTTP Endpoint
```http
POST /api/prm/{tenant}/requests/{requestId}/stages/verify/actions/verify
```
### Path Parameters
| Parameter | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **`tenant`** | String | Yes | Your organization's tenant slug. |
| **`requestId`** | String | Yes | The ID of the request to be verified (e.g., `req_841203`). |
### Request Headers
Pass your administrative API key in the `X-API-Key` header (refer to the [Authentication Guide](/privacy-portal-api/authentication/) on how to generate this key):
```http
X-API-Key:
Content-Type: application/json
```
### Request Body
The API does not require a complex payload for verification. You pass an empty JSON object:
```json
{}
```
---
## 3. Code Examples
Select your preferred language to see how to programmatically verify a request via the Admin API:
```python
import requests
url = "https://bigidprivacy.cloud/api/prm/my-tenant/requests/req_841203/stages/verify/actions/verify"
headers = {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json"
}
response = requests.post(url, headers=headers, json={})
print(response.json())
```
```javascript
const url = 'https://bigidprivacy.cloud/api/prm/my-tenant/requests/req_841203/stages/verify/actions/verify';
const headers = {
'X-API-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
};
async function verifyRequest() {
try {
const response = await fetch(url, {
method: 'POST',
headers: headers,
body: JSON.stringify({})
});
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Error verifying request:', error);
}
}
verifyRequest();
```
**Response (`200 OK`):**
The API responds with a successful transition message, reflecting that the request has now entered the next stage.
```json
{
"requestId": "req_841203",
"action": "verify",
"status": "SUCCESS",
"newProcessingStage": "COLLECT",
"timestamp": "2026-08-12T14:48:00.000Z"
}
```
---
## 4. Verifying via an External Agent (Alternate)
If verification is performed by a dedicated validation script acting as an agent, you can also log the agent's meta-information by targeting the `verify-agent` endpoint:
```http
POST /api/prm/{tenant}/requests/{requestId}/stages/verify/actions/verify-agent
```
**Request Payload:**
```json
{
"agentName": "OktaIDV-Automation",
"verificationScore": 98,
"transactionId": "tx_abc123789"
}
```
---
## Handling Common Errors
If you attempt to verify a request that has already been verified or is not in the `VERIFY` stage, the server will block the transition.
**Response (`403 Forbidden`):**
```json
{
"message": "User has insufficient permissions or request is not in VERIFY stage",
"status": 403
}
```
**Response (`404 Not Found`):**
If the `requestId` does not exist or the `tenant` is misspelled:
```json
{
"message": "Request req_841203 not found",
"status": 404
}
```