API
HTTP API iza svakog Nessie klijenta i način na koji se autentifikujete prema njemu.
Šta je API
Nessie ima jedan HTTP API. Admin veb aplikacija, klijenti za macOS i iOS i udaljeni executor daemon su obični klijenti tog API-ja, i ništa što oni mogu da urade nije dostupno nijednim drugim putem. Reč je o JSON API-ju nad Fastify serverom, to nije GraphQL i nema poseban „javni“ podskup: skup mogućnosti koje pozivaju vaše skripte isti je onaj koji poziva i sam proizvod.
Putanja nema segment za verziju. Sve rute nalaze se pod /api, osim dolazne MCP krajnje tačke POST /mcp i nekoliko dokumenata za otkrivanje pod /.well-known/. Kompatibilnost se održava tako što se polja samo dodaju, pa klijent koji ignoriše ono što ne prepoznaje nastavlja da radi.
Osnovni URL
Nessie se samostalno hostuje, pa je osnovni URL origin na kojem vaša instalacija služi API. Operater ga postavlja kao NESSIE_API_PUBLIC_URL, a ista vrednost je ono što API koristi kada treba da napravi sopstveni apsolutni URL. Na instanci koju mi pokrećemo to je https://api.nessie.works; razvojna instanca pokrenuta sa pnpm dev odgovara na http://localhost:5454.
Admin veb aplikacija je drugi origin u odnosu na API, pa su browser klijenti podvrgnuti CORS listi dozvoljenih (NESSIE_CORS_ORIGINS). Zahtevi iz skripte ili sa servera nemaju origin koji bi se proveravao i nisu pogođeni.
Autentifikacija
Svaki zahtev van malog skupa namerno javnih ruta nosi pristupni token u standardnom zaglavlju:
Authorization: Bearer <token>Token je potpisani JWT koji izdaje API. Kratkog je veka: podrazumevano trideset minuta (NESSIE_AUTH_TOKEN_TTL), i to je jedini akreditiv koji običan pozivalac ikad prikazuje. Nessie nema dugotrajne lične API ključeve. Ako želite da skripta poziva API, prijavljuje se na isti način kao korisnikov pregledač i obnavlja token dok radi.
Dobijanje tokena
POST /api/auth/session izdaje sesiju. Odgovara pristupnim tokenom i identitetom pozivaoca i postavlja token za obnavljanje kao zaseban HTTP-only kolačić.
Način dokazivanja identiteta zavisi od toga kako je instanca postavljena. Instanca koja radi u local režimu prihvata imejl adresu i lozinku. Na instanci tipa hosted ili selfHosted prijava lozinkom se odmah odbija sa 403 PASSWORD_AUTH_DISABLED, a jedini ulaz je konfigurisani provajder identiteta, tako da nalog nikad ne može da dobije drugi, slabiji način prijave pored njega.
Prijava preko provajdera identiteta
Jedinstvena prijava je OAuth razmena autorizacionog koda uz PKCE, u tri koraka. Nessie-jev sopstveni provajder identiteta je UnlikeOtherAI, koji ujedno poseduje strukturu organizacija i timova koju instanca preslikava; razmena ispod ima isti oblik za svakog konfigurisanog provajdera.
GET /api/auth/providersnavodi provajdere koje ova instanca prihvata, svakog saproviderId, nazivom i podatkom da li stranica za prijavu treba automatski da preusmeri na njega.GET /api/auth/providers/:providerId/authorize, sacodeChallenge,redirectUriistatekao parametrima upita, odgovara sa{ data: { authorizeUrl } }. Preusmerite korisnika tamo.- Provajder vraća korisnika na vaš
redirectUrisa kodom. Pošaljite taj kod nazad kaoPOST /api/auth/sessionsa{ providerId, code, codeVerifier, redirectUri }. Sva četiri su obavezna zajedno, a nepotpun skup daje400 EXTERNAL_AUTH_INCOMPLETE.
Obnavljanje sesije
Token za obnavljanje nikad nije u telu odgovora. Postavlja se kao HTTP-only kolačić pod nazivom nessie_refresh, vezan za putanju /api/auth, sa podrazumevanim trajanjem od trideset dana (NESSIE_AUTH_REFRESH_TOKEN_TTL). Pozivanje POST /api/auth/refresh sa tim kolačićem troši ga, izdaje svež pristupni token i postavlja novi kolačić umesto starog. Tokeni se rotiraju, pa je ukraden token upotrebljiv samo do sledećeg osvežavanja pravog klijenta.
Pregledač prijavljen na više naloga čuva jedan takav kolačić za svaku prijavu, pod nazivom nessie_refresh_<slot>, i navodi onaj na koji se zahtev odnosi zaglavljem x-nessie-account-slot. Klijent koji takvo zaglavlje ne šalje odnosi se na nessie_refresh, što je sve što je potrebno klijentu iz komandne linije.
Klijent iz komandne linije zato treba skladište kolačića (cookie jar). Uz curl, to su --cookie-jar pri pozivu za prijavu i --cookie pri osvežavanju, kao u primeru ispod.
Šta se proverava pri svakom zahtevu
Autentifikacija nije samo provera potpisa. Pre nego što se handler pokrene, API proverava potpis i rok važenja tokena, potvrđuje da sesija nije opozvana (i prema brojaču verzije tokena za celog korisnika i prema pojedinačnom ID-ju sesije) i učitava članstvo pozivaoca u organizaciji koju token navodi. Neaktivno članstvo daje 403. Kada je organizacija zasnovana na spoljnom provajderu identiteta, trenutni status pozivaoca se proverava kod tog provajdera pri samom zahtevu, a ne čita iz tokena, pa pristup ukinut spolja prestaje odmah, a ne tek pri sledećem isteku.
Akreditivi koji nisu tokeni sesije
Postoje još dva bearer akreditiva, i nijedan nije opšti ključ. Akreditiv glasovnog uređaja prihvata se samo na rutama za glasovne pozive; akreditiv agenta prihvata se samo na MCP krajnjoj tački. Ako se bilo koji od njih prikaže bilo gde drugde, odbija se sa 403 koji imenuje akreditiv, a ne ponovo proba kao slabija sesija. Pogledajte MCP i povezani alati za način izdavanja akreditiva agenta.
Omotač odgovora
Svaki uspešan odgovor je objekat sa ključem data. Nema golog niza ni payload-a na najvišem nivou, pa klijent nikad ne mora da prepoznaje oblik:
{
"data": { "id": "3f1c…", "label": "engineering" }
}Krajnje tačke sa listama dodaju objekat meta pored njega. Na najvišem nivou se nikad ništa drugo ne dodaje.
Greške koriste takođe jedan oblik, bez obzira na to šta ih je izazvalo:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "A message needs content or at least one attachment",
"field": "body",
"details": { "formErrors": [], "fieldErrors": {} }
}
}code je deo namenjen mašini i na njemu treba grananje; message je za čoveka. field i details postoje kada je greška dovoljno specifična da se lokalizuje. Neobrađena serverska greška je uvek 500 INTERNAL_ERROR sa fiksnom porukom. Tekst podležeće greške se beleži, ali se nikad ne vraća, jer je mogao da pokupi podatke iz zahteva dok izlazi napolje.
Kodovi grešaka koje ćete najpre sresti
| Status | Kod | Značenje |
|---|---|---|
| 400 | VALIDATION_ERROR | Telo ili upit nisu odgovarali šemi. |
| 401 | AUTH_REQUIRED | Nema bearer tokena ili je zaglavlje neispravno. |
| 401 | TOKEN_EXPIRED | Pristupni token je istekao. Osvežite ga i pokušajte ponovo. |
| 401 | TOKEN_REVOKED | Sesija je odjavljena ili su sve sesije prinudno prekinute. |
| 403 | ACCOUNT_DEACTIVATED | Članstvo u ovoj organizaciji je deaktivirano. |
| 404 | NOT_FOUND | Nema takve rute ili nema takvog zapisa vidljivog ovom pozivaocu. |
| 429 | RATE_LIMITED | Iskorišćen je limit zahteva. Pročitajte Retry-After. |
| 500 | INTERNAL_ERROR | Neobrađena serverska greška, namerno neotkrivena. |
Paginacija
Krajnje tačke sa listama stranicu traže neprozirnim kursorom zasnovanim na ključu, a ne ofsetom. Redovi se umeću dok neko čita, a sa ofsetom druga stranica tiho ponavlja zapis koji je prva već prikazala.
Prihvataju se tri parametra upita: cursor, limit i direction (forward ili backward). Podrazumevana stranica ima 25 stavki, a gornja granica je 100; veća vrednost se svodi na granicu, a ne odbija. Krajnje tačke koje takođe sortiraju ili filtriraju dodaju sort, order i slobodan tekst q.
meta stranice pokazuje gde se nalazite:
{
"data": [ … ],
"meta": {
"hasMore": true,
"nextCursor": "2026-09-12T08:31:44.005Z|8b2f…",
"prevCursor": null,
"total": 134
}
}nextCursor i prevCursor su oba stvarna: „Prethodna“ i „Sledeća“ su dve kontrole koje korisnik očekuje. Oni su neprozirni: server u njih kodira sopstveni ključ sortiranja, pa klijent koji ih parsira ili pravi sam pokvariće se čim lista promeni redosled. total postoji u većini lista i izostavlja se tamo gde brojanje nema smisla, na primer kod rangiranih rezultata pretrage.
Ograničenja broja zahteva
Ograničenja važe po fiksnom vremenskom prozoru i proveravaju se pre nego što se telo zahteva uopšte pročita. Odbijen zahtev dobija 429 sa kodom RATE_LIMITED i zaglavljem Retry-After u sekundama.
Većina ograničenja vezuje se za IP adresu klijenta. Ona osetljiva na akreditive (prijava, osvežavanje, ponovna provera lozinke i upisi MCP akreditiva) pored toga čuvaju brojač po nalogu i odbijaju zahtev kada se okine bilo koji od njih, pa i jedna adresa koja šalje mnogo naloga i jedan nalog napadnut sa mnogo adresa udaraju u zid. Prijava podrazumevano dozvoljava 10 pokušaja po IP adresi i 5 po imejl adresi u deset minuta; slanje poruke podrazumevano 60 u minuti; upisi pod /api/agents 60 u minuti. Svaki prag se može podesiti po instalaciji, pa ove vrednosti shvatite kao podrazumevane isporučene vrednosti, a ne kao ugovor. Provere zdravlja i strimovi u realnom vremenu su izuzeti.
U realnom vremenu
Praćenje razgovora čitanjem liste uzastopno nije pravi način. Umesto toga postoje tri strimujuće krajnje tačke:
GET /api/events/stream— server-sent događaji za sve što prijavljena osoba može da vidi.GET /api/threads/:threadId/stream— server-sent događaji za jednu nit. PoštujeLast-Event-ID, pa se ponovno povezivanje nastavlja, a ne kreće ispočetka.GET /api/activity— WebSocket koji nosi feed aktivnosti.
Pregledači ne mogu da postavljaju zaglavlja pri WebSocket rukovanju (handshake), pa WebSocket ruta prihvata i pristupni token kao parametar upita ?token=. To je jedino mesto gde token sme da bude u URL-u; sve druge rute uzimaju zaglavlje.
Porodice resursa
Ima znatno više od hiljadu krajnjih tačaka, što je previše da bi se sve navele. Grupisane su u porodice, a kada znate prefiks porodice, pojedinačne rute su predvidljive: kolekcija na prefiksu, zapis ispod nje i imenovane radnje kao POST podputanje.
| Porodica | Prefiks | Šta obuhvata |
|---|---|---|
| Autentifikacija | /api/auth | Prijava, obnavljanje, odjava, trenutni identitet i sopstvene sesije osobe. |
| Kanali i niti | /api/channels, /api/threads | Model razgovora: kanali drže niti, niti drže poruke, a odgovori su ugnježdeni ispod korena. |
| Agenti | /api/agents | Definicije agenata, njihova vezivanja za kanale, razgovori, zadaci i status. |
| Projekti i table | /api/projects, /api/tasks, /api/iterations | Praćenje rada: table i kolone unutar projekta, zadaci na njima i iteracije nad njima. |
| Baza znanja | /api/knowledge-base | Prostori sa verzionisanim stranicama i fajlovima, sa komentarima, povratnim vezama i pretragom. |
| Pošta | /api/mail, /api/mailbox-connections | Povezani poštanska sandučad, sandučad agenata i nacrti koje je agent napisao da ih osoba odobri. |
| Odobrenja | /api/approvals | Red radnji koje agent ne sme da preduzme sam i jedan poziv kojim se one razrešavaju. |
| Revizija | /api/audit-log | Hash-ulančani revizioni trag organizacije, sa krajnjom tačkom za proveru. Vlasnici i administratori. |
| Troškovi i potrošnja | /api/ledger, /api/billing | Obračun tokena i troškova, budžeti i vremena izvršavanja; odvojeno, krediti i dopune. Vlasnici i administratori. |
| Organizacija i timovi | /api/organizations, /api/teams, /api/users | Administracija organizacije, timova i članstava, uključujući pozivnice. |
| Okidači | /api/triggers | Rasporedi i webhook-ovi koji pokreću rad agenata, sa istorijom okidanja. |
| Pozivi i glas | /api/calls, /api/voice | Pozivi unutar kanala, kao i sesija glasovnog asistenta, transkript i tokeni uređaja. |
| Executori | /api/executors, /api/execution-environment-templates | Mašine na kojima agenti rade, njihova okruženja i kanal za uparivanje daemona. |
| Izvršavanja | /api/runs | Aktivna izvršavanja agenata: otkazivanje, nastavak, ponovno pokretanje, vezivanje za executor. |
| Otpremanja | /api/uploads, /api/attachments | Prvo otpremite fajl, a zatim vežite vraćeni ID za poruku. |
| Pretraga | /api/messages/search | Pretraga poruka, za celu organizaciju ili ograničena na jedan kanal. |
| Tajne i podešavanja | /api/secrets, /api/settings/scoped | Skladište akreditiva samo za upis, sa eksplicitnim dozvolama i slojevitim konfiguracionim vrednostima. |
| Povezani alati | /api/mcp | MCP serveri kojima ova instanca šalje zahteve. Pogledajte MCP i povezani alati. |
| Provere stanja | /api/health | Provera živosti i spremnosti, obe javne. /api/health/ready takođe proverava bazu podataka. |
Prava se proveravaju po ruti, a ne po porodici. Uglavnom: revizioni trag, evidencija troškova, pravila politike i kontrolna ravan za inferenciju su površine za vlasnike i administratore organizacije, dok su razgovori, projekti, znanje i odobrenja otvoreni svakom članu i sužavaju se prema tome šta taj član zaista može da vidi.
Primeri
Prijava i čuvanje kolačića za obnavljanje
Na instanci u local režimu, sa lozinkom. Pristupni token dolazi u telu odgovora, a token za obnavljanje ide u skladište kolačića.
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
}
}
}
}Kada token istekne, zamenite kolačić za novi:
curl -sS -X POST https://api.example.com/api/auth/refresh \
--cookie nessie.cookies --cookie-jar nessie.cookiesOdgovor je isti { token, me } oblik, a skladište kolačića sada drži rotirani token za obnavljanje.
Izlistavanje kanala koje vidite
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
}
]
}Dodajte ?teamId=… da suzite na jedan tim, ili ?includeArchived=true da uključite arhivirane kanale. defaultThreadId je ono na šta sledeće šaljete poruku.
Slanje poruke
Poruka mora imati neki content ili bar jedan otpremljen prilog. Pošaljite Idempotency-Key, pa će ponovljeni zahtev biti razrešen u poruku koju je napravio prvi pokušaj, umesto da pošalje drugi primerak.
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."}'Nova poruka dobija 201:
{
"data": {
"message": {
"id": "d70e…",
"threadId": "c41a…",
"role": "user",
"content": "Deploying the migration now.",
"createdAt": "2026-09-16T09:04:11.882Z"
},
"pendingAgentInvites": []
}
}Ponavljanje istog ključa za idempotentnost odgovara sa 200, sa originalnom porukom i praznim nizom pendingAgentInvites, jer je prvi pokušaj već obavio obaveštavanje i slanje koje taj niz opisuje.
Dva odbijanja na ovoj ruti vredna su posebne obrade. Sadržaj iznad granice dužine poruke dobija 413 MESSAGE_TOO_LARGE, a pravi odgovor je otpremiti tekst kao fajl. Sadržaj koji po strukturi liči na akreditiv dobija 422 SECRET_INTERCEPTED. Server odbija zahtev pre nego što se poruka sačuva, pošalje ili zabeleži, a akreditiv pripada u /api/secrets.