diff --git a/CHANGELOG.md b/CHANGELOG.md index fc85af6..03644bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.1.0] - 2026-10-03 + +Four methods called routes the API has never served, so every call returned HTTP 404 (#125). They now reject immediately with `OilPriceAPIError` code `ENDPOINT_NOT_AVAILABLE`, name the supported alternative, and make no request. They are deprecated and will be removed in the next major. + +### Deprecated + +- `client.drilling.trends()`: no trends endpoint exists. Use `summary()`, or `completions()` / `wellsDrilled()` / `ducWells()` for basin-level series. +- `client.drilling.basin(name)`: no per-basin endpoint exists. Read the `by_basin` maps from `completions()`, `wellsDrilled()`, `ducWells()` or `fracSpreads()`. +- `client.futures.spreads(contract1, contract2)`: no arbitrary two-contract spread endpoint exists. Use `client.futures.family(slug).spreads()`, e.g. `client.futures.brent().spreads()`. +- `client.futures.continuous(contract, months)`: no per-contract continuous endpoint exists. Use `continuousFrontMonth()`. + +### Added + +- `client.futures.continuousFrontMonth("brent" | "wti")` for `GET /v1/futures/continuous/{family}`, returning `ContinuousFrontMonth`. +- `tests/api-path-contract.test.ts`, which fails CI when any SDK path is missing from a snapshot of the API's routes. Refresh the snapshot with `scripts/refresh-api-paths.mjs`. + +### Security + +- Dev-only lockfile updates: fast-uri 3.1.8 (GHSA-hrr3-gc8f-f4qj) and markdown-it 14.3.2 (GHSA-253c-mchw-3w2r). + ## [2.0.0] - 2026-09-14 A major release: several resources were typed and called in ways production never supported. The changes below are breaking at compile time or runtime, and in nearly every case the old call could not work correctly against the live API. diff --git a/package-lock.json b/package-lock.json index 654498b..8d19264 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "oilpriceapi", - "version": "2.0.0", + "version": "2.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "oilpriceapi", - "version": "2.0.0", + "version": "2.1.0", "license": "MIT", "dependencies": { "@types/node": "^26.1.1", @@ -1617,9 +1617,9 @@ } }, "node_modules/brace-expansion": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", - "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1992,9 +1992,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz", - "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", + "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", "dev": true, "funding": [ { @@ -2618,9 +2618,9 @@ } }, "node_modules/markdown-it": { - "version": "14.3.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.3.0.tgz", - "integrity": "sha512-RCEsPjR+sr0x+AuYp601tKTkgFG4YEPLCzHST3cQ/fhlJkqAkz1L2/Qbp1j9qw5SBwQHFBoW8+hoN5xssOF0Tw==", + "version": "14.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.3.2.tgz", + "integrity": "sha512-sHHjZ5fJKlgrG4qns2YwVcdNep35h5fERrfkD2YNsb9UFk0UIHarbiTaHKVMlPuWAoiilyK8Fv/jAm11slsY7Q==", "dev": true, "funding": [ { diff --git a/package.json b/package.json index 4a70e32..454d407 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "oilpriceapi", - "version": "2.0.0", + "version": "2.1.0", "description": "Official Node.js SDK for source-timestamped OilPriceAPI energy data", "type": "module", "main": "./dist/cjs/index.js", diff --git a/scripts/refresh-api-paths.mjs b/scripts/refresh-api-paths.mjs new file mode 100644 index 0000000..64cdd1b --- /dev/null +++ b/scripts/refresh-api-paths.mjs @@ -0,0 +1,79 @@ +#!/usr/bin/env node +// Regenerate tests/fixtures/api-paths.json, the route snapshot that +// tests/api-path-contract.test.ts checks every SDK path against (#125). +// +// Run from an oilpriceapi-api checkout of origin/main: +// bundle exec rails routes > /tmp/routes.txt +// node scripts/refresh-api-paths.mjs \ +// --routes /tmp/routes.txt \ +// --swagger /swagger/v1/swagger.json \ +// --policy /config/openapi_route_policy.yml \ +// --commit "$(git -C rev-parse --short HEAD)" + +import { readFileSync, writeFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; + +const args = Object.fromEntries( + process.argv.slice(2).reduce((pairs, arg, i, all) => { + if (arg.startsWith("--")) pairs.push([arg.slice(2), all[i + 1]]); + return pairs; + }, []), +); +for (const required of ["routes", "swagger", "policy"]) { + if (!args[required]) { + console.error(`Missing --${required}. See the header of this script.`); + process.exit(2); + } +} + +// Every path parameter, however spelled (:code, {code}), becomes {param}. +const normalize = (path) => + path + .replace(/\(\.:format\)$/, "") + .replace(/:[A-Za-z_]+/g, "{param}") + .replace(/\{[^}]+\}/g, "{param}") + .replace(/\/$/, ""); + +const swaggerPaths = new Set( + Object.keys(JSON.parse(readFileSync(args.swagger, "utf8")).paths).map(normalize), +); + +// The policy file is a flat list of `- prefix:` / `audience:` pairs. +const exclusions = []; +let current; +for (const line of readFileSync(args.policy, "utf8").split("\n")) { + const prefix = line.match(/^\s*-\s*prefix:\s*(\S+)/); + if (prefix) exclusions.push((current = { prefix: prefix[1] })); + const audience = line.match(/^\s*audience:\s*(\S+)/); + if (audience && current) current.audience = audience[1]; +} +exclusions.sort((a, b) => b.prefix.length - a.prefix.length); + +const routes = new Map(); +for (const line of readFileSync(args.routes, "utf8").split("\n")) { + const match = line.match(/\b(GET|POST|PUT|PATCH|DELETE)\s+(\/v1\/\S+)/); + if (!match) continue; + const path = normalize(match[2]); + const key = `${match[1]} ${path}`; + if (routes.has(key)) continue; + const audience = swaggerPaths.has(path) + ? "openapi" + : (exclusions.find((e) => path === e.prefix || path.startsWith(`${e.prefix}/`) || path.startsWith(e.prefix)) + ?.audience ?? "unlisted"); + routes.set(key, { method: match[1], path, audience }); +} + +const snapshot = { + generatedFrom: { + api: "OilpriceAPI/oilpriceapi-api", + commit: args.commit ?? "unknown", + generatedAt: new Date().toISOString().slice(0, 10), + }, + routes: [...routes.values()].sort((a, b) => + `${a.path} ${a.method}`.localeCompare(`${b.path} ${b.method}`), + ), +}; + +const out = fileURLToPath(new URL("../tests/fixtures/api-paths.json", import.meta.url)); +writeFileSync(out, `${JSON.stringify(snapshot, null, 2)}\n`); +console.log(`Wrote ${snapshot.routes.length} routes to ${out}`); diff --git a/src/index.ts b/src/index.ts index e6230e5..d38b3dd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -59,6 +59,8 @@ export type { FuturesCurveData, ContinuousContractPrice, ContinuousFuturesData, + ContinuousFrontMonth, + ContinuousFuturesFamily, FuturesSpreadHistoryPoint, FuturesSpreadHistory, FuturesContractFamilySlug, diff --git a/src/resources/drilling.ts b/src/resources/drilling.ts index f2b2f23..0b24c28 100644 --- a/src/resources/drilling.ts +++ b/src/resources/drilling.ts @@ -6,7 +6,7 @@ */ import type { OilPriceAPI } from "../client.js"; -import { ValidationError } from "../errors.js"; +import { OilPriceAPIError } from "../errors.js"; /** * Drilling intelligence data point @@ -205,15 +205,8 @@ export interface BasinDrillingData { * const latest = await client.drilling.latest(); * console.log(`Active rigs: ${latest.total_rigs}`); * - * // Get drilling trends - * const trends = await client.drilling.trends(); - * trends.forEach(trend => { - * console.log(`${trend.date}: ${trend.metric} = ${trend.value}`); - * }); - * - * // Get basin-specific data - * const permian = await client.drilling.basin('Permian'); - * console.log(`Permian rigs: ${permian.active_rigs}`); + * // Basin-level completions (by_basin keyed by basin code) + * const completions = await client.drilling.completions(); * ``` */ export class DrillingIntelligenceResource { @@ -298,29 +291,22 @@ export class DrillingIntelligenceResource { } /** - * Get drilling activity trends - * - * Returns time series data showing trends in drilling metrics. - * - * @returns Array of drilling trend data points + * Drilling activity trends. * - * @throws {OilPriceAPIError} If API request fails - * @throws {AuthenticationError} If API key is invalid + * @deprecated The API has no `/v1/drilling-intelligence/trends` route; every + * call returned HTTP 404 (#125). Use {@link summary} for the current + * aggregate, or {@link completions} / {@link wellsDrilled} / + * {@link ducWells} for basin-level series. Removed in the next major. * - * @example - * ```typescript - * const trends = await client.drilling.trends(); - * trends.forEach(point => { - * console.log(`${point.date}: ${point.metric} = ${point.value} (${point.trend})`); - * }); - * ``` + * @throws {OilPriceAPIError} Always, with code `ENDPOINT_NOT_AVAILABLE`. */ async trends(): Promise { - const response = await this.client["request"]< - DrillingTrend[] | { trends: DrillingTrend[] } - >("/v1/drilling-intelligence/trends", {}); - - return Array.isArray(response) ? response : response.trends; + throw new OilPriceAPIError( + "client.drilling.trends() is not supported: the API has no drilling trends endpoint. " + + "Use client.drilling.summary(), or completions() / wellsDrilled() / ducWells() for basin-level data.", + undefined, + "ENDPOINT_NOT_AVAILABLE", + ); } /** @@ -454,34 +440,22 @@ export class DrillingIntelligenceResource { } /** - * Get basin-specific drilling intelligence - * - * Returns comprehensive drilling data for a specific basin. - * - * @param name - Basin name (e.g., "Permian", "Eagle Ford", "Bakken") - * @returns Basin drilling intelligence data + * Basin-specific drilling intelligence. * - * @throws {NotFoundError} If basin not found - * @throws {OilPriceAPIError} If API request fails - * @throws {AuthenticationError} If API key is invalid + * @deprecated The API has no `/v1/drilling-intelligence/basin/{name}` route; + * every call returned HTTP 404 (#125). Basin-level values are returned in the + * `by_basin` maps of {@link completions}, {@link wellsDrilled}, + * {@link ducWells} and {@link fracSpreads}. Removed in the next major. * - * @example - * ```typescript - * const permian = await client.drilling.basin('Permian'); - * console.log(`Permian Basin as of ${permian.as_of_date}:`); - * console.log(` Active rigs: ${permian.active_rigs}`); - * console.log(` Frac spreads: ${permian.frac_spreads}`); - * console.log(` DUC wells: ${permian.duc_wells}`); - * ``` + * @throws {OilPriceAPIError} Always, with code `ENDPOINT_NOT_AVAILABLE`. */ - async basin(name: string): Promise { - if (!name || typeof name !== "string") { - throw new ValidationError("Basin name must be a non-empty string"); - } - - return this.client["request"]( - `/v1/drilling-intelligence/basin/${encodeURIComponent(name)}`, - {}, + async basin(_name: string): Promise { + throw new OilPriceAPIError( + "client.drilling.basin() is not supported: the API has no per-basin endpoint. " + + "Read the by_basin maps from completions(), wellsDrilled(), ducWells() or fracSpreads().", + undefined, + "ENDPOINT_NOT_AVAILABLE", ); } + } diff --git a/src/resources/futures.ts b/src/resources/futures.ts index e5ac960..35840e8 100644 --- a/src/resources/futures.ts +++ b/src/resources/futures.ts @@ -6,7 +6,7 @@ */ import type { OilPriceAPI } from "../client.js"; -import { ValidationError } from "../errors.js"; +import { OilPriceAPIError, ValidationError } from "../errors.js"; /** * A single futures contract month within a {@link FuturesPrice} response. @@ -248,6 +248,44 @@ export interface ContinuousFuturesData { prices: ContinuousContractPrice[]; } +/** + * Auto-rolled continuous front-month futures price, from + * `GET /v1/futures/continuous/{family}`. + */ +export interface ContinuousFrontMonth { + /** Commodity code, e.g. `"BRENT_FUTURES_CONTINUOUS"` */ + commodity: string; + source: string; + description: string; + price: number; + currency: string; + /** ISO 8601 timestamp of the settlement */ + updated_at: string; + ohlc: { + open: number | null; + high: number | null; + low: number | null; + close: number | null; + volume: number | null; + }; + changes: { + change_24h: number | null; + change_percent_24h: number | null; + }; + metadata: { + data_source: string; + ticker: string; + is_continuous: boolean; + roll_method: string; + history_available_from: string; + total_historical_records: number; + unit: string; + }; +} + +/** Families served by `GET /v1/futures/continuous/{family}`. */ +export type ContinuousFuturesFamily = "brent" | "wti"; + /** * Spread-history data point for a contract family. */ @@ -633,38 +671,25 @@ export class FuturesResource { } /** - * Get spread between two futures contracts - * - * Calculates the price difference between two contracts (contract1 - contract2). - * - * @param contract1 - First contract symbol - * @param contract2 - Second contract symbol - * @returns Spread data + * Spread between two arbitrary futures contracts. * - * @throws {NotFoundError} If either contract not found - * @throws {OilPriceAPIError} If API request fails + * @deprecated The API has no `/v1/futures/spreads` route; every call returned + * HTTP 404 (#125). Calendar spreads for a contract family are served by + * `client.futures.family(slug).spreads()` (e.g. `client.futures.brent().spreads()`, + * `GET /v1/futures/{slug}/spreads`). Removed in the next major. * - * @example - * ```typescript - * // Calculate spread between front month and second month - * const spread = await client.futures.spreads('CL.1', 'CL.2'); - * console.log(`CL.1 - CL.2 spread: $${spread.spread}`); - * ``` + * @throws {OilPriceAPIError} Always, with code `ENDPOINT_NOT_AVAILABLE`. */ - async spreads(contract1: string, contract2: string): Promise { - if (!contract1 || typeof contract1 !== "string") { - throw new ValidationError("First contract symbol must be a non-empty string"); - } - if (!contract2 || typeof contract2 !== "string") { - throw new ValidationError("Second contract symbol must be a non-empty string"); - } - - return this.client["request"]("/v1/futures/spreads", { - contract1, - contract2, - }); + async spreads(_contract1: string, _contract2: string): Promise { + throw new OilPriceAPIError( + "client.futures.spreads(contract1, contract2) is not supported: the API has no arbitrary two-contract spread endpoint. " + + "Use client.futures.family(slug).spreads(), e.g. client.futures.brent().spreads().", + undefined, + "ENDPOINT_NOT_AVAILABLE", + ); } + /** * Get futures curve for a contract * @@ -694,38 +719,49 @@ export class FuturesResource { } /** - * Get continuous futures contract data + * Continuous futures series by contract symbol. * - * Returns a continuous time series by rolling contracts before expiration. + * @deprecated The API has no `/v1/futures/{contract}/continuous` route; every + * call returned HTTP 404. Use {@link continuousFrontMonth} (`"brent"` or + * `"wti"`). Removed in the next major. * - * @param contract - Base contract symbol (e.g., "CL", "BZ") - * @param months - Number of months for continuous contract (default: 1 for front month) - * @returns Continuous contract data + * @throws {OilPriceAPIError} Always, with code `ENDPOINT_NOT_AVAILABLE`. + */ + async continuous(_contract: string, _months?: number): Promise { + throw new OilPriceAPIError( + "client.futures.continuous(contract, months) is not supported: the API has no per-contract continuous endpoint. " + + "Use client.futures.continuousFrontMonth('brent') or continuousFrontMonth('wti').", + undefined, + "ENDPOINT_NOT_AVAILABLE", + ); + } + + /** + * Auto-rolled continuous front-month price for a futures family. * - * @throws {NotFoundError} If contract not found + * @param family - `"brent"` or `"wti"` + * @returns The latest continuous front-month settlement with OHLC + * + * @throws {ValidationError} If the family is not supported * @throws {OilPriceAPIError} If API request fails * * @example * ```typescript - * // Get continuous front month contract - * const continuous = await client.futures.continuous('CL', 1); - * console.log(`${continuous.prices.length} data points`); + * const brent = await client.futures.continuousFrontMonth('brent'); + * console.log(`${brent.metadata.ticker}: $${brent.price}`); * ``` */ - async continuous(contract: string, months?: number): Promise { - if (!contract || typeof contract !== "string") { - throw new ValidationError("Contract symbol must be a non-empty string"); + async continuousFrontMonth(family: ContinuousFuturesFamily): Promise { + if (family !== "brent" && family !== "wti") { + throw new ValidationError('Continuous futures family must be "brent" or "wti"'); } - - const params: Record = {}; - if (months !== undefined) params.months = months.toString(); - - return this.client["request"]( - `/v1/futures/${encodeURIComponent(contract)}/continuous`, - params, + return this.client["request"]( + `/v1/futures/continuous/${encodeURIComponent(family)}`, + {}, ); } + /** * Get a typed helper for a specific contract family (issue #1). * diff --git a/src/version.ts b/src/version.ts index db3fa5a..980cd3e 100644 --- a/src/version.ts +++ b/src/version.ts @@ -7,7 +7,7 @@ * - X-Client-Version header * - Package.json (should match) */ -export const SDK_VERSION = "2.0.0"; +export const SDK_VERSION = "2.1.0"; /** * SDK identifier used in User-Agent and X-Api-Client headers diff --git a/tests/api-path-contract.test.ts b/tests/api-path-contract.test.ts new file mode 100644 index 0000000..325bea5 --- /dev/null +++ b/tests/api-path-contract.test.ts @@ -0,0 +1,124 @@ +/** + * Every `/v1/...` path the SDK sends must exist in the API (#125). + * + * Three methods shipped against routes that never existed and returned HTTP + * 404 on every call, while the mocked resource tests stayed green. This test + * reads the SDK source, extracts each path literal, and checks it against a + * snapshot of `rails routes` from oilpriceapi-api. Regenerate the snapshot + * with `scripts/refresh-api-paths.mjs` when the API adds routes. + */ +import { readFileSync, readdirSync } from "node:fs"; +import { join } from "node:path"; +import { describe, expect, it } from "vitest"; +import snapshot from "./fixtures/api-paths.json"; + +type Route = { method: string; path: string; audience: string }; + +// Browser-session and operations routes are not an API-key surface. +const NOT_CUSTOMER_FACING = new Set(["dashboard", "website", "internal", "operations"]); + +const srcDir = join(__dirname, "..", "src"); + +function sourceFiles(dir: string): string[] { + return readdirSync(dir, { withFileTypes: true }).flatMap((entry) => + entry.isDirectory() + ? sourceFiles(join(dir, entry.name)) + : entry.name.endsWith(".ts") + ? [join(dir, entry.name)] + : [], + ); +} + +function stripComments(source: string): string { + return source + .replace(/\/\*[\s\S]*?\*\//g, "") + .split("\n") + .filter((line) => !line.trim().startsWith("//")) + .join("\n"); +} + +export function normalizeSdkPath(literal: string): string { + return literal + .split("?")[0] + .replace(/\$\{[^}]+\}/g, "{param}") + .replace(/\/$/, ""); +} + +function extractPaths(source: string): string[] { + const found = new Set(); + for (const match of stripComments(source).matchAll(/["'`](\/v1\/[^"'`\s]*)["'`]/g)) { + found.add(normalizeSdkPath(match[1])); + } + return [...found]; +} + +const routes = (snapshot.routes as Route[]).filter( + (route) => !NOT_CUSTOMER_FACING.has(route.audience), +); + +// Data Connector (BYOS) routes are classed as dashboard in the API's policy, +// but answer an API key with 403 ORG_REQUIRED rather than 401, so the SDK's +// dataSources resource is a supported surface for organization members. +const ALLOWED_NON_PUBLIC_PREFIXES = ["/v1/data-sources"]; + +// A route parameter accepts any SDK segment. An SDK parameter (e.g. a futures +// family slug) also accepts a literal route segment, because the API serves +// several families as literal routes rather than one parameterised route. +export function matchesRoute(sdkPath: string, routePath: string): boolean { + const sdk = sdkPath.split("/"); + const route = routePath.split("/"); + if (sdk.length !== route.length) return false; + return sdk.every( + (segment, i) => route[i] === "{param}" || segment === "{param}" || segment === route[i], + ); +} + +export function unroutedPaths(paths: string[], candidates: Route[] = routes): string[] { + return paths.filter( + (path) => + !ALLOWED_NON_PUBLIC_PREFIXES.some((prefix) => path === prefix || path.startsWith(`${prefix}/`)) && + !candidates.some((route) => matchesRoute(path, route.path)), + ); +} + +describe("API path contract (#125)", () => { + const sdkPaths = sourceFiles(srcDir).flatMap((file) => + extractPaths(readFileSync(file, "utf8")).map((path) => ({ + path, + file: file.slice(srcDir.length + 1), + })), + ); + + it("finds the SDK's paths", () => { + expect(sdkPaths.length).toBeGreaterThan(100); + }); + + it("sends only paths that exist in the API", () => { + const missing = sdkPaths.filter(({ path }) => unroutedPaths([path]).length > 0); + expect(missing).toEqual([]); + }); + + it("flags the paths that 404'd in production before #125", () => { + expect( + unroutedPaths([ + "/v1/drilling-intelligence/trends", + "/v1/drilling-intelligence/basin/{param}", + "/v1/futures/spreads", + "/v1/futures/{param}/continuous", + ]), + ).toEqual([ + "/v1/drilling-intelligence/trends", + "/v1/drilling-intelligence/basin/{param}", + "/v1/futures/spreads", + "/v1/futures/{param}/continuous", + ]); + }); + + it("does not let a browser-session route satisfy an SDK path", () => { + const dashboardOnly = (snapshot.routes as Route[]).find( + (route) => route.audience === "dashboard" && !route.path.startsWith("/v1/data-sources"), + ); + expect(dashboardOnly).toBeDefined(); + expect(unroutedPaths([dashboardOnly!.path])).toEqual([dashboardOnly!.path]); + }); +}); diff --git a/tests/fixtures/api-paths.json b/tests/fixtures/api-paths.json new file mode 100644 index 0000000..53bad25 --- /dev/null +++ b/tests/fixtures/api-paths.json @@ -0,0 +1,2609 @@ +{ + "generatedFrom": { + "api": "OilpriceAPI/oilpriceapi-api", + "commit": "b64093719", + "generatedAt": "2026-10-03" + }, + "routes": [ + { + "method": "GET", + "path": "/v1/account", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/alerts", + "audience": "product_preview" + }, + { + "method": "POST", + "path": "/v1/alerts", + "audience": "product_preview" + }, + { + "method": "DELETE", + "path": "/v1/alerts/{param}", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/alerts/{param}", + "audience": "product_preview" + }, + { + "method": "PATCH", + "path": "/v1/alerts/{param}", + "audience": "product_preview" + }, + { + "method": "PUT", + "path": "/v1/alerts/{param}", + "audience": "product_preview" + }, + { + "method": "POST", + "path": "/v1/alerts/{param}/test", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/alerts/analytics_history", + "audience": "product_preview" + }, + { + "method": "POST", + "path": "/v1/alerts/test", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/alerts/triggers", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/analytics/correlation", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/analytics/forecast", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/analytics/performance", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/analytics/spread", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/analytics/statistics", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/analytics/trend", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/api-keys", + "audience": "openapi" + }, + { + "method": "POST", + "path": "/v1/api-keys", + "audience": "openapi" + }, + { + "method": "DELETE", + "path": "/v1/api-keys/{param}", + "audience": "openapi" + }, + { + "method": "POST", + "path": "/v1/api-keys/{param}/roll", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/aviation-fuel/pilot", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/benchmarks/close", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/bunker-fuels", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/bunker-fuels/all", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/bunker-fuels/compare", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/bunker-fuels/export", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/bunker-fuels/historical/{param}", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/bunker-fuels/latest", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/bunker-fuels/ports/{param}", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/bunker-fuels/spreads/ports", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/commodities", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/commodities/{param}", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/commodities/{param}/coverage", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/commodities/categories", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/context/market", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/context/market/economic", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/context/market/indicators", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/context/market/news", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/costco-gas/warehouses", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/costco-gas/warehouses/{param}", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/current_price", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/current_price/{param}", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/dashboard", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/data-quality/reports", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/data-quality/reports/{param}", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/data-quality/summary", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/data-source-requests", + "audience": "dashboard" + }, + { + "method": "POST", + "path": "/v1/data-source-requests", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/data-sources", + "audience": "dashboard" + }, + { + "method": "POST", + "path": "/v1/data-sources", + "audience": "dashboard" + }, + { + "method": "DELETE", + "path": "/v1/data-sources/{param}", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/data-sources/{param}", + "audience": "dashboard" + }, + { + "method": "PATCH", + "path": "/v1/data-sources/{param}", + "audience": "dashboard" + }, + { + "method": "PUT", + "path": "/v1/data-sources/{param}", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/data-sources/{param}/health", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/data-sources/{param}/logs", + "audience": "dashboard" + }, + { + "method": "POST", + "path": "/v1/data-sources/{param}/rotate_credentials", + "audience": "dashboard" + }, + { + "method": "POST", + "path": "/v1/data-sources/{param}/test", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/demo/commodities", + "audience": "discovery_preview" + }, + { + "method": "GET", + "path": "/v1/demo/prices", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/demo/prices/{param}", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/demo/prices/latest", + "audience": "discovery_preview" + }, + { + "method": "GET", + "path": "/v1/diesel-prices", + "audience": "openapi" + }, + { + "method": "POST", + "path": "/v1/diesel-prices/stations", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/disasters/earthquakes", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/disasters/energy-impact", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/disasters/fires", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/drilling-intelligence", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/drilling-intelligence/completions", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/drilling-intelligence/duc-wells", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/drilling-intelligence/frac-spreads", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/drilling-intelligence/latest", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/drilling-intelligence/summary", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/drilling-intelligence/well-permits", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/drilling-intelligence/wells-drilled", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/drilling/latest", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/ei/dpr/production", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/drilling_productivities", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/drilling_productivities/{param}", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/drilling_productivities/by_basin", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/drilling_productivities/duc_wells", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/drilling_productivities/historical", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/drilling_productivities/latest", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/drilling_productivities/summary", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/drilling_productivities/trends", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/forecasts", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/forecasts/{param}", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/forecasts/compare", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/forecasts/historical", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/forecasts/latest", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/forecasts/prices", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/forecasts/production", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/forecasts/summary", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/frac-focus", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/frac-focus/{param}", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/frac-focus/{param}/chemicals", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/frac-focus/by-chemical", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/frac-focus/by-operator", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/frac-focus/by-state", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/frac-focus/for-well/{param}", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/frac-focus/latest", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/frac-focus/search", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/frac-focus/summary", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/oil_inventories", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/oil_inventories/{param}", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/oil_inventories/by_product", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/oil_inventories/cushing", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/oil_inventories/historical", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/oil_inventories/latest", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/oil_inventories/summary", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/opec_production/{param}", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/opec_production/by_country", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/opec_production/historical", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/opec_production/latest", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/opec_production/top_producers", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/opec_production/total", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/opec_productions", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/opec_productions/{param}", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/opec_productions/by_country", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/opec_productions/historical", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/opec_productions/latest", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/opec_productions/top_producers", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/opec_productions/total", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/rig_counts", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/rig_counts/{param}", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/rig_counts/by_basin", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/rig_counts/by_state", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/rig_counts/historical", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/rig_counts/latest", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/well-permits", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/well-permits/{param}", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/well-permits/by-formation", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/well-permits/by-operator", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/well-permits/by-state", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/well-permits/latest", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/well-permits/preview", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/well-permits/search", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/well-permits/states", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/well-permits/states/{param}", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/well-permits/summary", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/ei/wells/{param}/timeline", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/electricity", + "audience": "openapi" + }, + { + "method": "POST", + "path": "/v1/email_captures", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/events/disruptions", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/events/disruptions/{param}", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/excel/run", + "audience": "first_party_client" + }, + { + "method": "GET", + "path": "/v1/feedback_experiments/{param}", + "audience": "website" + }, + { + "method": "POST", + "path": "/v1/feedback_responses", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/forecasts/monthly", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/forecasts/monthly/{param}", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/forecasts/monthly/accuracy", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/forecasts/monthly/archive", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/fuel-surcharge", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/fuel-surcharge/{param}/history", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/fuel-surcharge/{param}/latest", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/fuel-surcharge/parcel", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/fuel-surcharge/parcel/{param}/history", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/fuel-surcharge/parcel/{param}/latest", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/fundamentals/natural-gas/consumption", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/fundamentals/natural-gas/lng-exports", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/fundamentals/natural-gas/production", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/fundamentals/natural-gas/storage", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/brent", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/futures/brent/curve", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/brent/historical", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/futures/brent/intraday", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/brent/ohlc", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/brent/spread-history", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/brent/spreads", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/futures/continuous/brent", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/continuous/brent/historical", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/continuous/wti", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/continuous/wti/historical", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/eu-carbon", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/eu-carbon/curve", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/eu-carbon/historical", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/eu-carbon/intraday", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/eu-carbon/ohlc", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/eu-carbon/spread-history", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/eu-carbon/spreads", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/eua-carbon", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/eua-carbon/curve", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/eua-carbon/historical", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/eua-carbon/intraday", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/eua-carbon/ohlc", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/eua-carbon/spread-history", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/eua-carbon/spreads", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/gasoil", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/gasoil/curve", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/gasoil/historical", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/gasoil/intraday", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/gasoil/ohlc", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/gasoil/spread-history", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/gasoil/spreads", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/gold", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/gold/curve", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/gold/historical", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/gold/intraday", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/gold/ohlc", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/gold/spread-history", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/gold/spreads", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/ice-brent", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/futures/ice-brent/curve", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/ice-brent/historical", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/ice-brent/intraday", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/ice-brent/ohlc", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/ice-brent/spread-history", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/ice-brent/spreads", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/ice-gasoil", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/ice-gasoil/curve", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/ice-gasoil/historical", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/ice-gasoil/intraday", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/ice-gasoil/ohlc", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/ice-gasoil/spread-history", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/ice-gasoil/spreads", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/ice-wti", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/futures/ice-wti/curve", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/ice-wti/historical", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/ice-wti/intraday", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/ice-wti/ohlc", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/ice-wti/spread-history", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/ice-wti/spreads", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/jkm", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/jkm-lng", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/jkm-lng/curve", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/jkm-lng/historical", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/jkm-lng/intraday", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/jkm-lng/ohlc", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/jkm-lng/spread-history", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/jkm-lng/spreads", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/jkm/curve", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/jkm/historical", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/jkm/intraday", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/jkm/ohlc", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/jkm/spread-history", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/jkm/spreads", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/lng-jkm", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/lng-jkm/curve", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/lng-jkm/historical", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/lng-jkm/intraday", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/lng-jkm/ohlc", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/lng-jkm/spread-history", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/lng-jkm/spreads", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/natural-gas", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/futures/natural-gas/curve", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/natural-gas/historical", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/futures/natural-gas/intraday", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/natural-gas/ohlc", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/natural-gas/spread-history", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/natural-gas/spreads", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/futures/rbob", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/futures/ttf-gas", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/ttf-gas/curve", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/ttf-gas/historical", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/ttf-gas/intraday", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/ttf-gas/ohlc", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/ttf-gas/spread-history", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/ttf-gas/spreads", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/uk-carbon", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/uk-carbon/curve", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/uk-carbon/historical", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/uka", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/uka-carbon", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/uka-carbon/curve", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/uka-carbon/historical", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/uka/curve", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/uka/historical", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/ukets-carbon", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/ukets-carbon/curve", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/ukets-carbon/historical", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/wti", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/futures/wti/curve", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/wti/historical", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/futures/wti/intraday", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/wti/ohlc", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/wti/spread-history", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/futures/wti/spreads", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/gasoline-prices", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/geopolitical/events", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/geopolitical/history", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/geopolitical/hotspots", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/geopolitical/regions", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/geopolitical/risk", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/health", + "audience": "operations" + }, + { + "method": "GET", + "path": "/v1/health/change-24h", + "audience": "operations" + }, + { + "method": "GET", + "path": "/v1/health/detailed", + "audience": "operations" + }, + { + "method": "GET", + "path": "/v1/health/price-monitor", + "audience": "operations" + }, + { + "method": "GET", + "path": "/v1/indicators/annotations", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/indicators/annotations/batch", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/indicators/cftc-positioning", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/indicators/cftc-positioning/all", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/indicators/cftc-positioning/historical", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/indicators/congressional-trades", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/indicators/fuel-switching", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/indicators/fuel-switching/historical", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/indicators/price-context", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/indicators/storage-analytics", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/indicators/storage-analytics/all", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/infrastructure/{param}", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/infrastructure/near", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/infrastructure/status", + "audience": "product_preview" + }, + { + "method": "POST", + "path": "/v1/internal/chat_logs", + "audience": "internal" + }, + { + "method": "POST", + "path": "/v1/internal/ops_alerts", + "audience": "internal" + }, + { + "method": "POST", + "path": "/v1/internal/send_email", + "audience": "internal" + }, + { + "method": "GET", + "path": "/v1/latest", + "audience": "compatibility" + }, + { + "method": "POST", + "path": "/v1/leads", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/macro/calendar", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/macro/releases", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/macro/releases/this-week", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/marine-fuels/latest", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/marine-ports", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/marine-ports/{param}", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/maritime-fuels/bundle", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/maritime-fuels/historical", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/market_brief", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/market-brief", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/meta/limits", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/natural-gas/hubs", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/natural-gas/hubs/{param}", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/natural-gas/storage", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/notification_preferences", + "audience": "dashboard" + }, + { + "method": "PATCH", + "path": "/v1/notification_preferences", + "audience": "dashboard" + }, + { + "method": "PUT", + "path": "/v1/notification_preferences", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/notifications", + "audience": "dashboard" + }, + { + "method": "POST", + "path": "/v1/notifications/{param}/mark_read", + "audience": "dashboard" + }, + { + "method": "POST", + "path": "/v1/notifications/mark_all_read", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/notifications/unread_count", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/onboarding/first_call_example", + "audience": "dashboard" + }, + { + "method": "POST", + "path": "/v1/onboarding/run_first_call", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/onboarding/status", + "audience": "dashboard" + }, + { + "method": "POST", + "path": "/v1/onboarding/track_event", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/organizations/{param}/dashboard", + "audience": "dashboard" + }, + { + "method": "DELETE", + "path": "/v1/phone", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/phone", + "audience": "dashboard" + }, + { + "method": "POST", + "path": "/v1/phone", + "audience": "dashboard" + }, + { + "method": "POST", + "path": "/v1/phone/resend", + "audience": "dashboard" + }, + { + "method": "POST", + "path": "/v1/phone/verify", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/price", + "audience": "unlisted" + }, + { + "method": "GET", + "path": "/v1/price/latest", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/prices", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/prices/{param}", + "audience": "unlisted" + }, + { + "method": "GET", + "path": "/v1/prices/all", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/prices/all/health", + "audience": "openapi" + }, + { + "method": "POST", + "path": "/v1/prices/batch", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/prices/brent", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/prices/BRENT_CRUDE_USD", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/prices/BRENT_CRUDE_USD/latest", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/prices/BRENT_USD", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/prices/crude-oil", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/prices/current", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/prices/data-connector", + "audience": "first_party_client" + }, + { + "method": "GET", + "path": "/v1/prices/excel-latest", + "audience": "first_party_client" + }, + { + "method": "GET", + "path": "/v1/prices/export", + "audience": "first_party_client" + }, + { + "method": "GET", + "path": "/v1/prices/historical", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/prices/historical/week", + "audience": "unlisted" + }, + { + "method": "GET", + "path": "/v1/prices/history", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/prices/history/week", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/prices/latest", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/prices/marine-fuels", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/prices/marine-fuels/historical", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/prices/marine-fuels/latest", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/prices/marine-fuels/ports", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/prices/metrics", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/prices/multi", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/prices/past_day", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/prices/past_day_price", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/prices/past_month", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/prices/past_week", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/prices/past_year", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/prices/past/1y", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/prices/past/30d", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/prices/widget", + "audience": "first_party_client" + }, + { + "method": "GET", + "path": "/v1/prices/widget/diesel", + "audience": "first_party_client" + }, + { + "method": "GET", + "path": "/v1/prices/widget/ping", + "audience": "first_party_client" + }, + { + "method": "GET", + "path": "/v1/pricing", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pricing/validate", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/drilling-intelligence/basins", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/drilling-intelligence/basins/{param}", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/drilling-intelligence/cycle-times/{param}", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/drilling-intelligence/duc-wells", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/drilling-intelligence/frac-focus", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/drilling-intelligence/frac-focus/chemicals", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/drilling-intelligence/frac-focus/full", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/drilling-intelligence/operators", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/drilling-intelligence/operators/{param}", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/drilling-intelligence/permits", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/drilling-intelligence/states", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/drilling-intelligence/states/{param}", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/drilling-intelligence/states/{param}/full", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/drilling-intelligence/summary", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/drilling-intelligence/well-permits", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/drilling-intelligence/well-permits/nearby", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/drilling-intelligence/well-permits/weekly-digest", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/drilling-intelligence/wells/sitemap-seed", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/forecasts", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/inventories", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/inventories/cushing", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/inventories/history", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/inventories/padd", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/inventories/spr", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/macro/calendar", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/macro/cpi", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/macro/dashboard", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/macro/dxy", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/macro/unemployment", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/production", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/production/{param}", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/production/compliance", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/production/countries", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/production/history", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/production/us", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/rig-counts", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/rig-counts/basins", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/rig-counts/historical", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/pseo/rig-counts/states", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/referrals", + "audience": "dashboard" + }, + { + "method": "POST", + "path": "/v1/referrals", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/referrals/{param}", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/referrals/earnings_calculator", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/referrals/leaderboard", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/referrals/sharing_resources", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/referrals/stats", + "audience": "dashboard" + }, + { + "method": "POST", + "path": "/v1/referrals/track_click", + "audience": "dashboard" + }, + { + "method": "POST", + "path": "/v1/referrals/track_share", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/rig-counts", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/rig-counts/current", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/rig-counts/historical", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/rig-counts/latest", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/rig-counts/summary", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/rig-counts/trends", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/rig-counts/us", + "audience": "compatibility" + }, + { + "method": "GET", + "path": "/v1/sample", + "audience": "discovery_preview" + }, + { + "method": "GET", + "path": "/v1/scheduled-deliveries", + "audience": "unlisted" + }, + { + "method": "POST", + "path": "/v1/scheduled-deliveries", + "audience": "unlisted" + }, + { + "method": "DELETE", + "path": "/v1/scheduled-deliveries/{param}", + "audience": "unlisted" + }, + { + "method": "GET", + "path": "/v1/scheduled-deliveries/{param}", + "audience": "unlisted" + }, + { + "method": "PATCH", + "path": "/v1/scheduled-deliveries/{param}", + "audience": "unlisted" + }, + { + "method": "PUT", + "path": "/v1/scheduled-deliveries/{param}", + "audience": "unlisted" + }, + { + "method": "POST", + "path": "/v1/scheduled-deliveries/{param}/send_now", + "audience": "unlisted" + }, + { + "method": "GET", + "path": "/v1/shipping/chokepoints", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/shipping/chokepoints/{param}", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/shipping/chokepoints/anomalies", + "audience": "product_preview" + }, + { + "method": "POST", + "path": "/v1/sms/confirm_phone", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/sms/eligibility", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/sms/messages", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/sms/notification_preferences", + "audience": "dashboard" + }, + { + "method": "PATCH", + "path": "/v1/sms/notification_preferences/{param}", + "audience": "dashboard" + }, + { + "method": "DELETE", + "path": "/v1/sms/optin", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/sms/optin", + "audience": "dashboard" + }, + { + "method": "POST", + "path": "/v1/sms/optin", + "audience": "dashboard" + }, + { + "method": "POST", + "path": "/v1/sms/resend_verification", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/sms/settings", + "audience": "dashboard" + }, + { + "method": "PATCH", + "path": "/v1/sms/settings", + "audience": "dashboard" + }, + { + "method": "POST", + "path": "/v1/sms/test", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/sms/verification_status", + "audience": "dashboard" + }, + { + "method": "POST", + "path": "/v1/sms/verify", + "audience": "dashboard" + }, + { + "method": "POST", + "path": "/v1/sms/verify_phone", + "audience": "dashboard" + }, + { + "method": "POST", + "path": "/v1/sms/webhook/twilio", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/spreads/basis", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/spreads/basis/all", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/spreads/basis/historical", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/spreads/crack", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/spreads/crack/all", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/spreads/crack/historical", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/spreads/curve-structure", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/spreads/curve-structure/all", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/spreads/gasoil-crack", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/spreads/margin", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/spreads/margin/all", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/spreads/margin/historical", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/spreads/physical-premium", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/spreads/physical-premium/all", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/spreads/physical-premium/historical", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/status", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/storage", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/storage/cushing", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/storage/history/{param}", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/storage/regional", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/storage/spr", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/subscriptions", + "audience": "openapi" + }, + { + "method": "POST", + "path": "/v1/subscriptions", + "audience": "openapi" + }, + { + "method": "DELETE", + "path": "/v1/subscriptions/{param}", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/subscriptions/{param}", + "audience": "openapi" + }, + { + "method": "PATCH", + "path": "/v1/subscriptions/{param}", + "audience": "openapi" + }, + { + "method": "PUT", + "path": "/v1/subscriptions/{param}", + "audience": "openapi" + }, + { + "method": "POST", + "path": "/v1/subscriptions/{param}/pause", + "audience": "openapi" + }, + { + "method": "POST", + "path": "/v1/subscriptions/{param}/resume", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/subscriptions/events", + "audience": "openapi" + }, + { + "method": "POST", + "path": "/v1/users/activate-trial", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/users/api-keys", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/users/health", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/users/profile", + "audience": "dashboard" + }, + { + "method": "PATCH", + "path": "/v1/users/profile", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/users/usage", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/webhooks", + "audience": "openapi" + }, + { + "method": "POST", + "path": "/v1/webhooks", + "audience": "openapi" + }, + { + "method": "DELETE", + "path": "/v1/webhooks/{param}", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/webhooks/{param}", + "audience": "openapi" + }, + { + "method": "PATCH", + "path": "/v1/webhooks/{param}", + "audience": "openapi" + }, + { + "method": "PUT", + "path": "/v1/webhooks/{param}", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/webhooks/{param}/events", + "audience": "product_preview" + }, + { + "method": "POST", + "path": "/v1/webhooks/{param}/reactivate", + "audience": "product_preview" + }, + { + "method": "POST", + "path": "/v1/webhooks/{param}/test", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/welcome/first-call", + "audience": "dashboard" + }, + { + "method": "GET", + "path": "/v1/well-lifecycle/cohorts", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/well-lifecycle/states", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/well-lifecycle/states/{param}", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/well-lifecycle/wells/{param}", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/well-production", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/well-production/cycle-time", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/well-production/cycle-time/cohorts", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/well-production/pru/{param}", + "audience": "product_preview" + }, + { + "method": "GET", + "path": "/v1/well-production/states", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/well-production/states/{param}", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/well-production/summary", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/well-production/top-producers", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/well-production/wells/{param}", + "audience": "openapi" + }, + { + "method": "GET", + "path": "/v1/wells/lookup", + "audience": "website" + }, + { + "method": "GET", + "path": "/v1/x402/prices/latest", + "audience": "protocol_preview" + } + ] +} diff --git a/tests/release-readiness.test.ts b/tests/release-readiness.test.ts index e06d57f..1f33c13 100644 --- a/tests/release-readiness.test.ts +++ b/tests/release-readiness.test.ts @@ -10,7 +10,7 @@ describe("release readiness", () => { const changelog = read("CHANGELOG.md"); const firstRelease = changelog.match(/^## \[([^\]]+)\]/m); - expect(packageJson.version).toBe("2.0.0"); + expect(packageJson.version).toBe("2.1.0"); expect(versionSource).toContain(`SDK_VERSION = "${packageJson.version}"`); expect(firstRelease?.[1]).toBe(packageJson.version); }); diff --git a/tests/resources/drilling.test.ts b/tests/resources/drilling.test.ts index 564ae6c..8d0a610 100644 --- a/tests/resources/drilling.test.ts +++ b/tests/resources/drilling.test.ts @@ -141,50 +141,16 @@ describe("DrillingResource", () => { }); describe("trends()", () => { - it("should fetch drilling trends as array", async () => { - const mockData: DrillingTrend[] = [ - { - date: "2024-01-01", - metric: "rig_count", - value: 618, - trend: "up", - }, - { - date: "2024-01-08", - metric: "rig_count", - value: 620, - moving_average: 619, - trend: "up", - }, - { - date: "2024-01-15", - metric: "rig_count", - value: 625, - moving_average: 621, - trend: "up", - }, - ]; + // No /v1/drilling-intelligence/trends route exists; it 404'd live (#125). + it("rejects with ENDPOINT_NOT_AVAILABLE and never calls the API", async () => { + const requestSpy = vi.spyOn(client as any, "request"); - const requestSpy = vi.spyOn(client as any, "request").mockResolvedValue(mockData); - - const result = await client.drilling.trends(); - - expect(requestSpy).toHaveBeenCalledWith("/v1/drilling-intelligence/trends", {}); - expect(result).toHaveLength(3); - expect(result[0].metric).toBe("rig_count"); - expect(result[2].value).toBe(625); - }); - - it("should unwrap trends property when response is wrapped", async () => { - const mockData: DrillingTrend[] = [{ date: "2024-01-15", metric: "rig_count", value: 625 }]; - - vi.spyOn(client as any, "request").mockResolvedValue({ - trends: mockData, + await expect(client.drilling.trends()).rejects.toMatchObject({ + name: "OilPriceAPIError", + code: "ENDPOINT_NOT_AVAILABLE", }); - - const result = await client.drilling.trends(); - - expect(result).toEqual(mockData); + await expect(client.drilling.trends()).rejects.toThrow(/drilling\.summary\(\)/); + expect(requestSpy).not.toHaveBeenCalled(); }); }); @@ -383,63 +349,16 @@ describe("DrillingResource", () => { }); describe("basin()", () => { - it("should fetch basin-specific drilling data", async () => { - const mockData: BasinDrillingData = { - basin: "Permian", - active_rigs: 310, - frac_spreads: 120, - permits: 450, - duc_wells: 1450, - completions: 320, - wells_drilled: 280, - as_of_date: "2024-01-15", - }; - - const requestSpy = vi.spyOn(client as any, "request").mockResolvedValue(mockData); - - const result = await client.drilling.basin("Permian"); - - expect(requestSpy).toHaveBeenCalledWith("/v1/drilling-intelligence/basin/Permian", {}); - expect(result.basin).toBe("Permian"); - expect(result.active_rigs).toBe(310); - expect(result.frac_spreads).toBe(120); - expect(result.as_of_date).toBe("2024-01-15"); - }); - - it("should fetch basin data for Eagle Ford", async () => { - const mockData: BasinDrillingData = { - basin: "Eagle Ford", - active_rigs: 65, - as_of_date: "2024-01-15", - }; - - const requestSpy = vi.spyOn(client as any, "request").mockResolvedValue(mockData); - - const result = await client.drilling.basin("Eagle Ford"); + // No /v1/drilling-intelligence/basin/{name} route exists; it 404'd live (#125). + it("rejects with ENDPOINT_NOT_AVAILABLE and never calls the API", async () => { + const requestSpy = vi.spyOn(client as any, "request"); - // The space is percent-encoded by the resource now rather than by - // `new URL()` later; the wire is byte-for-byte identical either way - // (both produce /v1/drilling-intelligence/basin/Eagle%20Ford). See #92. - expect(requestSpy).toHaveBeenCalledWith( - "/v1/drilling-intelligence/basin/Eagle%20Ford", - {}, - ); - expect(result.basin).toBe("Eagle Ford"); - }); - - it("should throw error for empty basin name", async () => { - await expect(client.drilling.basin("")).rejects.toThrow( - "Basin name must be a non-empty string", - ); - }); - - it("should throw error for non-string basin name", async () => { - await expect(client.drilling.basin(null as any)).rejects.toThrow( - "Basin name must be a non-empty string", - ); - await expect(client.drilling.basin(42 as any)).rejects.toThrow( - "Basin name must be a non-empty string", - ); + await expect(client.drilling.basin("Permian")).rejects.toMatchObject({ + name: "OilPriceAPIError", + code: "ENDPOINT_NOT_AVAILABLE", + }); + await expect(client.drilling.basin("Permian")).rejects.toThrow(/by_basin/); + expect(requestSpy).not.toHaveBeenCalled(); }); }); }); diff --git a/tests/resources/futures.test.ts b/tests/resources/futures.test.ts index 06f8a3b..0a68b4c 100644 --- a/tests/resources/futures.test.ts +++ b/tests/resources/futures.test.ts @@ -203,36 +203,18 @@ describe("FuturesResource", () => { }); describe("spreads()", () => { - it("should calculate spread between two contracts", async () => { - const mockSpread: FuturesSpread = { - contract1: "CL.1", - contract2: "CL.2", - spread: 0.5, - spread_percent: 0.66, - timestamp: "2024-01-15T10:00:00Z", - }; - - const requestSpy = vi.spyOn(client as any, "request").mockResolvedValue(mockSpread); - - const result = await client.futures.spreads("CL.1", "CL.2"); + // No /v1/futures/spreads route exists; it 404'd live (#125). + it("rejects with ENDPOINT_NOT_AVAILABLE and never calls the API", async () => { + const requestSpy = vi.spyOn(client as any, "request"); - expect(requestSpy).toHaveBeenCalledWith("/v1/futures/spreads", { - contract1: "CL.1", - contract2: "CL.2", + await expect(client.futures.spreads("CL.1", "CL.2")).rejects.toMatchObject({ + name: "OilPriceAPIError", + code: "ENDPOINT_NOT_AVAILABLE", }); - expect(result).toEqual(mockSpread); - }); - - it("should throw error for empty first contract", async () => { - await expect(client.futures.spreads("", "CL.2")).rejects.toThrow( - "First contract symbol must be a non-empty string", - ); - }); - - it("should throw error for empty second contract", async () => { - await expect(client.futures.spreads("CL.1", "")).rejects.toThrow( - "Second contract symbol must be a non-empty string", + await expect(client.futures.spreads("CL.1", "CL.2")).rejects.toThrow( + /futures\.brent\(\)\.spreads\(\)/, ); + expect(requestSpy).not.toHaveBeenCalled(); }); }); @@ -296,44 +278,42 @@ describe("FuturesResource", () => { }); describe("continuous()", () => { - it("should fetch continuous contract without months parameter", async () => { - const mockData: ContinuousFuturesData = { - contract: "CL", - months: 1, - prices: [ - { date: "2024-01-01", price: 75.0, active_contract: "CL.1" }, - { date: "2024-01-02", price: 75.25, active_contract: "CL.1" }, - ], - }; + // No /v1/futures/{contract}/continuous route exists; it 404'd live (#125). + it("rejects with ENDPOINT_NOT_AVAILABLE and never calls the API", async () => { + const requestSpy = vi.spyOn(client as any, "request"); - const requestSpy = vi.spyOn(client as any, "request").mockResolvedValue(mockData); - - const result = await client.futures.continuous("CL"); - - expect(requestSpy).toHaveBeenCalledWith("/v1/futures/CL/continuous", {}); - expect(result).toEqual(mockData); + await expect(client.futures.continuous("CL", 2)).rejects.toMatchObject({ + name: "OilPriceAPIError", + code: "ENDPOINT_NOT_AVAILABLE", + }); + await expect(client.futures.continuous("CL")).rejects.toThrow(/continuousFrontMonth/); + expect(requestSpy).not.toHaveBeenCalled(); }); + }); - it("should fetch continuous contract with months parameter", async () => { - const mockData: ContinuousFuturesData = { - contract: "CL", - months: 2, - prices: [], + describe("continuousFrontMonth()", () => { + it("requests the continuous family route", async () => { + const mock = { + commodity: "BRENT_FUTURES_CONTINUOUS", + price: 102.56, + metadata: { ticker: "BZ=F", is_continuous: true }, }; + const requestSpy = vi.spyOn(client as any, "request").mockResolvedValue(mock); - const requestSpy = vi.spyOn(client as any, "request").mockResolvedValue(mockData); + const result = await client.futures.continuousFrontMonth("brent"); - await client.futures.continuous("CL", 2); - - expect(requestSpy).toHaveBeenCalledWith("/v1/futures/CL/continuous", { - months: "2", - }); + expect(requestSpy).toHaveBeenCalledWith("/v1/futures/continuous/brent", {}); + expect(result).toEqual(mock); }); - it("should throw error for empty contract", async () => { - await expect(client.futures.continuous("")).rejects.toThrow( - "Contract symbol must be a non-empty string", + it("rejects an unsupported family without calling the API", async () => { + const requestSpy = vi.spyOn(client as any, "request"); + + await expect(client.futures.continuousFrontMonth("CL" as any)).rejects.toThrow( + 'Continuous futures family must be "brent" or "wti"', ); + expect(requestSpy).not.toHaveBeenCalled(); }); }); + });