API
HTTP API-то на кое се потпираат сите клиенти на Nessie и како да се автентицирате со него.
Што е API
Nessie има еден HTTP API. Админ веб-апликацијата, клиентите за macOS и iOS и далечинскиот демон на извршителот се обични клиенти на него, и ништо што тие можат да направат не е достапно на друг начин. Тоа е JSON API над Fastify сервер, не е GraphQL и нема посебно „јавно“ подмножество: површината што ја користат вашите сопствени скрипти е истата површина што ја користи производот.
Во патеката нема сегмент за верзија. Сите рути се под /api, освен влезната MCP крајна точка POST /mcp и неколку документи за откривање под /.well-known/. Компатибилноста се одржува со тоа што секогаш само се додаваат полиња, па клиент што ги игнорира непознатите полиња продолжува да работи.
Основна адреса
Nessie се хостира сам, па основната адреса е изворот (origin) на кој вашето распоредување го служи API-то — операторот ја поставува како NESSIE_API_PUBLIC_URL, а истата вредност ја користи API-то кога треба да изгради сопствена апсолутна адреса. На инстанцата што ја управуваме, таа е https://api.nessie.works; развојната инстанца стартувана со pnpm dev одговара на http://localhost:5454.
Админ веб-апликацијата е друг извор од API-то, па клиентите во прелистувачот се подложени на CORS листа на дозволени (NESSIE_CORS_ORIGINS). Барањата од скрипта или од сервер немаат извор што треба да се провери и не се засегнати.
Автентикација
Секое барање, освен мал број намерно јавни рути, носи токен за пристап во стандардното заглавие:
Authorization: Bearer <token>Токенот е потпишан JWT што го издава API-то. Краткотраен е — стандардно триесет минути (NESSIE_AUTH_TOKEN_TTL) — и е единствената акредитација што обичниот повикувач некогаш ја прикажува. Nessie нема долготрајни персонални API клучеви. Ако сакате скрипта да го повикува API-то, таа се најавува на ист начин како прелистувачот на човекот и го обновува својот токен додека работи.
Добивање токен
POST /api/auth/session создава сесија. Одговара со токенот за пристап и идентитетот на повикувачот и го поставува токенот за обновување како посебно HTTP-only колаче.
Начинот на кој ќе докажете кој сте зависи од тоа како е распоредена инстанцата. Инстанца што работи во режимот local прифаќа е-пошта и лозинка. Кај инстанца hosted или selfHosted најавата со лозинка е директно одбиена со 403 PASSWORD_AUTH_DISABLED, а единствениот пат е конфигурираниот провајдер за идентитет — така сметката никогаш не може да добие втор, послаб пат за идентитет покрај него.
Најава преку провајдер за идентитет
Единствената најава (SSO) е размена на код за авторизација преку OAuth со PKCE, во три чекори. Сопствениот провајдер за идентитет на Nessie е UnlikeOtherAI, кој ја поседува и структурата на организацијата и тимовите што ја огледува инстанцата; размената подолу има иста форма за секој конфигуриран провајдер.
GET /api/auth/providersги наведува провајдерите што ги прифаќа оваа инстанца, секој соproviderId, ознака и тоа дали страницата за најава треба автоматски да пренасочи кон него.GET /api/auth/providers/:providerId/authorize, соcodeChallenge,redirectUriиstateкако параметри во адресата, одговара со{ data: { authorizeUrl } }. Пренасочете го лицето таму.- Провајдерот го враќа лицето на вашиот
redirectUriсо код. Испратете го тоа назад какоPOST /api/auth/sessionсо{ providerId, code, codeVerifier, redirectUri }— сите четири се задолжителни заедно, а нецелосен збир дава400 EXTERNAL_AUTH_INCOMPLETE.
Обновување на сесија
Токенот за обновување никогаш не е во телото на одговорот. Се поставува како HTTP-only колаче со име nessie_refresh, ограничено на патеката /api/auth, со стандардна важност од триесет дена (NESSIE_AUTH_REFRESH_TOKEN_TTL). Повикот на POST /api/auth/refresh со тоа колаче го троши, издава нов токен за пристап и на негово место поставува ново колаче — токените се ротираат, па украден токен е валиден само до следното обновување на вистинскиот клиент.
Прелистувач најавен на повеќе сметки чува по едно такво колаче за секоја најава, со име nessie_refresh_<slot>, и со заглавието x-nessie-account-slot ја наведува оној на кого се однесува барањето. Клиент што не испраќа такво заглавие се однесува на nessie_refresh, што е сè што му треба на клиент од командна линија.
Затоа на клиентот од командна линија му треба складиште за колачиња. Со curl тоа е --cookie-jar при повикот за најава и --cookie при обновувањето, како во примерот подолу.
Што се проверува при секое барање
Автентикацијата не е само проверка на потпис. Пред да се изврши обработувачот, API-то го проверува потписот и рокот на токенот, потврдува дека сесијата не е поништена (и со верзијата на токенот за целиот корисник и со поединечниот ид на сесијата) и го вчитува членството на повикувачот во организацијата што ја наведува токенот. Деактивирано членство е 403. Кога организацијата се потпира на надворешен провајдер за идентитет, моменталниот статус на повикувачот се проверува повторно кај тој провајдер при барањето, а не се чита од токенот, па пристапот укинат однадвор престанува веднаш, а не при следното истекување.
Акредитиви што не се токени за сесија
Постојат уште два bearer акредитиви, а ниту еден не е клуч за општа намена. Акредитивот на гласовен уред се прифаќа само на рутите за гласовни повици; акредитивот за пристап на агент се прифаќа само на MCP крајната точка. Прикажување на кој било од нив на друго место се одбива со 403 што го именува акредитивот, а не се обидува повторно како послаба сесија — видете MCP и поврзани алатки за тоа како се издава акредитивот на агентот.
Формат на одговорот
Секој успешен одговор е објект со клуч data. Нема гола низа и нема податоци на највисокото ниво, па клиентот никогаш не мора да прави разлика според обликот:
{
"data": { "id": "3f1c…", "label": "engineering" }
}Крајните точки со списоци додаваат објект meta покрај него. На највисокото ниво никогаш не се додава ништо друго.
И грешките користат еден облик, без оглед на тоа што ги предизвикало:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "A message needs content or at least one attachment",
"field": "body",
"details": { "formErrors": [], "fieldErrors": {} }
}
}code е делот што го чита машината и според кој треба да се одлучува; message е за човекот. field и details се присутни кога грешката е доволно специфична за да се лоцира. Необработена серверска грешка е секогаш 500 INTERNAL_ERROR со фиксна порака — основниот текст на грешката се запишува во дневникот, никогаш не се враќа, бидејќи може да содржи податоци од барањето.
Кодови на грешки што прво ќе ги сретнете
| Статус | Код | Значење |
|---|---|---|
| 400 | VALIDATION_ERROR | Телото или барањето не одговараат на шемата. |
| 401 | AUTH_REQUIRED | Нема bearer токен, или заглавието е неисправно. |
| 401 | TOKEN_EXPIRED | Токенот за пристап е истечен. Обновете го и обидете се повторно. |
| 401 | TOKEN_REVOKED | Сесијата е одјавена, или сите сесии се принудно прекинати. |
| 403 | ACCOUNT_DEACTIVATED | Членството во оваа организација е деактивирано. |
| 404 | NOT_FOUND | Нема таква рута, или нема таков запис видлив за повикувачот. |
| 429 | RATE_LIMITED | Кофата е полна. Прочитајте Retry-After. |
| 500 | INTERNAL_ERROR | Необработена серверска грешка, намерно нејасна. |
Страничење
Крајните точки со списоци се страничат со непрозирен keyset курсор, а не со офсет — редови се вметнуваат додека некој чита, а со офсет втората страница тивко повторува запис што првата веќе го прикажала.
Прифаќаат три параметри: cursor, limit и direction (forward или backward). Стандардната страница е 25, а горната граница е 100; поголема вредност се сведува на границата, а не се одбива. Крајните точки што уште и подредуваат или филтрираат додаваат sort, order и слободно текстуално q.
meta на страницата ви кажува каде сте:
{
"data": [ … ],
"meta": {
"hasMore": true,
"nextCursor": "2026-09-12T08:31:44.005Z|8b2f…",
"prevCursor": null,
"total": 134
}
}nextCursor и prevCursor се и двете вистински: Претходна и Следна се контроли што човекот ги очекува. Тие се непрозирни — серверот во нив го кодира сопствениот клуч за подредување, а клиент што ќе ги расчлени или составува ќе се скрши во моментот кога списокот ќе промени начин на подредување. total е присутен кај повеќето списоци и се изоставува таму каде бројењето нема смисла, како кај рангирани резултати од пребарување.
Ограничувања на бројот барања
Ограничувањата важат по фиксен прозорец и се применуваат пред дури и да се прочита телото на барањето. Одбиено барање е 429 со код RATE_LIMITED и заглавие Retry-After во секунди.
Повеќето кофи се врзани за IP адресата на клиентот; оние осетливи на акредитиви — најава, обновување, повторно докажување на лозинка, запишување MCP акредитиви — покрај тоа водат бројач по сметка и одбиваат кога било кој ќе се активира, па и една адреса што распрсне многу сметки и една сметка нападната од многу адреси наидуваат на ѕид. Најавата стандардно дозволува 10 обиди по IP и 5 по адреса на е-пошта во десет минути; објавувањето порака стандардно е 60 во минута; запишувањата под /api/agents се 60 во минута. Секој праг може да се конфигурира по распоредување, затоа овие вредности земете ги како испорачани стандардни вредности, а не како договор. Проверките на здравјето и стриминзите во живо се изземени.
Реално време
Повторното прашување на список не е добар начин да се следи разговор. Наместо тоа постојат три крајни точки за стриминг:
GET /api/events/stream— server-sent events за сè што најавеното лице може да види.GET /api/threads/:threadId/stream— server-sent events за една нишка. Го почитуваLast-Event-ID, па повторното поврзување продолжува, а не започнува од почеток.GET /api/activity— WebSocket што ја носи листата на активности.
Прелистувачите не можат да поставуваат заглавија при WebSocket ракување, па WebSocket рутата го прифаќа токенот за пристап и како параметар ?token= во адресата. Тоа е единственото место каде токенот припаѓа во адреса; сите други рути го примаат во заглавието.
Семејства на ресурси
Има над илјада крајни точки, што е премногу за да се набројат. Групирани се во семејства, а штом го знаете префиксот на семејството, поединечните рути се предвидливи — колекција на префиксот, запис под неа и именувани дејства како подпатеки POST.
| Семејство | Префикс | Што опфаќа |
|---|---|---|
| Автентикација | /api/auth | Најава, обновување, одјава, тековниот идентитет, сопствените сесии на лицето. |
| Канали и нишки | /api/channels, /api/threads | Моделот на разговор: каналите содржат нишки, нишките содржат пораки, одговорите се гнездат под корен. |
| Агенти | /api/agents | Дефиниции на агенти, нивните врски со канали, разговори, задачи и статус. |
| Проекти и табли | /api/projects, /api/tasks, /api/iterations | Следење на работа: табли и колони под еден проект, задачи на нив и итерации над нив. |
| Знаење | /api/knowledge-base | Простори со верзионирани страници и датотеки, со коментари, повратни врски и пребарување. |
| Пошта | /api/mail, /api/mailbox-connections | Поврзани поштенски сандачиња, поштенски сандачиња на агенти и нацрти што агент ги напишал за лице да ги одобри. |
| Одобренија | /api/approvals | Редот на дејства што агент не смее да ги преземе самостојно и единствениот повик со кој се решаваат. |
| Ревизија | /api/audit-log | Ревизискиот запис на организацијата, поврзан со хеш-синџир, со крајна точка за проверка. Сопственици и администратори. |
| Трошоци и користење | /api/ledger, /api/billing | Евиденција на токени и трошоци, буџети и времиња на извршување; посебно, кредити и полнења. Сопственици и администратори. |
| Организација и тимови | /api/organizations, /api/teams, /api/users | Администрација на тенантот, тимовите и членствата, вклучително и поканите. |
| Активатори | /api/triggers | Распореди и webhook-и што ја започнуваат работата на агентите, со нивната историја на активирања. |
| Повици и глас | /api/calls, /api/voice | Повици во каналот, и сесијата, транскриптот и токените на уредот на гласовниот асистент. |
| Извршители | /api/executors, /api/execution-environment-templates | Машините на кои работат агентите, нивните средини и каналот за спарување на демонот. |
| Извршувања | /api/runs | Агентски извршувања во живо: откажување, продолжување, рестарт, врзување за извршител. |
| Качувања | /api/uploads, /api/attachments | Прво качете датотека, па врзете го вратениот ид со порака. |
| Пребарување | /api/messages/search | Пребарување на пораки, на ниво на организација или ограничено на еден канал. |
| Тајни и поставки | /api/secrets, /api/settings/scoped | Складиште за акредитиви само за запишување со експлицитни дозволи, и слоевити конфигурациски вредности. |
| Поврзани алатки | /api/mcp | MCP сервери на кои оваа инстанца им се јавува. Видете MCP и поврзани алатки. |
| Здравје | /api/health | Живост и подготвеност, двете јавни. /api/health/ready ја проверува и базата на податоци. |
Правата се применуваат по рута, а не по семејство. Во основа: ревизискиот дневник, книгата на трошоците, правилата на политиката и контролната рамнина на инференцијата се површини за сопствениците и администраторите на организацијата, додека разговорите, проектите, знаењето и одобренијата се отворени за секој член и се стеснуваат според тоа што тој член навистина може да го види.
Работни примери
Најавете се и зачувајте го колачето за обновување
На инстанца во режим local, со лозинка. Токенот за пристап се враќа во телото; токенот за обновување завршува во складиштето за колачиња.
curl -sS https://api.example.com/api/auth/session \
--cookie-jar nessie.cookies \
-H 'Content-Type: application/json' \
-d '{"email":"ada@example.com","password":"…"}'{
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…",
"me": {
"user": {
"id": "1d4e…",
"email": "ada@example.com",
"displayName": "Ada Lovelace",
"roleIds": ["owner"],
"superAdmin": false
},
"session": {
"sessionId": "9c07…",
"issuedAt": "2026-09-16T09:00:00.000Z",
"expiresAt": "2026-09-16T09:30:00.000Z"
},
"context": {
"organizationId": "5a81…",
"projectId": "7b12…",
"teamId": "2e60…",
"channelId": null,
"bootstrapMode": false
},
"auth": {
"providerId": "local",
"providerType": "local-bootstrap",
"autoRedirectToSso": false
}
}
}
}Кога токенот ќе истече, разменете го колачето за нов:
curl -sS -X POST https://api.example.com/api/auth/refresh \
--cookie nessie.cookies --cookie-jar nessie.cookiesОдговорот има ист облик { token, me }, а складиштето за колачиња сега го држи ротираниот токен за обновување.
Наведете ги каналите што ги гледате
curl -sS https://api.example.com/api/channels \
-H "Authorization: Bearer $TOKEN"{
"data": [
{
"id": "8b2f…",
"label": "engineering",
"type": "standard",
"visibility": "public",
"projectId": "…",
"projectName": "Platform",
"teamId": "…",
"defaultThreadId": "c41a…",
"unreadCount": 3,
"lastMessageAt": "2026-09-15T18:22:07.441Z",
"viewerCanManage": true
}
]
}Додајте ?teamId=… за да се стеснат на еден тим, или ?includeArchived=true за да се вклучат архивираните канали. defaultThreadId е оној каде следно ќе објавите.
Објавете порака
Пораката треба или некоја content, или барем еден качен прилог. Испратете Idempotency-Key, па повторено барање се решава во пораката што ја создал првиот обид, наместо да објави втора копија.
curl -sS https://api.example.com/api/threads/c41a…/messages \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: 0f9c2d16-8f4a-4c1e-9a77-6b0d4f2e1a53' \
-d '{"content":"Deploying the migration now."}'Нова порака е 201:
{
"data": {
"message": {
"id": "d70e…",
"threadId": "c41a…",
"role": "user",
"content": "Deploying the migration now.",
"createdAt": "2026-09-16T09:04:11.882Z"
},
"pendingAgentInvites": []
}
}Повторување со истиот клуч за идемпотентност одговара со 200 со оригиналната порака и празно pendingAgentInvites, бидејќи првиот обид веќе ги направил известувањето и испраќањето што ги опишува низата.
Две одбивања на оваа рута заслужуваат експлицитна обработка. Содржина над ограничувањето на должината на разговорот е 413 MESSAGE_TOO_LARGE, а правилниот одговор е текстот да се качи како датотека. Содржина што структурно личи на акредитив е 422 SECRET_INTERCEPTED — серверот одбива пред пораката да се зачува, испрати или запише во дневник, а акредитивот припаѓа во /api/secrets.