Feature Store
Define features once and serve them consistently for training and inference. These endpoints define entities, feature views, and feature services, ingest and refresh values, resolve online features for serving, and generate point-in-time correct training data.
Feature stores are created in the MLOps user interface, where the offline Postgres and optional online Redis storage is bound. These endpoints operate on a store you already have access to.
All endpoints require authentication via the X-API-Key header. Every request is authorized against your access to the target store; discovering a store does not imply permission to use it.
GET /api/v1/feature-store/stores
List the feature stores your key can access.
Response
{
"data": [
{
"_id": "store_abc123",
"name": "driver_features",
"status": "ready",
"onlineEnabled": true
}
]
}
GET /api/v1/feature-store/stores/:id
Retrieve a single feature store.
POST /api/v1/feature-store/apply
Register or update entities, feature views, and feature services. Objects are applied in dependency order (entities, then views, then services). Applying is idempotent: an unchanged view is a no-op, an additive change updates in place, and a breaking change creates a new version. Send only the object kinds you want to change.
Request
{
"store_id": "store_abc123",
"entities": [
{ "name": "driver", "joinKeys": ["driver_id"], "valueType": "int64" }
],
"views": [
{
"name": "driver_stats",
"entities": ["driver"],
"mode": "batch",
"ttl": "2d",
"schema": [
{ "name": "conv_rate", "dtype": "float64" },
{ "name": "acc_rate", "dtype": "float64" }
],
"source": {
"serviceKind": "addon",
"serviceId": "pg_addon_1",
"type": "postgres",
"table": "driver_stats_raw",
"timestampField": "event_timestamp",
"entityKeyColumns": { "driver_id": "driver_id" }
}
}
],
"services": [
{
"name": "driver_model_v1",
"features": [
{ "view": "driver_stats", "feature": "conv_rate" },
{ "view": "driver_stats", "feature": "acc_rate" }
]
}
]
}
Response
{
"entities": ["driver"],
"views": [{ "viewId": "view_1", "version": 1, "change": "created" }],
"services": [{ "serviceId": "svc_1", "version": 1 }]
}
POST /api/v1/feature-store/write
Ingest feature rows into a view's history. Each row carries the entity join keys, an event_timestamp, and the view's feature columns.
Request
{
"store_id": "store_abc123",
"view": "driver_stats",
"rows": [
{ "driver_id": 1001, "event_timestamp": "2026-07-30T00:00:00Z", "conv_rate": 0.75, "acc_rate": 0.9 }
]
}
Response
{ "view": "driver_stats", "version": 1, "written": 1 }
POST /api/v1/feature-store/materialize
Run the incremental refresh that publishes a view's latest values to the online store. Only changed values are moved, and an older value never overwrites a newer one.
Request
{ "store_id": "store_abc123", "view": "driver_stats" }
Response
{ "view": "driver_stats", "version": 1, "materialized": 1, "watermark": "2026-07-30T00:00:00Z" }
POST /api/v1/feature-store/online-features
Resolve the latest online feature values for a feature service. Send only the entity keys per row. Each result reports the resolved features and lists any that were missing; absent data is always reported, never fabricated. Set max_staleness_seconds to treat a value older than your tolerance as missing rather than serving it stale.
Request
{
"store_id": "store_abc123",
"feature_service": "driver_model_v1",
"entity_rows": [{ "driver_id": 1001 }],
"max_staleness_seconds": 600
}
Response
{
"feature_service": "driver_model_v1",
"version": 1,
"results": [
{ "keys": { "driver_id": 1001 }, "features": { "conv_rate": 0.75, "acc_rate": 0.9 }, "missing": [] }
]
}
POST /api/v1/feature-store/historical-features
Generate leak-free, point-in-time training data: for each labeled row, features are resolved as of that row's event_timestamp. Supply the entity and label frame exactly one way: entity_rows (inline) or entity_s3_uri (an s3:// URI or bare key of a Parquet or CSV frame, the bulk path for training-scale sets). Set entity_format to parquet or csv if it cannot be inferred from the key.
The result is delivered as a Parquet snapshot you read directly.
Request (inline)
{
"store_id": "store_abc123",
"feature_service": "driver_model_v1",
"entity_rows": [
{ "driver_id": 1001, "event_timestamp": "2026-07-30T12:00:00Z", "label": 1 },
{ "driver_id": 1002, "event_timestamp": "2026-07-30T12:00:00Z", "label": 0 }
]
}
Request (bulk)
{
"store_id": "store_abc123",
"feature_service": "driver_model_v1",
"entity_s3_uri": "s3://your-bucket/labels/train.parquet"
}
Response
{
"feature_service": "driver_model_v1",
"version": 1,
"num_rows": 2,
"snapshot_key": "feature-store-snapshots/store_abc123/driver_model_v1/9f2c.parquet",
"snapshot_url": "https://s3.amazonaws.com/...&X-Amz-Signature=..."
}