# mdapi.io - Minimal Data API I/O: a content transformation layer primitive for AI systems.


Transforms documents, images, and webpages into AI-ready Markdown and structured data, optimized for LLM efficiency and token usage.

## Agent entrypoint

- **Start AI discovery** → https://mdapi.io/.well-known/ai-discovery.json
- **Use skill** → https://mdapi.io/.well-known/skill.md

## Quick Start

Choose your entry point based on your role:

| Role                                                  | Protocol                     | Endpoint                  | When to use                                                                                                  |
| ----------------------------------------------------- | ---------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------ |
| IDE / coding agent (JetBrains, Cursor, VS Code, etc.) | ACP (Agent Client Protocol)  | POST /acp                 | You are an IDE plugin or coding agent. Use tools/call with convert tool.                                     |
| AI agent (Claude Code, Codex, OpenClaw, Hermes, etc.) | A2A (Agent-to-Agent)         | POST /a2a                 | You are an autonomous agent. Use message/send with data in text parts. Supports streaming and task tracking. |
| AI agent (any framework)                              | MCP (Model Context Protocol) | GET  /mcp + POST /mcp     | You need tool discovery. Use tools/call with convert tool.                                                   |
| OpenAI-compatible client                              | OpenAI API                   | POST /v1/chat/completions | You already use OpenAI SDK. Pass URL/file in messages. Supports streaming.                                   |
| Direct HTTP / curl / script                           | REST API                     | GET  / or POST /          | Simplest path. GET returns Markdown directly. POST returns JSON with metadata.                               |

### Universal discovery

All protocols and capabilities are described in one file:
GET /.well-known/ai-discovery.json

## Features

- Stateless, in-memory processing
- Edge execution with automatic scaling
- Prompt-driven transformation
- AI‑optimized output for LLMs
- Pay-per-use via x402 v1/v2 or manual payment

~~~meta
version: 1.0.0
base_url: https://mdapi.io
auth: bearer
auth_header: Authorization
content_type: application/json
errors: standard
~~~

## Method Semantics

| Method | Response Format | Notes                                     |
| ------ | --------------- | ----------------------------------------- |
| GET    | Markdown        | Always returns Markdown, including errors |
| POST   | JSON            | Always returns JSON, including errors     |
| Error  | Same as method  | GET errors = Markdown, POST errors = JSON |

## Response Format Rules

- **GET /** - Returns raw Markdown. Use for direct content.
- **GET /** with errors - Returns Markdown with error description.
- **POST /** - Returns JSON with success, markdown, prompt_result, metadata.
- **result=both** on GET - Returns markdown followed by `## Prompt Result` section.
- **result=both** on POST - Returns JSON with both markdown and prompt_result fields.

## Streaming

Enable streaming with `stream: true` (boolean) parameter. Uses SSE in the
OpenAI-compatible `chat.completion.chunk` format:

1. First message (token info):
```
data: {"type":"token_info","status":"valid","balance":99,"expires":2027-01-01}
```

2. Content chunks (OpenAI `choices`/`delta` shape):
```
data: {"choices":[{"index":0,"delta":{"content":" markdown chunk "},"finish_reason":null}]}
```

3. Final chunk (stop):
```
data: {"choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
```

4. End marker:
```
data: [DONE]
```

Error during streaming ends with error chunk in format: {"error":"message","code":400}

### Native streaming per protocol

Every protocol delivers a *real* content stream when `stream: true`, but each
emits it in its own native frame format:

| Protocol | Streaming frame format                                                                                                            |
| -------- | --------------------------------------------------------------------------------------------------------------------------------- |
| REST     | OpenAI-compatible `choices/delta` frames                                                                                          |
| OpenAI   | `chat.completion.chunk` (`choices/delta`)                                                                                         |
| MCP      | `notifications/message` content chunks, then one final `tools/call` result frame                                                  |
| ACP      | incremental JSON-RPC `result.content` text chunks, then a final full `result` frame                                               |
| A2A      | incremental `task.artifacts[].parts[].text` chunks (`TASK_STATE_WORKING`), then a final completed `task` (`TASK_STATE_COMPLETED`) |

## Global Types

```typescript
interface ConversionRequest {
  input: string;                                    // Unified input: URL, text, or data URI (auto-detected)
  prompt?: string;
  result?: "markdown" | "prompt" | "both";
  token?: string;
  memo?: string;
}

interface ConversionResponse {
  success: boolean;
  markdown?: string;
  prompt_result?: string;
  resource?: string;
  mimetype?: string;
  token_status?: "free" | "valid" | "invalid" | "expired" | "exhausted" | "expired_pending" | "activated" | "verification_error" | "invalid_payment" | "error" | "pending";
  token_balance?: number;
  token_expires?: string;
}

interface ErrorResponse {
  error: string;
  code?: string;
  message?: string;
}
```

## Capability: Convert

~~~meta
id: convert
transport: HTTP GET / or HTTP POST /
auth: optional
~~~

### Intention

Converts input content to clean Markdown format. Accepts a URL, raw text, or a file (via data URI) through the unified `input` parameter. The type is auto-detected: starts with `http://` or `https://` → URL; starts with `data:` → file; otherwise → text.

Free tier available (10 requests per day (no token required), within the service’s overall free quota). Paid tier at min $0.01 per conversion (USDC on Solana) via x402 protocol.

### Auth Intention

No authentication required for free tier. For paid tier, use Bearer token in Authorization header or ?token query parameter.
A token must be activated first with memo before use-see token activation flow.

### Logic Constraints

- The `input` parameter is required
- Maximum file size: 50 MB
- Maximum URL content: 50 MB
- Rate limit: 10,000 requests per hour
- Free tier: 10 requests per day (no token required), within the service’s overall free quota
- Paid tier: min $0.01 per conversion (USDC on Solana)
- Token validity: 1 year from activation
- **URL length:** GET requests with long `input` or `prompt` may exceed browser URL limits (~2048 chars). Use POST for large payloads.

### Input

```typescript
interface ConvertRequest {
  input: string;                                  // Unified input: URL, text, or data URI (auto-detected)
  prompt?: string;                               // Custom LLM instructions
  result?: "markdown" | "prompt" | "both";       // Response format
  token?: string;                                // Access token for paid tier
  memo?: string;                                 // Token activation memo
}
```

### Output

```typescript
interface ConvertResponse {
  success: boolean;
  markdown: string;                              // Converted Markdown content
  prompt_result?: string;                        // LLM result (when prompt + result=prompt/both)
  resource: string;                              // Original resource identifier
  mimetype: string;                              // Source MIME type
  token_status?: "free" | "valid" | "invalid" | "expired" | "exhausted" | "expired_pending" | "activated" | "verification_error" | "invalid_payment" | "error" | "pending";
  token_balance?: number;                        // Remaining balance (paid tier)
  token_expires?: string;                        // Token expiry timestamp
}
```

 ### Errors

- 400 Bad Request: Missing required parameter (input)
- 401 Unauthorized: Invalid or expired token
- 402 Payment Required: Token requires activation or payment
- 413 Payload Too Large: File exceeds 50 MB limit
- 429 Rate Limited: Exceeded rate limit
- 500 Server Error: Internal processing error

### Examples

```bash
# URL conversion (GET)
curl "https://mdapi.io/?input=https://example.com/doc.pdf"

# URL with prompt (GET)
curl "https://mdapi.io/?input=https://example.com/doc.pdf&prompt=Summarize&result=both"

# Text conversion (GET)
curl "https://mdapi.io/?input=Hello+World&prompt=Summarize+this&result=prompt"

# File upload via data URI (POST)
curl -X POST -H "Content-Type: application/json" -d '{"input":"data:application/pdf;base64,JVBERi0xLjQK..."}' "https://mdapi.io/"

# URL conversion (POST)
curl -X POST -H "Content-Type: application/json" -d '{"input":"https://example.com/doc.pdf"}' "https://mdapi.io/"

# Token activation (POST)
curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer TOKEN" -H "X-Memo-Required: MEMO" -d '{"input":"https://example.com/doc.pdf"}' "https://mdapi.io/"
```

## Capability: OpenAI Compatible

~~~meta
id: openai.chat
transport: HTTP POST /v1/chat/completions
auth: optional
~~~

### Intention

OpenAI-compatible API for streaming chat completion with document conversion.
Supports URL extraction, image_url, file uploads, and streaming SSE responses.

### Input

```typescript
interface ChatCompletionRequest {
  model: string;                                 // Model identifier (any string)
  messages: Array<{
    role: "user" | "system" | "assistant";
    content: string | Array<ContentPart>;
  }>;
  stream?: boolean;                              // Enable SSE streaming
}

type ContentPart =
  | { type: "text"; text: string }
  | { type: "image_url"; image_url: { url: string } }
  | { type: "file"; file: { data: string; filename: string } };
```

### Output

```typescript
interface ChatCompletionResponse {
  id: string;
  object: "chat.completion";
  created: number;
  model: string;
  choices: Array<{
    index: number;
    message: { role: string; content: string };
    finish_reason: "stop" | "length";
  }>;
  usage?: {
    prompt_tokens: number;
    completion_tokens: number;
    total_tokens: number;
  };
}
```

### Example

```python
from openai import OpenAI

client = OpenAI(
    base_url="https://mdapi.io/v1",
    api_key="YOUR_TOKEN"
)

response = client.chat.completions.create(
    model="markdown-v1",
    messages=[{"role": "user", "content": "Convert https://example.com/doc.pdf"}]
)
print(response.choices[0].message.content)
```

## Capability: MCP Server

~~~meta
id: mcp.manifest
transport: HTTP GET /mcp
auth: none
~~~

### Intention

Returns MCP (Model Context Protocol) server manifest with available tools for AI Agents.
The convert tool provides unified document conversion through MCP protocol.

### Output

```typescript
interface McpManifest {
  $schema: string;
  version: string;
  protocolVersion: string;
  serverInfo: {
    name: string;
    description: string;
    version: string;
  };
  description: string;
  transport: {
    type: string;
    endpoint: string;
  },
  capabilities: {
    tools: { listChanged: boolean };
    resources: { subscribe: boolean; listChanged: boolean };
  };
  tools: Array<{
    name: string;
    description: string;
    inputSchema: object;
  }>;
  resources: Array<{
    uri: string;
    name: string;
    mimeType: string;
    description: string;
  }>;
  instructions: string;
  tokenActivation: {
    description: string;
    steps: Array<string>;
  };
  examples: {
    activate: string;
    useActivated: string;
  };
}
```

### Example

```json
{
  "mcpServers": {
    "mdapi": {
      "url": "https://mdapi.io/mcp"
    }
  }
}
```

## Capability: MCP Tool Call

~~~meta
id: mcp.tools.call
transport: HTTP POST /mcp
auth: optional
~~~

### Intention

Execute MCP tool calls via JSON-RPC. The convert tool handles all conversion operations.

### Required Headers

| Header               | Required | Value                            |
| -------------------- | -------- | -------------------------------- |
| MCP-Protocol-Version | Yes      | 2026-07-28                       |
| Mcp-Method           | Yes      | Method name (e.g., tools/call)   |
| Mcp-Name             | Yes      | Client/server name (e.g., mdapi) |
| Content-Type         | Yes      | application/json                 |

### Input

```typescript
interface McpToolCallRequest {
  jsonrpc: "2.0";
  id: string | number;
  method: "tools/call";
  params: {
    name: "convert";
    arguments: {
      input: string;                              // Unified input: URL, text, or data URI (auto-detected)
      prompt?: string;
      result?: "markdown" | "prompt" | "both";
      token?: string;
      memo?: string;
    };
  };
}
```

### Output

```typescript
interface McpToolCallResponse {
  jsonrpc: "2.0";
  id: string | number;
  result: {
    content: Array<{
      type: "text";
      text: string;                              // JSON string with ConversionResponse
    }>;
    isError: boolean;
  };
}
```

## Capability: Health Check

~~~meta
id: system.health
transport: HTTP GET /health
auth: none
~~~

### Intention

Public health check endpoint for monitoring. Returns service status, version, available endpoints, and limits.

### Output

```typescript
interface HealthResponse {
  status: string;
  service: string;
  description: string;
  version: string;
  endpoints: {
    about: string;
    conversion: string;
    openai: string;
    mcp: string;
    acp: string;
    a2a: string;
    health: string;
    llms: string;
    llms_full: string;
    ai_discovery: string;
    agent_discovery: string;
    a2a_manifest: string;
    acp_manifest: string;
    x402_manifest: string;
    openapi_json: string;
    openapi_yaml: string;
    mapi: string;
    skill: string;
  };
  examples: {
    convert_url: string;
    convert_with_prompt: string;
    convert_data: string;
    openai_sdk: string;
    mcp_rpc: string;
    acp_rpc: string;
    a2a_rpc: string;
  };
  limits: {
    max_file_size: string;
    max_url_content: string;
    rate_limit: string;
    free_tier: string;
    paid_tier: string;
    token_validity: string;
  };
}
```

### Example

```bash
curl "https://mdapi.io/health"
```

Response:

```json
{
  "status": "ok",
  "service": "mdapi.io",
  "description": "...",
  "version": "1.0.0",
  "endpoints": { ... },
  "examples": { ... },
  "limits": { ... }
}
```

## Capability: AI Discovery

~~~meta
id: agent.discovery
transport: HTTP GET /.well-known/ai-discovery.json
auth: none
~~~

### Intention

Unified AI Agent discovery endpoint combining MCP, ACP, A2A, and other protocol manifests.
Agents use this to discover available capabilities and protocols.

### Output

```typescript
interface AiDiscoveryResponse {
  version: string;
  provider: {
    name: string;
    description: string;
    url: string;
  };
  routing: {
    ide_agents: { protocol: string; endpoint: string; reason: string };
    ai_agents: { protocol: string; endpoint: string; reason: string; alternative: { protocol: string; endpoint: string; reason: string } };
    openai_clients: { protocol: string; endpoint: string; reason: string };
    direct_http: { protocol: string; endpoint: string; reason: string };
  };
  protocols: {
    mcp: { name: string; version: string; description: string; manifest: string; endpoints: Record<string, string>; capabilities: string[] };
    acp: { name: string; version: string; description: string; manifest: string; endpoints: Record<string, string>; capabilities: string[] };
    a2a: { name: string; version: string; description: string; manifest: string; endpoints: Record<string, string>; capabilities: string[] };
    x402: { name: string; version: string; description: string; manifest: string; endpoints: Record<string, string>; payment: { scheme: string; network: string; price: string } };
    openapi: { name: string; version: string; description: string; endpoints: Record<string, string> };
    openai: { name: string; description: string; endpoints: Record<string, string>; capabilities: string[] };
  };
  capabilities: Record<string, unknown>;
  endpoints: Record<string, { method: string; url: string; description: string }>;
  links: Record<string, string>;
}
```

## Capability: x402 Payment

~~~meta
id: payment.x402
transport: HTTP GET /.well-known/x402.json
auth: none
~~~

### Intention

x402 v2 payment manifest for autonomous agents. Describes payment requirements,
pricing, and token activation flow.

### Logic Constraints

- Free tier: 10 requests per day (no token required), within the service’s overall free quota
- Paid tier: min $0.01 per conversion (USDC on Solana)
- Tokens must be activated before use (token + memo)
- Activated tokens valid for 1 year

### Output

```typescript
interface X402Manifest {
  version: "2.0";
  protocol: "x402";
  provider: { name: string; url: string };
  payment: {
    scheme: string;
    network: string;
    recipient: string;
    token: string;
    minAmount: number;
  };
  pricing: {
    free: { limit: number; window: string };
    paid: { price: number; currency: string };
  };
  activation: {
    required: boolean;
    flow: string[];
  };
}
```

## Lifecycle: Token

~~~states
free: Free tier available (10 requests per day (no token required), within the service’s overall free quota)
valid: Token active with balance
invalid: Token not found or invalid
expired: Token validity period expired
exhausted: Token balance depleted
expired_pending: Activation memo expired
activated: Token just activated
verification_error: Payment verification failed
invalid_payment: No valid payment found
error: Internal error occurred
~~~

### Paid Token Flow

1. Request without token → 402 with NEW token+memo
2. Pay USDC to wallet with memo from 402
3. Retry with token+memo from 402 → activated
4. Use token until exhausted/expired → request new 402

### Token States

| State              | Description                                                 | Terminal |
| ------------------ | ----------------------------------------------------------- | -------- |
| free               | Free tier (10/day), within the service’s overall free quota | No       |
| valid              | Token active                                                | No       |
| invalid            | No token provided                                           | No       |
| expired            | Validity expired                                            | Yes      |
| exhausted          | Balance depleted                                            | Yes      |
| expired_pending    | Memo expired before activation                              | Yes      |
| activated          | Just activated                                              | No       |
| verification_error | Payment not found                                           | No       |
| invalid_payment    | No valid payment found                                      | No       |
| error              | Internal error                                              | No       |
| pending            | Payment pending verification                                | No       |

### Rules

- **NEW token on 402**: Each 402 gives NEW token+memo
- **Exact match**: Use token+memo from 402 exactly
- **No pre-pay**: Can't pay before 402
- **No top-up**: Can't add to valid token
- Make payment to wallet with memo from 402
- Retry request with token+memo from 402 → activation

**Not supported:**
- Pre-payment before receiving 402 (memo without token is ignored)
- Top-up while token is still valid (new memo is ignored)

### State Descriptions

| State              | Terminal | Description                                                                      |
| ------------------ | -------- | -------------------------------------------------------------------------------- |
| free               | no       | 10 requests per day (no token required), within the service’s overall free quota |
| valid              | no       | Paid token activated and usable                                                  |
| invalid            | no       | Token not found or not provided                                                  |
| expired            | yes      | Token validity period ended                                                      |
| exhausted          | yes      | Token balance depleted                                                           |
| expired_pending    | yes      | Memo expired before activation                                                   |
| activated          | no       | Token just activated (in progress)                                               |
| verification_error | no       | Payment verification failed                                                      |
| invalid_payment    | no       | No valid payment found                                                           |
| error              | no       | Internal error during processing                                                 |
| pending            | no       | Payment required (token not yet activated)                                       |

## Rate Limiting

The service enforces rate limits to ensure fair usage.

### Rate Limit Headers

All responses include rate limit information:

| Header                | Description                          |
| --------------------- | ------------------------------------ |
| X-RateLimit-Remaining | Requests remaining in current window |
| X-RateLimit-Reset     | Unix timestamp when the limit resets |

### Limits

| Tier | Limit                                                                            |
| ---- | -------------------------------------------------------------------------------- |
| Free | 10 requests per day (no token required), within the service’s overall free quota |
| Paid | 10,000 requests per hour                                                         |

When exceeded, returns HTTP 429.

## Payment Modes

mdapi.io supports two distinct payment flows:

### Manual Payment (Human-initiated)

Uses X-* headers for payment requirements:
- X-Token-Required - Token needed for paid tier
- X-Memo-Required - Memo for token activation
- X-Wallet-Address - Solana wallet for payment
- X-QR-Payment - QR code payload for easy payment

Flow:
1. Request conversion - receive 402 with X-* headers
2. User sends USDC to wallet with memo
3. Retry with token + memo to activate
4. After activation, use token only

### Autonomous Payment (Agent-initiated)

Uses PAYMENT-* headers for automated payment:
- PAYMENT-REQUIRED - Payment requirements from service
- PAYMENT-SIGNATURE - Signed payment payload from client
- PAYMENT-RESPONSE - Payment confirmation from service

Flow:
1. Request conversion - receive 402 with PAYMENT-REQUIRED
2. Agent prepares and signs payment
3. Retry with PAYMENT-SIGNATURE header
4. Service verifies and returns PAYMENT-RESPONSE

Do NOT mix these flows. Use X-* for manual, PAYMENT-* for autonomous.

## Capability: ACP

~~~meta
id: acp.rpc
transport: HTTP POST /acp
auth: optional
~~~

### Intention

Agent Client Protocol (ACP) endpoint for IDE agents (JetBrains, Cursor, VS Code, etc.). Enables tool calls and resource access.

### Supported Methods

- `initialize` - initialize ACP session
- `tools/list` - list available tools (includes `convert`)
- `tools/call` - invoke `convert` tool (single request only; batch not supported)
- `resources/list` - list available resources
- `resources/read` - read resource content

### Example

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "convert",
    "arguments": {
      "input": "https://example.com/doc.pdf",
      "prompt": "Summarize",
      "result": "both"
    }
  }
}
```

## Capability: A2A (Agent-to-Agent)

~~~meta
id: a2a.rpc
transport: HTTP POST /a2a
auth: optional
~~~

### Intention

Agent-to-Agent Protocol endpoint for autonomous AI agents (Claude Code, Codex, OpenClaw, Hermes, etc.). Enables task delegation, streaming, and inter-agent communication.

### Supported Methods

- `message/send` - send a message to initiate conversion
- `message/stream` - send message with SSE streaming updates
- `tasks/get` - get task status and results by ID
- `tasks/list` - list tasks with optional filtering
- `tasks/cancel` - cancel an in-progress task
- `tasks/resubscribe` - subscribe to task updates via SSE

### Example

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "message/send",
  "params": {
    "message": {
      "messageId": "msg_1",
      "parts": [{ "text": "Convert https://example.com/doc.pdf" }]
    }
  }
}
```

### Task States

| State       | Description                   |
| ----------- | ----------------------------- |
| `working`   | Task is being processed       |
| `completed` | Task finished successfully    |
| `failed`    | Task failed during processing |
| `canceled`  | Task was canceled by client   |
| `rejected`  | Task was rejected by server   |

## Standard Errors

When errors: standard is set, these error codes apply:

| Status | Type              | Description                       |
| ------ | ----------------- | --------------------------------- |
| 400    | Bad Request       | Missing required parameters       |
| 401    | Unauthorized      | Invalid or expired token          |
| 402    | Payment Required  | Token needs activation or payment |
| 404    | Not Found         | Resource not found                |
| 413    | Payload Too Large | File exceeds 50 MB limit          |
| 429    | Rate Limited      | Exceeded rate limit               |
| 500    | Server Error      | Internal error                    |

## Links

- **About service:** https://mdapi.io/about
- **API docs:** https://mdapi.io
- **MCP server manifest:** https://mdapi.io/mcp
- **Health check:** https://mdapi.io/health
- **API documentation:** https://mdapi.io/llms.txt
- **Full API documentation:** https://mdapi.io/llms-full.txt
- **AI discovery:** https://mdapi.io/.well-known/ai-discovery.json                                        or https://mdapi.io/ai-discovery.json
- **AI Agent discovery:** https://mdapi.io/.well-known/agent.json                                         or https://mdapi.io/agent.json
- **A2A Agent card:** https://mdapi.io/.well-known/agent-card.json                                        or https://mdapi.io/agent-card.json
- **ACP manifest:** https://mdapi.io/.well-known/acp.json                                                 or https://mdapi.io/acp.json
- **x402 payment manifest:** https://mdapi.io/.well-known/x402.json                                       or https://mdapi.io/x402.json
- **OpenAPI specification (JSON):** https://mdapi.io/.well-known/openapi.json                             or https://mdapi.io/openapi.json
- **OpenAPI specification (YAML):** https://mdapi.io/.well-known/openapi.yaml                             or https://mdapi.io/openapi.yaml
- **MAPI specification (case-insensitive path MAPI.md support):** https://mdapi.io/.well-known/mapi.md    or https://mdapi.io/mapi.md
- **Skill specification (case-insensitive path SKILL.md support):** https://mdapi.io/.well-known/skill.md or https://mdapi.io/skill.md

## External Links

- **github.com** https://github.com/mdapiio/mdapi.io
- **skills.sh** https://www.skills.sh/mdapiio/mdapi.io
- **clawhub.ai** https://clawhub.ai/mdapiio
- **x.com** https://x.com/mdapiio

## Disclaimer

**The service is provided "AS IS".**


> mdapi.io is an edge-native service-transport primitive for AI, autonomous-agents, and the Web4 ecosystem.
