Status: Active — this document is the canonical reference for all API response formats. Last updated: 2026-02-15 Compatibility policy: Additive-only changes (see Change Policy)
- HTTP Envelope
- MCP ToolResult Envelope
- Backend Tools (87)
- RADA Tools (4)
- OpenReyestr Tools (27)
- Common Patterns
- Change Policy
All REST API calls to POST /api/tools/:toolName return one of two envelopes:
{
"error": "Tool execution failed",
"message": "human-readable error",
"tool": "search_legal_precedents",
"cost_tracking": { ... }
}{
"success": false,
"error": "Tool not found",
"message": "No handler registered for tool: unknown_tool"
}Every tool returns a ToolResult. The actual data is JSON-stringified inside content[0].text.
interface ToolResult {
content: Array<{ type: "text"; text: string }>; // text = JSON.stringify(data, null, 2)
isError?: boolean;
}Error variant:
{
content: [{ type: "text", text: "Error: <message>" }],
isError: true
}To get the typed data, parse: JSON.parse(result.content[0].text).
{
service: string; // "legal_research" | "document_vault" | "workflow_automation" | "due_diligence"
task: string; // "answer_question" | "semantic_search" | "run_workflow" | "bulk_review"
inputs: {
question: string;
jurisdiction: string; // "UA"
language: string; // "uk"
};
depth: string; // "quick" | "standard" | "deep"
confidence: number; // 0.0–1.0
}{
cases: Array<{
id: string; // EDRSR doc_id (legacy: ZakonOnline doc_id)
source: string; // "edrsr" | "zakononline" (legacy)
title: string;
url?: string;
date?: string; // YYYY-MM-DD
court?: string;
text?: string;
}>;
laws: Array<{
id: string; // "rada_id:article_number"
source: string; // "rada"
title: string;
url?: string;
article?: string;
text?: string;
rada_id?: string;
}>;
guidance: Array<any>; // Reserved, currently empty
confidence: number;
}{
success_arguments: string[];
risk_factors: string[];
confidence: number; // 0.0–1.0
}{
is_valid: boolean;
confidence: number;
issues: Array<{
type: string; // "missing_source" | "invalid_citation" | "warning"
message: string;
}>;
}{
articles: string[];
patterns_count: number;
}{
status: string; // Precedent status description
}{
doc_id?: number;
case_number?: string;
url?: string;
depth: number; // 1–5
sections: Array<{
type: string; // "FACTS" | "COURT_REASONING" | "DECISION" | ...
text: string;
}>;
full_text_length: number;
}{
case_number: string;
total_documents: number;
// When group_by_instance=false:
documents?: Array<CaseDocument>;
// When group_by_instance=true (default):
grouped_documents?: {
"Перша інстанція": CaseDocument[];
"Апеляція": CaseDocument[];
"Касація": CaseDocument[];
"Велика Палата ВС": CaseDocument[];
"Невідомо": CaseDocument[];
};
search_strategy: {
variations_tried: string[];
sources: {
by_title: number;
filtered_out: number;
duplicates_removed: number;
};
note: string;
};
summary: {
instances: {
first_instance: number;
appeal: number;
cassation: number;
grand_chamber: number;
};
document_types: {
decisions: number;
rulings: number;
orders: number;
};
};
}
// Shared type
interface CaseDocument {
doc_id: number;
case_number: string;
document_type: string; // "Рішення" | "Постанова" | "Ухвала" | "Вирок" | "Окрема думка" | "Невідомо"
instance: string; // "Перша інстанція" | "Апеляція" | "Касація" | "Велика Палата ВС" | "Невідомо"
court?: string;
chamber?: string;
judge?: string;
date?: string;
url?: string;
resolution?: string;
snippet?: string;
full_text?: string; // Only if include_full_text=true
}{
sections: Array<{
type: string; // "ФАКТИ" | "ОБОСНУВАННЯ" | "РІШЕННЯ"
text: string;
}>;
}{
requested_docs: number;
unique_docs: number;
duplicates_removed: number;
processed_docs: number;
limited_to: number;
time_taken_ms: number;
estimated_cost_usd: number;
note: string;
deduplication_note?: string;
warning?: string;
}{
query: string;
search_query_used: string;
date_from: string;
date_to?: string;
pages_fetched: number;
unique_doc_ids_collected: number;
max_docs: number;
max_pages: number;
time_taken_ms: number;
cost_estimate_usd: {
search_api: number;
scrape_max: number;
};
note: string;
}{
patterns: Array<any>; // Legal pattern objects from Qdrant pattern store
}{
party_name: string;
party_type: string; // "plaintiff" | "defendant" | "any"
search_query: string;
total_unique_cases: number;
unique_doc_ids_found: number;
pages_fetched: number;
time_taken_ms: number;
cost_estimate_usd: number;
date_from?: string;
date_to?: string;
filtering_method?: string;
note?: string;
warning?: string;
scanned_documents?: number;
cases?: Array<{ // Only if return_cases=true
cause_num: string;
doc_id: number;
title: string;
resolution: string;
judge: string;
court_code: string;
adjudication_date: string;
url: string;
}>;
cases_returned?: number;
}{
similar: Array<any>; // Embedding search results from Qdrant
}{
graph: any; // Citation graph structure
}Text-based search:
{
results: Array<any>; // ZakonOnline search results
intent: object;
search_method: "text_based";
total: number;
warnings?: string[];
}Smart search (by case number):
{
source_case: {
cause_num: string;
doc_id: number;
title: string;
resolution: string;
judge: string;
court_code: string;
adjudication_date: string;
url: string;
category_code: string;
justice_kind: string;
};
search_method: "smart_text_search_with_pagination";
text_source: string;
text_length: number;
extracted_terms: {
law_articles: string[];
keywords: string[];
dispute_type: string;
case_essence: string;
};
search_query: string;
similar_cases: Array<{
cause_num: string;
doc_id: number;
title: string;
resolution: string;
judge: string;
court_code: string;
adjudication_date: string;
url: string;
similarity_reason: string;
}>;
total_found: number;
pages_fetched: number;
reached_safety_limit: boolean;
displaying: number;
total_available_info: string;
}Count-all mode:
{
query: string;
count_all_mode: true;
total_count: number;
pages_fetched: number;
time_taken_ms: number;
cost_estimate_usd: number;
note: string;
warning: string | null;
}{
procedure_code: string; // "cpc" | "gpc" | "cac" | "crpc"
query: string;
time_range: any;
applied_filters: {
court_level: string; // "SC" | "GrandChamber"
date_from?: string;
date_to?: string;
};
results: Array<{
doc_id: number;
court?: string;
chamber?: string;
date?: string;
case_number?: string;
url?: string;
section_focus?: string[];
snippets: string[];
}>;
total_returned: number;
warning?: string;
}{
procedure_code: string;
query: string;
time_range: any;
pro: Array<PracticeCase>;
contra: Array<PracticeCase>;
total_pro: number;
total_contra: number;
warning?: string;
}
interface PracticeCase {
doc_id: number;
court?: string;
chamber?: string;
date?: string;
case_number?: string;
url?: string;
snippet?: string;
}{
procedure_code: string;
time_range: any;
extracted_search_terms: string[];
search_query: string;
results: Array<{
doc_id: number;
court?: string;
chamber?: string;
date?: string;
case_number?: string;
url?: string;
why_similar: string[];
}>;
warning: string;
time_range_warning?: string;
}{
conclusion: {
summary: string;
conditions: string;
risks: string;
};
procedure_code: string;
event_type: string;
appeal_type: string;
event_date: string;
received_full_text_date?: string;
days: number;
variants: Array<{
rule: string;
start_date: string;
end_date: string;
}>;
norms: {
act: string;
article: string;
quote: string;
commentary: string;
source_url: string;
query_used: string;
error?: string;
};
renewal_criteria: {
title: string;
criteria: Array<{
criterion: string;
explanation: string;
}>;
source_note: string;
};
sources: {
supreme_court_practice: Array<any>;
practice_query: string;
practice_queries_tried: string[];
practice_time_range: string | null;
practice_disable_time_range: boolean;
practice_use_court_practice: boolean;
practice_case_map_max: number;
court_practice_map_stats?: object;
practice_error?: string;
supreme_court_practice_expanded?: {
requested: number;
depth: number;
returned: number;
time_taken_ms: number;
items: Array<any>;
warning?: string;
};
};
risks_and_counterarguments: {
title: string;
counterarguments: Array<{
argument: string;
basis: string;
mitigation: string;
}>;
procedural_risks: string[];
};
action_checklist: {
title: string;
steps: Array<{
step: string;
details: string;
}>;
required_evidence: string[];
};
supreme_court_theses?: Array<{
thesis: string;
court_and_date: string;
quote: string;
context: string;
section_type: string;
doc_id: number;
url: string;
}>;
structured_cases?: Array<{
case_number: string;
court: string;
date: string;
relevance_reason: string;
quote: string;
section_type: string;
doc_id: number;
url: string;
}>;
warnings: string[];
}{
stage: string;
procedure_code: string;
case_category?: string;
steps: string[];
typical_refusal_grounds: string[];
norms_reference?: string;
warning: string;
}{
amount: number;
date_from: string;
date_to: string;
claim_type: string;
calculation: {
days: number;
three_percent?: number;
};
warning: string;
}Returns plain text (not JSON):
B. Норма / правова рамка
Норма: [title] (№ [law_number])
Стаття: [article]
Джерело: [url]
Цитата:
- [snippet1]
- [snippet2]
{
rada_id: string;
article_number: string;
title: string;
full_text: string;
url: string;
metadata: object;
html?: string; // Only if include_html=true
}{
rada_id: string;
article_number: string;
title: string;
full_text: string;
url: string;
metadata: object;
resolved_from?: {
query: string;
method: string; // "regexp" | "ai" | "explicit"
confidence?: number;
};
html?: string;
}{
rada_id: string;
total_found: number;
articles: Array<{
article_number: string;
title: string;
full_text: string;
url: string;
}>;
html?: string;
}Direct reference resolved:
{
query: string;
resolved_reference: {
rada_id: string;
article_number: string;
source: string; // "regexp" | "ai"
confidence?: number;
};
total_found: number;
articles: Array<{
rada_id: string;
article_number: string;
title: string;
full_text: string;
url: string;
}>;
html?: string;
}Semantic search:
{
query: string;
total_found: number;
articles: Array<{
rada_id: string;
article_number: string;
title: string;
full_text: string; // Truncated to 500 chars + "..."
url: string;
}>;
html?: string;
suggestion?: string;
}{
rada_id: string;
title: string;
short_title: string;
type: string;
total_articles: number;
table_of_contents: any;
articles_summary: Array<{ // First 20 articles
article_number: string;
title: string;
byte_size: number;
}>;
}{
text: string;
metadata: {
pageCount: number;
source: string; // "native" | "ocr" | "mammoth" | "html"
mimeType?: string;
filename?: string;
[key: string]: any;
};
}{
clauses: Array<{
type: string; // "parties_subject" | "rights_obligations" | "terms_conditions" |
// "payments_finance" | "liability_penalties" | "force_majeure" |
// "termination" | "confidentiality"
text: string;
confidence: number;
riskLevel?: "low" | "medium" | "high";
pageNumber?: number;
}>;
riskReport: {
riskFactors: any[];
highRiskClauses: Array<ExtractedClause>;
};
}{
executiveSummary: string; // 2–3 paragraphs
detailedSummary: string;
keyFacts: {
parties?: string[];
dates?: string[];
amounts?: string[];
};
}{
changes: Array<{
type: "addition" | "deletion" | "modification";
oldText?: string;
newText?: string;
location: string;
importance: "critical" | "significant" | "minor";
}>;
summary: string;
}{
id: string; // UUID
title: string;
type: "contract" | "legislation" | "court_decision" | "internal" | "other";
content: string;
metadata: {
uploadedAt: string; // ISO timestamp
processedAt: string;
processingTimeMs: number;
sectionCount: number;
embeddingCount: number;
patterns: {
riskFactors: string[];
keyArguments: string[];
confidence: number;
};
documentDate?: string;
tags: string[];
parties: string[];
jurisdiction?: string;
documentSubtype?: string;
uploadedBy?: string;
category?: string;
riskLevel?: "low" | "medium" | "high";
[key: string]: any; // Extensible metadata
};
sections: Array<{
type: string;
text: string;
}>;
patterns: {
riskFactors: string[];
keyArguments: string[];
confidence: number;
};
}{
id: string;
title: string;
type: string;
content: string;
metadata: object;
sections?: Array<{ // If includeSections=true
type: string;
text: string;
}>;
patterns?: { // If includePatterns=true
riskFactors: string[];
keyArguments: string[];
confidence: number;
};
}
// Returns null if not found{
documents: Array<{
id: string;
title: string;
type: string;
content: ""; // Always empty in list view
metadata: object;
}>;
total: number;
}{
results: Array<{
documentId: string;
title: string;
relevance: number; // 0.0–1.0
matchedSections: Array<{
sectionType: string;
text: string;
relevance: number;
}>;
metadata: object;
}>;
}All DD tools wrap responses in a versioned envelope:
{
version: "v1";
trace_id: string;
data: <tool-specific result>;
error?: {
code: string;
message: string;
retryable: boolean;
details?: any;
};
}// data field:
{
reviewId: string;
totalDocuments: number;
reviewedDocuments: number;
failedDocuments: number;
findings: Array<{
documentId: string;
documentTitle: string;
category: string;
riskLevel: "critical" | "high" | "medium" | "low";
title: string;
description: string;
recommendation?: string;
anchor?: {
sectionType: string;
quote: string;
};
}>;
riskScores: Array<DocumentRiskScore>;
processingTimeMs: number;
}
interface DocumentRiskScore {
documentId: string;
documentTitle: string;
overallRisk: "critical" | "high" | "medium" | "low";
score: number; // 0–100
breakdown: {
contractual: number;
financial: number;
compliance: number;
legal: number;
operational: number;
};
criticalFindingsCount: number;
highFindingsCount: number;
}// data field:
{
riskScores: Array<DocumentRiskScore>; // Same shape as above
}JSON format (default):
// data field:
{
id: string;
title: string;
createdAt: string;
overallRisk: "critical" | "high" | "medium" | "low";
executiveSummary: string;
summary: string;
findings: Array<DDFinding>;
riskScores: Array<DocumentRiskScore>;
recommendations: string[];
processingTimeMs: number;
}Markdown/HTML format: returns formatted string.
{
total: number;
completed: number;
failed: number;
results: Array<{
fileId: string;
filename: string;
success: boolean;
error?: string;
result?: {
parsed?: { text: string; metadata: object };
clauses?: { clauses: Array<ExtractedClause>; riskReport: any };
summary?: { executiveSummary: string; detailedSummary: string; keyFacts: object };
};
executionTimeMs: number;
costEstimate?: {
openai_usd: number;
vision_usd: number;
total_usd: number;
};
}>;
totalCostUsd: number;
totalExecutionTimeMs: number;
}All RADA tools use the standard ToolResult envelope.
In Unified Gateway mode, tool names are prefixed with rada_.
{
query: string;
status: "registered" | "first_reading" | "second_reading" | "adopted" | "rejected" | "all";
total_found: number;
bills: Array<{
bill_number: string;
title: string;
registration_date: string | null;
status: string | null;
stage: string | null;
initiator_type: string | null;
initiator_names: string[];
main_committee_name: string | null;
url: string | null;
}>;
}Single deputy:
{
rada_id: string;
full_name: string;
short_name: string;
active: boolean;
faction_name: string | null;
committee_name: string | null;
committee_role: string | null;
region: string | null;
district: string | null;
birth_date: string | null;
birth_place: string | null;
biography: string | null;
photo_url: string | null;
voting_statistics?: { // If include_voting_record=true
total_votes: number;
voted_for: number;
voted_against: number;
abstained: number;
not_present: number;
attendance_rate: number;
};
assistants?: Array<{ // If include_assistants=true
assistant_type: string;
full_name: string;
start_date: string;
end_date: string;
}>;
}Multiple matches (<10):
{
multiple_matches: true;
count: number;
deputies: Array<{
rada_id: string;
full_name: string;
faction_name: string;
active: boolean;
}>;
message: string;
}Faction list (>10): returns plain text listing.
{
law_number: string;
title: string;
law_type: string | null;
adoption_date: string | null;
effective_date: string | null;
status: string | null;
url: string;
full_text_plain: string;
article?: { // If specific article requested
number: string;
title?: string;
text: string;
parent_chapter?: string;
};
court_citations?: { // If include_court_citations=true
total: number;
recent: Array<{
case_number: string;
citation_count: number;
last_citation_date: string;
}>;
};
}{
deputy: {
rada_id: string;
full_name: string;
faction: string;
};
period: {
from: string;
to: string;
};
statistics: {
total_votes: number;
voted_for: number;
voted_against: number;
abstained: number;
not_present: number;
attendance_rate: number; // Percentage
};
voting_records: Array<{ // Max 20 recent
date: string;
question: string;
bill_number: string | null;
vote: "За" | "Проти" | "Утримався" | "Не голосував";
result: string;
}>;
ai_analysis?: Array<{ // If analyze_patterns=true
pattern_type: "consistency" | "topic_based" | "time_based" | "abstention";
description: string;
frequency: number; // 0–100
confidence: number; // 0–1
examples: string[];
}>;
}All OpenReyestr tools use the standard ToolResult envelope.
In Unified Gateway mode, tool names are prefixed with openreyestr_.
Found results:
Array<{
id: number;
record: string;
edrpou?: string; // UO/FSU only
name: string;
short_name?: string;
opf?: string; // UO only (організаційно-правова форма)
type_subject?: string; // FSU only
type_branch?: string; // FSU only
stan: string; // "зареєстровано" | "припинено" | ...
registration: string; // Date
entity_type: "UO" | "FOP" | "FSU";
}>No results:
[{
found: false;
query: string;
message: string;
availableRegistries: Array<{
registry: string;
records: number;
}>;
suggestions: string[];
}]{
entityType: "UO" | "FOP" | "FSU";
record: string;
mainInfo: {
id: number;
record: string;
edrpou?: string;
name: string;
short_name?: string;
opf?: string;
stan: string;
registration: string;
// ... all fields from entity table
};
founders?: Array<{
id: number;
entity_type: string;
entity_record: string;
founder_info: string;
share?: string;
}>;
beneficiaries?: Array<{
id: number;
entity_type: string;
entity_record: string;
beneficiary_info: string;
}>;
signers?: Array<{
id: number;
entity_type: string;
entity_record: string;
signer_info: string;
}>;
members?: Array<any>; // UO only
branches?: Array<any>; // UO only
predecessors?: Array<any>;
assignees?: Array<any>; // UO only
executivePower?: object; // UO only
terminationStarted?: object;
bankruptcyInfo?: object; // UO only
exchangeData?: Array<any>;
}Array<{
id: number;
entity_type: "UO" | "FOP" | "FSU";
entity_record: string;
beneficiary_info: string;
entity_name: string; // Joined from entity table
}>Found:
{
id: number;
record: string;
edrpou: string;
name: string;
short_name?: string;
opf?: string;
stan: string;
registration: string;
entity_type: "UO" | "FSU"; // FOP don't have EDRPOU
// ... all fields from entity table
}Not found:
{
found: false;
edrpou: string;
message: string;
availableRegistries: Array<{
registry: string;
records: number;
}>;
suggestions: string[];
}{
totalEntities: {
legalEntities: number;
individualEntrepreneurs: number;
publicAssociations: number;
total: number;
};
activeEntities: {
legalEntities: number;
individualEntrepreneurs: number;
publicAssociations: number;
total: number;
};
}| Type | Format | Example |
|---|---|---|
| Date | ISO 8601 YYYY-MM-DD |
"2024-01-15" |
| Timestamp | ISO 8601 full | "2024-01-15T10:30:00.000Z" |
| Internal ID | UUID v4 | "550e8400-e29b-41d4-a716-446655440000" |
| EDRSR/ZO doc_id | number | 12345678 |
| Confidence | float 0.0–1.0 | 0.85 |
| Cost USD | float | 0.0234 |
| URL | absolute URL string | "https://reyestr.court.gov.ua/..." |
Fields marked with ? may be absent from the response. Consumers should always handle missing optional fields with defaults.
Some fields are truncated in list/search responses:
search_legislation→full_texttruncated to 500 chars +"..."list_documents→contentis always""
All tools follow the same error pattern:
{
content: [{ type: "text", text: "Error: <human-readable message>" }],
isError: true
}Some tools return structured errors inside the data:
{ error: string; suggestion?: string; }-
Additive only — new fields MAY be added to any response at any time. Consumers MUST ignore unknown fields.
-
No removals — existing fields MUST NOT be removed or renamed. If a field becomes obsolete, it continues to be returned (may be
nullor empty). -
No type changes — a field that returns
stringtoday MUST NOT start returningnumbertomorrow. If a new type is needed, add a new field. -
No semantic changes — the meaning of a field MUST NOT change.
confidence: 0.8must always mean "80% confident", not "80 out of 100". -
Deprecation — before eventually removing a field (in a future v2), mark it deprecated in this document for at least 2 months and add a
deprecated_fieldsarray to the response listing the field names.
The following changes are breaking and require a new API version:
- Removing or renaming a response field
- Changing a field's type
- Changing the HTTP envelope structure
- Changing authentication scheme
- Changing URL structure
| Date | Change | Type |
|---|---|---|
| 2026-02-15 | Initial contract documented | Baseline |
{ "success": true, "tool": "search_legal_precedents", "service": "backend", // "backend" | "rada" | "openreyestr" "result": { // ToolResult — see below "content": [{ "type": "text", "text": "<JSON or plain text>" }], "isError": false }, "cost_tracking": { "request_id": "uuid", "estimate_before": { ... }, "actual_cost": { ... } } }