Integration Guide
Headless Oracle provides a cryptographically verifiable defensive execution layer for autonomous agents operating across global markets.
28 exchanges. Global 24/7 coverage.
The scheduling complexity a timezone library was not built to solve.
holidays
different per country, non-overlapping
days
NYSE closes at 1:00 PM, not 4:00 PM
transitions
incl. 3-week US/EU phantom window
sessions
Tokyo 11:30–12:30, HK 12:00–13:00
across 28 venues
per calendar year
A timezone library handles zero of these. Headless Oracle handles all of them.
Think it's simple?
Common assumptions about market schedules, and what the data actually shows.
| What you assume | What actually happens |
|---|---|
| NYSE closes at 4pm ET | Except Good Friday (1pm), day before July 4th (1pm), Christmas Eve (1pm), and emergency halts at any time |
| Tokyo is open 9am–3pm JST | There's a lunch break 11:30–12:30 every trading day. Your bot thinks it's open. It's not. |
| "Just use a timezone library" | pytz handles DST. It doesn't handle that NYSE closes for Juneteenth but LSE doesn't, or that Hong Kong closes for Lunar New Year but Singapore doesn't. |
| "I'll hardcode the holidays" | Which year? 2026 holidays differ from 2027. When a holiday falls on a weekend, some exchanges shift to Friday, others Monday, others don't shift. |
| "I'll check once at market open" | Circuit breaker at 2:47pm. Your cached "OPEN" is now wrong. Your bot trades into a halted market. |
Quick Start
Using Claude Code? Add Headless Oracle to Claude Code in 3 steps →
Copy one .mcp.json file and your agent has pre-trade verification instantly.
Try the public demo endpoint instantly — no API key needed:
curl https://headlessoracle.com/v5/demo # Any supported exchange: curl "https://headlessoracle.com/v5/demo?mic=XLON" curl "https://headlessoracle.com/v5/demo?mic=XJPX"
For production use, fetch a signed attestation with your API key:
curl -X GET "https://headlessoracle.com/v5/status?mic=XNYS" \
-H "X-Oracle-Key: YOUR_API_KEY"
Check when the next session opens (no auth required):
curl "https://headlessoracle.com/v5/schedule?mic=XNYS"
Verify Oracle's signing infrastructure is live:
curl "https://headlessoracle.com/v5/health"
Supported Exchanges
28 exchanges across 7 regions — 23 equity venues plus 5 extended markets (CME/NYMEX futures, Cboe options, Coinbase + Binance 24/7 crypto). All DST transitions are handled server-side using IANA timezone data — no hardcoded UTC offsets.
| MIC | Exchange | Local Hours | Timezone | DST |
|---|---|---|---|---|
| XNYS | New York Stock Exchange | 09:30 – 16:00 | America/New_York | Mar 8 |
| XNAS | NASDAQ | 09:30 – 16:00 | America/New_York | Mar 8 |
| XBSP | B3 (São Paulo Stock Exchange) | 10:00 – 17:00 | America/Sao_Paulo | Brazil (Nov) |
| XLON | London Stock Exchange | 08:00 – 16:30 | Europe/London | Mar 29 |
| XPAR | Euronext Paris | 09:00 – 17:30 | Europe/Paris | Mar 29 |
| XSWX | SIX Swiss Exchange | 09:00 – 17:30 | Europe/Zurich | Mar 29 |
| XMIL | Borsa Italiana (Milan) | 09:00 – 17:30 | Europe/Rome | Mar 29 |
| XHEL | Nasdaq Helsinki | 10:00 – 18:30 | Europe/Helsinki | Mar 29 |
| XSTO | Nasdaq Stockholm | 09:00 – 17:30 | Europe/Stockholm | Mar 29 |
| XIST | Borsa Istanbul | 10:00 – 18:00 | Europe/Istanbul | Mar 29 |
| XSAU | Saudi Exchange (Tadawul) Sun–Thu | 10:00 – 15:00 | Asia/Riyadh | None |
| XDFM | Dubai Financial Market Sun–Thu | 10:00 – 14:00 | Asia/Dubai | None |
| XJSE | Johannesburg Stock Exchange | 09:00 – 17:00 | Africa/Johannesburg | None |
| XSHG | Shanghai Stock Exchange | 09:30 – 15:00 (lunch 11:30–13:00) | Asia/Shanghai | None |
| XSHE | Shenzhen Stock Exchange | 09:30 – 15:00 (lunch 11:30–13:00) | Asia/Shanghai | None |
| XHKG | Hong Kong Exchanges and Clearing | 09:30 – 16:00 (lunch 12:00–13:00) | Asia/Hong_Kong | None |
| XJPX | Japan Exchange Group (Tokyo) | 09:00 – 15:30 (lunch 11:30–12:30) | Asia/Tokyo | None |
| XKRX | Korea Exchange (Seoul) | 09:00 – 15:30 | Asia/Seoul | None |
| XBOM | BSE Ltd (Bombay Stock Exchange) | 09:15 – 15:30 | Asia/Kolkata | None |
| XNSE | National Stock Exchange of India | 09:15 – 15:30 | Asia/Kolkata | None |
| XSES | Singapore Exchange | 09:00 – 17:00 | Asia/Singapore | None |
| XASX | Australian Securities Exchange (Sydney) | 10:00 – 16:00 | Australia/Sydney | Oct (AU) |
| XNZE | New Zealand Exchange (Auckland) | 10:00 – 16:45 | Pacific/Auckland | Oct (NZ) |
| Extended venues — derivatives & 24/7 crypto | ||||
| XCBT | CME / Chicago Board of Trade futures (overnight) | Sun 17:00 – Fri 16:00 (60-min daily break) | America/Chicago | Mar 8 |
| XNYM | NYMEX (CME Group) futures (overnight) | Sun 17:00 – Fri 16:00 (60-min daily break) | America/Chicago | Mar 8 |
| XCBO | Cboe Options Exchange | 09:30 – 16:15 | America/New_York | Mar 8 |
| XCOI | Coinbase Exchange 24/7 | Always open | UTC | None |
| XBIN | Binance 24/7 | Always open | UTC | None |
All times are local to the exchange. Retrieve the full exchange directory programmatically at /v5/exchanges.
2026 DST Risk Events
Any bot using hardcoded UTC offsets will compute incorrect hours after these dates:
- Mar 8 — US clocks spring forward (EST → EDT). Affects XNYS, XNAS.
- Mar 29 — UK/EU clocks spring forward (GMT/CET → BST/CEST). Affects XLON, XPAR.
- Oct 25 — UK/EU clocks fall back. Affects XLON, XPAR.
- Nov 1 — US clocks fall back. Affects XNYS, XNAS.
Headless Oracle handles all transitions automatically.
Available Endpoints
GET /v5/status Authenticated
Primary endpoint for production market status checks.
Header: X-Oracle-Key: YOUR_API_KEY Query: mic=XNYS (or any of the 28 supported MIC codes — see /v5/exchanges)
GET /v5/demo Public
Returns a signed receipt for any exchange. No auth required. Use this for integration testing and verification development.
Query: mic=XNYS (optional, defaults to XNYS)
GET /v5/schedule Public
Returns the next scheduled open and close times for any exchange. Includes lunch_break for exchanges with midday breaks (XJPX, XHKG). Does not reflect real-time halts or manual overrides.
{
"mic": "XJPX",
"name": "Japan Exchange Group (Tokyo)",
"timezone": "Asia/Tokyo",
"queried_at": "2026-03-10T01:00:00.000Z",
"current_status": "OPEN",
"next_open": "2026-03-10T03:30:00.000Z",
"next_close": "2026-03-10T06:30:00.000Z",
"lunch_break": { "start": "11:30", "end": "12:30" },
"note": "Times are UTC. lunch_break times are local exchange time (see timezone field)."
}
// Exchanges without a lunch break return lunch_break: null
// { ..., "lunch_break": null, ... }
GET /v5/exchanges Public
Returns the full directory of supported exchanges with MIC codes, names, and timezones.
GET /v5/keys Public
Returns the current Ed25519 public key registry and the canonical payload specification for independent signature verification.
{
"keys": [{
"key_id": "key_2026_v1",
"algorithm": "Ed25519",
"format": "hex",
"public_key": "03dc27993a2c90856cdeb45e228ac065f18f69f0933c917b2336c1e75712f178",
"valid_from": "2026-01-01T00:00:00Z",
"valid_until": null
}],
"canonical_payload_spec": { ... }
}
// valid_until is null when no key rotation is scheduled.
// It will be set in advance of any planned rotation.
GET /v5/batch Authenticated
Fetch signed status receipts for multiple exchanges in one request. Each receipt is independently signed and verifiable in isolation.
Header: X-Oracle-Key: YOUR_API_KEY Query: mics=XNYS,XNAS,XLON (comma-separated, deduplicated)
All MICs are validated up front — one invalid MIC returns 400 for the whole request. Tier 3 signing failure fails the whole batch.
GET /v5/health Public
Signed liveness probe. Use this to distinguish Oracle is down from market is genuinely UNKNOWN. A valid signed response means the signing infrastructure is alive.
{
"receipt_id": "uuid-v4",
"issued_at": "2026-03-10T13:00:00.000Z",
"expires_at": "2026-03-10T13:01:00.000Z",
"status": "OK",
"source": "SYSTEM",
"public_key_id": "key_2026_v1",
"signature": "hex_string"
}
// 200 + valid signature = Oracle is alive
// 500 CRITICAL_FAILURE = signing system offline
Response Schema
{
"receipt_id": "uuid-v4",
"issued_at": "ISO-8601-Timestamp (UTC)",
"expires_at": "ISO-8601-Timestamp (UTC, issued_at + 60s)",
"mic": "XNYS",
"status": "OPEN", // OPEN | CLOSED | HALTED | UNKNOWN
"source": "SCHEDULE", // SCHEDULE | OVERRIDE | SYSTEM
"schema_version": "v5.0",
"public_key_id": "key_2026_v1",
"signature": "hex_string"
}
// Do not act on a receipt whose expires_at has passed.
Status Values
- OPEN: Market is in an active trading session. Safe to execute.
- CLOSED: Market is outside trading hours — weekend, holiday, or after close.
- HALTED: Market has been manually halted (circuit breaker or emergency override). Treat identically to CLOSED.
- UNKNOWN: Oracle cannot determine status. Treat as CLOSED. Halt all execution.
Source Values
- SCHEDULE: Status derived from the exchange holiday/hours calendar.
- OVERRIDE: Status set manually. Active during circuit breakers or emergency halts. Includes a
reasonfield. - SYSTEM: Internal fallback — returned when an error occurred in the primary logic. Always paired with UNKNOWN.
Verification Logic
To confirm a receipt is authentic, reconstruct the signed payload and verify the Ed25519 signature against the published public key.
1. Reconstruct the Payload
The signature covers a fixed allowlist of receipt fields enumerated in canonical_payload_spec at /v5/keys (union of receipt_fields, override_fields, health_fields). Filter the response to that allowlist, sort the keys alphabetically, then minify with no whitespace. Decoration the worker added to the response after signing — extensions, a duplicated receipt wrapper, discovery_url — must be excluded so the message bytes match what was signed.
// 1. Fetch the canonical field spec from /v5/keys
const { keys, canonical_payload_spec: spec } =
await fetch("https://headlessoracle.com/v5/keys").then(r => r.json());
// 2. Build the allowlist (union of all receipt-type field lists)
const canonicalFields = [
...spec.receipt_fields,
...(spec.override_fields || []),
...(spec.health_fields || []),
].filter((v, i, a) => a.indexOf(v) === i).sort();
// 3. Filter the receipt to the allowlist (drops signature + decoration)
const filtered = {};
for (const key of canonicalFields) {
if (key !== "signature" && key in receipt) filtered[key] = receipt[key];
}
const canonical = JSON.stringify(filtered); // no spaces, alphabetical keys
2. Implementation Samples
Python (PyNaCl)
import json, requests
from nacl.signing import VerifyKey
from nacl.exceptions import BadSignatureError
ORACLE_BASE = "https://headlessoracle.com"
def verify_and_check(mic="XNYS"):
# Fetch the receipt and the public key + canonical field spec
receipt = requests.get(
f"{ORACLE_BASE}/v5/status?mic={mic}",
headers={"X-Oracle-Key": "YOUR_KEY"},
timeout=4,
).json()
keys_doc = requests.get(f"{ORACLE_BASE}/v5/keys", timeout=4).json()
public_key_hex = keys_doc["keys"][0]["public_key"]
spec = keys_doc["canonical_payload_spec"]
# Build the allowlist (union of receipt / override / health fields)
canonical_fields = sorted(set(
spec["receipt_fields"]
+ spec.get("override_fields", [])
+ spec.get("health_fields", [])
))
# Filter the receipt to the allowlist — drops signature + decoration
# (extensions, discovery_url, duplicate `receipt` wrapper)
filtered = {k: receipt[k] for k in canonical_fields
if k != "signature" and k in receipt}
# Canonical message: alphabetical sort, no whitespace, UTF-8
canonical = json.dumps(filtered, sort_keys=True, separators=(",", ":"))
sig = bytes.fromhex(receipt["signature"])
try:
VerifyKey(bytes.fromhex(public_key_hex)).verify(canonical.encode(), sig)
except BadSignatureError:
return False # fail closed
return receipt["status"] == "OPEN"
JavaScript (Web Crypto API)
const ORACLE_BASE = "https://headlessoracle.com";
async function verifyReceipt(receipt) {
// Fetch the public key + canonical field spec
const { keys, canonical_payload_spec: spec } =
await fetch(`${ORACLE_BASE}/v5/keys`).then(r => r.json());
// Build the allowlist (union of receipt / override / health fields)
const canonicalFields = [
...spec.receipt_fields,
...(spec.override_fields || []),
...(spec.health_fields || []),
].filter((v, i, a) => a.indexOf(v) === i).sort();
// Filter the receipt to the allowlist — drops signature + decoration
const filtered = {};
for (const key of canonicalFields) {
if (key !== "signature" && key in receipt) filtered[key] = receipt[key];
}
const canonical = JSON.stringify(filtered);
const keyBytes = hexToBytes(keys[0].public_key);
const sigBytes = hexToBytes(receipt.signature);
const msgBytes = new TextEncoder().encode(canonical);
const cryptoKey = await crypto.subtle.importKey(
"raw", keyBytes, { name: "Ed25519" }, false, ["verify"]
);
return crypto.subtle.verify({ name: "Ed25519" }, cryptoKey, sigBytes, msgBytes);
}
function hexToBytes(hex) {
return new Uint8Array(hex.match(/.{2}/g).map(b => parseInt(b, 16)));
}
Or use the browser-based verifier — paste any receipt and verify the Ed25519 signature instantly with zero server calls. An independent client-side verifier on a separate trust boundary is also live at verify.headlessoracle.com.
Reference implementations: github.com/headlessoracle/demo-agent — ~100-line Node.js reference verifier (MIT). Architectural context: github.com/headlessoracle/essays — including The Trust Primitive: Why Agent Commerce Needs Environment-State Attestation (v1.6.4, April 2026, CC BY 4.0).
Fail-Closed Architecture
The oracle uses a three-tier safety cascade. If anything fails at any tier, the response always defaults to UNKNOWN — never a false OPEN.
Integrator Rule (Binding)
Execute only if status === 'OPEN' AND the Ed25519 signature is valid. Any other result — CLOSED, HALTED, UNKNOWN, timeout, network error, or invalid signature — must halt execution. This is a contractual obligation under the Terms of Service.
MCP Integration
Headless Oracle exposes an MCP (Model Context Protocol) server at POST /mcp. Any agent framework that supports MCP tool calling — Claude Desktop, Cursor, or custom agents built on the MCP spec — can call Oracle directly without writing HTTP client code.
POST /mcp Public · JSON-RPC 2.0
Protocol version: 2024-11-05 (MCP Streamable HTTP transport). No authentication required for MCP tool calls.
Available MCP Tools
get_market_status
Check whether a stock exchange is currently open or closed. Returns a cryptographically signed receipt. MANDATORY: treat UNKNOWN or HALTED as CLOSED and halt execution.
Input: { "mic": "XNYS" } — any of the 28 supported MIC codes.
get_market_schedule
Get next open and close times for an exchange. Includes lunch break windows for XJPX and XHKG. Not cryptographically signed — does not reflect real-time halts.
Input: { "mic": "XJPX" }
list_exchanges
Returns all 28 supported exchanges with MIC codes, names, and timezones.
No input required.
Setup: Claude Desktop ~30 seconds
Add this to your Claude Desktop config file, then restart Claude Desktop:
macOS:
~/Library/Application Support/Claude/claude_desktop_config.json
Windows:
%APPDATA%\Claude\claude_desktop_config.json
Option 1 — Local stdio (one-prompt install via npx, Node.js 18+):
{
"mcpServers": {
"headless-oracle": {
"command": "npx",
"args": ["-y", "headless-oracle-mcp"]
}
}
}
Runs the headless-oracle-mcp npm package locally over stdio. Add "env": { "HEADLESS_ORACLE_API_KEY": "ho_live_..." } for authenticated tiers.
Option 2 — Remote URL (no local Node required):
{
"mcpServers": {
"headless-oracle": {
"url": "https://headlessoracle.com/mcp"
}
}
}
After restarting Claude Desktop, Headless Oracle tools will appear automatically. No API key required for the demo tools.
Example Prompts
Try these after setup:
- ›"Is the NYSE open right now?"
- ›"Check if any Asian markets are currently in their lunch break"
- ›"What time does the London Stock Exchange close today?"
- ›"Are there any market holidays this week across all exchanges?"
- ›"Before I execute this trade — verify XNYS is open and give me the signed receipt"
Key Provenance
The Ed25519 public key for verifying receipts returned by MCP tools is published at /.well-known/oracle-keys.json (RFC 8615 standard).
API Keys & Billing
Production access to /v5/status, /v5/batch, and /v5/account requires an API key passed in the X-Oracle-Key header. All other endpoints are public.
Get an API Key
Subscribe via anonymous Paddle checkout — no account creation required. Your API key is emailed to you after payment and is shown once.
curl -X POST https://headlessoracle.com/v5/checkout
# Returns: { "url": "https://checkout.paddle.com/..." }
# Redirect the user (or open the URL) to complete payment.
Keys are prefixed ok_live_ for easy identification in logs and environment variables.
Check Your Account
curl https://headlessoracle.com/v5/account \
-H "X-Oracle-Key: YOUR_API_KEY"
# Returns: { "plan": "pro", "status": "active", "key_prefix": "ok_live_..." }
Error Codes
- 401 API_KEY_REQUIRED: No key in header.
- 403 INVALID_API_KEY: Key not found. Check for typos or use
/v5/accountto confirm it's active. - 402 PAYMENT_REQUIRED: Subscription is suspended or cancelled. Update billing to restore access.
Open Standards
Headless Oracle publishes the protocols it implements as Apache 2.0 open standards. Any operator can implement these independently — the value is in the interoperability, not the lock-in.
Signed Market Attestation
Defines the canonical JSON schema for a cryptographically signed market-state receipt: fields, Ed25519 signing algorithm, canonical payload serialisation (alphabetical key sort), 60-second TTL, and fail-closed UNKNOWN semantics.
Multi-Party Attestation Aggregation
Defines how N independent oracle operators' SMA receipts are aggregated into a single AggregatedAttestation with quorum consensus. A 2-of-3 quorum means no single compromised operator can produce a forged result. Includes consumer verification algorithm, operator registration conventions, and on-chain verification sketch.
Agent Pre-Trade Safety Standard
A vendor-neutral 6-step checklist every autonomous trading agent must complete before executing: fetch signed attestation → verify circuit breakers → verify settlement window → verify TTL → verify Ed25519 signature → halt on any failure. Includes CI badge, CHECKLIST.yaml, and conformance table.
Circuit Breaker Overrides
The OVERRIDE source tier allows manual status injection — used to broadcast HALTED during exchange circuit breakers, emergency closures, or scheduled maintenance windows.
When an override is active, the receipt includes a reason field explaining the override:
{
"receipt_id": "uuid-v4",
"issued_at": "2026-03-09T19:30:00.000Z",
"expires_at": "2026-03-09T19:31:00.000Z",
"mic": "XNYS",
"status": "HALTED",
"source": "OVERRIDE",
"reason": "NYSE circuit breaker Level 1 triggered",
"schema_version": "v5.0",
"public_key_id": "key_2026_v1",
"signature": "hex_string"
}
Override receipts are signed with the same Ed25519 key as schedule-based receipts. Verification logic is identical. Your bot does not need to handle overrides differently — treat HALTED identically to CLOSED.