Agent API
Call one of your agents from your own server. You send a question, we answer it from that agent’s documents and name the documents the answer came from.
We will not break a working integration without telling you first.
Server-side only
Getting a key
Open an agent in the dashboard and create a key under API access. Keys look like nsk_live_… and belong to one agent — a key for your support agent cannot query your legal agent.
The key is shown once. We store a hash of it, so there is no way to display it again. If you lose it, revoke it and create another.
You can hold several keys per agent and revoke them one at a time, which is what makes rotation possible with no downtime: create the new key, deploy it, then revoke the old one.
Available on paid plans. Owners and admins can manage keys.
What we store
Nothing from the conversation. No messages, no questions, no data about the person asking. Your users are your users.
We record one row per call for billing and support: which key, which agent, how many tokens, whether it succeeded, and when. That is for your invoice, not for the content.
This is why history exists in the request below — we have no memory of the previous turn, so if you want follow-up questions to work, you send the recent turns yourself.
Making a request
POST https://api.nenosource.com/api/v1/agents/{agentId}/chat
Authorization: Bearer nsk_live_...
Content-Type: application/json{
"message": "What is our refund window?",
"history": [
{ "role": "user", "content": "Hi" },
{ "role": "assistant", "content": "How can I help?" }
]
}| Field | Type | Notes |
|---|---|---|
message | string, required | The question. 2–4000 characters. |
history | array, optional | Recent turns from your own chat, oldest first. Roles are “user” or “assistant” only. |
conversationId | string, reserved | Accepted and ignored today. Declared now so we can add server-side conversations later without breaking you. |
History rules
History tokens count toward your plan’s usage. Everything you send is re-read by the model on every request, so a long history makes every call cost more. Send the last few turns, not the whole conversation.
Too much history is rejected, not trimmed. Over 20 turns, or roughly 4000 tokens, answers 400. We do not quietly drop the rest — a request that silently stopped reading half its input would produce answers you could not explain.
role: "system"is rejected. The agent’s own instructions are configured in the dashboard and cannot be overridden per request.
The response
{
"success": true,
"data": {
"agentId": "018f8c1f-...",
"answer": "Refunds are accepted within 30 days of purchase.",
"sources": ["refund-policy.pdf"]
}
}| Field | Type | Notes |
|---|---|---|
agentId | string | The agent that answered. Echoed so one handler can serve several agents. |
answer | string | Plain markdown, ready to show your user. No citation markers. |
sources | string[] | Names of the documents the answer was built from, best match first, no duplicates. Empty when nothing matched. |
The answer is prose, not annotated text. Internally the model is asked to cite each claim, which is what keeps it inside your documents rather than answering from its own training — but those markers are stripped before the answer reaches you. Render it as-is.
sources lists every document the answer drew on, not only the ones the model happened to mention. If it is empty, nothing in your indexed documents matched the question — check the file finished indexing.
Errors
Failures use the same envelope as every response: { "success": false, "error": { "code", "message" } }. Branch on code, never on message — the codes are stable and the wording is not.
| Status | Code | What happened |
|---|---|---|
400 | VALIDATION_FAILED | Malformed body, a system role in history, or history over the limit. |
401 | UNAUTHENTICATED | Missing, malformed, unknown or revoked key. |
402 | LIMIT_EXCEEDED | The workspace is out of monthly usage, or its plan does not include the API. |
404 | NOT_FOUND | No such agent — including when the key belongs to a different one. |
409 | CONFLICT | The agent is archived and cannot answer. |
429 | RATE_LIMITED | Too many requests for this key. Honour Retry-After. |
503 | SERVICE_UNAVAILABLE | Temporarily off. Retry with backoff rather than failing permanently. |
Rate limits
Each key is limited independently, so one runaway integration cannot exhaust another. The default is 60 requests per minute per key; a 429 carries Retry-After.
Separately, your plan’s monthly usage applies. Running out answers 402, which retrying will not fix — it means upgrade or wait for the period to reset.
Not available yet
Browser-safe publishable keys, session tokens for a website widget, domain allow-lists, and conversations stored on our side. Those arrive together with the embeddable widget.
Until then, conversationId is accepted and ignored, so adding it later will not break anything you write today.