API
A HTTP API, amely minden Nessie-kliens mögött van, és az, hogyan hitelesítheted magad vele szemben.
Mi az API
A Nessie-nek egyetlen HTTP API-ja van. Az admin webalkalmazás, a macOS- és iOS-kliensek és a távoli executor-démon mind ugyanennek az API-nak a hagyományos kliensei, és amit csak tesznek, azt máshogy nem lehet elérni. Ez egy JSON API, amely Fastify szerveren fut, nem GraphQL, és nincs külön „nyilvános” részhalmaza: amelyet a saját szkriptjeid hívnak, az ugyanaz a felület, amelyet a termék is hív.
Az útvonalban nincs verziószegmens. Minden útvonal a /api alatt van, kivéve a bejövő MCP-végpontot, a POST /mcp-t, és néhány /.well-known/ felfedező dokumentumot. A kompatibilitást úgy tartjuk fenn, hogy csak mezőket adunk hozzá, így az a kliens, amely figyelmen kívül hagyja az általa nem ismert dolgokat, továbbra is működik.
Alap-URL
A Nessie saját üzemeltetésű, ezért az alap-URL az az origin, amelyen a telepítésed kiszolgálja az API-t. Az üzemeltető a NESSIE_API_PUBLIC_URL értékét állítja be, és ugyanezt használja az API akkor is, amikor saját abszolút URL-t kell létrehoznia. A nálunk futó példányon ez a https://api.nessie.works; a pnpm dev paranccsal indított fejlesztői példány a http://localhost:5454 címen válaszol.
Az admin webalkalmazás más origin, mint az API, ezért a böngészős kliensekre CORS-engedélyezési lista (NESSIE_CORS_ORIGINS) vonatkozik. Szkriptből vagy szerverről érkező kérésnek nincs ellenőrizendő origin-je, ezért ezeket nem érinti.
Hitelesítés
A néhány szándékosan nyilvános útvonaltól eltekintve minden kérés szabványos fejlécben viszi a hozzáférési tokent:
Authorization: Bearer <token>A token az API által kiállított, aláírt JWT. Rövid életű — alapértelmezetten harminc perc (NESSIE_AUTH_TOKEN_TTL) —, és ez az egyetlen hitelesítő adat, amelyet egy átlagos hívó valaha is bemutat. A Nessie-nek nincsenek hosszú életű személyes API-kulcsai. Ha azt szeretnéd, hogy egy szkript hívja az API-t, ugyanúgy jelentkezzen be, ahogyan a személy böngészője teszi, és közben frissítse a tokenjét.
Token megszerzése
A POST /api/auth/session munkamenetet hoz létre. Válaszként visszaadja a hozzáférési tokent és a hívó identitását, a megújító tokent pedig külön, csak HTTP-s sütiben állítja be.
Az, hogy hogyan igazolod magad, attól függ, hogyan van telepítve a példány. A local módban futó példány e-mail címet és jelszót fogad el. hosted vagy selfHosted példányon a jelszavas bejelentkezést a rendszer egyszerűen elutasítja, a 403 PASSWORD_AUTH_DISABLED válasszal, és az egyetlen bejutási út a beállított identitásszolgáltató — így a fióknak sosem nőhet ki mellette második, gyengébb identitási útja.
Bejelentkezés identitásszolgáltatón keresztül
Az egyszeri bejelentkezés (SSO) egy OAuth engedélyezési kódcsere PKCE-vel, három lépésben. A Nessie saját identitásszolgáltatója az UnlikeOtherAI, amely a példány által tükrözött szervezeti és csapatstruktúrát is birtokolja; az alábbi csere alakja bármely beállított szolgáltatónál ugyanaz.
- A
GET /api/auth/providersfelsorolja, mely szolgáltatókat fogadja el ez a példány; mindegyiknél megadja aproviderId-t, a címkét, és azt, hogy a bejelentkezési oldal automatikusan átirányít-e oda. - A
GET /api/auth/providers/:providerId/authorizevégpont acodeChallenge, aredirectUriés astatelekérdezési paraméterekkel dolgozik, és a{ data: { authorizeUrl } }választ adja. Küldd oda a személyt. - A szolgáltató egy kóddal visszaküldi a személyt a
redirectUri-jukra. Ezt küldd visszaPOST /api/auth/session-ként a{ providerId, code, codeVerifier, redirectUri }tartalommal — mind a négy értéket együtt kell megadni, a hiányos készlet pedig400 EXTERNAL_AUTH_INCOMPLETEhibát ad.
Munkamenet megújítása
A megújító token soha nincs a válasz törzsében. nessie_refresh néven HTTP-s sütiként állítják be, a /api/auth útvonalra korlátozva, alapértelmezetten harminc napos élettartammal (NESSIE_AUTH_REFRESH_TOKEN_TTL). A POST /api/auth/refresh hívás ezzel a sütivel felhasználja a tokent, új hozzáférési tokent ad ki, és új sütit állít a régi helyére — a tokenek forognak, így az ellopott token csak addig érvényes, amíg az igazi kliens legközelebb nem frissít.
Több fiókba bejelentkezett böngésző minden bejelentkezéshez tart egy ilyen sütit, nessie_refresh_<slot> néven, és az x-nessie-account-slot fejléccel nevezi meg, hogy a kérés melyikre vonatkozik. Az a kliens, amely nem küld ilyen fejlécet, a nessie_refresh-re vonatkozik, ami egy parancssori kliens számára elegendő.
A parancssori kliensnek ezért sütitároló (cookie jar) kell. A curl-lel ez a bejelentkező hívás --cookie-jar, a frissítésé pedig --cookie kapcsolóval jár, ahogyan az alábbi kidolgozott példában látható.
Mit ellenőriz minden kérés
A hitelesítés nem csak aláírás-ellenőrzés. Mielőtt a kezelő lefut, az API ellenőrzi a token aláírását és lejáratát, megerősíti, hogy a munkamenetet nem vonták-e vissza (mind a felhasználó-szintű tokenverzió, mind az egyedi munkamenet-azonosító alapján), és betölti a hívó tagságát a tokenben megnevezett szervezetben. A deaktivált tagság 403-at ad. Ha a szervezet külső identitásszolgáltatóra épül, a hívó aktuális állapotát a kérésnél ellenőrzik az adott szolgáltatónál, ahelyett hogy a token tartalmából olvasnák, így a felsőbb szinten visszavont hozzáférés azonnal megszűnik, nem csak a következő lejáratkor.
Nem munkamenet-tokenek
Két másik hozzáférési hitelesítő adat létezik, és egyik sem általános célú kulcs. Egy hangeszköz-hitelesítő adatot csak a hanghívási útvonalak fogadnak el, egy ügynök-hozzáférési hitelesítő adatot pedig csak az MCP-végpont. Másutt bemutatva mindkettő 403-as hibát kap, amely megnevezi a hitelesítő adatot, és nem próbálják gyengébb munkamenetként újra — lásd: MCP és csatlakoztatott eszközök, ott olvasható, hogyan adják ki az ügynök-hozzáférési hitelesítő adatot.
A válasz szerkezete
Minden sikeres válasz egy data kulcsot tartalmazó objektum. Nincs csupasz tömb, és nincs felső szintű adat a gyökérben, így a kliensnek sosem kell alak szerint döntenie:
{
"data": { "id": "3f1c…", "label": "engineering" }
}A listavégpontok ezen kívül egy meta objektumot adnak hozzá. A felső szinten soha nem kerül más.
A hibák is egyetlen alakot követnek, bármi okozta is őket:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "A message needs content or at least one attachment",
"field": "body",
"details": { "formErrors": [], "fieldErrors": {} }
}
}A code a gépi olvasható rész, és ez alapján érdemes elágaznod; a message embernek szól. A field és a details akkor szerepel, ha a hiba elég konkrét a helymeghatározáshoz. Egy kezeletlen szerverhiba mindig 500 INTERNAL_ERROR rögzített üzenettel érkezik — a mögöttes hibaszöveg naplózódik, de sosem kerül vissza, mert útközben a kérés adatait is felszedhette.
Az első hibakódok, amelyekkel találkozni fogsz
| Állapot | Kód | Jelentés |
|---|---|---|
| 400 | VALIDATION_ERROR | A törzs vagy a lekérdezés nem felelt meg a sémának. |
| 401 | AUTH_REQUIRED | Nincs hordozó (bearer) token, vagy a fejléc sérült. |
| 401 | TOKEN_EXPIRED | A hozzáférési token lejárt. Frissíts, és próbáld újra. |
| 401 | TOKEN_REVOKED | A munkamenetet kijelentkeztették, vagy minden munkamenetet kényszerítve megszüntettek. |
| 403 | ACCOUNT_DEACTIVATED | Ennek a szervezetnek a tagsága deaktiválva lett. |
| 404 | NOT_FOUND | Nincs ilyen útvonal, vagy nincs ilyen rekord, amelyet ez a hívó láthat. |
| 429 | RATE_LIMITED | Egy keret megtelt. Olvasd ki a Retry-After fejlécet. |
| 500 | INTERNAL_ERROR | Kezeletlen szerverhiba, szándékosan általános szöveggel. |
Lapozás
A listavégpontok átlátszatlan, kulcsalapú (keyset) kurzorral lapoznak, nem eltolásos (offset) módon — a sorok akkor is beszúródnak, amikor valaki olvassa őket, és eltolással a második oldal csendben megismétel egy rekordot, amelyet az első oldal már megmutatott.
Három lekérdezési paramétert fogadnak el: cursor, limit és direction (forward vagy backward). Az alapértelmezett oldalméret 25, a felső határ 100; a nagyobb értéket levágják, nem utasítják el. A rendezést vagy szűrést is támogató végpontok még a sort, order és a szabadszöveges q paramétert adják hozzá.
Az oldal meta része megmondja, hol tartasz:
{
"data": [ … ],
"meta": {
"hasMore": true,
"nextCursor": "2026-09-12T08:31:44.005Z|8b2f…",
"prevCursor": null,
"total": 134
}
}A nextCursor és a prevCursor egyaránt valódi: az Előző és a Következő is olyan gomb, amelyet a személy vár. Átlátszatlanok — a szerver saját rendezési kulcsát kódolja beléjük, és az a kliens, amely megfejti vagy összerakja őket, el fog törni, amint a lista megváltoztatja a rendezését. A total a legtöbb listán megvan, és ott hagyják ki, ahol a számlálás nem értelmes, például rangsorolt keresési eredményeknél.
Sebességkorlátok
A korlátok rögzített ablakokon belül érvényesek, és még a kérés törzsének beolvasása előtt érvényesülnek. Az elutasított kérés 429 válasz, RATE_LIMITED kóddal és másodpercben megadott Retry-After fejléccel.
A legtöbb keret a kliens IP-címéhez kötött. A hitelesítésre érzékeny keretek — bejelentkezés, megújítás, jelszó-újraigazolás, MCP-hitelesítő adat írása — emellett fiókonkénti számlálót is tartanak, és már akkor elutasítanak, ha bármelyik átlépődik, így az egy címről sok fiókot támadó, és az egy fiókot sok címről támadó kísérlet is falba ütközik. A bejelentkezés alapértelmezésként IP-címenként 10, e-mail-címenként 5 próbálkozást enged tíz percenként; üzenetküldés percenként 60; a /api/agents alatti írások percenként 60. Minden küszöb telepítésenként állítható, ezért ezeket a szállított alapértelmezésként kezeld, nem szerződésként. Az állapotellenőrzések és a valós idejű adatfolyamok mentesek.
Valós idejű
Egy beszélgetés követése listalekérdezéssel rossz módszer. Ehelyett három adatfolyam-végpont van:
GET /api/events/stream— szerveroldali események (SSE) mindarról, amit a bejelentkezett személy láthat.GET /api/threads/:threadId/stream— szerveroldali események egyetlen szálhoz. Tiszteletben tartja aLast-Event-IDfejlécet, így az újracsatlakozás folytatódik, nem indul újra.GET /api/activity— WebSocket, amely az aktivitási hírfolyamot viszi.
A böngészők nem tudnak fejlécet beállítani a WebSocket-kézfogásnál, ezért a WebSocket-útvonal a hozzáférési tokent ?token= lekérdezési paraméterként is elfogadja. Ez az egyetlen hely, ahol token szerepelhet az URL-ben; minden más útvonal fejlécet fogad el.
Az erőforráscsaládok
Jóval több mint ezer végpont van, ami túl sok ahhoz, hogy felsoroljuk őket. Családokba rendeződnek, és ha ismered egy család előtagját, az egyes útvonalak kiszámíthatók — egy gyűjtemény az előtagon, egy rekord alatta, a megnevezett műveletek pedig POST alútvonalként.
| Család | Előtag | Mit fed le |
|---|---|---|
| Hitelesítés | /api/auth | Bejelentkezés, megújítás, kijelentkezés, az aktuális identitás, és egy személy saját munkamenetei. |
| Csatornák és szálak | /api/channels, /api/threads | A beszélgetési modell: a csatornák szálakat tartalmaznak, a szálak üzeneteket, a válaszok pedig a gyökerük alá ágaznak. |
| Ügynökök | /api/agents | Ügynök-definíciók, csatornakötéseik, beszélgetéseik, teendőik és állapotuk. |
| Projektek és táblák | /api/projects, /api/tasks, /api/iterations | Munkakövetés: táblák és oszlopok egy projekt alatt, a rajtuk lévő feladatok, és ezek fölött az iterációk. |
| Tudásbázis | /api/knowledge-base | Verziózott oldalak és fájlok terei, megjegyzésekkel, visszautalásokkal és kereséssel. |
| Levelezés | /api/mail, /api/mailbox-connections | Csatlakoztatott postafiókok, ügynök-postafiókok, és azok a piszkozatok, amelyeket egy ügynök írt, hogy a személy jóváhagyja. |
| Jóváhagyások | /api/approvals | Azoknak a műveleteknek a sora, amelyeket egy ügynök nem tehet meg egyoldalúan, és az egyetlen hívás, amely ezeket megoldja. |
| Audit | /api/audit-log | A szervezet hash-láncolt naplózási nyoma, ellenőrző végponttal. Tulajdonosok és adminisztrátorok. |
| Költség és használat | /api/ledger, /api/billing | Token- és költségelszámolás, költségkeretek és futási idők; külön a kreditek és feltöltések. Tulajdonosok és adminisztrátorok. |
| Szervezet és csapatok | /api/organizations, /api/teams, /api/users | Bérlő-, csapat- és tagságkezelés, a meghívásokkal együtt. |
| Triggerek | /api/triggers | Ütemezések és webhook-ok, amelyek ügynöki munkát indítanak, a lefutási előzményeikkel együtt. |
| Hívások és hang | /api/calls, /api/voice | Csatornán belüli hívás, valamint a hangasszisztens munkamenete, átirata és eszközhitelesítő adatai. |
| Executorok | /api/executors, /api/execution-environment-templates | Azok a gépek, amelyeken az ügynökök dolgoznak, a hozzájuk tartozó környezetek, és a démon-párosítási csatorna. |
| Futások | /api/runs | Élő ügynökfutások: megszakítás, folytatás, újraindítás, kötés egy executorhoz. |
| Feltöltések | /api/uploads, /api/attachments | Előbb tölts fel egy fájlt, majd kapcsold a visszaadott azonosítót egy üzenethez. |
| Keresés | /api/messages/search | Üzenetkeresés, szervezetszintűen vagy egyetlen csatornára szűkítve. |
| Titkok és beállítások | /api/secrets, /api/settings/scoped | Csak írható hitelesítő adat-tárolás explicit jogosultságokkal, és rétegzett konfigurációs értékek. |
| Csatlakoztatott eszközök | /api/mcp | MCP-szerverek, amelyeket ez a példány hív. Lásd: MCP és csatlakoztatott eszközök. |
| Állapotellenőrzés | /api/health | Élő állapot és készenlét, mindkettő nyilvános. A /api/health/ready az adatbázist is ellenőrzi. |
A jogosultságokat útvonalanként érvényesítik, nem családonként. Nagyjából: az audit napló, a költségkönyvelés, a házirend-szabályok és az inferenciavezérlés a szervezet tulajdonosainak és adminisztrátorainak felületei; a beszélgetések, a projektek, a tudásbázis és a jóváhagyások bármely tag számára nyitottak, és arra szűkítve, amit az adott tag ténylegesen láthat.
Kidolgozott példák
Bejelentkezés, és a megújító süti megtartása
Egy local módú példányon, jelszóval. A hozzáférési token a válasz törzsében érkezik; a megújító token a sütitárolóba kerül.
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
}
}
}
}Amikor a token lejár, a sütivel kérj újat:
curl -sS -X POST https://api.example.com/api/auth/refresh \
--cookie nessie.cookies --cookie-jar nessie.cookiesA válasz ugyanaz a { token, me } alakú, a sütitároló pedig már a forgatott megújító tokent tartalmazza.
Listázd a látható csatornáidat
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
}
]
}Add hozzá a ?teamId=… paramétert, hogy egy csapatra szűkítsd, vagy a ?includeArchived=true paramétert, hogy az archivált csatornák is megjelenjenek. A defaultThreadId az, ahová legközelebb posztolsz.
Üzenet küldése
Egy üzenethez kell legalább némi content, vagy legalább egy feltöltött melléklet. Küldj Idempotency-Key fejlécet, és az újrapróbált kérés az első próbálkozás által létrehozott üzenetre oldódik fel, nem küld második példányt.
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."}'Az új üzenetre 201-es választ kapsz:
{
"data": {
"message": {
"id": "d70e…",
"threadId": "c41a…",
"role": "user",
"content": "Deploying the migration now.",
"createdAt": "2026-09-16T09:04:11.882Z"
},
"pendingAgentInvites": []
}
}Ugyanazzal az idempotencia-kulccsal érkező újrapróbálkozás 200-zal válaszol az eredeti üzenettel és egy üres pendingAgentInvites tömbbel, mert az első próbálkozás már elvégezte azt az értesítést és kiküldést, amelyet a tömb leír.
Ennél az útvonalnál két elutasítást érdemes külön kezelni. A csevegés hosszkorlátját meghaladó tartalom 413 MESSAGE_TOO_LARGE választ kap, és a helyes válasz az, hogy a szöveget fájlként töltsd fel. A hitelesítő adatra hasonlító tartalom 422 SECRET_INTERCEPTED választ kap — a szerver az üzenet tárolása, kiküldése vagy naplózása előtt elutasítja, a hitelesítő adatnak pedig a /api/secrets végponton van a helye.