Veritris Axiom — Exchange API Reference
Base URL (local, via nginx): http://localhost:8080/api/v1
Base URL (local, direct to a single replica, for interactive docs): http://localhost:8000 → Swagger UI at /docs, ReDoc at /redoc, raw spec at /openapi.json
Every endpoint below was exercised against a live PostgreSQL/PostGIS instance while writing this document; request/response bodies shown are real captured output, not hand-written examples.
Table of contents
- Authentication & authorization
- Where Exchange fits
- Conventions
- Health
- Resellers & customers (tenancy)
- Assets / properties
- Geo queries
- Utilities
- Plans
- Rate history
- Usage
- Enrollments
- Exchange transactions
- Query registry
- SDKs
- Error format
- Contact
See also: API_TIERS.md (documentation/access
classification), AUTH_ARCHITECTURE.md (full
scope model and metering), PRICING.md, LICENSING.md.
Where Exchange fits
Axiom Exchange is one module of the Veritris Axiom energy platform — the connected layer for energy data, transactions, partners, developers, and API-first infrastructure. It is not the whole platform.
Veritris (master brand)
├── VTT ......................... communications / network services
├── Veritris Technologies ....... IP ownership and licensing
├── StrataLink + SecureWave ..... defense / critical-infrastructure resilience
└── Veritris Energy ............. energy operating brand
├── OEPC .................... Oklahoma Electric Power Company (utility-facing vehicle)
└── Veritris Axiom .......... modular energy platform
├── Grid, Market/ETRM, Forecast
├── Retail/CRM, eMobility
├── DER, Resilience
└── Exchange ← this API
Sibling Axiom modules (Grid, Market/ETRM, Forecast, DER, Retail/CRM, Resilience, eMobility, and the AI-assisted Orchestrate workflow layer) are out of scope for this API. Exchange is deliberately the integration and transaction surface the others — and third parties — call into.
Positioning constraints
These are hard constraints on how this API and its docs describe the business, not stylistic preferences:
- Veritris, Veritris Energy, and OEPC are not described as a regulated utility, retail electric provider, RTO, ISO, telecom carrier, defense contractor, or authorized market participant. Those statuses require independent verification.
- Axiom Exchange interoperates with market and utility systems (including SPP where applicable). It is not a market operator.
- Retail energy functionality is only exercised where legally authorized in the relevant jurisdiction. The presence of plan and enrollment endpoints in this API is not a representation that retail choice is available anywhere in particular.
- The trademark symbol is not used on "Veritris" or "Axiom" in this repository, pending confirmation of registration scope and clearance.
Authentication & authorization
Every route except /healthz, /readyz, /, /auth/token, and
/contact requires one of:
| Method | Header | Notes |
|---|---|---|
| API key | X-API-Key: <key> |
sk_test_... = sandbox, sk_live_... = production. Configured via AXIOM_API_KEYS. Simplest for service-to-service and sandbox use. |
| Bearer JWT | Authorization: Bearer <token> |
Obtained from POST /auth/token (or a real OAuth2/OIDC provider once federated — see AUTH_ARCHITECTURE.md). Expires after AXIOM_JWT_EXPIRE_MINUTES (default 60). |
| Gateway identity | X-Axiom-Gateway-Identity + X-Axiom-Gateway-Secret |
Set by a trusted API Gateway that has already authenticated the caller — see DEPLOYMENT.md. Bypasses scope checks entirely; only for internal/trusted service traffic. |
Authentication alone is not authorization. Every operation additionally
requires a specific OAuth-style scope — visible per-operation in the
security block of /openapi.json, and enforced identically whether the
caller used a JWT, an API key, or (not yet, but by design) an OIDC token.
Full scope list, role bundles, and the metering that fires on every
authorized call: AUTH_ARCHITECTURE.md. Which
operations are public vs. gated behind partner/trading access:
API_TIERS.md.
No credentials:
GET /api/v1/assets
401 Unauthorized
{"detail":"Missing credentials. Provide Authorization: Bearer <jwt> or X-API-Key."}
Get a token, resolving scopes from a role's default bundle:
POST /api/v1/auth/token
Content-Type: application/json
{"api_key": "sk_test_local_dev_0001", "role": "developer"}
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in_minutes": 60,
"role": "developer",
"environment": "sandbox",
"scopes": ["catalog:read", "tariffs:read", "market:read", "serviceability:read", "forecast:read", "quote:read"]
}
Explicit scopes override the role's bundle when you need a narrower grant than the role's ceiling:
POST /api/v1/auth/token
{"api_key": "sk_test_local_dev_0001", "role": "partner", "scopes": ["customer:read", "meter:read"]}
Reseller-scoped token — pass reseller_id and every tenant-aware
endpoint automatically filters to that reseller. Omit for a
platform-level token that sees across all resellers:
POST /api/v1/auth/token
{"api_key": "sk_test_local_dev_0001", "role": "customer", "reseller_id": "e45961fe-f23c-4366-8330-543c2bd6ab42"}
Privileged scopes require production, verified in this session:
a trading_partner token issued with "environment": "sandbox"
requesting POST /exchange/transactions (which needs trading:execute)
is rejected —
{"detail": "trading:execute is a privileged scope and requires a production credential (this request authenticated as environment=sandbox)."}
— regardless of the role's default bundle including that scope. Set
"environment": "production" to actually exercise a privileged scope.
Privileged scopes: trading:execute, der:control, settlement:write,
grid:control, admin.
Conventions
- All geometry is WGS84 / SRID 4326 (longitude, latitude — in that order, matching GeoJSON and ArcGIS defaults).
- Every list/spatial endpoint accepts
?format=geojson(default) or?format=esriwhere noted, switching between an RFC 7946 GeoJSONFeatureCollectionand an Esri JSONFeatureSet. attributeson any resource is a free-form JSON object — put whatever domain-specific fields that record needs there; nothing needs a migration.- Timestamps are ISO 8601 UTC.
- IDs are UUIDv4, generated server-side on create.
- Tenancy applies everywhere. A reseller-scoped caller only ever sees
its own customers, properties, and transactions — including through the
spatial
/geo/*endpoints. Reading another reseller's resource returns403; listing simply excludes it.
Health
GET /healthz
Liveness probe. No auth required. Always returns {"status": "ok"} if the process is running — used by nginx/orchestrators to know the container is alive.
GET /readyz
Readiness probe. Runs SELECT 1 against the database. Returns 503-equivalent failure (connection error) if the DB is unreachable, so a load balancer can pull the instance out of rotation.
Resellers & customers (tenancy)
The account model: a reseller is the tenant and the account holder; a customer belongs to many resellers; a property is an asset owned by a customer and serviced by one of those resellers.
The customer↔reseller attachment is a first-class record because the cost key lives on the relationship, not on either side.
POST /resellers
{"code": "LONESTAR-RETAIL", "name": "Lone Star Retail Energy", "operating_states": ["TX", "NM"]}
201 Created — operating_states is enforced: attaching a customer in a
state the reseller doesn't operate in is rejected with 422.
GET /resellers
Optional state filter. A reseller-scoped caller sees only itself.
GET /resellers/{id} · PUT /resellers/{id}
GET /resellers/{id}/customers
Every customer attached to this reseller. Optional state.
GET /resellers/{id}/properties
Every property this reseller services, across all states. Optional state.
POST /customers
{"name": "Brazos Property Holdings", "email": "ap@brazos.example", "external_ref": "CUST-4471"}
A customer is deliberately not owned by a single reseller.
GET /customers
Optional reseller_id, state. A reseller-scoped caller sees only
customers attached to it.
GET /customers/{id} — the one-to-many view
Returns the customer plus every reseller attached to it:
{
"customer": {"id": "...", "name": "Brazos Property Holdings", "external_ref": "CUST-4471", "status": "active"},
"resellers": [
{"reseller_id": "e45961fe-...", "cost_key": "e45961fe-f23c-4366-8330-543c2bd6ab42", "state": "TX", "is_primary": true},
{"reseller_id": "8c609aef-...", "cost_key": "SOONER-COST-882", "state": "OK", "is_primary": false},
{"reseller_id": "e45961fe-...", "cost_key": "e45961fe-f23c-4366-8330-543c2bd6ab42", "state": "NM", "is_primary": false}
]
}
A reseller-scoped caller gets the same customer but only its own attachment rows — it never learns which other resellers share the customer.
POST /customers/{id}/resellers — attach (creates the cost key)
{"reseller_id": "8c609aef-...", "state": "OK", "cost_key": "SOONER-COST-882", "is_primary": false}
cost_keyis optional. Omitted, it defaults to the reseller's UUID, so it is never empty and always resolves back to a reseller row. Supply your own billing/settlement code to override.- Uniqueness is on
(customer_id, reseller_id, state)— the same pair may legitimately repeat across states. A duplicate returns409. - Attaching in a state outside the reseller's
operating_states→422.
GET /customers/{id}/resellers
The attachment list. Reseller-scoped callers see only their own row.
POST /customers/{id}/resellers/{link_id}/primary
Promotes one attachment to primary and demotes the incumbent in the same
transaction. At most one primary per customer is enforced twice: the
service layer demotes before promoting, and a partial unique index
(uq_customer_one_primary_reseller ON customer_resellers (customer_id) WHERE is_primary)
makes two primaries impossible even under concurrent writes.
DELETE /customers/{id}/resellers/{link_id}
204. Detaching does not delete the customer.
GET /cost-keys — cost attribution rollup
The reverse lookup billing needs: given a cost key, which reseller does it bill to and what is it currently carrying.
[
{
"cost_key": "SOONER-COST-882",
"reseller_id": "8c609aef-...", "reseller_code": "SOONER-POWER", "reseller_name": "Sooner Power Partners",
"customer_count": 1, "property_count": 1, "states": ["OK"]
},
{
"cost_key": "e45961fe-f23c-4366-8330-543c2bd6ab42",
"reseller_id": "e45961fe-...", "reseller_code": "LONESTAR-RETAIL", "reseller_name": "Lone Star Retail Energy",
"customer_count": 1, "property_count": 2, "states": ["NM", "TX"]
}
]
Reseller-scoped callers see only their own keys.
Assets / properties
The generic geospatial registry. One asset type, discriminated by asset_type, backs every kind of spatial thing in the platform — polygons, lines, or points.
POST /assets — create any type of asset
POST /api/v1/assets
X-API-Key: axiom-local-dev-key
Content-Type: application/json
{
"organization_id": "veritris-energy",
"asset_type": "well_pad",
"name": "Permian Well Pad 12",
"geometry": {
"type": "Polygon",
"coordinates": [[[-102.1,31.9],[-102.09,31.9],[-102.09,31.91],[-102.1,31.91],[-102.1,31.9]]]
},
"attributes": {"api_number": "42-135-12345", "operator": "Veritris Energy"}
}
201 Created:
{
"id": "eb491174-4e09-410b-bf6b-545f6321ad36",
"organization_id": "veritris-energy",
"asset_type": "well_pad",
"name": "Permian Well Pad 12",
"status": "active",
"external_ref": null,
"description": null,
"attributes": {"operator": "Veritris Energy", "api_number": "42-135-12345"},
"created_at": "2026-08-21T21:18:55.521612Z",
"updated_at": "2026-08-21T21:18:55.521612Z"
}
Tenancy fields. reseller_id, customer_id, and state make an
asset a property attributable to a reseller relationship. The
cost_key is derived, not trusted — the API resolves it from the
customer's attachment to that reseller for that state, so a property can
never be stamped with a key that doesn't correspond to a real
relationship. If no attachment covers the property's state, the request
fails with 422 naming the states that are covered. A reseller-scoped
token supplies reseller_id implicitly.
GET /assets additionally filters on reseller_id, customer_id,
state, and cost_key.
geometry accepts any standard GeoJSON geometry type — Point, LineString, Polygon, MultiPolygon, etc. Use a Point for a meter/premise, a LineString for a pipeline or feeder segment, a Polygon/MultiPolygon for a lease, farm, or territory.
Note: this endpoint (and PUT) does not return the geometry itself — fetch it separately via GET /assets/{id}/geometry, which is the endpoint built for map rendering.
GET /assets — list / filter
Query params: organization_id, asset_type, status, limit (default 100, max 1000), offset.
GET /api/v1/assets?asset_type=solar_farm
[{
"id": "5d8f9269-4878-4dea-a252-f56725708536",
"organization_id": "veritris-energy",
"asset_type": "solar_farm",
"name": "Lone Star Solar Array 1",
"status": "active",
"external_ref": "LSSA-001",
"description": "Demo utility-scale solar generation asset",
"attributes": {"operator": "Veritris Energy", "capacity_mw": 85.5, "commissioned": "2024-06-01"},
"created_at": "2026-08-21T21:16:12.498872Z",
"updated_at": "2026-08-21T21:16:12.498872Z"
}]
GET /assets/{id} — single asset, non-spatial fields
Returns the same shape as above for one asset. 404 if not found.
GET /assets/{id}/geometry — map-ready geometry (the ArcGIS-facing endpoint)
Query param: format=geojson (default) | format=esri.
GET /api/v1/assets/5d8f9269-4878-4dea-a252-f56725708536/geometry
{
"type": "Feature",
"id": "5d8f9269-4878-4dea-a252-f56725708536",
"geometry": {
"type": "Polygon",
"coordinates": [[[-97.8,30.2],[-97.78,30.2],[-97.78,30.22],[-97.8,30.22],[-97.8,30.2]]]
},
"properties": {
"id": "5d8f9269-4878-4dea-a252-f56725708536",
"organization_id": "veritris-energy",
"asset_type": "solar_farm",
"name": "Lone Star Solar Array 1",
"status": "active",
"external_ref": "LSSA-001",
"description": "Demo utility-scale solar generation asset",
"operator": "Veritris Energy",
"capacity_mw": 85.5,
"commissioned": "2024-06-01"
}
}
GET /api/v1/assets/5d8f9269-4878-4dea-a252-f56725708536/geometry?format=esri
{
"geometry": {
"rings": [[[-97.8,30.2],[-97.78,30.2],[-97.78,30.22],[-97.8,30.22],[-97.8,30.2]]],
"spatialReference": {"wkid": 4326}
},
"attributes": {
"id": "5d8f9269-4878-4dea-a252-f56725708536",
"organization_id": "veritris-energy",
"asset_type": "solar_farm",
"name": "Lone Star Solar Array 1",
"status": "active",
"external_ref": "LSSA-001",
"operator": "Veritris Energy",
"capacity_mw": 85.5,
"commissioned": "2024-06-01"
}
}
See ARCGIS_INTEGRATION.md for how to point an ArcGIS JS API map layer directly at this endpoint.
PUT /assets/{id} — partial update
Any subset of name, status, description, geometry, attributes. Only fields present in the body are changed.
PUT /api/v1/assets/eb491174-4e09-410b-bf6b-545f6321ad36
{"status": "under_review"}
{"id":"eb491174-...","status":"under_review", "...": "..."}
DELETE /assets/{id}
204 No Content on success, 404 if the asset doesn't exist.
Geo queries
The spatial-join layer — "what's here" and "what's near this."
POST /geo/query — arbitrary polygon intersection
Body: a GeoJSON geometry (typically a polygon drawn or exported from ArcGIS), plus optional asset_types, organization_id, limit (default 500, max 5000). Query param format=geojson|esri.
POST /api/v1/geo/query
{
"geometry": {"type": "Polygon", "coordinates": [[[-97.9,30.15],[-97.6,30.15],[-97.6,30.45],[-97.9,30.45],[-97.9,30.15]]]}
}
Returns a FeatureCollection of every asset — regardless of its own geometry type — that intersects the supplied polygon. In testing, a query polygon around a demo solar farm and a demo premise correctly returned both the Polygon asset and the Point asset in one response.
POST /geo/query/radius — proximity search
Body: lon, lat, radius_meters (max 200,000), optional asset_types, limit.
POST /api/v1/geo/query/radius
{"lon": -97.7431, "lat": 30.2672, "radius_meters": 5000}
Uses a true geodesic distance calculation (geography cast in PostGIS), not a flat-earth approximation, so it stays accurate at any latitude.
GET /geo/bbox — map viewport load
Query params: min_lon, min_lat, max_lon, max_lat, optional asset_type, organization_id, limit (max 5000), format.
GET /api/v1/geo/bbox?min_lon=-98&min_lat=30&max_lon=-97.4&max_lat=30.6
The standard "load whatever's currently on screen" call — pass the map's current extent (as most JS map SDKs, including the ArcGIS JS API's view.extent, expose it).
POST /geo/customers/search — spatial customer search
Given any GeoJSON area, returns the customers holding properties inside it, each with the properties found, the servicing reseller, and the cost key they bill to. This is the reseller-facing "who do I have in this territory" query.
{
"property_count": 4,
"customers": [
{
"customer_id": "81b1a684-...", "customer_name": "Brazos Property Holdings",
"external_ref": "CUST-4471", "states": ["TX"], "property_count": 2,
"properties": [
{"id": "...", "name": "Austin Distribution Center", "asset_type": "commercial_property",
"state": "TX", "cost_key": "e45961fe-...", "reseller_code": "LONESTAR-RETAIL"}
]
}
]
}
POST /geo/utilities/status — utility status across an area
Every utility territory intersecting the area, with its current service status and how many in-scope properties sit inside it.
{
"utilities": [
{"code": "CTX-DEMO", "name": "Central Texas Demo Utility", "state": "TX",
"service_status": "degraded", "affected_property_count": 3,
"affected_properties": [{"id": "...", "name": "Austin Distribution Center"}]},
{"code": "NORTHTX-DEMO", "name": "North Texas Demo Delivery", "state": "TX",
"service_status": "operational", "affected_property_count": 1, "affected_properties": []}
]
}
Both endpoints are tenant-scoped: a reseller drawing a polygon over three states gets back only its own customers and only its own affected properties.
Utilities
GET /utilities
Optional state filter (2-letter code).
GET /utilities/lookup — point-in-polygon utility lookup
GET /api/v1/utilities/lookup?lon=-97.74&lat=30.27
{
"utility": {
"id": "30d0c50b-d60d-43db-9261-b0020094a8e1",
"code": "DEMO-UTIL-TX",
"name": "Central Texas Demo Utility",
"state": "TX",
"attributes": {"deregulated": true, "regulatory_body": "PUCT"}
},
"matched_by": "point_in_territory"
}
If no territory contains the point: {"utility": null, "matched_by": "not_found"}.
This is the geospatial equivalent of PowerHQ's "find the utility for this zip/address" lookup — instead of a zip-code table, it's a true polygon boundary, so it works at any resolution/shape a real utility territory actually has.
GET /utilities/{id}
Single utility by ID. 404 if not found.
Plans
GET /plans
Optional utility_id, is_business filters.
[{
"id": "690c27f5-c734-49c3-8c18-02f5ce96ba08",
"utility_id": "30d0c50b-d60d-43db-9261-b0020094a8e1",
"name": "AXIOM Fixed 12",
"rate_type": "fixed",
"rate_cents_per_kwh": 11.4,
"term_months": 12,
"is_business": false,
"signup_url": "https://example.com/signup/axiom-fixed-12",
"effective_date": "2026-08-21",
"expiration_date": null
}]
GET /plans/{id}
Rate history
GET /rates/{utility_id}/history
Optional area_code, since (date), limit.
[{"area_code": "78701", "rate_cents_per_kwh": 12.1, "recorded_at": "2026-07-22"}]
Usage
GET /usage/me — the calling credential's own usage
Powers the developer portal dashboard. Requires only a valid credential,
not a specific scope — a usage-introspection call isn't itself a
billable product call. Aggregates real usage_events by billing code:
{
"subject": "apikey:sk_test_loca",
"role": "developer",
"environment": "sandbox",
"since_hours": 720,
"total_calls": 7,
"estimated_total_cost_usd": 0.015,
"by_billing_code": [
{"billing_code": "BASIC_CALL", "calls": 5, "units": 5.0, "reference_price_usd": 0.001, "estimated_cost_usd": 0.005},
{"billing_code": "ADDRESS_SERVICEABILITY", "calls": 2, "units": 2.0, "reference_price_usd": 0.005, "estimated_cost_usd": 0.01}
],
"recent_calls": [
{"endpoint": "/api/v1/plans", "method": "GET", "scope": "tariffs:read", "billing_code": "BASIC_CALL", "status_code": 200, "occurred_at": "2026-08-22T20:39:51.350297+00:00"}
]
}
Optional since_hours (default 720 = 30 days, max 8760 = 1 year).
GET /usage/{premise_asset_id}
premise_asset_id is the id of any asset with asset_type='premise'. Returns period/kWh history, most recent first.
[
{"period_start": "2026-07-21", "period_end": "2026-08-21", "kwh": 815.0},
{"period_start": "2026-06-21", "period_end": "2026-07-21", "kwh": 830.0}
]
GET /usage/{premise_asset_id}/forecast
Query param periods_ahead (default 3, max 24). Returns a naive trailing-average projection — replace usage_forecast() in app/api/v1/routers/usage.py with a real forecasting model as volume grows; the response contract (period_start, period_end, projected_kwh) is deliberately model-agnostic.
[
{"period_start": "2026-08-21", "period_end": "2026-09-21", "projected_kwh": 837.5}
]
Enrollments
POST /enrollments
{
"plan_id": "690c27f5-c734-49c3-8c18-02f5ce96ba08",
"premise_asset_id": "b833da92-f92b-4fdb-a1f3-0f82fab6d536",
"customer_name": "Jordan Rivera",
"customer_email": "jordan@example.com"
}
201 Created, status: "pending", enrolled_at set to submission time.
GET /enrollments/{id}
Exchange transactions
Nominations, trades, and settlements — optionally tied back to a physical asset via asset_id, so a trade can be reconciled against the generation asset or delivery point it actually corresponds to.
POST /exchange/transactions
{
"counterparty_id": "4facce94-386a-48f7-bd98-1a873869f711",
"asset_id": "5d8f9269-4878-4dea-a252-f56725708536",
"transaction_type": "trade",
"volume_mwh": 250.5,
"price_per_mwh": 34.20,
"delivery_start": "2026-09-01T00:00:00Z",
"delivery_end": "2026-09-02T00:00:00Z"
}
transaction_type must be one of nomination, trade, settlement. 201 Created, status: "pending".
GET /exchange/transactions
Filters: counterparty_id, transaction_type, status, asset_id. Filtering by asset_id returns every transaction reconciled against a given physical asset — e.g. every trade settled against a specific solar farm.
GET /exchange/transactions/{id}
SDKs
Thin, unopinionated clients — no caching, no business logic, no
duplicated authorization. Every call still hits the authenticated,
metered API; possessing or redistributing an SDK confers no API access
on its own. MIT-licensed, source in sdks/ in this repository.
| Language | Package | Import |
|---|---|---|
| Python | sdks/python (pyproject.toml) |
from axiom_exchange import AxiomExchangeClient |
| JavaScript / TypeScript | sdks/javascript (package.json) |
import { AxiomExchangeClient } from "@veritris/axiom-exchange" |
| PHP | sdks/php (composer.json) |
use Veritris\AxiomExchange\AxiomExchangeClient; |
from axiom_exchange import AxiomExchangeClient
client = AxiomExchangeClient(api_key="sk_test_...")
plans = client.plans.list()
lookup = client.utilities.lookup(lon=-97.74, lat=30.27)
import { AxiomExchangeClient } from "@veritris/axiom-exchange";
const client = new AxiomExchangeClient({ apiKey: "sk_test_..." });
const plans = await client.plans.list();
$client = new Veritris\AxiomExchange\AxiomExchangeClient(apiKey: 'sk_test_...');
$plans = $client->plans()->list();
Each client accepts either api_key/apiKey (sandbox/production static
key) or access_token/accessToken (a Bearer JWT — from POST /auth/token today, from OAuth2 Authorization Code + PKCE once OIDC is
live, with no client-code change required either way).
Error format
Validation errors (422) follow FastAPI/Pydantic's standard shape:
{
"detail": [
{
"type": "uuid_parsing",
"loc": ["body", "counterparty_id"],
"msg": "Input should be a valid UUID, invalid length: expected length 32 for simple format, found 0",
"input": ""
}
]
}
Auth failures (401/403) and not-found (404) return {"detail": "<message>"}.
Query registry
Saved queries live in a separate database from the parcel/asset
inventory — its own engine, pool, and connection string
(AXIOM_QUERY_DATABASE_URL). Different retention, different write profile,
independent blast radius. No foreign keys cross the boundary; reseller_id
and asset ids are stored as opaque values.
POST /queries — instantiate and get a query_id
{
"endpoint": "/geo/customers/search",
"label": "TX territory sweep",
"request_params": {"geometry": {"type": "Polygon", "coordinates": [[[-98.2,29.9],[-96.4,29.9],[-96.4,33.3],[-98.2,33.3],[-98.2,29.9]]]}},
"is_stateful": false,
"ttl_days": 90
}
201 Created returns a short, quotable handle:
{"query_id": "qry_cd0bd1b079fd", "label": "TX territory sweep", "endpoint": "/geo/customers/search",
"is_stateful": false, "replay_count": 0, "created_by": "apikey:axiom-lo"}
Stateful vs stateless, chosen per request:
is_stateful: true— supplyresponse_snapshotand the result is frozen. Later reads of thatquery_idreturn exactly what the caller saw at instantiation. Use for audit trails and shareable frozen results.is_stateful: false— only the request definition is stored. Replays re-execute against live data. Use for saved searches and dashboards.
Supplying a response_snapshot without is_stateful returns 422.
endpoint must be one of an allowlist (/geo/query, /geo/query/radius,
/geo/bbox, /geo/customers/search, /geo/utilities/status, /assets,
/cost-keys). Anything else returns 422 — a saved query must never
become a generic server-side request forwarder.
GET /queries · GET /queries/{query_id}
List (filters: endpoint, is_stateful, include_expired) or fetch one.
Fetching a stateful query returns its frozen response_snapshot. An expired
query returns 410 with its expiry timestamp. Reseller-scoped callers only
see their own queries; another reseller's returns 403.
POST /queries/{query_id}/replay
Re-executes the stored request against current inventory and increments
replay_count. For a stateful query this deliberately does not overwrite
the frozen snapshot — the response reports both so you can diff then-vs-now.
DELETE /queries/{query_id}
204.
Contact
Veritris Group, Inc. — veritris.com
| energy@veritris.com | |
| Phone | +1 580-713-4927 |
| Address | 2828 NW 57th Street #207, Oklahoma City, OK 73112 |
| Contact page | veritris.com/lawton-managed-msp-contact |
| Client / support portal | veritris.com/clients |
| Veritris Energy | veritris.energy |
| Axiom platform | veritrisaxiom.energy |
Also available programmatically at GET /api/v1/contact (unauthenticated).
GET /contact
Unauthenticated. Returns the same details as JSON for programmatic use.
{
"organization": "Veritris Group, Inc.",
"product": "Veritris Axiom — Exchange",
"platform": "Veritris Axiom",
"module_of": "Veritris Energy",
"version": "0.1.0",
"website": "https://www.veritris.com",
"energy_site": "https://www.veritris.energy",
"platform_site": "https://www.veritrisaxiom.energy",
"contact_page": "https://www.veritris.com/lawton-managed-msp-contact/",
"support_url": "https://www.veritris.com/clients",
"phone": "+1-580-713-4927",
"support_hours": "24/7",
"address": {
"street": "2828 NW 57th Street #207",
"city": "Oklahoma City", "state": "OK", "postal_code": "73112", "country": "US"
},
"email": "energy@veritris.com"
}