Docs
Api

/v1/tools REST Gateway

Reference documentation for the /v1/tools REST gateway: authenticating with a personal API key, discovering the public tool catalog, invoking tools, handling idempotency, and interpreting billing and error responses.

The /v1/tools endpoint is a catalog-derived REST gateway that lets you invoke every tool in the public MarkDocket tool catalog using a personal API key. It is the same surface used by the @markdocket/cli package in password-mode and by any script that needs direct programmatic access without a full OAuth 2.1 / MCP client.

One catalog, four surfaces. The same tool registry that powers the CLI, the stdio MCP server, and the hosted /mcp endpoint also backs /v1/tools. A tool added to the catalog automatically becomes available here — no extra integration needed.

Authentication

All requests to /v1/tools must carry a personal API key in the Authorization header:

Authorization: Bearer mdk_live_...

Personal API keys are generated from your account settings in the MarkDocket dashboard. API keys authenticate as your user identity and are subject to the same plan limits and billing rules as interactive sessions.

OAuth access tokens cannot be used here. OAuth 2.1 access tokens issued during the browser-login flow are resource-bound to the hosted /mcp endpoint and are rejected on /v1/tools. If you authenticated with markdocket login (browser flow), use the hosted MCP surface instead; /v1/tools is for personal API key sessions only.

HMAC catalog provenance

Requests are verified to have originated through the cataloged gateway before any /api/ route is reached. This happens transparently — you do not compute the proof yourself; it is added by the gateway before dispatching your call. The practical effect is that only tools explicitly listed in the catalog can be invoked; no undocumented /api/ routes are reachable through this surface.

Discovering the tool catalog

Fetch the full list of available tools with a GET request:

GET /v1/tools
Authorization: Bearer mdk_live_...

Each entry in the response includes the tool name, a human-readable description, its JSON Schema for inputs, the tool category, and whether the operation is read-only or write-mutating.

{
  "tools": [
    {
      "name": "trademark_search",
      "description": "Search USPTO trademark records by keyword, owner, or serial number.",
      "category": "trademarks",
      "readOnly": true,
      "inputSchema": {
        "type": "object",
        "properties": {
          "query": { "type": "string" }
        },
        "required": ["query"]
      }
    }
  ]
}

Agent-only tools are excluded. Tools such as web_search and classify_goods that depend on a live LLM agent session are not available through /v1/tools or the CLI. The catalog you see here is the full public surface.

Invoking a tool

Call any cataloged tool with a POST request:

POST /v1/tools/{tool_name}
Authorization: Bearer mdk_live_...
Content-Type: application/json
Idempotency-Key: <your-unique-key>

{
  "query": "ACME coffee"
}

Replace {tool_name} with the exact name returned by the catalog listing.

The request body must conform to the inputSchema for the tool. Requests that fail schema validation return 400 Bad Request with a description of the validation error.

Response format

A successful invocation returns 200 OK with the tool's structured result:

{
  "result": { ... }
}

The shape of result varies by tool and matches the tool's documented output schema.

Idempotency

All write-mutating tool calls (tools where readOnly is false) require an Idempotency-Key header. Read-only tools accept the header but do not require it.

Idempotency-Key: 01HZEX7Q3BVN4YK1A2C5D6E7F8

Choose any string that is unique per logical operation — a UUID or ULID works well. The server persists your key and deduplicates replays: if you retry a request with the same key, you receive the original response without the operation being performed again.

Omitting Idempotency-Key on a write-mutating call returns 400 Bad Request. Generate a fresh key for each distinct operation and reuse it only on retries of that same operation.

Billing and plan limits

Tool invocations are metered against your plan. When a call would exceed your available balance or requires a plan upgrade, the API returns 402 Payment Required with a structured billing block envelope:

{
  "reason": "insufficient_credits",
  "requiredAction": "add_credits",
  "contractVersion": "2026-07-12-payg-v1",
  "feature": "trademark_search"
}

The reason field describes why the call was blocked. The requiredAction field tells you what the user must do to unblock it. Possible actions include adding credits, upgrading the plan, or enabling a specific feature. These two fields always correspond — a given reason maps to exactly one requiredAction.

Handle 402 responses by surfacing the requiredAction to your user and directing them to the billing section of the dashboard.

Error reference

StatusMeaning
400 Bad RequestInvalid input — schema validation failure or missing Idempotency-Key on a write call
401 UnauthorizedMissing or invalid API key
402 Payment RequiredBilling block — see billing block envelope above
403 ForbiddenValid key but insufficient scope or feature access
404 Not FoundTool name not found in the current catalog
409 ConflictOptimistic concurrency failure (relevant for update operations)
429 Too Many RequestsRate limit exceeded — back off and retry
500 Internal Server ErrorUnexpected server error — safe to retry with the same Idempotency-Key

Rate limits and retries

When you receive 429 Too Many Requests, wait before retrying. The response includes a Retry-After header indicating the minimum number of seconds to wait. For 5xx errors, retrying with the original Idempotency-Key is safe and will not duplicate the operation.

Comparison with other surfaces

SurfaceAuth methodTransportAgent-only tools
/v1/toolsPersonal API keyREST (JSON)Excluded
Hosted /mcpOAuth 2.1 access tokenMCP Streamable HTTPExcluded
@markdocket/cli (password mode)Personal API keyREST via /v1/toolsExcluded
@markdocket/cli (browser login)OAuth 2.1MCP via hosted /mcpExcluded
Agent chatSession cookie or delegated tokenSSE streamingIncluded

Quick start example

The following example searches for USPTO trademark records using curl:

curl -X POST https://api.markdocket.com/v1/tools/trademark_search \
  -H "Authorization: Bearer mdk_live_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"query": "ACME coffee"}'

To list all available tools first:

curl https://api.markdocket.com/v1/tools \
  -H "Authorization: Bearer mdk_live_YOUR_KEY_HERE"

TypeScript example

const key = process.env.MARKDOCKET_API_KEY;

// Discover the catalog
const catalogRes = await fetch('https://api.markdocket.com/v1/tools', {
  headers: { Authorization: `Bearer ${key}` },
});
const { tools } = await catalogRes.json();

// Invoke a tool
const res = await fetch('https://api.markdocket.com/v1/tools/trademark_search', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${key}`,
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({ query: 'ACME coffee' }),
});

if (res.status === 402) {
  const block = await res.json();
  // Direct user to billing: block.requiredAction
  throw new Error(`Billing required: ${block.requiredAction}`);
}

if (!res.ok) {
  throw new Error(`Tool call failed: ${res.status}`);
}

const { result } = await res.json();
console.log(result);

On this page