nessie

API

HTTP API, kuriuo remiasi visi Nessie klientai, ir kaip jame autentifikuotis.

Kas yra API

Nessie turi vieną HTTP API. Administravimo žiniatinklio programa, macOS ir iOS klientai bei nuotolinio vykdytojo fono tarnyba yra įprasti jo klientai, ir nieko, ką jie gali daryti, nepasieksite kitu būdu. Tai JSON API, veikianti per Fastify serverį; tai ne GraphQL, ir jis neturi atskiro „viešo“ poaibio: sąsaja, kurią kviečia jūsų skriptai, yra ta pati, kurią kviečia produktas.

Kelyje versijos segmento nėra. Visi maršrutai yra po /api, išskyrus gaunamą MCP galinį tašką POST /mcp ir keletą /.well-known/ atradimo dokumentų. Suderinamumas užtikrinamas tik nuolat pridedant laukus, todėl klientas, kuris ignoruoja tai, ko neatpažįsta, toliau veikia.

Bazinis URL

Nessie talpinamas savarankiškai, todėl bazinis URL yra kilmės adresas, kuriuo jūsų diegimas teikia API. Operatorius jį nustato kaip NESSIE_API_PUBLIC_URL, o tą pačią reikšmę API naudoja, kai jam reikia sudaryti savo absoliutų URL. Mūsų diegime tai https://api.nessie.works; kūrimo diegimas, paleistas su pnpm dev, atsako adresu http://localhost:5454.

Administravimo žiniatinklio programa yra kitas kilmės adresas nei API, todėl naršyklės klientams taikomas CORS leidžiamųjų kilmių sąrašas (NESSIE_CORS_ORIGINS). Skriptų ar serverių užklausos neturi kilmės, kurią reikėtų tikrinti, todėl jos nepaveikiamos.

Autentifikavimas

Kiekviena užklausa, išskyrus nedidelį tyčia viešų maršrutų rinkinį, neša prieigos žetoną standartinėje antraštėje:

Authorization: Bearer <token>

Žetonas yra pasirašytas JWT, sukurtas API. Jis trumpai galioja — pagal numatytuosius nustatymus trisdešimt minučių (NESSIE_AUTH_TOKEN_TTL) — ir tai vienintelis prisijungimo duomenų tipas, kurį paprastas klientas kada nors pateikia. Nessie nenaudoja ilgaamžių asmeninių API raktų. Jei norite, kad skriptas kviestų API, jis prisijungia taip pat, kaip žmogaus naršyklė, ir atnaujina savo žetoną darbo metu.

Žetono gavimas

POST /api/auth/session sukuria seansą. Atsakyme pateikiamas prieigos žetonas ir prašytojo tapatybė, o atnaujinimo žetonas nustatomas kaip atskiras slapukas, pasiekiamas tik HTTP.

Tai, kaip įrodote, kas esate, priklauso nuo to, kaip diegimas paleistas. Diegimas, veikiantis local režimu, priima el. pašto adresą ir slaptažodį. Diegime hosted ar selfHosted režimu prisijungimas slaptažodžiu visiškai atmetamas su 403 PASSWORD_AUTH_DISABLED, ir vienintelis būdas įeiti yra sukonfigūruotas tapatybės teikėjas — todėl paskyra niekada negali įgyti antros, silpnesnės tapatybės šalia jo.

Prisijungimas per tapatybės teikėją

Vienkartinis prisijungimas yra OAuth autorizacijos kodo mainai su PKCE trijuose žingsniuose. Nessie tapatybės teikėjas yra UnlikeOtherAI, kuris taip pat valdo organizacijos ir komandų struktūrą, kurią atspindi diegimas; mainai žemiau yra tokie patys bet kuriam sukonfigūruotam teikėjui.

  1. GET /api/auth/providers išvardija, ką šis diegimas priima: kiekvieną su providerId, pavadinimu ir tuo, ar prisijungimo puslapis turi automatiškai nukreipti į jį.
  2. GET /api/auth/providers/:providerId/authorize su užklausos parametrais codeChallenge, redirectUri ir state atsako { data: { authorizeUrl } }. Nukreipkite žmogų ten.
  3. Teikėjas grąžina žmogų į jūsų redirectUri su kodu. Siųskite jį atgal kaip POST /api/auth/session su { providerId, code, codeVerifier, redirectUri } — visi keturi reikalingi kartu, o nepilnas rinkinys yra 400 EXTERNAL_AUTH_INCOMPLETE.

Seanso atnaujinimas

Atnaujinimo žetono niekada nėra atsakymo turinyje. Jis nustatomas kaip slapukas tik HTTP, pavadinimu nessie_refresh, apimantis tik kelią /api/auth, o numatytasis galiojimas — trisdešimt dienų (NESSIE_AUTH_REFRESH_TOKEN_TTL). Iškvietus POST /api/auth/refresh su tuo slapuku, jis sunaudojamas, išduodamas naujas prieigos žetonas ir vietoj jo nustatomas naujas slapukas. Žetonai rotuojami, todėl pavogtas žetonas galioja tik tol, kol tikrasis klientas kitą kartą atsinaujins.

Naršyklė, prisijungusi prie kelių paskyrų, kiekvienam prisijungimui laiko po tokį slapuką, pavadintą nessie_refresh_<slot>, o užklausą, kurios reikia, nurodo antrašte x-nessie-account-slot. Klientas, kuris tokios antraštės nesiunčia, kalba apie nessie_refresh, ir to pakanka komandinės eilutės klientui.

Todėl komandinės eilutės klientui reikia slapukų failo. Su curl tai — --cookie-jar prisijungimo užklausoje ir --cookie atnaujinimo užklausoje, kaip žemiau pateiktame darbiniame pavyzdyje.

Ką tikrina kiekviena užklausa

Autentifikavimas nėra vien parašo tikrinimas. Prieš paleidžiant apdorojimo funkciją API patikrina žetono parašą ir galiojimo laiką, įsitikina, kad seansas nebuvo panaikintas (tiek pagal visos naudotojo žetono versiją, tiek pagal atskirą seanso identifikatorių), ir įkelia prašytojo narystę organizacijoje, kurią nurodo žetonas. Išjungta narystė yra 403. Kai organizacija remiasi išorine tapatybės teikėju, dabartinė prašytojo būklė tikrinama pas tą teikėją kiekvienos užklausos metu, o ne skaitoma iš žetono, todėl prieiga, panaikinta išorėje, nustoja veikti iš karto, o ne kitam galiojimo termino pabaigai.

Prisijungimo duomenys, kurie nėra seanso žetonai

Egzistuoja dar du bearer prisijungimo duomenų tipai, ir nė vienas nėra bendrosios paskirties raktas. Balso įrenginio prisijungimo duomenys priimami tik balso skambučių maršrutuose; agento prieigos prisijungimo duomenys priimami tik MCP galiniame taške. Pateikus bet kurį iš jų kitur, užklausa atmetama su 403, nurodant prisijungimo duomenis, o ne bandant kaip silpnesnį seansą — žr. MCP ir prijungtos priemonės, kaip išduodami agento prisijungimo duomenys.

Atsakymo apvalkalas

Kiekvienas sėkmingas atsakymas yra objektas su raktu data. Šakninio lygio masyvo ar tiesioginio turinio nėra, todėl klientui niekada nereikia atskirti pagal formą:

{
  "data": { "id": "3f1c…", "label": "engineering" }
}

Sąrašų galiniai taškai prie jo prideda objektą meta. Viršutiniame lygyje niekas kitas nepridedama.

Klaidos turi vieną formą, nesvarbu, kas jas sukėlė:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "A message needs content or at least one attachment",
    "field": "body",
    "details": { "formErrors": [], "fieldErrors": {} }
  }
}

code yra mašinai skirta dalis, pagal ją ir reikia šakoti; message skirta žmogui. field ir details pateikiami, kai klaida yra pakankamai konkreti, kad ją būtų galima nurodyti. Nepagauta serverio klaida visada yra 500 INTERNAL_ERROR su fiksuotu pranešimu — pagrindinis klaidos tekstas įrašomas į žurnalą, bet niekada negrąžinamas, nes jis galėjo pakeliui pasiimti užklausos duomenų.

Pirmieji klaidų kodai, su kuriais susidursite

BūsenaKodasReikšmė
400VALIDATION_ERRORTurinys ar užklausa neatitiko schemos.
401AUTH_REQUIREDNėra bearer žetono arba antraštė sugadinta.
401TOKEN_EXPIREDPrieigos žetonas nebegalioja. Atnaujinkite jį ir bandykite dar kartą.
401TOKEN_REVOKEDSeansas buvo užbaigtas atsijungiant arba visi seansai priverstinai nutraukti.
403ACCOUNT_DEACTIVATEDŠios organizacijos narystė išjungta.
404NOT_FOUNDNėra tokio maršruto arba prašytojas negali matyti tokio įrašo.
429RATE_LIMITEDLimitas išnaudotas. Perskaitykite Retry-After.
500INTERNAL_ERRORNepagauta serverio klaida, tyčia neatskleidžiama.

Puslapiavimas

Sąrašų galiniai taškai puslapiuoja neskaidriu raktiniu kursoriumi, o ne poslinkiu — eilutės įterpiamos, kol kas nors skaito, o su poslinkiu antras puslapis tyliai pakartotų įrašą, kurį jau parodė pirmas.

Priimami trys užklausos parametrai: cursor, limit ir direction (forward arba backward). Numatytasis puslapis — 25, viršutinė riba — 100; didesnė reikšmė apribojama, o ne atmetama. Galiniai taškai, kurie dar rūšiuoja ar filtruoja, prideda sort, order ir laisvo teksto paiešką q.

Puslapio meta parodo, kur esate:

{
  "data": [ … ],
  "meta": {
    "hasMore": true,
    "nextCursor": "2026-09-12T08:31:44.005Z|8b2f…",
    "prevCursor": null,
    "total": 134
  }
}

nextCursor ir prevCursor abu yra tikri: „Ankstesnis“ ir „Kitas“ yra valdikliai, kurių žmogus tikisi. Jie neskaidrūs — serveris į juos įkomponuoja savo rūšiavimo raktą, todėl klientas, kuris juos analizuoja ar sudaro, nutrūks, kai sąrašas pakeis tvarką. total yra daugumoje sąrašų, o praleidžiamas ten, kur skaičiuoti nėra prasmės, pavyzdžiui, ranguotiems paieškos rezultatams.

Užklausų limitai

Limitai taikomi fiksuotame laiko lange ir tikrinami dar prieš nuskaitant užklausos turinį. Atmesta užklausa gauna 429 su kodu RATE_LIMITED ir antraštę Retry-After, sekundėmis.

Dauguma limitų skaičiuojami pagal kliento IP. Jautrūs prisijungimo duomenims — prisijungimas, atnaujinimas, slaptažodžio patvirtinimas iš naujo, MCP prisijungimo duomenų įrašymas — dar saugo skaitiklį kiekvienai paskyrai ir atmeta, kai sustoja bet kuris iš jų. Taip ir vienas adresas, bombarduojantis daugybę paskyrų, ir viena paskyra, puolama iš daugybės adresų, atsitrenkia į sieną. Prisijungimas pagal numatytuosius nustatymus leidžia 10 bandymų per IP ir 5 per el. pašto adresą per dešimt minučių; žinutės siuntimas — 60 per minutę; rašymas maršrutuose /api/agents — 60 per minutę. Kiekviena riba konfigūruojama pagal diegimą, todėl šiuos dydžius laikykite numatytaisiais, o ne sutartimi. Sveikatos patikros ir realiojo laiko srautai neribojami.

Realusis laikas

Sekti pokalbį apklausiant sąrašą yra netinkamas būdas. Vietoj to yra trys srautiniai galiniai taškai:

  • GET /api/events/stream — serverio siunčiami įvykiai visam, ką prisijungęs asmuo gali matyti.
  • GET /api/threads/:threadId/stream — serverio siunčiami įvykiai vienai gijai. Jis palaiko Last-Event-ID, todėl atsijungus ir prisijungus iš naujo srautas tęsiamas, o ne pradedamas iš naujo.
  • GET /api/activity — WebSocket, perduodantis veiklos srautą.

Naršyklės negali nustatyti antraščių WebSocket rankos paspaudimo metu, todėl WebSocket maršrutas taip pat priima prieigos žetoną kaip užklausos parametrą ?token=. Tai vienintelė vieta, kur žetonas priklauso URL; visi kiti maršrutai jį priima antraštėje.

Išteklių šeimos

Galinių taškų yra gerokai daugiau nei tūkstantis — per daug, kad juos visus išvardytume. Jie sugrupuoti į šeimas, ir kai žinote šeimos priešdėlį, atskiri maršrutai nuspėjami: kolekcija priešdėlyje, įrašas po ja ir pavadinti veiksmai kaip POST pakeliai.

ŠeimaPriešdėlisKą apima
Autentifikavimas/api/authPrisijungimas, atnaujinimas, atsijungimas, dabartinė tapatybė, paties asmens seansai.
Kanalai ir gijos/api/channels, /api/threadsPokalbių modelis: kanalai talpina gijas, gijos talpina žinutes, o atsakymai įsiterpia po pradine žinute.
Agentai/api/agentsAgentų apibrėžimai, jų kanalų susiejimai, pokalbiai, užduotys ir būsena.
Projektai ir lentos/api/projects, /api/tasks, /api/iterationsDarbų sekimas: projekte esančios lentos ir stulpeliai, juose esančios užduotys, jų viršuje — iteracijos.
Žinios/api/knowledge-baseVersijuojamų puslapių ir failų erdvės su komentarais, atgalinėmis nuorodomis ir paieška.
Paštas/api/mail, /api/mailbox-connectionsPrijungtos pašto dėžutės, agentų pašto dėžutės ir juodraščiai, kuriuos agentas parengė žmogui patvirtinti.
Patvirtinimai/api/approvalsVeiksmų, kurių agentas negali atlikti savarankiškai, eilė ir vienas iškvietimas, kuris jas išsprendžia.
Auditas/api/audit-logOrganizacijos hash grandine saugomas audito žurnalas su tikrinimo galiniu tašku. Savininkams ir administratoriams.
Sąnaudos ir naudojimas/api/ledger, /api/billingŽetonų ir kainų apskaita, biudžetai ir paleidimų trukmės; atskirai — kreditai ir papildymai. Savininkams ir administratoriams.
Organizacija ir komandos/api/organizations, /api/teams, /api/usersNuomininko, komandų ir narystės administravimas, įskaitant kvietimus.
Paleidikliai/api/triggersTvarkaraščiai ir webhook'ai, paleidžiantys agentų darbą, su jų paleidimų istorija.
Skambučiai ir balsas/api/calls, /api/voiceSkambinimas kanale ir balso asistento seansas, transkripcija bei įrenginių žetonai.
Vykdytojai/api/executors, /api/execution-environment-templatesMašinos, kuriose dirba agentai, jų aplinkos ir vykdytojo susiejimo kanalas.
Paleidimai/api/runsGyvi agentų paleidimai: atšaukti, tęsti, paleisti iš naujo, susieti su vykdytoju.
Įkėlimai/api/uploads, /api/attachmentsPirmiausia įkelkite failą, tada susiekite gautą identifikatorių su žinute.
Paieška/api/messages/searchŽinučių paieška visoje organizacijoje arba vienam kanalui.
Paslaptys ir nustatymai/api/secrets, /api/settings/scopedTik rašymui skirta prisijungimo duomenų saugykla su aiškiais leidimais ir sluoksniuotos konfigūracijos reikšmės.
Prijungtos priemonės/api/mcpMCP serveriai, prie kurių šis diegimas kreipiasi. Žr. MCP ir prijungtos priemonės.
Būklė/api/healthGyvybingumas ir parengtis, abu vieši. /api/health/ready taip pat tikrina duomenų bazę.

Teisės taikomos kiekvienam maršrutui, o ne šeimai. Apibendrinant: audito žurnalas, sąnaudų registras, politikos taisyklės ir modelių vykdymo valdymo plokštuma yra organizacijos savininkų ir administratorių sritys, o pokalbiai, projektai, žinios ir patvirtinimai prieinami bet kuriam nariui ir siaurinami pagal tai, ką tas narys iš tiesų mato.

Darbiniai pavyzdžiai

Prisijunkite ir išsaugokite atnaujinimo slapuką

Diegime, veikiančiame local režimu, su slaptažodžiu. Prieigos žetonas grįžta atsakymo turinyje; atnaujinimo žetonas patenka į slapukų failą.

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
      }
    }
  }
}

Pasibaigus žetonui, iškeiskite slapuką į naują:

curl -sS -X POST https://api.example.com/api/auth/refresh \
  --cookie nessie.cookies --cookie-jar nessie.cookies

Atsakymas yra toks pat { token, me } formos, o slapukų failas dabar laiko pasuktą atnaujinimo žetoną.

Išvardykite kanalus, kuriuos matote

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
    }
  ]
}

Pridėkite ?teamId=…, kad susiaurintumėte iki vienos komandos, arba ?includeArchived=true, kad įtrauktumėte archyvuotus kanalus. defaultThreadId yra gija, į kurią siųsite toliau.

Siųskite žinutę

Žinutei reikia arba kokio nors content, arba bent vieno įkelto priedo. Siųskite Idempotency-Key, ir pakartota užklausa atitiks žinutę, kurią sukūrė pirmasis bandymas, o ne siųs antrą kopiją.

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."}'

Nauja žinutė grąžinama su 201:

{
  "data": {
    "message": {
      "id": "d70e…",
      "threadId": "c41a…",
      "role": "user",
      "content": "Deploying the migration now.",
      "createdAt": "2026-09-16T09:04:11.882Z"
    },
    "pendingAgentInvites": []
  }
}

Pakartotas tas pats idempotencijos raktas atsako 200 su pradine žinute ir tuščiu pendingAgentInvites, nes pirmasis bandymas jau atliko įspėjimą ir siuntimą, kuriuos aprašo masyvas.

Verta atskirai apdoroti du šio maršruto atmetimus. Turinys, viršijantis pokalbio ilgio ribą, yra 413 MESSAGE_TOO_LARGE, ir tinkamas atsakymas — įkelti tekstą kaip failą. Turinys, struktūriškai panašus į prisijungimo duomenis, yra 422 SECRET_INTERCEPTED — serveris atmeta jį prieš išsaugant, siunčiant ar registruojant, o prisijungimo duomenys priklauso /api/secrets.