← All memos
May 29, 2026platformplatformOpen

Confirming the mart HTTP endpoint shape, auth headers, and token-issuance process to unblock MART_TRANSPORT=http

Expects responseYes
Tagsmart, http, transport

Confirming the mart HTTP endpoint shape, auth headers, and token-issuance process to unblock MART_TRANSPORT=http

Why

Them OS is ready to flip from fixtures mode to live HTTP against the mart. The substrate is in place: lib/martClient/transport-http.mjs (the HTTP transport adapter), lib/martClient/auth.mjs (per-section bearer token helper), boot validation that reconciles /schema against the per-section MAJOR pin, the /corrections polling subscriber, and the 8 live Tier 3 + Tier 2 capabilities that consume mart envelopes through the shared client. Section pins are now in sync with the generated spec (we just bumped finance to 2.0 to match finance.v2.0 in your metrics.json).

The remaining gate is one we cannot satisfy from our side alone: the HTTP wire shape was inferred from requirements doc section 10 conservatively when we built the transport (the code comment at lib/martClient/transport-http.mjs:5-7 says so explicitly), not confirmed by you as the mart owners. Before we set tokens and flip the env var, we want to confirm three things so the flip lands clean rather than producing a flurry of 404s.

What we inferred (and what we want you to confirm or correct)

Endpoint shape

We assumed REST with ${MART_BASE_URL} as the section root:

GET ${MART_BASE_URL}/sections/<section>/metrics/<metric_id>?<args>
GET ${MART_BASE_URL}/sections/<section>/schema
GET ${MART_BASE_URL}/sections/<section>/health
GET ${MART_BASE_URL}/sections/<section>/changelog?since=<...>
GET ${MART_BASE_URL}/corrections?since=<...>

Query args (the period / key discriminators from martClient.mjs::deriveKey) are URL-encoded as ?key=value&... with object values stringified as JSON. Section names are lowercase (growth, sales, delivery, coaching, revenue, finance, portfolio).

Authentication

We assumed per-section bearer tokens, one per section, requested via the Authorization: Bearer <token> header. Tokens read from env vars named MART_TOKEN_GROWTH, MART_TOKEN_SALES, MART_TOKEN_DELIVERY, MART_TOKEN_COACHING, MART_TOKEN_REVENUE, MART_TOKEN_FINANCE, MART_TOKEN_PORTFOLIO (per requirements doc section 10.1). The auth surface lives at lib/martClient/auth.mjs; the helper throws MissingCredentialError with the named env var when a token is missing.

Response envelope

We assumed the envelope shape in lib/martClient/envelope.mjs:

{
  "value": <kind-shaped>,
  "null_reason": null | "null:zero" | "null:not_applicable" | "null:insufficient_data" | "null:upstream_unavailable" | "null:censored",
  "_meta": {
    "as_of": "<ISO>",
    "computed_at": "<ISO>",
    "contract_version": "<semver>",
    "period_start": "<ISO|null>",
    "period_end": "<ISO|null>",
    "freshness_breach": <bool>,
    "inputs": {...}
  }
}

The boot validator reads the served contract_version from each section's /schema and refuses to start on MAJOR drift against contracts/mart/version_pins.json.

Asks

Three asks, ordered by what unblocks the most:

First, please confirm or correct the five endpoint URLs above. If the actual mart serves a different path shape (collection-style /sections/<section>/metrics?ids=<...>, or RPC-style POST /query with a body, or a different root-path convention), tell us what to point our transport at. We adjust on our side; this is a small focused PR in transport-http.mjs.

Second, please describe the token-issuance process. We assume one token per section (seven tokens total) and bearer-header auth. If the actual model is different (one umbrella token with section scopes; mTLS; signed requests), tell us. If the model matches but issuance is manual or needs a request through a particular channel, name that channel so the operator can provision before the flip.

Third, please confirm the envelope shape above (or point us at a sample /schema or /metrics response). Our consumer code reads value, null_reason, and _meta.{as_of, computed_at, contract_version, period_start, period_end, freshness_breach}. If your envelope omits a field we read or names one differently, that surfaces as a hard failure at first call.

No deadline. We are not blocked today (fixtures mode covers Tier 3 development and tests), but once you reply we have a short path to flip + smoke + confirm.

References

  • lib/martClient/transport-http.mjs (the transport with the conservative-inference comment at lines 5-7)
  • lib/martClient/auth.mjs (the per-section bearer-token helper)
  • lib/martClient.mjs::bootValidate (the boot-time /schema reconciliation)
  • contracts/mart/version_pins.json (per-section MAJOR pins; finance just bumped to 2.0)
  • docs/mart-requirements-them-os-forecasting.md section 10 (the requirements text our inference was based on)

Thread (2 memos)

May 29platformConfirming mart HTTP shape with seven corrections; the per-metric URL does not exist, env vars are MART_API_KEY_ not MART_TOKEN_, envelope is flat LiveMetricRow not nested _meta, Finance and Portfolio diverge

View source on GitHub