Developer Guide – DriftMind API

This guide explains how to use the DriftMind API end-to-end with concrete examples in curl, Python, and Java.

Base URL: https://api.thingbook.io/access/api/driftmind/v1 Auth header: Auth: <token> Core flow: Create → Feed → Predict → Delete

Overview

DriftMind is a cold-start, self-adaptive forecasting engine designed for fast data environments. You create a forecaster, feed time-aligned observations, then request forecasts and anomaly scores.

What Swagger is for

Contract reference: endpoints, schemas, status codes, examples.

What this guide is for

Workflow, best practices, and runnable examples (Java + Python + curl).

For interactive testing of endpoints, use the Swagger UI: Open Swagger UI.
For a gentle introduction to DriftMind, visit: Reflexive Memory Is All You Need.
Looking for a pre-built wrapper? Check out the DriftMind Python Client.
For a deep dive into the architecture and design principles behind DriftMind, DriftMind Whitepaper.

Authentication

All requests require an API token passed via the Auth header:

HTTP Header
Auth: <YOUR_API_TOKEN>

Environment setup

Linux / macOS
export DRIFTMIND_TOKEN="your-token-here"
Windows (PowerShell)
$Env:DRIFTMIND_TOKEN="your-token-here"

Quickstart

The golden path is: Create → Feed → Predict → Delete. The snippet below runs the full cycle end-to-end.

DriftMind may return 422 FORECAST_NOT_READY if insufficient data has been fed. In that case, feed additional observations and retry.
Python — full cycle
import os, requests, time

BASE_URL = "https://api.thingbook.io/access/api/driftmind/v1"
headers  = {"Auth": os.environ["DRIFTMIND_TOKEN"], "Content-Type": "application/json"}

# 1. Create
r = requests.post(f"{BASE_URL}/forecasters", headers=headers, json={
    "forecasterName": "My First Forecaster",
    "features": ["temperature", "humidity"],
    "inputSize": 30,
    "outputSize": 1
})
r.raise_for_status()
fid = r.json()["forecasterId"]
print("Created:", fid)

# 2. Feed — send enough observations to satisfy inputSize
observations = {
    "temperature": [18.0 + i * 0.1 for i in range(35)],
    "humidity":    [0.60 + i * 0.002 for i in range(35)]
}
r = requests.post(f"{BASE_URL}/forecasters/{fid}/observations", headers=headers, json=observations)
r.raise_for_status()
print("Fed observations")

# 3. Predict
r = requests.get(f"{BASE_URL}/forecasters/{fid}/predictions", headers=headers)
r.raise_for_status()
result = r.json()
print(f"Anomaly score: {result['anomalyScore']}")
print(f"Temperature forecast: {result['features']['temperature']['predictions']}")

# 4. Delete
r = requests.delete(f"{BASE_URL}/forecasters/{fid}", headers=headers)
r.raise_for_status()
print("Deleted")
curl — full cycle
# 1. Create — save the returned forecasterId
FID=$(curl -s -X POST https://api.thingbook.io/access/api/driftmind/v1/forecasters \
  -H "Auth: $DRIFTMIND_TOKEN" -H "Content-Type: application/json" \
  -d '{"forecasterName":"My First Forecaster","features":["temperature","humidity"],"inputSize":30,"outputSize":1}' \
  | grep -o '"forecasterId":"[^"]*"' | cut -d'"' -f4)
echo "Created: $FID"

# 2. Feed
curl -s -X POST https://api.thingbook.io/access/api/driftmind/v1/forecasters/$FID/observations \
  -H "Auth: $DRIFTMIND_TOKEN" -H "Content-Type: application/json" \
  -d '{"temperature":[18.0,18.1,18.2,18.3,18.4,18.5,18.6,18.7,18.8,18.9,19.0,19.1,19.2,19.3,19.4,19.5,19.6,19.7,19.8,19.9,20.0,20.1,20.2,20.3,20.4,20.5,20.6,20.7,20.8,20.9,21.0,21.1,21.2,21.3,21.4],"humidity":[0.60,0.60,0.61,0.61,0.62,0.62,0.60,0.59,0.58,0.60,0.61,0.62,0.60,0.59,0.61,0.62,0.63,0.60,0.59,0.58,0.60,0.61,0.62,0.63,0.60,0.61,0.62,0.59,0.58,0.60,0.61,0.62,0.63,0.60,0.59]}'

# 3. Predict
curl -s -X GET https://api.thingbook.io/access/api/driftmind/v1/forecasters/$FID/predictions \
  -H "Auth: $DRIFTMIND_TOKEN"

# 4. Delete
curl -s -X DELETE https://api.thingbook.io/access/api/driftmind/v1/forecasters/$FID \
  -H "Auth: $DRIFTMIND_TOKEN"

Create a Forecaster

Creates a multivariate forecaster and returns a forecasterId.

Endpoint: POST /driftmind/v1/forecasters
FieldDescription
forecasterName Human-readable label for this forecaster.
features Array of feature names. Each name must match the keys you send in observation payloads.
inputSize Sliding window size — the number of past observations the model uses to generate each forecast. Larger values capture longer patterns but require more data before the first prediction is ready.
outputSize Number of future time steps to forecast. Use 1 for single-step prediction or a higher value for a multi-step horizon.

curl

curl
curl -X POST https://api.thingbook.io/access/api/driftmind/v1/forecasters \
  -H "Auth: $DRIFTMIND_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "forecasterName": "Temperature Forecaster",
    "features": ["temperature", "humidity"],
    "inputSize": 30,
    "outputSize": 1
  }'

Python (requests)

Python
import os
import requests

# Note the /v1/ at the end
BASE_URL = "https://api.thingbook.io/access/api/driftmind/v1"
headers = {"Auth": os.environ["DRIFTMIND_TOKEN"], "Content-Type": "application/json"}

payload = {
  "forecasterName": "Temperature Forecaster",
  "features": ["temperature", "humidity"],
  "inputSize": 30,
  "outputSize": 1
}

r = requests.post(f"{BASE_URL}/forecasters", json=payload, headers=headers)
r.raise_for_status()
forecaster_id = r.json()["forecasterId"]
print("forecasterId:", forecaster_id)

Java (JAX-RS Client)

Java
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;

public class CreateForecasterExample {
  public static void main(String[] args) {
    String baseUrl = "https://api.thingbook.io/access/api/driftmind/v1";
    String token = System.getenv("DRIFTMIND_TOKEN");

    String payload = """
    {
      "forecasterName": "Temperature Forecaster",
      "features": ["temperature", "humidity"],
      "inputSize": 30,
      "outputSize": 1
    }
    """;

    Client client = ClientBuilder.newClient();
    Response resp = client.target(baseUrl + "/forecasters")
        .request(MediaType.APPLICATION_JSON)
        .header("Auth", token)
        .post(Entity.json(payload));

    String body = resp.readEntity(String.class);
    System.out.println(resp.getStatus() + " " + body);
  }
}

Feed Observations

Feed time-aligned arrays for each feature. All feature arrays must have the same length.

Endpoint: POST /driftmind/v1/forecasters/{id}/observations

curl

curl
curl -X POST https://api.thingbook.io/access/api/driftmind/v1/forecasters/abc123-xyz789/observations \
  -H "Auth: $DRIFTMIND_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "temperature": [18.0, 19.5, 20.1, 20.7],
    "humidity":    [0.62, 0.60, 0.58, 0.59]
  }'

Python

Python
import os
import requests

BASE_URL = "https://api.thingbook.io/access/api/driftmind/v1"
headers = {"Auth": os.environ["DRIFTMIND_TOKEN"], "Content-Type": "application/json"}

forecaster_id = "abc123-xyz789"
payload = {
  "temperature": [18.0, 19.5, 20.1, 20.7],
  "humidity":    [0.62, 0.60, 0.58, 0.59]
}

# Note: POST to .../forecasters/{id}/observations
url = f"{BASE_URL}/forecasters/{forecaster_id}/observations"
r = requests.post(url, json=payload, headers=headers)
r.raise_for_status()
print(r.json())

Java (JAX-RS Client)

Java
String forecasterId = "abc123-xyz789";
String payload = """
{
  "temperature": [18.0, 19.5, 20.1, 20.7],
  "humidity":    [0.62, 0.60, 0.58, 0.59]
}
""";

Response resp = client.target(baseUrl + "/forecasters/" + forecasterId + "/observations")
    .request(MediaType.APPLICATION_JSON)
    .header("Auth", token)
    .post(Entity.json(payload));

System.out.println(resp.getStatus() + " " + resp.readEntity(String.class));

Request Predictions

Request a forecast for a forecaster. If insufficient data exists, the API returns 422 FORECAST_NOT_READY.

Endpoint: GET /driftmind/v1/forecasters/{id}/predictions
curl
curl -X GET https://api.thingbook.io/access/api/driftmind/v1/forecasters/abc123-xyz789/predictions \
  -H "Auth: $DRIFTMIND_TOKEN"

Not enough data (422)

{
  "error": "FORECAST_NOT_READY"
}

Understand the Response

The prediction response includes an overall anomaly score and a per-feature results map.

Example response
{
  "anomalyScore": 0.27,
  "numberOfClusters": 12,
  "features": {
    "temperature": {
      "timestamps": ["2025-12-31 10:00:00"],
      "predictions": [20.7],
      "upperConfidence": [21.3],
      "lowerConfidence": [20.1],
      "anomalyScore": 0.12,
      "forecastingMethod": "CLUSTER",
      "numberOfClusters": 4
    },
    "humidity": {
      "timestamps": ["2025-12-31 10:00:00"],
      "predictions": [0.59],
      "upperConfidence": [0.61],
      "lowerConfidence": [0.57],
      "anomalyScore": 0.42,
      "forecastingMethod": "CLUSTER",
      "numberOfClusters": 8
    }
  }
}
Field Description
anomalyScore Average anomaly score across all features (rounded to 2 decimals).
numberOfClusters Total clusters across all features (sum of per-feature values).
features Map keyed by feature name containing per-feature forecast outputs and metadata.
predictions Forecasted values for the horizon.
upperConfidence / lowerConfidence Confidence bounds per forecast point.
forecastingMethod CLUSTER — normal operation; the model has formed stable clusters and is generating confident forecasts. FALLBACK — the model is still warming up or encountered an unstable state; predictions are based on simpler heuristics and anomaly scores are less reliable. Feed more observations and the method will transition to CLUSTER automatically.

List all Forecasters

Retrieve a list of all forecasters belonging to your API token, including metadata like creation time and usage statistics.

Endpoint: GET /driftmind/v1/forecasters

curl

curl
curl -X GET https://api.thingbook.io/access/api/driftmind/v1/forecasters \
  -H "Auth: $DRIFTMIND_TOKEN"

Python (requests)

Python
# Assumes BASE_URL and headers are defined
r = requests.get(f"{BASE_URL}/forecasters", headers=headers)
r.raise_for_status()
print(r.json())

Java (JAX-RS Client)

Java
Client client = ClientBuilder.newClient();
Response resp = client.target(baseUrl + "/forecasters")
    .request(MediaType.APPLICATION_JSON)
    .header("Auth", token)
    .get();

System.out.println(resp.readEntity(String.class));

Example response

[
  {
    "objectId": "abc123-xyz789",
    "objectName": "Web Traffic Forecaster",
    "createdAt": "2026-05-12",
    "createdBy": "VfYZ3hLQk6FohdPTkKXB0lC30DworzI5Mz....",
    "objectType": "FORECASTER",
    "dataProcessed": 14120.42,
    "requestsProcessed": 328,
    "anomalyCount24h": 3,
    "echoCount24h": 1
  }
]
Field Description
dataProcessed Megabytes of payload metered against this forecaster over the last 30 days.
requestsProcessed Total number of API requests that touched this forecaster over the last 30 days.
anomalyCount24h Number of anomaly events this forecaster has produced in the last 24 hours of model-clock time (the timestamp the forecaster itself wrote when the event closed, NOT the wall time the row landed in the database). Drives the Anomalies pill in the dashboard's Active Forecasters table.
echoCount24h Number of Echo pattern matches this forecaster has discovered in the last 24 hours of model-clock time. Drives the Echo pill in the dashboard's Active Forecasters table.

Get Forecaster Data

Retrieve the most recent observations currently held in the forecaster's memory buffer. This is useful for validation and debugging to ensure data is being fed correctly.

Endpoint: GET /driftmind/v1/forecasters/{id}/observations

curl

curl
curl -X GET https://api.thingbook.io/access/api/driftmind/v1/forecasters/abc123-xyz789/observations \
  -H "Auth: $DRIFTMIND_TOKEN"

Python (requests)

Python
forecaster_id = "abc123-xyz789"
url = f"{BASE_URL}/forecasters/{forecaster_id}/observations"

r = requests.get(url, headers=headers)
r.raise_for_status()
print(r.json())

Java (JAX-RS Client)

Java
String forecasterId = "abc123-xyz789";
Response resp = client.target(baseUrl + "/forecasters/" + forecasterId + "/observations")
    .request(MediaType.APPLICATION_JSON)
    .header("Auth", token)
    .get();

System.out.println(resp.readEntity(String.class));

Get Forecaster Details

Retrieve the specific configuration of a forecaster, including its input/output sizes, feature names, and internal parameters.

Endpoint: GET /driftmind/v1/forecasters/{id}

curl

curl
curl -X GET https://api.thingbook.io/access/api/driftmind/v1/forecasters/abc123-xyz789 \
  -H "Auth: $DRIFTMIND_TOKEN"

Python (requests)

Python
forecaster_id = "abc123-xyz789"
url = f"{BASE_URL}/forecasters/{forecaster_id}"

r = requests.get(url, headers=headers)
r.raise_for_status()
details = r.json()
print(f"Name: {details['forecasterName']}")
print(f"Config: {details['configuration']}")

Java (JAX-RS Client)

Java
String forecasterId = "abc123-xyz789";
Response resp = client.target(baseUrl + "/forecasters/" + forecasterId)
    .request(MediaType.APPLICATION_JSON)
    .header("Auth", token)
    .get();

System.out.println(resp.readEntity(String.class));

Error Handling

All errors are returned as:

{
  "error": "ERROR_CODE",
  "details": ["optional", "context"]
}
Status Code Meaning Action
401 TOKEN_REJECTED Token missing or invalid Check the Auth header
402 CREDIT_EXHAUSTED Insufficient credit Top up / change plan / reduce calls
404 FORECASTER_NOT_FOUND Forecaster does not exist Verify forecasterId
422 FORECAST_NOT_READY Not enough data to forecast Feed more observations and retry
500 REDIS_UNAVAILABLE Temporary backend issue Retry with backoff
400 PATTERN_VALIDATION_FAILED Umbrella code for any structural problem with a POST /echo/patterns payload. The details array carries a classified sub-code per problem in the form "<SUB_CODE>: <message>" so a UI can branch on the specific cause. Inspect details and surface the sub-codes verbatim
409 PATTERN_NAME_TAKEN A pattern with that patternName already exists for the customer Pick a different name, or update / delete the existing pattern

Pattern validation sub-codes

These ride inside details on a 400 PATTERN_VALIDATION_FAILED. A payload can fail several at once — all are reported in one response so the caller can fix them in one round trip.

Sub-code Meaning
PATTERN_NAME_MISSING patternName is null, blank, or longer than 64 characters.
PATTERN_FEATURES_MISSING The features map is null, empty, or contains zero entries.
PATTERN_FEATURE_LENGTH_MISMATCH Feature arrays disagree on length. Every feature must have the same number of samples.
PATTERN_LENGTH_OUT_OF_RANGE Each feature must have between 3 and 200 samples.
PATTERN_FEATURE_NON_FINITE A feature array contains NaN or ±Infinity; all values must be finite numbers.
PATTERN_FEATURE_CONSTANT A feature has zero standard deviation (every sample is the same value). Echo's matcher is Pearson-correlation based, which is undefined for a constant reference signal — the pattern must carry at least some variation per feature.
PATTERN_FEATURE_COUNT_OUT_OF_RANGE The number of features is outside the supported 1–32 range.
PATTERN_SEVERITY_INVALID severity is not one of info, warning, error, success.

Delete a Forecaster

Deletes a forecaster and its associated data. This cannot be undone.

Endpoint: DELETE /driftmind/v1/forecasters/{id}
curl
curl -X DELETE https://api.thingbook.io/access/api/driftmind/v1/forecasters/abc123-xyz789 \
  -H "Auth: $DRIFTMIND_TOKEN"

Success response

{
  "message": "FORECASTER_DELETED"
}

Delete an Anomaly Event

Removes a single anomaly event by its server-assigned eventId. The endpoint is customer-scoped — a caller can never delete a row that belongs to another tenant, even with a hand-crafted request. Use this to clear false positives from the dashboard, or to retract events produced during a backfill / replay you'd rather hide.

Endpoint: DELETE /driftmind/v1/anomaly-events/{eventId}
curl
curl -X DELETE https://api.thingbook.io/access/api/driftmind/v1/anomaly-events/1042 \
  -H "Auth: $DRIFTMIND_TOKEN"

Success response

{
  "message": "ANOMALY_EVENT_DELETED",
  "eventId": 1042,
  "removed": 1
}

removed is 1 when the row existed and was deleted, or 0 when the id did not match any row owned by this customer. The endpoint is idempotent — a repeat call still returns 200, not 404.

Scope: this only removes the single row in anomaly_events. It does NOT cascade to Echo detections, Echo patterns, alerts, or any other table — an anomaly event and an Echo match are produced by different subsystems and are independent surfaces. To remove an Echo match, use DELETE /driftmind/v1/echo/detections/{eventId}.

Echo Patterns

Echo is the pattern-of-interest layer that runs alongside the forecaster. You register a named, multi-feature reference signal once, attach it to one or more forecasters, and from then on every observation tick is silently scored for similarity. When the running similarity crosses 60% on the rising edge, an echo_detections row is written and an alert is emitted to the dashboard's alerts panel.

Endpoint root: /driftmind/v1/echo

Create a pattern

Endpoint: POST /driftmind/v1/echo/patterns. A pattern is 1–32 features, each a length-3–200 array of finite numbers. Every feature array must be the same length, and every feature must have non-zero standard deviation (a constant / flat-line feature is rejected because Echo's correlation matcher needs variation in the reference signal).

curl
curl -X POST https://api.thingbook.io/access/api/driftmind/v1/echo/patterns \
  -H "Auth: $DRIFTMIND_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "patternName": "bearing-failure",
    "description": "Rising temperature + vibration signature that precedes bearing seizure.",
    "severity": "error",
    "runbookUrl": "https://runbooks.example.com/bearing-failure",
    "features": {
      "temperature": [22, 24, 28, 35, 44, 55, 68],
      "vibration":   [0.3, 0.5, 0.8, 1.2, 2.1, 3.0, 4.5]
    }
  }'

Python (requests)

Python
payload = {
    "patternName": "bearing-failure",
    "description": "Rising temperature + vibration signature that precedes bearing seizure.",
    "severity": "error",
    "runbookUrl": "https://runbooks.example.com/bearing-failure",
    "features": {
        "temperature": [22, 24, 28, 35, 44, 55, 68],
        "vibration":   [0.3, 0.5, 0.8, 1.2, 2.1, 3.0, 4.5],
    },
}

r = requests.post(f"{BASE_URL}/echo/patterns", json=payload, headers=headers)
r.raise_for_status()
pattern_id = r.json()["patternId"]

Success response (201 Created)

{
  "patternId": "9c3b1e4f-3c0c-4b7e-9b1f-2a3b4d5e6f70",
  "patternName": "bearing-failure",
  "severity": "error",
  "length": 7,
  "featureNames": ["temperature", "vibration"]
}

List, get, update, delete

Method Path Purpose
GET /driftmind/v1/echo/patterns List every pattern owned by your token. Optional ?createdBy=<token> filter.
GET /driftmind/v1/echo/patterns/{patternId} Get the full pattern (metadata + every feature array).
PUT /driftmind/v1/echo/patterns/{patternId} Update editable metadata (description, severity, runbookUrl). Features are immutable — delete and re-create to change them.
DELETE /driftmind/v1/echo/patterns/{patternId} Remove the pattern. Cascades to any attachments and any historical detection rows.
GET /driftmind/v1/echo/summary Roll-up the dashboard's Echo card uses: total patterns + count of detections in the last 24h.

Attach a pattern to a forecaster

Pattern-of-interest matching only runs against forecasters the pattern is attached to. Attach the pattern to as many forecasters as you like — the matcher state for each (forecaster, pattern) pair is independent.

Endpoint: POST /driftmind/v1/echo/patterns/{patternId}/attachments/{forecasterId}
curl
curl -X POST https://api.thingbook.io/access/api/driftmind/v1/echo/patterns/$PATTERN_ID/attachments/$FORECASTER_ID \
  -H "Auth: $DRIFTMIND_TOKEN"

Detach with the same path and DELETE. Detaching does not remove historical detection rows — only stops new matching going forward.

Severity surfaced on every match comes from the pattern itself (info / warning / error / success). Pick it deliberately at create time: it controls the colour of the alert pill in the dashboard's alerts panel and determines whether the match is treated as informational or as an actionable incident.

Echo Detections

Every time the rolling similarity for a (forecaster, pattern) pair crosses 60% on the rising edge, the matcher writes one row to echo_detections and emits a matching alert. Rows are timestamped with the forecaster's model clock at the moment of the crossing — not the wall time at which the row physically landed in the database — so a replay produces the same timeline as the original observation stream.

List recent detections

Endpoint: GET /driftmind/v1/echo/detections (optional ?limit=N, default 200, max 500)
curl
curl -X GET "https://api.thingbook.io/access/api/driftmind/v1/echo/detections?limit=50" \
  -H "Auth: $DRIFTMIND_TOKEN"

Example response

[
  {
    "eventId": 1042,
    "forecasterId": "abc123-xyz789",
    "forecasterName": "mill-line-2",
    "patternId": "9c3b1e4f-3c0c-4b7e-9b1f-2a3b4d5e6f70",
    "patternName": "bearing-failure",
    "probability": 82.34,
    "detectedAt": "2026-06-01T10:14:08"
  }
]

probability is the percentage 0–100 (the rising-edge crossing happens at >= 60). detectedAt is the model-clock instant — the moment on the forecaster's internal timeline when the match was confirmed.

Delete a single detection

Same shape and semantics as the anomaly-event delete: customer-scoped, idempotent, returns removed: 0 rather than 404 when the id does not match a row you own.

Endpoint: DELETE /driftmind/v1/echo/detections/{eventId}
curl
curl -X DELETE https://api.thingbook.io/access/api/driftmind/v1/echo/detections/1042 \
  -H "Auth: $DRIFTMIND_TOKEN"

Success response

{
  "message": "ECHO_DETECTION_DELETED",
  "eventId": 1042,
  "removed": 1
}
Scope: this only removes the single row in echo_detections. It does NOT remove the pattern it matched (use DELETE /driftmind/v1/echo/patterns/{patternId}) and does NOT affect any anomaly events on the same forecaster.

Ready to Integrate?

The live API is documented in Swagger — explore endpoints, test payloads, and start sending data in minutes. No lengthy onboarding, no gatekeeping.

Create a free account and your first API key is ready immediately. The DriftMind overview explains what you can build on top.