Skip to main content

OpenAPI Specification

The DerivFabric REST API ships a complete, machine-readable OpenAPI document. It is the contract, served as a static file alongside this documentation.

Where the spec is

/api/openapi.yaml

Fetch it from this documentation site — the path is relative to wherever these pages are served, so it works on your deployment without knowing its hostname:

# From the host serving these docs.
curl -O https://<your-docs-host>/api/openapi.yaml

Set that host once and the commands below all reuse it:

export DERIVFABRIC_DOCS_URL=https://<your-docs-host>

It is also rendered interactively at /swagger, with try-it-out enabled, pointed at this same document.

Identity

OpenAPI version3.1.0
TitleDerivFabric API
API version0.1.0
LicenceMIT
Operations140

The servers: block declares local development targets. Point a generated client at your own deployment's base URL — DerivFabric is deployed per customer, so the spec's entries are conveniences, not the product's address.

Every response is wrapped

This is the single most important thing to know before generating a client.

Each operation's documented response schema describes the payload, not the bytes on the wire. The wire response is always the envelope:

{
"success": true,
"data": { },
"meta": { "manifest": { } },
"correlation_id": "019fd7c6-3b1a-7c4e-9f2d-8e5a1b0c4d7e",
"correlationId": "019fd7c6-3b1a-7c4e-9f2d-8e5a1b0c4d7e",
"timestamp": "2026-08-20T09:00:00Z"
}
  • data holds the schema the operation documents. It is absent on failure.
  • meta is present only when an operation attaches provenance. A priced number carries its run manifest under meta.manifest.
  • On failure, success is false, data is absent, and error holds { code, message }. The code is stable and machine-readable; the message is for a human and may change.
  • success, the correlation id and timestamp are always present.

The shape is stated machine-readably as the ApiEnvelope schema in components/schemas, with the failure half as ApiError. Individual operations deliberately do not $ref it — 140 hand-written wrappers would be 140 places for the convention to drift — so a generated client needs the envelope applied once, at the transport layer, rather than per method.

The correlation id has two spellings

The server emits the correlation id under both correlation_id and correlationId, carrying the same value in the same response. Either is safe to read.

The spec documents the camelCase name as the contract, and that is the one to prefer in new integrations. The snake_case name is emitted alongside it because the server shipped that spelling first and live integrations read it. Both are serialized from a single field, so they cannot disagree.

Quote either one in a support request — they identify the same call.

Two things the envelope is not

  1. /api/v1/validation/report returns a document, not an envelope. It renders Markdown, HTML or JSON according to its format query parameter, and the rendered body is the response. Errors on that endpoint still arrive as envelopes, so error handling is unchanged.

  2. 202 Accepted is a success. Job submission returns 202 with data populated and success true. A client that treats any status at or above 300 as a failure without reading success will break on job submission.

Authentication

The spec declares one security scheme:

ApiKeyAuth:
type: apiKey
in: header
name: X-API-Key

Security is declared globally rather than repeated per path, so a newly added endpoint is authenticated by default.

Exactly one operation opts out: GET /health, which sets security: []. It is a liveness probe, and a load balancer should not need a tenant key.

/metrics is not an exception — it shares the Health tag but inherits the global requirement, because a public metrics endpoint would publish request volume, error rates and pricer usage to anyone who can reach the API.

Keys carry scopes — price, calibrate, portfolio, xva, jobs, governance, validate and admin. admin satisfies any scope requirement; there is no wildcard. Two authorization failures are reported distinctly because they need different remedies: a missing scope (DF-AUTH-004) is fixed by issuing a key that carries it, an insufficient tier (DF-AUTH-005) by changing plan.

Tags

All 140 operations carry exactly one tag from this list.

TagOperationsCovers
Health2Liveness and build identity.
Pricing1The generic engine entry point; names the pricer explicitly.
Vanilla5European and binary options.
American1Early-exercisable options, on the numerical engine.
Convertible1Convertible bonds.
Structured Products2Notes and autocallables built from a payoff description.
Contract DSL4Contracts written in the algebra rather than chosen from a catalogue.
Portfolio8Stateless calculation over a portfolio supplied in the request.
Portfolios6Portfolios held by the tenant, and the positions in them.
Risk8Value at risk, stress and sensitivity analysis.
XVA3Valuation adjustments — CVA, DVA, FVA.
Hedging1Hedge construction and optimisation.
Calibration3Fitting model parameters to market quotes.
Market Data25Curves, surfaces, snapshots, reference instruments and connectors.
Counterparties19Counterparties, netting sets, collateral terms and credit data.
Trades5Trade capture and lifecycle.
Products16Product families, templates, versions and conventions.
Governance10Capabilities, entitlements, approvals and the audit trail.
Validation3Model validation runs and their reports.
Jobs3Asynchronous work; submission returns 202 with a job id.
Artifacts4Files a tenant has stored against its work.
Studio10Endpoints backing the Studio client specifically.

Portfolio calculates over a portfolio supplied inline in the request; Portfolios manages the stored kind.

A representative operation

POST /api/v1/price — operation id priceOption, tagged Vanilla.

{
"spot": 100,
"strike": 100,
"rate": 0.05,
"dividend": 0.0,
"volatility": 0.20,
"time": 1.0,
"optionType": "call"
}

spot, strike, rate, volatility and time are required. dividend is a continuous dividend yield defaulting to 0; optionType is call or put, defaulting to call; time is in years and rate is continuously compounded.

The documented 200 schema is PriceResponseprice, spot, strike, optionType. Remember the envelope: that schema is what arrives inside data, and a priced number attaches its run manifest under meta.manifest.

Model parameters

Nine pricers need model parameters — SABR wants its α, β, ρ and ν, Hull-White its mean reversion, Dupire a local volatility surface. Every other capability takes what it needs from the instrument and the market snapshot, and rejects a non-empty params rather than ignoring it.

The params field on POST /api/v1/engine/price is a oneOf over nine schemas, selected by pricerId. Each names its pricer, so a generated client gets the field names typed rather than a bare object:

SchemaPricer
BinomialParamsbinomial_tree
HestonFftParamsequity_vanilla_heston_fft
DupireParamsequity_local_vol_dupire
SabrHaganParamsequity_vanilla_sabr_hagan
HullWhiteAnalyticParamsrates_bond_option_hull_white
RoughBergomiParamsequity_rough_bergomi_mc
RoughHestonParamsequity_rough_heston_mc
JarrowYildirimParamsinflation_yoy_jy
Lg2fParamsrates_swaption_lg2f

These objects carry no defaults, so an unrecognised field name is a 400 rather than a silently substituted value. BinomialParams is the exception: omit params entirely and it uses 200 Cox-Ross-Rubinstein steps.

A CI gate compares each schema's properties against the struct the engine deserialises into, in both directions — a documented field the engine does not accept fails the build, and so does an engine field the spec omits.

Using the spec

Generate a client

Every generator below reads the spec straight from the URL.

npx @openapitools/openapi-generator-cli generate \
-i $DERIVFABRIC_DOCS_URL/api/openapi.yaml \
-g typescript-fetch \
-o ./derivfabric-client
go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest \
-generate types,client \
-package derivfabric \
-o derivfabric.gen.go \
$DERIVFABRIC_DOCS_URL/api/openapi.yaml
npx openapi-typescript $DERIVFABRIC_DOCS_URL/api/openapi.yaml -o derivfabric.d.ts

Whichever you choose, wrap the generated calls in one thin layer that unwraps data, surfaces error and records the correlation id — the generated response types describe the payload, not the envelope.

Import into a client tool

PostmanImport → Link, paste your docs host's /api/openapi.yaml. The 140 operations arrive grouped by tag. Add a collection-level X-API-Key header so every request inherits it.

InsomniaCreate → Import From → URL, same address.

View it

Already hosted at /swagger. To render it yourself:

npx @redocly/cli preview-docs $DERIVFABRIC_DOCS_URL/api/openapi.yaml
npx @redocly/cli lint $DERIVFABRIC_DOCS_URL/api/openapi.yaml

The spec is CI-gated

The document is hand-maintained, so automated gates hold it to the running service on every change. What they do and do not cover matters more than the reassurance:

Enforced — the endpoint list matches the router. A build-blocking test compares every public REST path in the spec against the paths the service registers, in both directions: an endpoint that is live but undocumented fails, and one documented but not served fails. A small list of deliberate exclusions is itself checked for staleness, so an exemption cannot outlive the route it was written for.

Enforced — the envelope stays uniform. A second suite checks that ApiEnvelope and ApiError exist, that the envelope's documented properties match the fields the server serializes, that the required fields are exactly the three always present, that no operation documents the wrapper in place of its payload, that the tag vocabulary is closed, that every $ref resolves, and that every success response declares a media type.

Enforced — the document is valid. The spec runs through a full OpenAPI validator in CI, not merely a YAML parse.

Reported, not enforced — breaking changes. Every pull request is diffed against the base branch and anything affecting an existing client is surfaced for a reviewer. It does not block: a breaking change is sometimes the correct change. The gate guarantees nobody makes one by accident.

What is not checked. Field-level agreement between a documented schema and the server's own types. The path gates catch an endpoint appearing or vanishing; they say nothing about a renamed field. So a passing build means no endpoint is missing or invented and the envelope convention holds — it does not mean every field is right.