Keldura Developer API

The Keldura Developer API gives you programmatic access to your folders, prompts, documents, and folder chat. It is authenticated with API keys you create and manage from your account.

Authentication

Every request must include a valid API key in the Authorization header using the Bearer scheme:

Authorization: Bearer YOUR_API_KEY

Generate keys from the API Keys page in settings. Each key is shown in full only once at creation time — store it somewhere safe. Keys can be deactivated at any time.

A missing or invalid key returns 401 Unauthorized with a WWW-Authenticate: Bearer challenge header.

Base URL & versioning

All endpoints below are relative to:

https://keldura.ai

The canonical developer endpoints live under /api/dev/v1. The unversioned /api/dev paths still work as deprecated aliases and respond with a Warning: 299 header — migrate to /api/dev/v1.

Pagination

List endpoints are paginated. Use the page (1-based, default 1) and per_page (default 50, max 200) query parameters. Responses wrap the items in a data array alongside a pagination object:

{
  "data": [ ... ],
  "pagination": {
    "count": 50,
    "total": 137,
    "per_page": 50,
    "total_pages": 3,
    "next_page": 2,
    "previous_page": null
  }
}

next_page and previous_page are null when there is no adjacent page.

Rate limits

Requests are rate limited per API-key owner. Every response carries the current window state:

  • x-ratelimit-limit — requests allowed per window.
  • x-ratelimit-remaining — requests left in the window.
  • x-ratelimit-reset — Unix epoch second when the window resets.

When the limit is exceeded the API responds with 429 Too Many Requests and a Retry-After header (seconds to wait).

OpenAPI spec

A machine-readable OpenAPI 3 contract is published (unauthenticated) so you can generate clients or load it into API tooling:

GET /api/dev/v1/openapi.json

Example

curl https://keldura.ai/api/dev/v1/openapi.json

Folders

List folders

Returns a page of folders owned by the authenticated user.

GET /api/dev/v1/folders?page=1&per_page=50
curl -H "Authorization: Bearer YOUR_API_KEY" \
     https://keldura.ai/api/dev/v1/folders

Create a folder

parentId is optional; the hierarchy is two levels only (a root folder plus direct subfolders), so it must reference a root folder.

POST /api/dev/v1/folders
{
  "name": "My Folder",
  "description": "Optional description",
  "parentId": "<optional-root-folder-id>"
}
curl -X POST \
     -H "Authorization: Bearer YOUR_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{"name":"My Folder"}' \
     https://keldura.ai/api/dev/v1/folders

Get a folder

GET /api/dev/v1/folders/{folderId}

Update a folder

PUT /api/dev/v1/folders/{folderId}
{
  "name": "Renamed Folder",
  "description": "Updated description"
}

Delete a folder

Permanently deletes the folder and any associated vector stores.

DELETE /api/dev/v1/folders/{folderId}

Prompts

List prompts

Returns a page of saved prompts belonging to the authenticated user.

GET /api/dev/v1/prompts?page=1&per_page=50
curl -H "Authorization: Bearer YOUR_API_KEY" \
     https://keldura.ai/api/dev/v1/prompts

Documents

Documents are folder-scoped. Each list item carries a status (syncing, synced, or sync_failed) reflecting indexing progress, plus a content_url to fetch the full document.

List documents in a folder

folder_id is required.

GET /api/dev/v1/documents?folder_id={folderId}&page=1&per_page=50

Create a document from text

Creates a text document and attaches it to the folder; indexing then proceeds asynchronously (poll the document's status). The Location response header points at the new document.

POST /api/dev/v1/documents
{
  "folder_id": "<folder-id>",
  "name": "Optional title",
  "content": "The document text."
}
curl -X POST \
     -H "Authorization: Bearer YOUR_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{"folder_id":"<folder-id>","content":"Hello"}' \
     https://keldura.ai/api/dev/v1/documents

Create a document from a file

Multipart upload. Text is extracted server-side (PDF, DOCX, PPT, images, or plain text), stored as a document, attached to the folder, and indexed asynchronously. Pass overwrite=true to replace an existing same-name document instead of receiving 409 Conflict.

POST /api/dev/v1/documents/file?folder_id={folderId}&overwrite=false
curl -X POST \
     -H "Authorization: Bearer YOUR_API_KEY" \
     -F "file=@notes.pdf" \
     "https://keldura.ai/api/dev/v1/documents/file?folder_id=<folder-id>"

Get a document

Returns the document including its inline content.

GET /api/dev/v1/documents/{documentId}

Remove a document from a folder

Soft-deletes the document's link to the given folder. folder_id is required.

DELETE /api/dev/v1/documents/{documentId}?folder_id={folderId}

Chat sessions

A chat session is scoped to one or more folders and an optional set of saved prompts. The response includes the session id you use to send and poll messages.

List chat sessions

GET /api/dev/v1/chats?page=1&per_page=50

Create a chat session

All fields are optional; omitted arrays default to empty.

POST /api/dev/v1/chats
{
  "name": "My Chat",
  "usedFolders": ["<folder-id>"],
  "usedPrompts": ["<prompt-id>"]
}
curl -X POST \
     -H "Authorization: Bearer YOUR_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{"name":"My Chat","usedFolders":["<folder-id>"],"usedPrompts":[]}' \
     https://keldura.ai/api/dev/v1/chats

Get a chat session

GET /api/dev/v1/chats/{sessionId}

Update a chat session

All fields optional; provided fields replace the current values.

PUT /api/dev/v1/chats/{sessionId}
{
  "name": "Renamed Chat",
  "usedFolders": ["<folder-id>"],
  "usedPrompts": ["<prompt-id>"]
}

Delete a chat session

Soft-deletes (reversible) the chat session.

DELETE /api/dev/v1/chats/{sessionId}

Messages

Send a chat message

Submits a user message to an existing chat session and starts an asynchronous answer. Supply an optional requestId (a UUID) to correlate the result; otherwise the response returns the server-generated one. Poll the next endpoint to read the assistant answer once it is ready.

POST /api/dev/v1/chats/{sessionId}/messages
{
  "text": "What is this folder about?",
  "requestId": "<optional-client-generated-uuid>"
}
curl -X POST \
     -H "Authorization: Bearer YOUR_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{"text":"What is inside this folder?"}' \
     https://keldura.ai/api/dev/v1/chats/<session-id>/messages

Poll for a chat answer

Returns the current status and (when complete) the assistant answer with citations.

GET /api/dev/v1/chats/{sessionId}/messages/{requestId}

Response shape

  • 200 OK with { "status": "PENDING" | "PROCESSING", "requestId": "..." } — still running, poll again shortly.
  • 200 OK with { "status": "COMPLETED", "requestId": "...", "result": { ... } } — payload includes the answer and citations.
  • 500 Internal Server Error with a plain-text body containing the error message — the request failed.
curl -H "Authorization: Bearer YOUR_API_KEY" \
     https://keldura.ai/api/dev/v1/chats/<session-id>/messages/<request-id>

Errors

  • 400 Bad Request — malformed request (e.g. a blank folder name or invalid body).
  • 401 Unauthorized — missing or invalid API key (returns a WWW-Authenticate: Bearer header).
  • 403 Forbidden — the authenticated user is not allowed to access the resource, or a plan/quota limit was reached.
  • 404 Not Found — the requested folder, document, chat session, or message could not be found.
  • 409 Conflict — a document with the same file name already exists (pass overwrite=true).
  • 410 Gone — the chat session has expired (soft-deleted).
  • 429 Too Many Requests — rate limit exceeded; see the Retry-After header.
  • 500 Internal Server Error — unexpected server-side failure; the response body contains the error message.

Need help?

Questions, bug reports, or feature requests are welcome at hello@wavelylabs.com.

© WavelyLabs. All rights reserved.