Features
The feature workflow has two parts. Discovery and association runs read an asset through its connection and produce candidate derived features and column-pair association scores. Feature lists are the curated selections you keep against an asset.
Everything here is private to you and keyed by an assetId from Discover assets on a connection. At most one feature list per asset is the default.
- GET /v1/features/lists List feature lists
- POST /v1/features/lists Create a feature list
- PUT /v1/features/lists/{list_id} Update a feature list
- DEL /v1/features/lists/{list_id} Delete a feature list
- POST /v1/features/lists/{list_id}/default Set a default feature list
- POST /v1/features/discovery/runs Run feature discovery
- GET /v1/features/discovery/results List derived features
- POST /v1/features/associations/runs Run association mining
- GET /v1/features/associations/results List associations
Paths are relative to /data-api
List feature lists
GET /data-api/v1/features/lists
Returns your feature lists, most recently updated first.
- Authentication
- Bearer token How it works
Parameters
No parameters.
Returns
Returns a list of feature-list objects in data.
curl "$VDF_BASE_URL/data-api/v1/features/lists" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" const response = await fetch(`${process.env.VDF_BASE_URL}/data-api/v1/features/lists`, {
headers: {
Authorization: `Bearer ${process.env.VDF_ACCESS_TOKEN}`,
},
});
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const data = await response.json(); import os
import requests
response = requests.get(
f"{os.environ['VDF_BASE_URL']}/data-api/v1/features/lists",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"data": [
{
"id": "f0e1d2c3-b4a5-4968-8776-5a4b3c2d1e0f",
"datasetId": "7a2d9e10-4c3b-4a1e-9f8d-2b6c1e0f5a3d",
"name": "Churn model features",
"featureCount": 18,
"isDefault": true,
"lastUsedAt": "2026-09-01T10:00:00Z"
}
]
} Create a feature list
POST /data-api/v1/features/lists
Creates a feature list for an asset.
Creates a feature list against one of your assets. Marking it default clears the default flag on your other lists for the same asset.
- Authentication
- Bearer token How it works
Body parameters application/json
-
datasetIdstring RequiredIdentifier of the asset the list belongs to.
-
namestring RequiredName of the feature list.
-
featureCountintegerNumber of features the list represents.
-
isDefaultbooleanWhether this becomes the default list for the asset.
Returns
Returns the created feature-list object in data.
Errors
- 400
datasetIdornameis missing. - 404 No asset with this id belongs to you.
curl -X POST "$VDF_BASE_URL/data-api/v1/features/lists" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"datasetId": "7a2d9e10-4c3b-4a1e-9f8d-2b6c1e0f5a3d",
"name": "Churn model features",
"featureCount": 18,
"isDefault": true
}' const response = await fetch(`${process.env.VDF_BASE_URL}/data-api/v1/features/lists`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.VDF_ACCESS_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
datasetId: '7a2d9e10-4c3b-4a1e-9f8d-2b6c1e0f5a3d',
name: 'Churn model features',
featureCount: 18,
isDefault: true,
}),
});
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const data = await response.json(); import os
import requests
response = requests.post(
f"{os.environ['VDF_BASE_URL']}/data-api/v1/features/lists",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
json={
"datasetId": "7a2d9e10-4c3b-4a1e-9f8d-2b6c1e0f5a3d",
"name": "Churn model features",
"featureCount": 18,
"isDefault": True,
},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"data": {
"id": "f0e1d2c3-b4a5-4968-8776-5a4b3c2d1e0f",
"datasetId": "7a2d9e10-4c3b-4a1e-9f8d-2b6c1e0f5a3d",
"name": "Churn model features",
"featureCount": 18,
"isDefault": true,
"lastUsedAt": "2026-09-01T10:00:00Z"
}
} Update a feature list
PUT /data-api/v1/features/lists/{list_id}
Updates a feature list you own.
Updates the fields you supply. Setting isDefault to true clears the default flag on your other lists for the same asset.
- Authentication
- Bearer token How it works
Path parameters
-
list_idstring RequiredIdentifier of the feature list to update.
Body parameters application/json
-
datasetIdstringMove the list to a different asset you own.
-
namestringNew name.
-
featureCountintegerNew feature count.
-
isDefaultbooleanWhether this list is the default for its asset.
Returns
Returns the updated feature-list object in data.
Errors
- 404 No feature list with this id belongs to you, or the target asset does not belong to you.
curl -X PUT "$VDF_BASE_URL/data-api/v1/features/lists/f0e1d2c3-b4a5-4968-8776-5a4b3c2d1e0f" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Churn features v2",
"featureCount": 20
}' const response = await fetch(`${process.env.VDF_BASE_URL}/data-api/v1/features/lists/f0e1d2c3-b4a5-4968-8776-5a4b3c2d1e0f`, {
method: 'PUT',
headers: {
Authorization: `Bearer ${process.env.VDF_ACCESS_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Churn features v2',
featureCount: 20,
}),
});
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const data = await response.json(); import os
import requests
response = requests.put(
f"{os.environ['VDF_BASE_URL']}/data-api/v1/features/lists/f0e1d2c3-b4a5-4968-8776-5a4b3c2d1e0f",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
json={
"name": "Churn features v2",
"featureCount": 20,
},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"data": {
"id": "f0e1d2c3-b4a5-4968-8776-5a4b3c2d1e0f",
"datasetId": "7a2d9e10-4c3b-4a1e-9f8d-2b6c1e0f5a3d",
"name": "Churn features v2",
"featureCount": 20,
"isDefault": true,
"lastUsedAt": "2026-09-01T10:05:00Z"
}
} Delete a feature list
DEL /data-api/v1/features/lists/{list_id}
Deletes a feature list you own.
Permanently deletes the feature list.
- Authentication
- Bearer token How it works
Path parameters
-
list_idstring RequiredIdentifier of the feature list to delete.
Returns
Returns a confirmation object in data.
Errors
- 404 No feature list with this id belongs to you.
curl -X DELETE "$VDF_BASE_URL/data-api/v1/features/lists/f0e1d2c3-b4a5-4968-8776-5a4b3c2d1e0f" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" const response = await fetch(`${process.env.VDF_BASE_URL}/data-api/v1/features/lists/f0e1d2c3-b4a5-4968-8776-5a4b3c2d1e0f`, {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.VDF_ACCESS_TOKEN}`,
},
});
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const data = await response.json(); import os
import requests
response = requests.delete(
f"{os.environ['VDF_BASE_URL']}/data-api/v1/features/lists/f0e1d2c3-b4a5-4968-8776-5a4b3c2d1e0f",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"data": {
"deleted": true
}
} Set a default feature list
POST /data-api/v1/features/lists/{list_id}/default
Marks a feature list as the default for its asset.
Makes this list the default for its asset and clears the default flag on your other lists for the same asset.
- Authentication
- Bearer token How it works
Path parameters
-
list_idstring RequiredIdentifier of the feature list to make default.
Returns
Returns a confirmation object in data.
Errors
- 404 No feature list with this id belongs to you.
curl -X POST "$VDF_BASE_URL/data-api/v1/features/lists/f0e1d2c3-b4a5-4968-8776-5a4b3c2d1e0f/default" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" const response = await fetch(`${process.env.VDF_BASE_URL}/data-api/v1/features/lists/f0e1d2c3-b4a5-4968-8776-5a4b3c2d1e0f/default`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.VDF_ACCESS_TOKEN}`,
},
});
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const data = await response.json(); import os
import requests
response = requests.post(
f"{os.environ['VDF_BASE_URL']}/data-api/v1/features/lists/f0e1d2c3-b4a5-4968-8776-5a4b3c2d1e0f/default",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"data": {
"updated": true
}
} Run feature discovery
POST /data-api/v1/features/discovery/runs
Generates candidate derived features for an asset.
Reads the asset's schema through its connection and proposes derived features — rolling averages, lags, z-scores, date truncations, encodings, and interaction terms — each with a confidence score. The candidates are stored and returned. Read them again with List derived features.
- Authentication
- Bearer token How it works
Body parameters application/json
-
assetIdstring RequiredIdentifier of the asset to analyse.
Returns
Returns a list of derived-feature objects in data.
Errors
- 400
assetIdis missing. - 404 No asset with this id belongs to you, or its connection no longer exists.
- 502 The source could not be reached while reading the schema.
curl -X POST "$VDF_BASE_URL/data-api/v1/features/discovery/runs" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"assetId": "7a2d9e10-4c3b-4a1e-9f8d-2b6c1e0f5a3d"
}' const response = await fetch(`${process.env.VDF_BASE_URL}/data-api/v1/features/discovery/runs`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.VDF_ACCESS_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
assetId: '7a2d9e10-4c3b-4a1e-9f8d-2b6c1e0f5a3d',
}),
});
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const data = await response.json(); import os
import requests
response = requests.post(
f"{os.environ['VDF_BASE_URL']}/data-api/v1/features/discovery/runs",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
json={
"assetId": "7a2d9e10-4c3b-4a1e-9f8d-2b6c1e0f5a3d",
},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"data": [
{
"id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"datasetId": "7a2d9e10-4c3b-4a1e-9f8d-2b6c1e0f5a3d",
"name": "orders_total_rolling_avg",
"formula": "rolling_avg(orders_total, 7)",
"confidence": 0.85
}
]
} List derived features
GET /data-api/v1/features/discovery/results
Returns the derived features discovered for your assets.
Returns your derived features, most recent first. Pass assetId to scope the results to a single asset.
- Authentication
- Bearer token How it works
Query parameters
-
assetIdstringReturn only features discovered for this asset.
Returns
Returns a list of derived-feature objects in data.
curl "$VDF_BASE_URL/data-api/v1/features/discovery/results?assetId=7a2d9e10-4c3b-4a1e-9f8d-2b6c1e0f5a3d" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" const response = await fetch(`${process.env.VDF_BASE_URL}/data-api/v1/features/discovery/results?assetId=7a2d9e10-4c3b-4a1e-9f8d-2b6c1e0f5a3d`, {
headers: {
Authorization: `Bearer ${process.env.VDF_ACCESS_TOKEN}`,
},
});
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const data = await response.json(); import os
import requests
response = requests.get(
f"{os.environ['VDF_BASE_URL']}/data-api/v1/features/discovery/results",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
params={
"assetId": "7a2d9e10-4c3b-4a1e-9f8d-2b6c1e0f5a3d",
},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"data": [
{
"id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"datasetId": "7a2d9e10-4c3b-4a1e-9f8d-2b6c1e0f5a3d",
"name": "orders_total_rolling_avg",
"formula": "rolling_avg(orders_total, 7)",
"confidence": 0.85
}
]
} Run association mining
POST /data-api/v1/features/associations/runs
Scores associations between numeric columns of an asset.
Reads the asset's numeric columns through its connection and scores associations between column pairs using Pearson, Spearman, and mutual-information methods. The scored pairs are stored and returned. Read them again with List associations.
- Authentication
- Bearer token How it works
Body parameters application/json
-
assetIdstring RequiredIdentifier of the asset to analyse.
Returns
Returns a list of association objects in data.
Errors
- 400
assetIdis missing. - 404 No asset with this id belongs to you, or its connection no longer exists.
- 502 The source could not be reached while reading the schema.
curl -X POST "$VDF_BASE_URL/data-api/v1/features/associations/runs" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"assetId": "7a2d9e10-4c3b-4a1e-9f8d-2b6c1e0f5a3d"
}' const response = await fetch(`${process.env.VDF_BASE_URL}/data-api/v1/features/associations/runs`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.VDF_ACCESS_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
assetId: '7a2d9e10-4c3b-4a1e-9f8d-2b6c1e0f5a3d',
}),
});
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const data = await response.json(); import os
import requests
response = requests.post(
f"{os.environ['VDF_BASE_URL']}/data-api/v1/features/associations/runs",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
json={
"assetId": "7a2d9e10-4c3b-4a1e-9f8d-2b6c1e0f5a3d",
},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"data": [
{
"id": "d4c3b2a1-f6e5-4b7a-9d8c-1e0f2a3b4c5d",
"datasetId": "7a2d9e10-4c3b-4a1e-9f8d-2b6c1e0f5a3d",
"featureA": "orders_total",
"featureB": "lifetime_value",
"score": 0.742,
"method": "pearson"
}
]
} List associations
GET /data-api/v1/features/associations/results
Returns the column associations mined for your assets.
Returns your mined associations, most recent first. Pass assetId to scope the results to a single asset.
- Authentication
- Bearer token How it works
Query parameters
-
assetIdstringReturn only associations mined for this asset.
Returns
Returns a list of association objects in data.
curl "$VDF_BASE_URL/data-api/v1/features/associations/results?assetId=7a2d9e10-4c3b-4a1e-9f8d-2b6c1e0f5a3d" \
-H "Authorization: Bearer $VDF_ACCESS_TOKEN" const response = await fetch(`${process.env.VDF_BASE_URL}/data-api/v1/features/associations/results?assetId=7a2d9e10-4c3b-4a1e-9f8d-2b6c1e0f5a3d`, {
headers: {
Authorization: `Bearer ${process.env.VDF_ACCESS_TOKEN}`,
},
});
if (!response.ok) throw new Error(`Request failed with status ${response.status}`);
const data = await response.json(); import os
import requests
response = requests.get(
f"{os.environ['VDF_BASE_URL']}/data-api/v1/features/associations/results",
headers={"Authorization": f"Bearer {os.environ['VDF_ACCESS_TOKEN']}"},
params={
"assetId": "7a2d9e10-4c3b-4a1e-9f8d-2b6c1e0f5a3d",
},
timeout=30,
)
response.raise_for_status()
data = response.json() {
"success": true,
"data": [
{
"id": "d4c3b2a1-f6e5-4b7a-9d8c-1e0f2a3b4c5d",
"datasetId": "7a2d9e10-4c3b-4a1e-9f8d-2b6c1e0f5a3d",
"featureA": "orders_total",
"featureB": "lifetime_value",
"score": 0.742,
"method": "pearson"
}
]
}