Developer API

Integrate diagnostic reasoning into your stack.

Submit field evidence and get back a ranked, evidence-linked diagnosis and the next best test, over a simple REST API. JSON in, audit-ready RCA out.

BASE URL https://dev.assetblue.ai/v1
cURL · create a diagnosis
curl https://dev.assetblue.ai/v1/diagnoses \
  -H "Authorization: Bearer ab_live_8f2c…" \
  -H "Content-Type: application/json" \
  -d '{
    "asset": { "id": "TX-4021", "type": "transformer" },
    "evidence": [
      { "kind": "symptom", "text": "Top-oil temperature climbing over the last week at unchanged load; Buchholz alarmed once overnight" },
      { "kind": "action",  "text": "Cooler-bank fans serviced last month — no change in the top-oil trend" }
    ],
    "readings": [
      { "metric": "top_oil_temp",   "value": 82.4, "unit": "C" },
      { "metric": "acetylene",  "value": 4.7,  "unit": "ppm" }
    ]
  }'

Overview

The AssetBlue API turns messy field evidence into an inspectable diagnosis. You send the asset and whatever you have — natural-language evidence (symptoms, diagnostics, maintenance actions), structured sensor readings, photos, HMI screenshots, voice notes — and AssetBlue reasons through failure physics and your engineering knowledge graph to a ranked root cause and the next best test.

The API is skill-based. Each endpoint runs one complete reliability skill and returns its result — the flagship is RCA (the diagnosis lifecycle), and alongside it are what-if (the boiler binary for boilers, an operating-envelope assessment for transformers) and knowledge Q&A (see Skill endpoints). It is REST over HTTPS, returns JSON, and uses standard verbs and status codes. Every response is evidence-linked and traceable, so a recommendation never arrives without its reasoning. There is deliberately no endpoint into the knowledge base itself — the KB stays behind the skills.

  • Base URL  https://dev.assetblue.ai/v1
  • Content type  application/json for all request and response bodies
  • Evidence kinds  each evidence[] item is symptom, diagnostic, or action — all plain language
  • Structured readings  send sensor / SCADA / instrument data as readings[] for charts, thresholds, and trend reasoning
  • Multimodal input  attach images, audio, and HMI captures by reference on any evidence or reading

Authentication

Authenticate every request with a secret API key in the Authorization header as a bearer token. Keys are scoped per environment.

HTTP
Authorization: Bearer ab_live_8f2c9d4a1b6e…

Use ab_test_… keys against the sandbox and ab_live_… keys in production. Never expose a secret key in client-side code or a mobile bundle — proxy requests through your own backend. Rotate keys from the console at any time.

Quickstart

Create your first diagnosis in one call over plain HTTP. Pass ?wait=true to block until reasoning settles and get the leading hypothesis and next best test back directly; omit it to get a diagnosis you can poll.

curl
curl -X POST 'https://dev.assetblue.ai/v1/diagnoses?wait=true' \
  -H 'Authorization: Bearer ab_live_8f2c…' \
  -H 'Content-Type: application/json' \
  -d '{
    "asset": { "id": "TX-4021", "type": "transformer" },
    "evidence": [
      { "kind": "symptom", "text": "Top-oil temperature climbing over the last week at unchanged load" },
      { "kind": "action",  "text": "Cooler-bank fans serviced last month, no change in the top-oil trend" }
    ],
    "readings": [
      { "metric": "top_oil_temp",  "value": 82.4, "unit": "C" },
      { "metric": "acetylene", "value": 4.7,  "unit": "ppm" }
    ]
  }'

The response carries the ranked leading_hypothesis and the single next_best_test. See GET /diagnoses/{id} for the full shape.

The diagnosis lifecycle

A diagnosis moves through four states. You can act on it at any point, and a human always signs off before it becomes a finalized RCA.

01

Submitted

Evidence received; reasoning starts.

02

Reasoned

Ranked hypotheses and the next best test.

03

Validated

An engineer confirms or overrides.

04

Finalized

Audit-ready RCA and asset memory.

Statuses surface as status on the diagnosis object: submitted, reasoning, reasoned, validated, finalized. Poll GET /diagnoses/{id} to follow the state, or pass ?wait=true on create/validate to block until reasoning settles.

Endpoints

Six endpoints cover the full lifecycle: create, retrieve, enrich, validate, report, and recall. Paths are relative to the base URL.

POST/v1/diagnoses

Start a diagnosis from any combination of evidence. Returns immediately with a diagnosis in the reasoning state.

FieldTypeDescription
assetobjectAsset id and type.required
evidencearrayOne or more natural-language evidence items. Each is { kind, text, at?, attachments? } where kind is one of symptom (what was observed), diagnostic (an engineer’s interpretation of a test or instrument), or action (a maintenance action that was performed). text is free-form prose — no metric/unit schema. Optional at ISO-8601 timestamp; optional attachments array of upload ids / URLs scoped to that item.required
readingsarrayStructured sensor / SCADA / instrument readings as { metric, value, unit, at? }. Send these in parallel with evidence[] — they drive charts, threshold comparisons, and trend reasoning. Engineers don’t have to interpret them in prose; AssetBlue will.
attachmentsarrayTop-level images, audio, or HMI captures that aren’t tied to a specific evidence item — by URL or upload id.
Request body · expanded
{
  "asset": { "id": "TX-4021", "type": "transformer" },
  "evidence": [
    {
      "kind": "symptom",
      "text": "Top-oil temperature climbing over the last week at unchanged load; Buchholz alarmed once overnight",
      "at": "2026-06-04T13:47Z"
    },
    {
      "kind": "diagnostic",
      "text": "Latest DGA shows acetylene with rising ethylene — a high-energy internal fault signature",
      "at": "2026-06-04T13:55Z",
      "attachments": ["upl_2Hk…"]
    },
    {
      "kind": "action",
      "text": "Cooler-bank fans serviced last month — no change in the top-oil trend",
      "at": "2026-06-03T09:00Z"
    }
  ],
  "readings": [
    { "metric": "top_oil_temp",  "value": 82.4, "unit": "C",    "at": "2026-06-04T13:55Z" },
    { "metric": "acetylene", "value": 4.7,  "unit": "ppm", "at": "2026-06-04T13:55Z" }
  ]
}
201 · Response
{
  "id": "dg_3Qa9kR2xVb",
  "status": "reasoning",
  "asset": { "id": "TX-4021", "type": "transformer" },
  "created_at": "2026-06-04T14:18:07Z"
}
GET/v1/diagnoses/{id}

Retrieve the current reasoning state: ranked hypotheses, the evidence weighed for and against each, and the single next test that best discriminates the cause.

200 · Response
{
  "id": "dg_3Qa9kR2xVb",
  "status": "reasoned",
  "leading_hypothesis": {
    "mechanism": "internal_arcing.high_energy_discharge",
    "confidence": 0.86,
    "evidence_for": [
      { "claim": "Acetylene present with rising ethylene — high-energy fault signature", "source": "dga" },
      { "claim": "Top-oil rise decoupled from load", "source": "thermal" }
    ],
    "evidence_against": [
      { "claim": "No gas accumulation in the Buchholz relay on inspection", "source": "field_note" }
    ]
  },
  "next_best_test": {
    "action": "Repeat DGA at 48 h and run a Duval triangle on the trend",
    "expected_confidence": 0.97
  }
}
POST/v1/diagnoses/{id}/evidence

Append one or more evidence items to an in-flight diagnosis — same shape as the evidence array on POST /v1/diagnoses. AssetBlue re-reasons and returns the updated diagnosis with revised confidence. To answer a clarifying question AssetBlue raised, set in_reply_to to its question_id.

FieldTypeDescription
kindstringOne of symptom, diagnostic, or action.required
textstringPlain-language description.required
atstringISO-8601 timestamp the evidence applies to. Defaults to now.
attachmentsarrayUpload ids or URLs scoped to this evidence item.
in_reply_tostringA question_id from a clarifying question AssetBlue raised, if this evidence answers it.
Request body
{
  "kind": "diagnostic",
  "text": "Repeat DGA at 48 h: acetylene up to 21 ppm; Duval places it in D2 (high-energy arcing)",
  "at": "2026-06-04T15:02Z",
  "attachments": ["upl_9Wq…"]
}
POST/v1/diagnoses/{id}/validate

Record a named engineer's decision. No work order is issued without sign-off. decision is confirm or override; on confirm a final reasoning pass emits the formal RCA and the diagnosis moves to finalized (fetch it via GET /rca), on override the note is recorded and the diagnosis stays validated. Returns the updated diagnosis object. Pass ?wait=true to block until the finalize pass completes.

Request body
{
  "decision": "confirm",
  "engineer_name": "R. Okafor",
  "engineer_email": "r.okafor@example.com",
  "note": "DGA trend confirms high-energy arcing. De-energize and schedule an internal inspection."
}
GET/v1/diagnoses/{id}/rca

Fetch the formal root-cause report: the confirmed root cause, causal chain, key evidence, eliminated causes, recommended actions with owners and due dates, confidence progression, and referenced standards. Finalizes on demand — if the diagnosis has no report yet, this runs the finalize reasoning pass inline and returns the result, so create → evidence → GET /rca works without an explicit /validate call (/validate remains the way to record engineer sign-off). Default response is JSON; pass ?format=pdf to download the same report as a formatted PDF (application/pdf).

GET/v1/assets

Your onboarded assets — the same register the web app shows, scoped to the account that created the API key. Use it to discover the asset ids (plus manufacturer/model context) to feed into /v1/diagnoses and /v1/whatif without re-entering them.

{
  "assets": [
    {
      "id": "seed-tx-bhel-220",
      "name": "GT-1 — BHEL 160 MVA 220/66 kV",
      "asset_type": "transformer",
      "manufacturer": "BHEL",
      "model_number": "220/66 kV, 50, 100 & 160 MVA POWER TRANSFORMER",
      "envelope_rows": 6
    }
  ],
  "count": 1
}
GET/v1/assets/{id}/cases

Recall an asset’s accumulated memory: prior evidence (symptoms, diagnostics, and maintenance actions), confirmed mechanisms, corrective actions, and recurrence patterns. Every new diagnosis starts from this intelligence, not a blank form.

Skill endpoints

The API is skill-based: every endpoint runs one complete reliability skill and returns its result. There is no endpoint into the knowledge base — no search, no traversal, no raw reads. The RCA lifecycle above is one skill; three more are below. All use the same Bearer-key auth, are scoped to your key, and scrub third-party source names from responses.

GET/v1/asset-types

The assets we support right now: boiler and transformer. This endpoint lists them, which skills each can run, and the state of its binary. Every asset_type you send must be one of these — anything else is a 400, never a silent default. Availability is live: what-if works for both off the knowledge base; the boiler binary is an extra engine for boilers, and the transformer binary is not built yet — it’s on the way.

200 · Response
{
  "supported": ["boiler", "transformer"],
  "asset_types": [
    { "asset_type": "boiler", "label": "Boiler", "supported": true,
      "skills": {
        "rca": { "available": true },
        "whatif": { "available": true, "engines": { "envelope_assessment": true, "boiler_binary": true } },
        "knowledge": { "available": true } },
      "binary": { "name": "boiler_binary", "available": true, "status": "available" } },
    { "asset_type": "transformer", "label": "Transformer", "supported": true,
      "skills": {
        "rca": { "available": true },
        "whatif": { "available": true, "engines": { "envelope_assessment": true, "transformer_binary": false } },
        "knowledge": { "available": true } },
      // the transformer binary is not built yet
      "binary": { "name": "transformer_binary", "available": false, "status": "on_the_way" } }
  ]
}
POST/v1/catalog/manufacturers · /v1/catalog/models

Look up the manufacturers and models you can address on a skill call. This is the catalog index — names only. It is a lookup, not a directory dump: manufacturers need a q (2+ chars), models are always scoped to one manufacturer, results are capped with a truncated flag, and there is no offset/cursor to walk. The knowledge itself — envelopes, mechanisms, diagnostics, documents — is never returned here; it stays behind the skills.

FieldTypeDescription
asset_typestringboiler | transformer.required
qstringmanufacturers: required, 2+ chars. models: optional narrowing.
manufacturerstringmodels only — required. There is no global model listing.required
Request · manufacturers
{ "asset_type": "boiler", "q": "bhel" }
200 · Response
{
  "asset_type": "boiler", "q": "bhel",
  "manufacturers": [ { "manufacturer": "Bharat Heavy Electricals Limited", "models": 12 } ],
  "returned": 1, "truncated": false
}
Request · models (scoped to one manufacturer)
{ "asset_type": "boiler", "manufacturer": "Bharat Heavy Electricals Limited" }
// → { "models": ["200/210 MW Subcritical …", …], "returned": 12, "truncated": false }
GET/v1/whatif/capabilities

What a what-if run accepts, keyed by asset_type — every family in one response, so there is no discriminator to pass (asset_type is only ever a body field on POST). Each asset type reports whatif.available (true for both — it runs off the knowledge base), its accepted inputs, and its engines: envelope_assessment (always available, no binary) plus the binary — boiler_binary (live, with its primitives/controls/mechanisms) or transformer_binary (on_the_way).

POST/v1/whatif

One what-if endpoint for every asset. The engine is chosen by the asset’s family (inferred from asset, or set asset_type explicitly) — there is no per-asset-type path. A boiler runs the boiler binary; a transformer is assessed against its operating envelope. The response carries asset_type and engine so you know which result shape you got. Boiler runs are synchronous (a few seconds); transformer assessments honour ?wait (default true returns the result; false returns a job to poll at GET /whatif/{id}).

Transformer: its what-if works today (knowledge base). Only the transformer binary is on the way — so primitive is the one input it doesn’t take, and sending it returns 501 Not Implemented saying so rather than quietly running something else.

FieldTypeDescription
asset_typestringboiler | transformer — selects the engine.required
assetobject{ manufacturer, model } — needed for a transformer (to pull its envelope); optional label for a boiler.
changestringNatural language description of the change. Any asset type; no binary needed.
primitivestringBoiler only, optional — runs the boiler binary. steady | cycling | load_ramp | chemistry_excursion | fuel_switch | tube_leak, with params / controls / horizon. See capabilities.
proposedobjectStructured conditions, e.g. { "load_pct": 115, "top_oil_C": 95, "duration": "6h" }. Any asset type.
Boiler · boiler-binary request
{
  "asset_type": "boiler",
  "asset": { "model": "200/210 MW subcritical" },
  "primitive": "chemistry_excursion",
  "params": { "species": "pH_low", "value": 8.2, "duration": "72h" },
  "horizon": "90d"
}
Natural language · any asset type
{
  "asset_type": "boiler",
  "asset": { "manufacturer": "BHEL", "model": "200/210 MW subcritical" },
  "change": "two-shift the unit daily for the next quarter"
}

// …or structured conditions, on any asset type:
{
  "asset_type": "transformer",
  "asset": { "manufacturer": "ABB", "model": "TrafoStar 400" },
  "proposed": { "load_pct": 118, "top_oil_C": 98, "duration": "6h" }
}
201 · Response (boiler)
{
  "id": "wif_73a7fdbdad",
  "skill": "whatif",
  "status": "done",
  "result": {
    "asset_type": "boiler",
    "engine": "boiler_binary",
    "prognosis": { "governing_mechanism": { "name": "Hydrogen damage" }, "failure_risk_pct": 12.0, "remaining_life_days": 540 },
    "final_state_token": "…"
  }
}

A transformer result instead carries "engine": "envelope_assessment" with an assessment (headroom, risks, go/caution/no-go) and envelopes_considered.

GET/v1/whatif/{id}

Recall a stored what-if run by id (scoped to your key). GET /v1/whatif lists your recent runs. For a transformer run started with ?wait=false, poll here until status is done.

POST/v1/answers

Knowledge Q&A. Answer a technical reliability question, grounded in the knowledge base and scrubbed of source names. Default ?wait=true returns the answer directly; ?wait=false returns a job to poll at GET /v1/answers/{id}.

FieldTypeDescription
questionstringThe technical question, in plain language.required
asset_typestringboiler or transformer — which corpus to draw from. Inferred from asset if omitted.
assetobjectOptional context, e.g. { manufacturer, model, asset_type }.
Request body
{
  "question": "What causes waterwall tube hydrogen damage and how is it detected?",
  "asset_type": "boiler"
}
201 · Response
{
  "id": "ans_1b7c9d0e2f",
  "skill": "knowledge_answer",
  "status": "done",
  "result": {
    "answer": "## Causes … (markdown, no source names)",
    "grounded": true,
    "asset_type": "boiler",
    "sources_considered": 8
  }
}

Using the API

The API is plain REST over HTTPS — no SDK required. Send a Bearer key and a JSON body from any language. Here is the same call in a few common clients.

Python · requests
import requests

r = requests.post(
    "https://dev.assetblue.ai/v1/answers?wait=true",
    headers={"Authorization": "Bearer ab_live_8f2c…"},
    json={"question": "What causes waterwall tube hydrogen damage?", "asset_type": "boiler"},
    timeout=60,
)
print(r.json()["result"]["answer"])
Node · fetch
const r = await fetch("https://dev.assetblue.ai/v1/answers?wait=true", {
  method: "POST",
  headers: {
    "Authorization": "Bearer ab_live_8f2c…",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ question: "What causes waterwall tube hydrogen damage?", asset_type: "boiler" }),
});
const { result } = await r.json();
console.log(result.answer);

Every example on this page is a plain request you can run with curl or any HTTP client — copy a request body, add your Bearer key, and go. Never expose a secret key in client-side code; proxy through your own backend.

Errors & limits

AssetBlue uses conventional HTTP status codes and returns a structured error body.

422 · Error
{
  "error": {
    "type": "invalid_request",
    "code": "missing_field",
    "message": "asset.id is required",
    "param": "asset.id"
  }
}
400 · 422Malformed or invalid request body.
401Missing or invalid API key.
404No such diagnosis or asset.
429Rate limit exceeded. Default 600 req/min; reasoning jobs are async and uncapped.
5xxTransient server error. Retry idempotently with backoff.

Send an Idempotency-Key header on writes to safely retry without creating duplicate diagnoses.