Versioning and compatibility

The API version is in the path: every resource URL starts with https://api.brellium.com/v2/, and no request header selects a version. A change that a correct v2 client could not handle ships under a new path prefix instead of /v2. Brellium has not published a formal compatibility policy for v2 yet, so the rules below describe current practice.

Changes Brellium may make without notice

These changes ship under /v2, so your client must handle each one:

  • New endpoints and new query parameters on existing endpoints.
  • New fields on any response object, including inside error.details.
  • New values for string fields that have no fixed list: audit status, document status, coding evaluation status, em_evaluation.uncodable_reason, the flags[] inside em_evaluation and icd_evaluation, and error.type. Today audit status is one of todo, processing, in_progress, completing, complete or error, and error.type is one of invalid_request_error, authentication_error, not_found_error, conflict_error, rate_limit_error or api_error.
  • New error codes and new message wording. An existing error.type or error.code keeps its meaning, while error.message and error.details can change (Rate limits and errors).
  • The order of fields in a JSON object.

Breaking changes

Each of these changes ships under a new path prefix:

  • Removing or renaming a field, an endpoint or a query parameter.
  • Changing a field's type or nullability.
  • Changing the meaning of an existing error.type or error.code, or the HTTP status an existing code returns.
  • Changing the meaning of an existing status value, or the pagination rules (limit, starting_after, ending_before, has_more).

Code defensively

These rules keep your client working when a change ships without notice:

  • Ignore fields you do not recognize, because a strict deserializer breaks when the API adds a field.
  • Expect new values in every string field. Any audit status other than complete means score and flags are not final.
  • Branch on error.type and error.code, never on error.message.
  • Treat ids as opaque strings that you compare but never parse.
  • Check object before you use a successful response. Every resource has one, for example audit, user or question_set, and every list envelope has list. An error response has no object field, only error.

The requests below fetch the sample audit a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d with an access token in $BRELLIUM_ACCESS_TOKEN (Authenticate with OAuth2 client credentials). A sandbox key returns that audit. With a live key, take an audit_id from GET /v2/audits?status=complete&limit=1 instead; the response then contains protected health information (PHI).

The curl request returns the audit; read object, status, score and flags first:

curl "https://api.brellium.com/v2/audits/a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d" \
  --header "Authorization: Bearer $BRELLIUM_ACCESS_TOKEN"
{
  "audit_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
  // check this before reading anything else
  "object": "audit",
  // branch on this; any other value means score and flags are not final
  "status": "complete",
  "score": 0.6666666666666666,
  "flags": ["Failed", "Missing Signature"]
  /* ... the other eight keys; see Understand an audit */
}

The samples below make the same request and parse the response so that an unknown field or a new status value cannot break them:

// Keep credentials on the server; never in a browser or mobile app.
const token = process.env.BRELLIUM_ACCESS_TOKEN;
if (!token) throw new Error("BRELLIUM_ACCESS_TOKEN is not set");
const auditId = "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d";

async function main() {
  const res = await fetch(`https://api.brellium.com/v2/audits/${auditId}`, {
    headers: { Authorization: `Bearer ${token}` },
  });
  if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);
  // Only the fields this code uses are typed; everything else is ignored.
  const audit = (await res.json()) as {
    object: string;
    status: string;
    score: number | null;
    flags: string[];
  };

  if (audit.object !== "audit") throw new Error("unexpected object");

  switch (audit.status) {
    case "complete":
      console.log("score", audit.score, "flags", audit.flags);
      break;
    case "error":
      console.log("audit failed; check back later");
      break;
    default:
      // todo, processing, in_progress, completing, or a value added later
      console.log("not final yet:", audit.status);
  }
}

main();
import BrelliumClient from "@brellium/api-client";

const client = new BrelliumClient({ oAuth2: process.env.BRELLIUM_ACCESS_TOKEN });

const audit = await client.audits.retrieve("a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d");
if (audit.object !== "audit") throw new Error("unexpected object");

switch (audit.status) {
  case "complete":
    console.log("score", audit.score, "flags", audit.flags);
    break;
  case "error":
    console.log("audit failed; check back later");
    break;
  default:
    // todo, processing, in_progress, completing, or a value added later
    console.log("not final yet:", audit.status);
}
import os

from brellium_api_client import BrelliumClient

client = BrelliumClient(o_auth2=os.environ["BRELLIUM_ACCESS_TOKEN"])

audit = client.audits.retrieve("a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d")
if audit.object != "audit":
    raise ValueError("unexpected object")

if audit.status == "complete":
    print("score", audit.score, "flags", audit.flags)
elif audit.status == "error":
    print("audit failed; check back later")
else:
    # todo, processing, in_progress, completing, or a value added later
    print("not final yet:", audit.status)
# Keep credentials on the server; never in a browser or mobile app.
import os

import requests

token = os.environ["BRELLIUM_ACCESS_TOKEN"]
audit_id = "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"

res = requests.get(
    f"https://api.brellium.com/v2/audits/{audit_id}",
    headers={"Authorization": f"Bearer {token}"},
    timeout=30,
)
res.raise_for_status()
audit = res.json()  # unknown fields are kept and ignored

if audit.get("object") != "audit":
    raise ValueError("unexpected object")

status = audit.get("status")
if status == "complete":
    print("score", audit["score"], "flags", audit["flags"])
elif status == "error":
    print("audit failed; check back later")
else:
    # todo, processing, in_progress, completing, or a value added later
    print("not final yet:", status)

Every sample checks the HTTP status before it reads object: the TypeScript fetch and Python requests samples throw on a 401 UNAUTHORIZED, 404 NOT_FOUND or 429 RATE_LIMITED, and the SDK samples raise an error on the same statuses (Rate limits and errors).

Detect changes with the OpenAPI document

https://api.brellium.com/v2/openapi.json is the OpenAPI 3.1 document of the running API, and the API reference is built from it. Fetch it again before you regenerate a client, and diff it against your copy when a response carries a field or value your code did not expect.

v1 deprecation policy

Brellium has not yet published a deprecation policy for v1 and has committed to no dates, notice periods or support windows. Contact Brellium support before you plan a migration deadline.

v1 keeps running while you migrate. A key created in the dashboard works on v2 only (Authenticate with OAuth2 client credentials). A v1 key that shows an API v1 badge in the dashboard mints tokens for both APIs with the same client id and secret.

Next