Integration Guide

Headless Oracle provides a cryptographically verifiable defensive execution layer for autonomous agents operating across global markets.

7 exchanges. ~1,300 edge cases per year.

The scheduling complexity a timezone library was not built to solve.

67
exchange-specific
holidays
different per country, non-overlapping
18
early close
days
NYSE closes at 1:00 PM, not 4:00 PM
8
DST
transitions
incl. 3-week US/EU phantom window
490
lunch break
sessions
Tokyo 11:30–12:30, HK 12:00–13:00
728
weekend days
across 7 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

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

Seven exchanges across four continents. 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
XLON London Stock Exchange 08:00 – 16:30 Europe/London Mar 29
XJPX Japan Exchange Group (Tokyo) 09:00 – 15:30 (lunch 11:30–12:30) Asia/Tokyo None
XPAR Euronext Paris 09:00 – 17:30 Europe/Paris Mar 29
XHKG Hong Kong Exchanges and Clearing 09:30 – 16:00 (lunch 12:00–13:00) Asia/Hong_Kong None
XSES Singapore Exchange 09:00 – 17:00 Asia/Singapore 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 XNAS, XLON, XJPX, XPAR, XHKG, XSES)

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 reason field.
  • 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 all fields in the receipt except signature itself, with keys sorted alphabetically, minified (no spaces).

// Remove signature, sort remaining keys alphabetically, then stringify
const { signature, ...payload } = receipt;
const sorted = {};
for (const key of Object.keys(payload).sort()) sorted[key] = payload[key];
const canonical = JSON.stringify(sorted); // no spaces, alphabetical keys

2. Implementation Samples

Python (PyNaCl)

import json, requests
from nacl.signing import VerifyKey

PUBLIC_KEY_HEX = "03dc27993a2c90856cdeb45e228ac065f18f69f0933c917b2336c1e75712f178"

def verify_and_check(mic="XNYS"):
    receipt = requests.get(
        f"https://headlessoracle.com/v5/status?mic={mic}",
        headers={"X-Oracle-Key": "YOUR_KEY"},
        timeout=4
    ).json()

    sig      = receipt.pop("signature")
    canonical = json.dumps(receipt, sort_keys=True, separators=(",", ":"))
    VerifyKey(bytes.fromhex(PUBLIC_KEY_HEX)).verify(
        canonical.encode(), bytes.fromhex(sig)
    )
    return receipt["status"] == "OPEN"

JavaScript (Web Crypto API)

async function verifyReceipt(receipt) {
  const { signature, ...payload } = receipt;
  const sorted = {};
  for (const key of Object.keys(payload).sort()) sorted[key] = payload[key];
  const canonical = JSON.stringify(sorted);
  const keyBytes  = hexToBytes("03dc27993a2c90856cdeb45e228ac065f18f69f0933c917b2336c1e75712f178");
  const sigBytes  = hexToBytes(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.

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.

TIER 0Manual override active — returns HALTED or CLOSED with signed reason.
TIER 1Schedule-based calculation — checks holidays, weekends, trading hours, half-days, lunch breaks.
TIER 2Fail-closed safety net — any Tier 1 error returns a signed UNKNOWN receipt.
TIER 3Catastrophic — signing system offline. Returns unsigned CRITICAL_FAILURE with HTTP 500.

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 7 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 7 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

{
  "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/account to confirm it's active.
  • 402 PAYMENT_REQUIRED: Subscription is suspended or cancelled. Update billing to restore access.

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.