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.
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 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
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 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.
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/accountto 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.