Docs
app.ezel.ai

Ezel API Reference

Build on Ezel: list matters, ask cited questions over your own documents, search U.S. case law, upload files, and run review tables, all over a simple REST API. The same account also works as a remote MCP server for Claude, Cursor, and Claude Code.

The API is organised around your matters (cases or projects) and the documents inside them. Every response is JSON. All requests are made over HTTPS to:

Base URL

https://app.ezel.ai/api/v1

Anything you can do in the Ezel app, you can do with the API, gated by the same plan and permissions on your account. Two things you get beyond a typical assistant API: real, citable U.S. case law (not a model's memory), and keyword or regex search across your own uploaded documents.

The API and the MCP connector share one credential. Create a key in Settings → Connector and use it for both.
Building with an LLM or agent? Fetch /llms.txt for a concise index, or /llms-full.txt for this whole reference as one plain-markdown file.
Quick start
# Verify your key
curl https://app.ezel.ai/api/v1/whoami \
  -H "Authorization: Bearer ezel_live_..."

# Ask a question, grounded in a matter
curl https://app.ezel.ai/api/v1/matters/MATTER_ID/ask \
  -H "Authorization: Bearer ezel_live_..." \
  -H "Content-Type: application/json" \
  -d '{"question":"What is the effective date?"}'

Authentication

Authenticate every request with a personal API key, sent as a bearer token. Keys look like ezel_live_... and are created (and revoked) in Settings → Connector. The full key is shown once at creation; Ezel only ever stores a hash, so keep it somewhere safe.

Send it in the Authorization header. For tools that cannot set that header, X-API-Key is also accepted.

Treat keys like passwords. They carry your account's access; never commit them to source control or expose them in a browser.
Authorization header
curl https://app.ezel.ai/api/v1/whoami \
  -H "Authorization: Bearer ezel_live_..."
200
{
  "user_id": "5d6bbf5e-...",
  "full_name": "Alex Morgan",
  "plan": "Pro",
  "scopes": ["read", "write"],
  "enabled_features": ["matters", "chat", "caselaw"]
}

Scopes

Each key carries one or more scopes, so you can hand out keys that can read but not change anything.

read Every query endpoint: whoami, usage, listing and reading matters and files, ask, search, case law, and reading review tables.
write Mutations: create or delete a matter, upload or delete a file, and create or run a review table.

A request that needs a scope the key lacks returns 403 with type insufficient_scope. The MCP connector only ever uses read.

403
{
  "error": {
    "type": "insufficient_scope",
    "message": "This key lacks the 'write' scope required for this endpoint."
  }
}

Rate limits

Requests are limited per key, per category, over a rolling 60 second window. Every response carries RateLimit-* headers; exceeding a limit returns 429 with a Retry-After header.

Ask 20 / min The completion endpoints (/ask).
Search 60 / min Document search and case law.
Write 60 / min Creating, uploading, deleting, running tables.
Default 150 / min Everything else (reads).

Token usage limits from your plan are enforced separately, on top of these.

Response headers
RateLimit-Limit: 20
RateLimit-Remaining: 18
RateLimit-Reset: 47

Errors

Errors use standard HTTP status codes and a consistent JSON body with a stable type and a human message.

400 Invalid request (bad_request).
401 Missing or invalid key (unauthorized).
403 Missing scope or feature not enabled.
404 Resource not found.
429 Rate limit or usage limit exceeded.
Error body
{
  "error": {
    "type": "not_found",
    "message": "Matter not found."
  }
}
GET/api/v1/whoami

Whoami

Returns the account behind the key: name, plan, the key's scopes, and which features are enabled. The quickest way to verify a key works.

Scopes

read

cURL
curl https://app.ezel.ai/api/v1/whoami \
  -H "Authorization: Bearer ezel_live_..."
GET/api/v1/usage

Usage

Returns the account's storage usage and current message-limit status.

Scopes

read

200
{
  "plan": "Pro",
  "storage": { "used_bytes": 13250667, "limit_bytes": 53687091200, "used_percent": 0.02 },
  "messages": { "within_limits": true, "status": "...", "reset_at": null }
}
GET/api/v1/matters

List matters

Lists the caller's matters, pinned first, each with a document count.

Scopes

read

cURL
curl https://app.ezel.ai/api/v1/matters \
  -H "Authorization: Bearer ezel_live_..."
200
{
  "matters": [{
    "id": "2a951d7c-...",
    "name": "Acme acquisition",
    "client_name": "Acme Corp",
    "document_count": 6
  }],
  "total": 1
}
POST/api/v1/matters

Create matter

Creates a new matter. The name must be unique within your account.

Scopes

write

Body

name string
required
The matter name.
client_name string Optional client label.
description string Optional description.
cURL
curl https://app.ezel.ai/api/v1/matters \
  -H "Authorization: Bearer ezel_live_..." \
  -H "Content-Type: application/json" \
  -d '{"name":"Acme acquisition","client_name":"Acme Corp"}'
201
{ "matter": { "id": "7d51cbad-...", "name": "Acme acquisition", "document_count": 0 } }
GET/api/v1/matters/{matter_id}

Get matter

Returns a single matter with its document count.

Scopes

read

Path parameters

matter_id uuid
required
The matter's id.
cURL
curl https://app.ezel.ai/api/v1/matters/MATTER_ID \
  -H "Authorization: Bearer ezel_live_..."
DEL/api/v1/matters/{matter_id}

Delete matter

Deletes a matter. Its files and chats are not deleted; they are released back to your drive (their matter_id is cleared).

Scopes

write

cURL
curl -X DELETE https://app.ezel.ai/api/v1/matters/MATTER_ID \
  -H "Authorization: Bearer ezel_live_..."
200
{ "deleted": true, "released_documents": 6, "released_chats": 2 }
GET/api/v1/matters/{matter_id}/files

List files

Lists the documents in a matter, newest first.

Scopes

read

cURL
curl https://app.ezel.ai/api/v1/matters/MATTER_ID/files \
  -H "Authorization: Bearer ezel_live_..."
POST/api/v1/matters/{matter_id}/files

Upload file

Uploads a document into a matter as multipart/form-data. The file is stored, its text indexed for search, and scanned PDFs are OCR'd in the background so they become searchable and answerable. Max 20 MB per file.

Scopes

write

Form fields

file binary
required
The document to upload.
cURL
curl https://app.ezel.ai/api/v1/matters/MATTER_ID/files \
  -H "Authorization: Bearer ezel_live_..." \
  -F "[email protected]"
201
{ "file": { "id": "b5733b49-...", "filename": "contract.pdf", "file_size": 109223 } }
DEL/api/v1/matters/{matter_id}/files/{file_id}

Delete file

Removes a file from a matter and deletes it from storage.

Scopes

write

cURL
curl -X DELETE https://app.ezel.ai/api/v1/matters/MATTER_ID/files/FILE_ID \
  -H "Authorization: Bearer ezel_live_..."
POST/api/v1/ask

Ask

Ask a general U.S. legal question. Ezel's research mode is on, so the agent can reach real case law on its own. The answer comes back with any citations and sources. Not scoped to your documents; for that, use Ask a matter.

Scopes

read

Body

question string
required
The question to answer.
stream boolean When true, the response is a text/event-stream of SSE frames instead of one JSON body.
Streaming emits {"type":"delta","text":...} frames as the answer is written, then a final {"type":"done", ...} with the full answer, citations, and sources, then [DONE].
cURL
curl https://app.ezel.ai/api/v1/ask \
  -H "Authorization: Bearer ezel_live_..." \
  -H "Content-Type: application/json" \
  -d '{"question":"What is the holding of Gideon v. Wainwright?"}'
200
{
  "answer": "Gideon v. Wainwright held that...",
  "citations": [],
  "sources": [],
  "conversation_id": "87a96194-..."
}
POST/api/v1/matters/{matter_id}/ask

Ask a matter

Ask a question answered from the documents in a specific matter. The answer is grounded only in that matter's files, with citations back to them. Supports the same stream flag as Ask.

Scopes

read

Body

question string
required
The question to answer.
stream boolean Stream the answer as SSE.
cURL (streamed)
curl -N https://app.ezel.ai/api/v1/matters/MATTER_ID/ask \
  -H "Authorization: Bearer ezel_live_..." \
  -H "Content-Type: application/json" \
  -d '{"question":"List the parties.","stream":true}'
SSE stream
data: {"type":"delta","text":"The "}
data: {"type":"delta","text":"parties "}
data: {"type":"done","answer":"...","citations":[]}
data: [DONE]
POST/api/v1/matters/{matter_id}/search

Search documents

Keyword or regex search across a matter's documents, returning page-level snippets. Useful when you want exact matches rather than an AI answer. Pass terms (literal) or regex.

Scopes

read

Body

terms string | string[] Literal terms. OR by default.
regex string A POSIX regular expression, case-insensitive.
match_all boolean Require all terms (AND) instead of any.
count_only boolean Return per-document match counts instead of snippets.
cURL
curl https://app.ezel.ai/api/v1/matters/MATTER_ID/search \
  -H "Authorization: Bearer ezel_live_..." \
  -H "Content-Type: application/json" \
  -d '{"terms":["indemnify","indemnification"],"match_all":false}'
200
{
  "results": [{
    "document_id": "3ff8...",
    "filename": "contract.pdf",
    "page": 4,
    "snippet": "...shall indemnify and hold harmless..."
  }]
}
GET/api/v1/cases/{cluster_id}

Get a case

Fetches the full opinion text and metadata for a case, using a cluster_id from case law search. Long opinions are truncated with truncated: true.

Scopes

read

cURL
curl https://app.ezel.ai/api/v1/cases/110254 \
  -H "Authorization: Bearer ezel_live_..."
200
{
  "case_name": "Rhode Island v. Innis",
  "court": "Supreme Court of the United States",
  "citation": "446 U.S. 291",
  "plain_text": "Mr. Justice Stewart delivered...",
  "truncated": false
}
POST/api/v1/matters/{matter_id}/review-tables

Create review table

Creates a review table: a grid of documents (rows) by questions (columns), where each cell is an AI-extracted answer with a cited quote and page. Define the columns and the documents in one call; the table starts filling immediately unless you pass run: false. Poll Get table for cell status.

Scopes

write

Body

columns object[]
required
Each { header, prompt, type }. The prompt is the question asked of every document.
all_documents boolean Add every document in the matter as a row.
document_ids uuid[] Or add specific documents.
name string Optional table name.
cURL
curl https://app.ezel.ai/api/v1/matters/MATTER_ID/review-tables \
  -H "Authorization: Bearer ezel_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Key terms",
    "columns": [
      {"header":"Parties","prompt":"Who are the parties?","type":"text"},
      {"header":"Date","prompt":"Effective date?","type":"date"}
    ],
    "all_documents": true
  }'
GET/api/v1/review-tables/{table_id}

Get review table

Returns the table with its columns, rows, and cells. Each cell has a status (pending, running, done, not_found, or error), and when done, the extracted value plus a cited quote and page. Poll this while a table fills.

Scopes

read

200
{
  "table": { "id": "bd85...", "status": "ready" },
  "columns": [{ "header": "Parties" }],
  "rows": [{ "filename": "contract.pdf" }],
  "cells": [{ "status": "done", "value": "Acme Corp; Beta LLC" }]
}
POST/api/v1/review-tables/{table_id}/run

Run review table

Fills any pending cells and retries errored ones. Cells normally fill automatically as columns and documents are added, so this is mostly for retries.

Scopes

write

cURL
curl -X POST https://app.ezel.ai/api/v1/review-tables/TABLE_ID/run \
  -H "Authorization: Bearer ezel_live_..."

Connect over MCP

Ezel is also a remote Model Context Protocol server, so you can use it directly from Claude (claude.ai and Desktop), Cursor, Claude Code, and ChatGPT. It exposes a read-only slice of this API as MCP tools.

Server URL

https://app.ezel.ai/mcp

Claude connects with OAuth: add a custom connector with the Server URL, sign in, and click Allow. No API key needed. Cursor, Claude Code, and others use an API key as a bearer token, the same key as the REST API.

ezel_list_matters
List your matters.
ezel_ask_matter
Cited answer from a matter's documents.
ezel_ask
General legal Q&A with research.
ezel_search_caselaw
Ranked U.S. case law search.
ezel_get_case
Full opinion text for a case.
Claude Code
claude mcp add ezel \
  --transport http https://app.ezel.ai/mcp \
  --header "Authorization: Bearer ezel_live_..."