nessie

API

Her Nessie istemcisinin arkasındaki HTTP API ve buna nasıl kimlik doğrulanacağı.

API nedir

Nessie’nin tek bir HTTP API’si vardır. Yönetici web uygulaması, macOS ve iOS istemcileri ile uzak executor daemon’ı bunun sıradan istemcileridir ve yapabildikleri hiçbir şeye başka bir yoldan erişilemez. Fastify sunucusu üzerinde çalışan bir JSON API’dir, GraphQL değildir ve ayrı bir “genel” alt kümesi yoktur: kendi betiklerinizin çağırdığı yüzey, ürünün çağırdığı yüzeydir.

Yolda bir sürüm bölümü yoktur. Gelen MCP uç noktası POST /mcp ve birkaç /.well-known/ keşif belgesi dışında her rota /api altında yer alır. Uyumluluk yalnızca alan eklenerek sağlanır; bu nedenle tanımadığı alanları yok sayan bir istemci çalışmaya devam eder.

Temel URL

Nessie kendi sunucunuzda barındırılır; bu nedenle temel URL, dağıtımınızın API’yi sunduğu origin’dir. İşletici bunu NESSIE_API_PUBLIC_URL olarak ayarlar ve API kendi mutlak URL’sini üretmesi gerektiğinde aynı değeri kullanır. Bizim çalıştırdığımız örnekte bu https://api.nessie.works’tür; pnpm dev ile başlatılan bir geliştirme örneği http://localhost:5454 adresinde yanıt verir.

Yönetici web uygulaması API’den farklı bir origin’dedir; bu nedenle tarayıcı istemcileri bir CORS izin listesine (NESSIE_CORS_ORIGINS) tabidir. Bir betikten ya da sunucudan gelen istekler kontrol edilecek bir origin taşımaz ve bundan etkilenmez.

Kimlik doğrulama

Bilerek herkese açık tutulan küçük bir rota kümesi dışındaki her istek, standart başlıkta bir erişim token’ı taşır:

Authorization: Bearer <token>

Token, API tarafından üretilen imzalı bir JWT’dir. Kısa ömürlüdür — varsayılan olarak otuz dakikadır (NESSIE_AUTH_TOKEN_TTL) — ve sıradan bir istemcinin sunduğu tek kimlik bilgisidir. Nessie’nin uzun ömürlü kişisel API anahtarı yoktur. Bir betiğin API’yi çağırmasını istiyorsanız, betik bir kişinin tarayıcısının yaptığı gibi oturum açar ve çalışırken token’ını yeniler.

Token alma

POST /api/auth/session bir oturum oluşturur. Erişim token’ını ve çağıranın kimliğini yanıtlar; yenileme token’ını ayrı bir, yalnızca HTTP çerezi olarak ayarlar.

Kimliğinizi nasıl kanıtladığınız, örneğin nasıl dağıtıldığına bağlıdır. local modunda çalışan bir örnek e-posta ve parolayı kabul eder. hosted ya da selfHosted bir örnekte parolayla oturum açma doğrudan 403 PASSWORD_AUTH_DISABLED ile reddedilir ve tek giriş yolu yapılandırılmış kimlik sağlayıcıdır; böylece bir hesap yanında ikinci, daha zayıf bir kimlik yolu oluşturamaz.

Bir kimlik sağlayıcı üzerinden oturum açma

Tek oturum açma (SSO), PKCE ile üç adımda gerçekleşen bir OAuth yetkilendirme kodu değişimidir. Nessie’nin kendi kimlik sağlayıcısı, bir örneğin yansıttığı kuruluş ve ekip yapısına da sahip olan UnlikeOtherAI’dir; aşağıdaki değişim, yapılandırılmış herhangi bir sağlayıcı için aynı biçimdedir.

  1. GET /api/auth/providers, bu örneğin kabul ettiği sağlayıcıları listeler; her birinin bir providerId’si, bir etiketi ve oturum açma sayfasının onlara otomatik olarak yönlendirip yönlendirmeyeceği bilgisi vardır.
  2. codeChallenge, redirectUri ve state sorgu parametreleriyle yapılan GET /api/auth/providers/:providerId/authorize çağrısı { data: { authorizeUrl } } ile yanıt verir. Kişiyi oraya gönderin.
  3. Sağlayıcı, kişiyi bir kodla birlikte redirectUri adresinize geri gönderir. Bu kodu { providerId, code, codeVerifier, redirectUri } ile birlikte POST /api/auth/session adresine geri gönderin — dört değerin tamamı birlikte gereklidir; eksik bir küme 400 EXTERNAL_AUTH_INCOMPLETE hatası verir.

Oturumu yenileme

Yenileme token’ı hiçbir zaman yanıt gövdesinde bulunmaz. nessie_refresh adıyla, /api/auth yoluna kapsamlı ve varsayılan olarak otuz günlük ömrü olan (NESSIE_AUTH_REFRESH_TOKEN_TTL) yalnızca HTTP çerezi olarak ayarlanır. Bu çerezle POST /api/auth/refresh çağrıldığında çerez tüketilir, yeni bir erişim token’ı verilir ve yerine yeni bir çerez ayarlanır. Token’lar döner; bu nedenle çalınmış bir token, gerçek istemci bir sonraki yenilemesini yapana kadar geçerlidir.

Birden fazla hesaba oturum açmış bir tarayıcı, her oturum açma için nessie_refresh_<slot> adında böyle bir çerez tutar ve bir isteğin hangisiyle ilgili olduğunu x-nessie-account-slot başlığıyla belirtir. Bu başlığı göndermeyen bir istemci nessie_refresh çerezini kullanır; komut satırı istemcisinin ihtiyacı olan tek şey budur.

Bu nedenle komut satırı istemcisinin bir çerez deposuna (cookie jar) ihtiyacı vardır. curl ile bu, oturum açma çağrısında --cookie-jar, yenilemede ise --cookie kullanmak demektir; aşağıdaki örnekte de böyledir.

Her istekte neler denetlenir

Kimlik doğrulama yalnızca imza denetimi değildir. Bir işleyici çalışmadan önce API; token’ın imzasını ve son kullanma tarihini doğrular, oturumun iptal edilmediğini onaylar (hem kullanıcı genelindeki token sürümüne hem de tekil oturum kimliğine göre) ve token’ın adını verdiği kuruluştaki çağıranın üyeliğini yükler. Devre dışı bırakılmış bir üyelik 403 döner. Kuruluş harici bir kimlik sağlayıcıya dayandığında çağıranın güncel durumu token’dan okunmak yerine istekte o sağlayıcıya karşı yeniden denetlenir; böylece üst sistemde kaldırılan erişim, bir sonraki son kullanma tarihini beklemeden hemen durur.

Oturum token’ı olmayan kimlik bilgileri

Başka iki taşıyıcı (bearer) kimlik bilgisi vardır ve bunlardan hiçbiri genel amaçlı bir anahtar değildir. Bir ses cihazı kimlik bilgisi yalnızca ses çağrısı rotalarında kabul edilir; bir ajan erişim kimlik bilgisi yalnızca MCP uç noktasında kabul edilir. Bunlardan birini başka bir yerde sunmak, kimlik bilgisinin adını belirten bir 403 ile reddedilir; daha zayıf bir oturum olarak yeniden denenmez. Ajan kimlik bilgisinin nasıl verildiğini görmek için bkz. MCP ve bağlı araçlar.

Yanıt zarfı

Her başarılı yanıt, data anahtarı olan bir nesnedir. Kökte çıplak bir dizi ya da üst düzey bir yük yoktur; bu nedenle bir istemcinin yapıya göre ayrım yapması hiçbir zaman gerekmez:

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

Liste uç noktaları yanlarına bir meta nesnesi ekler. Üst düzeye başka hiçbir şey eklenmez.

Hatalar da nedenleri ne olursa olsun tek bir yapı kullanır:

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

code, makine tarafından okunabilen kısımdır ve koşullu işlemlerinizi buna göre yapmanız gereken alandır; message ise insanlara yöneliktir. field ve details, hata yeri belirlenecek kadar özelse bulunur. İşlenmeyen bir sunucu hatası her zaman sabit bir mesajla 500 INTERNAL_ERROR döner — temel hata metni günlüğe yazılır, hiçbir zaman döndürülmez; çünkü dışarı çıkarken istek verisini içermiş olabilir.

Önce karşılaşacağınız hata kodları

DurumKodAnlam
400VALIDATION_ERRORİstek gövdesi ya da sorgusu şemaya uymadı.
401AUTH_REQUIREDTaşıyıcı token yok ya da başlık hatalı biçimde.
401TOKEN_EXPIREDErişim token’ının süresi dolmuş. Yenileyin ve yeniden deneyin.
401TOKEN_REVOKEDOturum kapatılmış ya da tüm oturumlar zorla sonlandırılmış.
403ACCOUNT_DEACTIVATEDBu kuruluştaki üyeliğiniz devre dışı bırakılmış.
404NOT_FOUNDBöyle bir rota yok ya da bu çağıran için görünür böyle bir kayıt yok.
429RATE_LIMITEDBir istek kotası dolmuş. Retry-After değerini okuyun.
500INTERNAL_ERRORİşlenmeyen bir sunucu hatası; bilinçli olarak ayrıntısız.

Sayfalama

Liste uç noktaları bir offset değil, opak bir keyset imleci (cursor) ile sayfalanır; çünkü biri okurken satırlar eklenir ve offset kullanıldığında ikinci sayfa, ilk sayfanın zaten gösterdiği bir kaydı sessizce tekrarlar.

Üç sorgu parametresi kabul edilir: cursor, limit ve direction (forward veya backward). Varsayılan sayfa boyutu 25, üst sınır ise 100’dür; daha büyük bir değer reddedilmek yerine sınırlandırılır. Sıralama ya da filtreleme yapan uç noktalar ayrıca sort, order ve serbest metin q parametrelerini ekler.

Sayfanın meta bölümü nerede olduğunuzu gösterir:

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

nextCursor ve prevCursor ikisi de gerçektir: Önceki ve Sonraki, bir kişinin beklediği iki denetimdir. Opaktırlar — sunucu kendi sıralama anahtarını bunlara kodlar; bunları ayrıştıran ya da oluşturan bir istemci, bir listenin sıralama biçimi değiştiği anda bozulur. total çoğu listede bulunur; sıralı arama sonuçları gibi sayımın anlamlı olmadığı yerlerde ise yer almaz.

Hız sınırları

Sınırlar sabit zaman pencereleri başınadır ve istek gövdesi okunmadan önce uygulanır. Reddedilen bir istek 429 döner; kodu RATE_LIMITED ve saniye cinsinden bir Retry-After başlığı içerir.

Çoğu kova istemci IP’sine göre anahtarlanır. Kimlik bilgisine duyarlı olanlar — oturum açma, yenileme, parola yeniden doğrulama, MCP kimlik bilgisi yazmaları — ayrıca hesap başına bir sayaç tutar ve iki sayaçtan biri aşıldığında reddeder. Böylece tek bir adresin birçok hesabı taramasıyla, tek bir hesabın da birçok adresten saldırıya uğramasıyla karşılaşılır. Oturum açma, on dakika içinde IP başına 10, e-posta adresi başına 5 deneme olarak varsayılan ayarlıdır; bir mesaj göndermek dakikada 60 olarak, /api/agents altındaki yazmalar da dakikada 60 olarak varsayılandır. Her eşik dağıtım bazında yapılandırılabilir; bu nedenle bunları bir sözleşme değil, sevk edilen varsayılanlar olarak değerlendirin. Sağlık denetimleri ve gerçek zamanlı akışlar muaftır.

Gerçek zamanlı

Bir konuşmayı takip etmek için bir listeyi sürekli sorgulamak yanlış bir yöntemdir. Bunun yerine üç akış uç noktası vardır:

  • GET /api/events/stream — oturum açmış kişinin görebildiği her şey için sunucu gönderimli olaylar.
  • GET /api/threads/:threadId/stream — tek bir ileti dizisi için sunucu gönderimli olaylar. Last-Event-ID değerine uyar; bu nedenle yeniden bağlanma baştan başlamak yerine kaldığı yerden devam eder.
  • GET /api/activity — etkinlik akışını taşıyan bir WebSocket.

Tarayıcılar bir WebSocket el sıkışmasında başlık ayarlayamaz; bu nedenle WebSocket rotası erişim token’ını ?token= sorgu parametresi olarak da kabul eder. Token’ın bir URL’de yer alabileceği tek yer burasıdır; diğer her rota token’ı başlıkta bekler.

Kaynak aileleri

Bin küsurdan çok uç nokta vardır; hepsini listelemek için çok fazla. Bunlar ailelere ayrılır; bir ailenin önekini bildiğinizde tekil rotalar öngörülebilir olur — önekte bir koleksiyon, altında bir kayıt ve POST alt yollarında adlandırılmış eylemler.

AileÖnekKapsam
Kimlik doğrulama/api/authOturum açma, yenileme, oturum kapatma, mevcut kimlik ve bir kişinin kendi oturumları.
Kanallar ve ileti dizileri/api/channels, /api/threadsKonuşma modeli: kanallar ileti dizilerini, ileti dizileri mesajları tutar; yanıtlar bir kökün altında iç içe yer alır.
Ajanlar/api/agentsAjan tanımları, kanal bağlamaları, konuşmalar, yapılacaklar ve durum.
Projeler ve panolar/api/projects, /api/tasks, /api/iterationsİş takibi: bir proje altındaki panolar ve sütunlar, üzerlerindeki görevler, bunların iterasyonları.
Bilgi/api/knowledge-baseYorumlar, geri bağlantılar ve aramayla birlikte sürümlü sayfa ve dosya alanları.
E-posta/api/mail, /api/mailbox-connectionsBağlı posta kutuları, ajan posta kutuları ve bir kişinin onaylaması için bir ajanın yazdığı taslaklar.
Onaylar/api/approvalsBir ajanın tek başına yapamayacağı eylemlerin kuyruğu ve bunları çözen tek çağrı.
Denetim/api/audit-logKuruluşun hash zincirli denetim izi ve bir doğrulama uç noktası. Owner ve Admin rolleri.
Maliyet ve kullanım/api/ledger, /api/billingToken ve maliyet hesabı, bütçeler ve çalışma süreleri; ayrıca krediler ve bakiye yüklemeleri. Owner ve Admin rolleri.
Kuruluş ve ekipler/api/organizations, /api/teams, /api/usersKiracı, ekip ve üyelik yönetimi; davetler dahil.
Tetikleyiciler/api/triggersAjan işlerini başlatan zamanlamalar ve webhook’lar, tetiklenme geçmişleriyle birlikte.
Aramalar ve ses/api/calls, /api/voiceKanal içi arama ve sesli asistanın oturumu, metni ve cihaz token’ları.
Executor’lar/api/executors, /api/execution-environment-templatesAjanların üzerinde çalıştığı makineler, ortamları ve daemon eşleştirme kanalı.
Çalışmalar/api/runsCanlı ajan çalışmaları: iptal etme, devam ettirme, yeniden başlatma, bir executor’a bağlama.
Yüklemeler/api/uploads, /api/attachmentsÖnce bir dosya yükleyin, ardından dönen kimliği bir mesaja bağlayın.
Arama/api/messages/searchMesaj araması; kuruluş genelinde ya da tek bir kanalla sınırlı.
Gizli bilgiler ve ayarlar/api/secrets, /api/settings/scopedAçık izinlerle yalnızca yazılabilen kimlik bilgisi depolama ve katmanlı yapılandırma değerleri.
Bağlı araçlar/api/mcpBu örneğin çağırdığı MCP sunucuları. Bkz. MCP ve bağlı araçlar.
Sağlık/api/healthCanlılık ve hazırlık, ikisi de herkese açık. /api/health/ready ayrıca veritabanını denetler.

Yetkiler aile bazında değil, rota bazında uygulanır. Genel olarak: denetim günlüğü, maliyet defteri, politika kuralları ve çıkarım (inference) kontrol düzlemi kuruluşun Owner ve Admin rollerine yönelik yüzeylerdir; konuşmalar, projeler, bilgi ve onaylar ise her üyeye açıktır ve o üyenin gerçekte görebildiklerine göre daraltılır.

Uygulamalı örnekler

Oturum açın ve yenileme çerezini saklayın

Parolayla, local modundaki bir örnekte. Erişim token’ı gövdede döner; yenileme token’ı çerez deposuna düşer.

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

Token’ın süresi dolduğunda çerezi yenisiyle değiştirin:

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

Yanıt aynı { token, me } yapısındadır ve çerez deposu artık döndürülmüş yenileme token’ını tutar.

Görebildiğiniz kanalları listeleyin

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

Tek bir ekibe daraltmak için ?teamId=…, arşivlenmiş kanalları da dahil etmek için ?includeArchived=true ekleyin. defaultThreadId, bir sonraki gönderinizin gideceği yerdir.

Bir mesaj gönderin

Bir mesajın ya bir miktar content ya da en az bir yüklenmiş ek dosyası olmalıdır. Bir Idempotency-Key gönderin; yeniden denenen bir istek, ikinci bir kopya göndermek yerine ilk denemenin oluşturduğu mesaja karşılık gelir.

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

Yeni bir mesaj 201 döner:

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

Aynı idempotency anahtarıyla yapılan yeniden gönderim, orijinal mesajı ve boş bir pendingAgentInvites ile birlikte 200 yanıtı verir; çünkü ilk deneme, dizinin tarif ettiği uyarı ve iletimi zaten yapmıştır.

Bu rotadaki iki reddi ayrıca ele almaya değer. Sohbet uzunluk sınırını aşan içerik 413 MESSAGE_TOO_LARGE ile döner; doğru yanıt, metni bunun yerine dosya olarak yüklemektir. Yapısal olarak bir kimlik bilgisine benzeyen içerik 422 SECRET_INTERCEPTED ile döner — sunucu mesajı saklamadan, iletmeden ya da günlüğe yazmadan reddeder ve kimlik bilgisi /api/secrets içinde olmalıdır.