---
title: "dataacrobat.com developer documentation"
description: "Connect to Jay Ozer's portfolio with the public Ask Jay API, read-only MCP tools, and Markdown. Includes OpenAPI, authentication, examples, and error handling."
canonical: "https://www.dataacrobat.com/developers"
last-updated: "2026-10-15"
---
# dataacrobat.com developer documentation

Read Jay Ozer's published work using Markdown or MCP, or ask a question through the public Ask Jay API. These interfaces cover his projects, experience, patents, open source, and background.

- [OpenAPI specification](https://www.dataacrobat.com/openapi.json)
- [Public API index](https://www.dataacrobat.com/api/v1)
- [MCP server card](https://www.dataacrobat.com/.well-known/mcp/server-card.json)

## When to use each interface

- **Read the portfolio:** start with [llms.txt](https://www.dataacrobat.com/llms.txt) for an overview or [index.md](https://www.dataacrobat.com/index.md) for the full page.
- **Retrieve evidence:** use the read-only MCP tools to search the portfolio or fetch specific information.
- **Ask a question:** use Ask Jay for a conversational answer grounded in the site's own content. You can also [open the chat in your browser](https://www.dataacrobat.com/#ask).

## Authentication and access

No API key, account, bearer token, or OAuth flow is required. The API and MCP server work with public portfolio content. Ask Jay requests are rate limited by IP. If you receive HTTP 429, honor `Retry-After` before retrying; the response's `detail` explains which limit was reached. Use requests from your server or MCP client; the examples below do not require cross-origin browser access.

## API versioning and compatibility

The current API major version is **v1**, at `/api/v1`. Discover it with **GET `/api/v1`**. Responses from its chat and quota endpoints include `API-Version: 1`.

Compatible additions, such as new optional response fields, stay in v1. Breaking request or response changes will use a new major URL, such as `/api/v2`. Clients should ignore response fields they do not recognize. The OpenAPI `info.version` tracks the contract release separately from the major URL.

The existing `/api` index and `/api/ask` chat/quota URLs remain supported compatibility aliases with the same behavior and shared rate limits. No retirement is scheduled. Any future deprecation will be documented here; a planned retirement will also be announced with HTTP deprecation and sunset headers before removal.

## Ask Jay API

Send JSON to **POST `/api/v1/ask`**. A single question is enough:

```sh
curl -N 'https://www.dataacrobat.com/api/v1/ask' \
  -H 'Content-Type: application/json' \
  -H 'Accept: text/event-stream' \
  --data '{"question":"What has Jay built?"}'
```

For follow-up questions, send the conversation as `messages` instead:

```json
{
  "messages": [
    { "role": "user", "content": "What has Jay built?" },
    { "role": "assistant", "content": "I build production AI systems." },
    { "role": "user", "content": "Tell me more about document intelligence." }
  ]
}
```

Each message must contain nonblank text of at most 10,000 characters. A conversation can contain 1–50 messages. Optional `sessionId` and `responseId` fields are accepted. Send the conversation history with each request; a session identifier alone does not supply earlier messages. If both `messages` and `question` are sent, `messages` takes precedence.

### Streaming responses and errors

Successful responses use `Content-Type: text/event-stream`. Read each SSE `data:` field as a JSON event, except for the final literal `[DONE]`. For example:

```text
data: {"type":"status","status":"thinking"}

data: {"type":"content","content":"I build production AI systems."}

data: [DONE]
```

The stream uses `status`, `content`, `done`, and `error` event types. A `done` event can include citations, retrieval evidence, and session or response identifiers. An error after streaming starts arrives as an `error` event within the HTTP 200 stream; check those events as well as the HTTP status.

Invalid request bodies return **HTTP 422** with a JSON `detail` array describing the invalid fields. Rate limits return **HTTP 429** with a JSON `detail` string. See the [OpenAPI schemas](https://www.dataacrobat.com/openapi.json) for the full request and HTTP error formats.

API failures retain `detail` and also provide an `error` object containing a code, message, and resolution, plus a documentation link. Unknown API URLs return **HTTP 404** as JSON. Unsupported methods return **HTTP 405** as JSON and include an `Allow` header. Unexpected failures before streaming starts return **HTTP 500** as JSON without internal details. These HTTP errors are separate from errors reported inside an already-started SSE stream.

### Rate limits and retries

Ask Jay responses include the caller's current quota, including validation errors and successful streams:

Call **GET `/api/v1/ask`** to inspect `scope`, `limit`, `remaining`, and `resetAfterSeconds` as JSON, with the same quota headers. This read-only request does not invoke the AI or consume chat quota. It is not cached.

- `RateLimit-Policy` names the quota, maximum requests (`q`), and window in seconds (`w`).
- `RateLimit` reports remaining requests (`r`) and seconds until the next quota release (`t`).
- `RateLimit-Limit`, `RateLimit-Remaining`, and `RateLimit-Reset` expose the same values separately; reset is a delay in seconds.
- Compatibility `X-RateLimit-*` headers are also supplied; `X-RateLimit-Reset` is a Unix timestamp.
- A **429** includes `Retry-After` in seconds. Wait at least that long and use backoff if another request is rejected.

The default chat quota is 30 accepted requests per minute per IP. Job analysis also has a quota of five analyses per hour. The deployed configuration can change the chat quota, so read the response headers. A job-analysis rejection reports its hourly policy and retry delay. Quotas use an in-memory rolling window on the backend and reset when it restarts.

## Browser tools with WebMCP

On the [portfolio page](https://www.dataacrobat.com/), browsers supporting WebMCP discover the read-only `ask_jay` tool. It accepts `{"question":"What has Jay built?"}`, opens the visible Ask Jay conversation, and returns `{"answer":"…","citations":["…"]}` after the response completes. Follow-up calls use the conversation shown in the drawer.

The tool uses `document.modelContext.registerTool`, with a fallback to `navigator.modelContext` for earlier implementations. The [WebMCP module](https://www.dataacrobat.com/webmcp.mjs) provides a stable public URL for the actual tool implementation and is preloaded at application startup. It supports cancellation, rejects concurrent requests, and returns an `error` object on failure; rate-limit errors also include `retryAfter` in seconds when available. Registration is removed when the page unmounts. WebMCP browser support is experimental; ordinary chat, the HTTP API, and the MCP server remain available.

## MCP server

Connect a Streamable HTTP MCP client to:

```text
https://www.dataacrobat.com/mcp
```

Initialize the connection and call `tools/list` to discover the live tool schemas. All tools are read-only:

- **`search_portfolio`** — Semantic search across Jay Ozer's work, experience, projects, patents, open source and approach.
- **`get_patents`** — Patents with status and application numbers.
- **`get_experience`** — Roles, dates, employers and what was delivered.
- **`get_projects`** — Shipped projects and open-source work.
- **`get_contact`** — How to reach Jay and where to find his profiles.

For example, call `search_portfolio` with `{"query":"document intelligence","limit":5}`. Use `get_contact`, `get_patents`, `get_experience`, or `get_projects` with an empty arguments object. The server also exposes `portfolio://about`, `portfolio://operating-system`, `portfolio://projects`, and `portfolio://resume` resources.

### MCP errors and recovery

An unknown tool name returns a JSON-RPC error with code `-32602` and a message; its `data` includes `code: "unknown_tool"`, a resolution hint, and the available tool names. Call `tools/list` to recover.

Invalid arguments for a known tool return `isError: true`, with `error.code`, `error.message`, `error.resolution`, and the invalid field paths in both JSON text content and `structuredContent`. Search queries must be nonblank and at most 10,000 characters; `limit` must be an integer from 1 to 10. Argument-free tools require an empty object. Unknown fields are rejected.

Retrieval failures also return `isError: true`, with code `retrieval_unavailable` and `retryable: true`. Retry later or use [the full portfolio Markdown](https://www.dataacrobat.com/index.md). Internal exception details are not exposed.

## Markdown and discovery

The portfolio, this developer page, and the privacy page each have a Markdown representation. Request it through the Accept header or the `.md` URL:

```sh
curl -H 'Accept: text/markdown' 'https://www.dataacrobat.com/developers'
curl 'https://www.dataacrobat.com/developers.md'
```

- [Agentic Resource Discovery catalog](https://www.dataacrobat.com/.well-known/ard.json)
- [API catalog](https://www.dataacrobat.com/.well-known/api-catalog)
- [Agent Skills index](https://www.dataacrobat.com/.well-known/agent-skills/index.json)
- [Sitemap](https://www.dataacrobat.com/sitemap.xml)
- [Crawl policy](https://www.dataacrobat.com/robots.txt)

This is a personal portfolio with public reading and question-answering interfaces. It has no accounts, payments, or booking flow. For professional enquiries, [contact Jay](https://www.dataacrobat.com/#contact). Read the [privacy policy](https://www.dataacrobat.com/privacy) before sending information through Ask Jay.
