Skip to main content

Getting started

The eCourtDate Chatbot API answers questions with grounded, cited answers drawn from documents you upload. It speaks the OpenAI chat completions dialect, so the official OpenAI SDKs work unmodified. This guide takes you from a freshly activated key to a cited answer in five steps.

Base URL: https://api.chatbots.ecourtdate.com/v1

1. Activate your key

Access requires a paid subscription. When your subscription is active, eCourtDate issues an API key for your account and activates it; there is no self-serve key page. The key looks like ecd_sk_ followed by 59 characters and carries one or both scopes:

ScopeNeeded for
chatChat completions, models, embeddings, conversations (steps 2, 3, and 5)
ingestUploading files, crawling, documents, jobs (step 4)

Keys are server-side secrets. Store yours in a secrets manager or an environment variable, never in a browser or a mobile app, and send it on every request as a Bearer token:

export API_KEY="ecd_sk_..."

See Authentication for the key lifecycle and rotation guidance.

2. Send your first chat completion

A request needs only messages. Omit model and the request is served by your account's default bot (when several bots are enabled and no default is configured, the API answers 400 and asks you to pass model).

curl -s "https://api.chatbots.ecourtdate.com/v1/chat/completions" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{ "role": "user", "content": "How do I request a continuance for a traffic hearing?" }
]
}'
{
"id": "chatcmpl-8a317b1a95a74321b73b6567",
"object": "chat.completion",
"created": 1787408210,
"model": "court-assistant",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "To request a continuance, file a written motion with the clerk at least five business days before the hearing date and include the case number.",
"refusal": null,
"tool_calls": null,
"citations": []
},
"logprobs": null,
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 812,
"completion_tokens": 143,
"total_tokens": 955
}
}

model in the response is the canonical slug of the bot that answered, even when you omitted it or used an alias. Every key of the message is always present: citations is an empty array here because the bot has a knowledge base but nothing in it was cited yet (step 5 changes that), and tool_calls is null because no tools were offered.

The same request with the official OpenAI Python SDK:

from openai import OpenAI

client = OpenAI(
api_key="ecd_sk_...",
base_url="https://api.chatbots.ecourtdate.com/v1",
)

completion = client.chat.completions.create(
model="default",
messages=[
{"role": "user", "content": "How do I request a continuance for a traffic hearing?"}
],
)
print(completion.choices[0].message.content)

And with the official OpenAI Node SDK:

import OpenAI from "openai";

const client = new OpenAI({
apiKey: process.env.API_KEY,
baseURL: "https://api.chatbots.ecourtdate.com/v1",
});

const completion = await client.chat.completions.create({
model: "default",
messages: [
{ role: "user", content: "How do I request a continuance for a traffic hearing?" },
],
});
console.log(completion.choices[0].message.content);

The SDKs require a model value; default selects the account default, exactly as omitting the field does over curl. See Chat completions for every supported parameter and Streaming to receive the answer as it is generated.

3. Discover your models

Each bot configured for your account, each alias that points at one, and default (the account default) are listed as models. Use the ids as the model value.

curl -s "https://api.chatbots.ecourtdate.com/v1/models" \
-H "Authorization: Bearer $API_KEY"
{
"object": "list",
"data": [
{
"id": "court-assistant",
"object": "model",
"created": 1784812800,
"owned_by": "maple-county-courts"
},
{
"id": "jury-helpdesk",
"object": "model",
"created": 1786022400,
"owned_by": "maple-county-courts"
},
{
"id": "default",
"object": "model",
"created": 1784812800,
"owned_by": "maple-county-courts"
}
]
}

owned_by is your account id, and the default entry carries the default bot's created. With the SDKs, client.models.list() returns the same page. An unknown or disabled id returns 404 model_not_found whose message lists the ids that are available. Bots, aliases, and the default are configured for your account by eCourtDate; see Models.

4. Upload your first document

The knowledge base is what makes answers grounded. Uploads need a key with the ingest scope and use multipart/form-data, one file part per file (PDF, DOCX, XLSX, CSV, TXT, Markdown, HTML, or EML, up to 25 MiB each and 20 per request). The optional namespace field defaults to general; a bot answers from the namespace eCourtDate configured for it, so upload into that namespace.

curl -s "https://api.chatbots.ecourtdate.com/v1/ingest/files" \
-H "Authorization: Bearer $API_KEY" \
-F "file=@traffic-division-faq.pdf" \
-F "namespace=general"
{
"job_id": "bfcde661-9eec-4142-a790-ca9a62c8e0f8",
"document_ids": ["17136f01-972d-4956-868e-8159833072e4"],
"status": "processing"
}

The response returns before any text is extracted. Poll the job until its status is terminal:

curl -s "https://api.chatbots.ecourtdate.com/v1/ingest/jobs/bfcde661-9eec-4142-a790-ca9a62c8e0f8" \
-H "Authorization: Bearer $API_KEY"
{
"job_id": "bfcde661-9eec-4142-a790-ca9a62c8e0f8",
"status": "completed",
"total_documents": 1,
"completed_documents": 1,
"failed_documents": 0,
"errors": []
}

status moves from processing to completed, completed_with_errors, or failed; errors names each document that could not be indexed and why (scanned PDFs without a text layer are the usual cause). A short polling loop in bash:

JOB_ID="bfcde661-9eec-4142-a790-ca9a62c8e0f8"
until [ "$STATUS" = "completed" ] || [ "$STATUS" = "completed_with_errors" ] || [ "$STATUS" = "failed" ]; do
sleep 5
STATUS=$(curl -s "https://api.chatbots.ecourtdate.com/v1/ingest/jobs/$JOB_ID" \
-H "Authorization: Bearer $API_KEY" | jq -r .status)
echo "$STATUS"
done

Instead of polling you can receive an ingest.completed webhook; see Jobs and webhooks. The OpenAI SDKs do not cover the knowledge base endpoints; call them with curl or any HTTP client (SDKs). Details of formats, namespaces, and per-document errors are in Ingesting files.

5. Ask a grounded question

Once the document is ready, ask a question it answers. The assistant cites the passages it used with [Source N] markers in the text, and the message carries a citations array describing each source.

curl -s "https://api.chatbots.ecourtdate.com/v1/chat/completions" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{ "role": "user", "content": "How many days before the hearing must I file a continuance request?" }
]
}'
{
"id": "chatcmpl-fbf6088fdd704a72ac569970",
"object": "chat.completion",
"created": 1787408377,
"model": "court-assistant",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "A written continuance request must be filed at least five business days before the scheduled hearing [Source 1].",
"refusal": null,
"tool_calls": null,
"citations": [
{
"source_index": 1,
"document_id": "17136f01-972d-4956-868e-8159833072e4",
"source_filename": "traffic-division-faq.pdf",
"chunk_index": 3,
"text_preview": "Continuances. A request for a continuance must be made in writing and filed with the clerk no later than five (5) business days before the hearing date.",
"score": 0.93
}
]
},
"logprobs": null,
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 1048,
"completion_tokens": 41,
"total_tokens": 1089
}
}

Each citation's source_index matches the [Source N] marker in content, document_id is the document you uploaded, and text_preview is the first 200 characters of the passage. citations is always present on a text answer from a bot with a knowledge base (an empty array when nothing was cited) and is null on a tool-call turn or for a bot that does not cite. The OpenAI SDKs keep unknown fields, so the array is reachable without any workaround:

completion = client.chat.completions.create(
model="default",
messages=[{"role": "user", "content": "How many days before the hearing must I file a continuance request?"}],
)
message = completion.choices[0].message
print(message.content)
for citation in (message.model_extra or {}).get("citations") or []:
print(f"[Source {citation['source_index']}] {citation['source_filename']} (chunk {citation['chunk_index']})")
const completion = await client.chat.completions.create({
model: "default",
messages: [{ role: "user", content: "How many days before the hearing must I file a continuance request?" }],
});
const message = completion.choices[0].message;
console.log(message.content);
for (const citation of message.citations ?? []) {
console.log(`[Source ${citation.source_index}] ${citation.source_filename} (chunk ${citation.chunk_index})`);
}

See Citations for every field and for rendering guidance.

Errors you may meet on the way

StatusCodeCause
401invalid_api_keyMissing, malformed, expired, or revoked key
403insufficient_scopeThe key lacks chat (steps 2, 3, 5) or ingest (step 4)
404model_not_foundmodel is not a bot or alias on your account
404not_foundUnknown job or document id
400validationA malformed body; param names the field
400validationmodel omitted while several bots are enabled and no default is configured; param is model
429rate_limit_exceededToo many requests this minute; wait for Retry-After

Go deeper

To...Read
Understand keys, scopes, and rotationAuthentication
Stream answers token by tokenStreaming
Keep chat history on the serverConversations
Call your own functions from the modelTools and structured output
Crawl your public website instead of uploadingCrawling websites
Get notified when ingestion finishesJobs and webhooks
Handle every failure modeErrors and Rate limits
Explore every endpointAPI reference