Authentication
Every request to a /v1 endpoint is authenticated with an API key sent as a
Bearer token in the Authorization header:
curl -s "https://api.chatbots.ecourtdate.com/v1/models" \
-H "Authorization: Bearer $API_KEY"
There is no unauthenticated /v1 operation. Only GET /health is open; it
returns {"status": "ok"} and takes no key. A request without a valid key is
rejected with 401 invalid_api_key; a valid
key that lacks the scope an operation needs is rejected with 403
insufficient_scope.
Getting a key
Access to the eCourtDate Chatbot API requires a paid subscription. API keys are issued and activated for your account by eCourtDate; there is no self-serve key page. Contact eCourtDate to request a key, to add or remove a scope, or to have a key revoked.
A key is the prefix ecd_sk_ followed by 59 characters, 66 characters in
total. The first 16 characters after the prefix identify the key; the rest is
the secret. When you talk to support about a key, quote the key id
(ecd_sk_ plus those 16 characters), never the full value.
The OpenAI SDKs send the key for you; pass it as api_key (Python) or
apiKey (Node) together with the base URL:
from openai import OpenAI
client = OpenAI(
api_key="ecd_sk_...",
base_url="https://api.chatbots.ecourtdate.com/v1",
)
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.API_KEY,
baseURL: "https://api.chatbots.ecourtdate.com/v1",
});
Send the header exactly as Authorization: Bearer ecd_sk_...: one space
between the scheme and the key. The scheme word is matched case-insensitively,
but other spellings and whitespace variants are not part of the contract.
Scopes
Each key carries one or both scopes. The scope gates which operations the key may call; within a scope, every resource is limited to your own account.
| Scope | Grants |
|---|---|
chat | Chat completions, models, embeddings, and conversations |
ingest | Uploading files, crawling websites, documents, and job status |
The full matrix:
| Operation | Scope |
|---|---|
POST /v1/chat/completions | chat |
GET /v1/models, GET /v1/models/{modelId} | chat |
POST /v1/embeddings | chat |
POST /v1/conversations, GET /v1/conversations/{conversationId}, DELETE /v1/conversations/{conversationId}, POST /v1/conversations/{conversationId}/messages | chat |
POST /v1/ingest/files, GET /v1/ingest/jobs/{jobId} | ingest |
POST /v1/ingest/crawl, GET /v1/ingest/crawl/{crawlJobId} | ingest |
GET /v1/documents, GET /v1/documents/{documentId}, DELETE /v1/documents/{documentId} | ingest |
The API reference shows the required scope on every
operation. A practical split: give the service that powers your public chat
widget a chat-only key, and give the back-office process that uploads
documents an ingest-only key. Neither can do the other's job, and each can
be rotated or revoked independently.
401 versus 403
The two statuses mean different things and call for different fixes.
401 invalid_api_key: the request did not authenticate. The body is
identical for every cause (missing header, wrong scheme, malformed key,
unknown key, wrong secret, expired key, revoked key, or a suspended account),
so the response never reveals which part was wrong. The response also carries
WWW-Authenticate: Bearer.
HTTP/1.1 401 Unauthorized
Content-Type: application/json
WWW-Authenticate: Bearer
X-Request-ID: 1f0a7b2c9d8e4f6a8b1c2d3e4f5a6b7c
{
"error": {
"message": "Incorrect API key provided.",
"type": "authentication_error",
"param": null,
"code": "invalid_api_key"
}
}
Fix: check that the header is present and well-formed and that the key is the one eCourtDate activated for your account. If the key was valid until recently, it may have expired or been revoked; see Key lifecycle.
403 insufficient_scope: the key authenticated, but it does not carry
the scope the operation requires. The message names the missing scope.
{
"error": {
"message": "This API key does not have the 'ingest' scope.",
"type": "invalid_request_error",
"param": null,
"code": "insufficient_scope"
}
}
Fix: use a key that carries the scope, or ask eCourtDate to add the scope to this key. Retrying the same request with the same key will not succeed.
Authentication and scope are checked before the rate limiter and before the request body is validated, so:
- A request with no key and a body that fails validation returns
401, not400. The one body check that runs earlier is JSON syntax: a body that is not valid JSON is rejected with400(Invalid JSON in request body.) even without a key. - Requests rejected with
401or403do not count against your rate limit. - The other checks that run earlier are a malformed
Content-Lengthheader (400), the request body size cap, which returns413request_too_largeeven without a key, and routing: an unknown path (404) or an unsupported method (405) is reported before the key is checked (evaluation order).
Key lifecycle
- Activation. A key works as soon as eCourtDate activates it for your account.
- Expiry. A key may carry an expiry date. Until that date it
authenticates normally; after it, every request returns
401invalid_api_key. eCourtDate tells you the expiry date when the key is issued. - Revocation. eCourtDate revokes a key on your request or when a
compromise is suspected. A revoked key returns
401invalid_api_key. Successful verifications are cached briefly, so allow up to a minute for a revocation to take effect everywhere. - Account status. If the subscription is suspended, every key on the
account returns
401invalid_api_keyuntil it is reinstated.
Nothing about expiry or revocation is reported in the error body; the 401
is the same in every case. Treat an unexpected 401 from a key that was
working as a signal to check with eCourtDate rather than to retry.
Rotation
Rotate keys on a schedule and immediately on suspected exposure. Because keys are issued by eCourtDate, plan the rotation so there is no gap:
- Ask eCourtDate for a replacement key with the same scopes.
- Deploy the new key to every integration that used the old one.
- Confirm the integrations authenticate with the new key (a
GET /v1/modelscall is a cheap check for achatkey;GET /v1/documents?limit=1for aningestkey). - Ask eCourtDate to revoke the old key.
Both keys authenticate during the overlap, so the order above never produces
a 401 in production. Keep one key per integration: a leak then affects a
single system, and the key id in your logs and in support conversations
points at exactly one deployment.
Server-side only
API keys are server-side secrets. Never embed one in a web page, a mobile
app, or any client you do not control: anyone who can read the client can
read the key and spend your quota. Browser-direct calls are not a supported
scenario, and cross-origin requests are accepted only from eCourtDate-owned
origins. Put your own service between the browser and the API: the browser
calls your backend, your backend holds the key and calls
https://api.chatbots.ecourtdate.com/v1.
Other handling rules:
- Store keys in a secrets manager or environment variables, never in source control.
- Do not log the
Authorizationheader. Log theX-Request-IDinstead; it identifies the request to support without exposing the key. - Scope each key as narrowly as the integration needs (Scopes).
See Security for data handling beyond keys.
Errors
| Status | Code | When |
|---|---|---|
401 | invalid_api_key | No key, malformed header, unknown, expired, or revoked key, suspended account |
403 | insufficient_scope | The key lacks the scope the operation requires |
413 | request_too_large | The body exceeds the cap; checked before authentication |
429 | rate_limit_exceeded, insufficient_quota | Checked after authentication and scope |