Skip to main content

Conventions

Rules that apply across the whole API, so each endpoint page does not have to repeat them. The API follows the OpenAI conventions wherever it offers the same operation, and extends them in the same style elsewhere.

Base URL and paths

https://api.chatbots.ecourtdate.com/v1
  • Every endpoint lives under /v1; the OpenAI SDKs take this full prefix as their base_url and append /chat/completions, /models, and /embeddings themselves.
  • Path segments are lowercase and kebab-case; path parameters are ids (/v1/conversations/{conversationId}, /v1/documents/{documentId}, /v1/ingest/jobs/{jobId}, /v1/ingest/crawl/{crawlJobId}, /v1/models/{modelId}).
  • Call paths without a trailing slash. A trailing slash answers with a 307 redirect to the canonical path, and some HTTP clients drop the body when following a redirect on POST.
  • GET /health (outside /v1) is the only unauthenticated route and returns {"status": "ok"}.

JSON only

Request bodies are JSON and must be sent with Content-Type: application/json (a charset parameter is fine). There is no 415: valid JSON sent with another content type, or with none, is rejected with 400 and the message Input should be a valid dictionary or object to extract fields from, and malformed JSON is rejected with 400 and the message Invalid JSON in request body. (an empty body gets Field required, except on POST /v1/conversations, where a missing or empty body is treated as {}). All three are validation errors with param: null. The JSON syntax check runs before authentication; the other two run after it (evaluation order).

The one exception is POST /v1/ingest/files, which takes multipart/form-data (Ingesting files).

Responses are JSON (Content-Type: application/json) except streamed chat, which is text/event-stream (Streaming), and DELETE /v1/conversations/{conversationId}, which returns 204 with an empty body and no Content-Type header. Every error, on every route, is the error envelope.

Naming

  • Field names are snake_case (finish_reason, chunk_count, tool_call_id).
  • Enumerated values are lowercase strings (completed_with_errors, search_query, json_schema).
  • Every resource carries an object string naming its shape:
objectReturned by
chat.completionPOST /v1/chat/completions
chat.completion.chunkEach streamed chunk of POST /v1/chat/completions
modelItems of GET /v1/models, GET /v1/models/{modelId}
embeddingItems of POST /v1/embeddings
conversationPOST /v1/conversations, GET /v1/conversations/{conversationId}
documentItems of GET /v1/documents, GET /v1/documents/{documentId}
document.deletedDELETE /v1/documents/{documentId}
listEvery list response

Job status bodies, the ingest and crawl submission responses, conversation message responses, and streamed conversation frames are plain objects without an object field.

List envelopes

Every list is wrapped in the OpenAI list envelope:

{
"object": "list",
"data": [
{ "id": "court-assistant", "object": "model", "created": 1784812800, "owned_by": "maple-county-courts" }
]
}

GET /v1/models and POST /v1/embeddings return the whole result in one page. GET /v1/documents is paginated: it takes limit (1 to 1000, default 100), an after cursor (the id of the last item you saw; an id that is not a document on your account is a 400 with param: after), and optional namespace, status, and source_type filters, and adds has_more, first_id, and last_id to the envelope (first_id and last_id are null on an empty page). Keep requesting with after=<last_id> until has_more is false. See Documents.

Identifiers

IdentifierFormatExample
Chat completion idchatcmpl- plus 24 lowercase hex characters (33 characters)chatcmpl-8a317b1a95a74321b73b6567
Conversation idUUID, hyphenated, no prefixc856e096-99bb-45c1-a0df-044dbbe0aeb7
Document idUUID, hyphenated, no prefix17136f01-972d-4956-868e-8159833072e4
Ingest job_idUUID, hyphenated, no prefixbfcde661-9eec-4142-a790-ca9a62c8e0f8
Crawl crawl_job_idUUID, hyphenated, no prefix2d6f8c0e-5b1a-4f3c-9e7d-0a1b2c3d4e5f
Model idThe bot's slug or an alias, as configured for your accountcourt-assistant
Tool call idOpaque string assigned by the model; pass it back unchanged in tool_call_id(varies)
API keyecd_sk_ plus 59 charactersecd_sk_...

Treat every id as an opaque, case-sensitive string: do not parse structure out of a UUID or rely on its length beyond what the table states. Ids are scoped to your account; another account's id behaves exactly like an unknown one (not_found).

Timestamps

Two styles, following the shape each object imitates:

  • Unix seconds (integer, UTC) on the OpenAI-style objects: created on chat completions, chunks, and models; created and updated on conversations. updated is bumped on every stored turn and is never less than created.
  • RFC 3339 UTC with a Z designator on documents: created_at and updated_at, with millisecond precision rendered as six fractional digits (the last three are always 0), for example 2026-08-21T14:03:11.214000Z (a value on a whole second renders as 2026-08-21T14:03:11.000000Z).

All clocks are UTC. The daily token quota also resets on the UTC day (Rate limits).

Request ids

Every response from an API operation carries an X-Request-ID header, including errors, 204 responses, and streamed responses (the only exception is a CORS preflight response, which is outside the API contract; see Browser clients). The value is the id to quote in support requests: it is recorded with the request's usage entry and, for ingestion and crawl jobs, carried through to the background processing logs.

You can supply your own id on the request. It is echoed back when it matches ^[a-zA-Z0-9_-]{1,64}$; any other value is replaced, never rejected, by a server-minted 32-character hex id.

curl -s "https://api.chatbots.ecourtdate.com/v1/models" \
-H "Authorization: Bearer $API_KEY" \
-H "X-Request-ID: clerk-portal-7f3e2a" \
-D - -o /dev/null
HTTP/1.1 200 OK
Content-Type: application/json
X-Request-ID: clerk-portal-7f3e2a
RateLimit-Limit: 60
RateLimit-Remaining: 59
RateLimit-Reset: 41

Log the request id next to your own correlation data on every call; it is the fastest route from a user report to the exact request.

Response headers

HeaderWhenMeaning
X-Request-IDEvery response from an API operationThe request id (above)
RateLimit-Limit, RateLimit-Remaining, RateLimit-ResetEvery authenticated /v1 response (not on 401, 403, 413, a routing error, a body that is not valid JSON, or a malformed Content-Length header, all decided before the limiter runs; absent only if the limiter itself is unavailable)Your per-minute request budget, what is left (never negative), and seconds until the window resets, or until the daily quota resets when that is what blocks you (Rate limits)
Retry-After429 and 503Seconds to wait before retrying (Rate limits)
WWW-Authenticate: Bearer401The request did not authenticate (Authentication)
Allow405The methods the path supports
Cache-Control: no-cache, Connection: keep-alive, X-Accel-Buffering: noStreamed responsesDisable buffering between the API and your client (Streaming)

Limits

LimitValue
JSON request body5 MiB (5,242,880 bytes, inclusive), checked before authentication (see the evaluation order for bodies sent without Content-Length)
Messages per chat completion request200
Characters per message (content)100,000 (Unicode characters, not bytes) on chat completions and conversation messages
max_completion_tokens / max_tokensValues above 8,192 are clamped to 8,192
nMust be 1
Embedding inputs per request96, each at most 100,000 characters
Embedding dimensions1,024 (fixed)
Conversation metadata16 keys; keys up to 64 characters, string values up to 512 characters
Files per upload request20
Size per uploaded file25 MiB (26,214,400 bytes, inclusive)
Filename255 UTF-8 bytes including the extension; no /, \, control characters, or other non-printable characters
namespace^[a-z0-9][a-z0-9_-]{0,63}$
Crawl seed_urls1 to 100
Crawl max_pages1 to 500 (default 50)
Crawl max_depth1 to 10 (default 3)
Crawl rate_limit_rps0.1 to 10 (default 2)
GET /v1/documents limit1 to 1000 (default 100)

Exceeding the body cap returns 413 request_too_large; every other limit is a 400 validation error naming the field in param. Per-account request and token limits are covered in Rate limits.

Strict typing

Request fields are typed as JSON types and are not coerced from strings: "stream": "yes", "temperature": "0.5", and "max_pages": "10" are each rejected with 400 and a param naming the field. Send booleans as true/false, integers as integers, and numbers as numbers. Numbers with a fractional part are rejected where an integer is expected ("n": 1.5, "max_pages": 10.5).

Required string fields must be non-empty where the endpoint says so: an empty or whitespace-only content on a system, developer, or user message, an empty or whitespace-only conversation message content, an empty stop string, and an empty input array are all 400 (a tool message may carry null or an empty string as an empty result).

Unknown fields

  • In requests, unknown fields are accepted and ignored. This is what lets the OpenAI SDKs send parameters the API does not implement without failing. Misspelled field names are therefore silently ignored too, so check the supported parameters when a setting seems to have no effect.
  • In responses, clients must ignore fields they do not recognize. New fields are added without notice (Versioning); the citations field on assistant messages is one such addition.

Validation errors

A request that fails validation returns 400 with the error envelope, type: invalid_request_error, code: null, and param pointing at the offending field in bracket form:

{
"error": {
"message": "A tool message requires tool_call_id.",
"type": "invalid_request_error",
"param": "messages[2].tool_call_id",
"code": null
}
}

Only the first failure is reported. Fix it, resend, and repeat; see Errors for the path forms and the messages you will see.

Evaluation order

Checks run in a fixed order, which tells you which error wins when several apply:

  1. Malformed Content-Length header: 400 (Invalid Content-Length header., param: null), before routing.
  2. Body size: 413 request_too_large, before routing and authentication. For a body sent without Content-Length (chunked), the cap is enforced while the body is read, after routing, so an unknown path or an unsupported method is reported first.
  3. Route: 404 for an unknown path, 405 (with Allow) for an unsupported method, before authentication.
  4. JSON syntax: 400 (Invalid JSON in request body., param: null), before authentication.
  5. Authentication: 401 invalid_api_key.
  6. Scope: 403 insufficient_scope.
  7. Rate limit and quota: 429 rate_limit_exceeded or insufficient_quota.
  8. Body validation: 400 (validation).
  9. Endpoint checks in the order each guide documents (for chat completions: n, then stop, then response_format, then the model lookup, then messages, then tools, then retrieval).

Browser clients

Cross-origin requests are accepted only from eCourtDate-owned origins. Browser-direct use is not a supported scenario: API keys are server-side secrets (Authentication). Route browser traffic through your own backend.

CORS preflight responses (OPTIONS with an Origin and an Access-Control-Request-Method header) are outside the API contract: they are answered by the CORS layer itself and carry neither X-Request-ID nor the error envelope. Every response to an actual API request follows the rules on this page.