Sessions API
Sessions API, agent'ınızı kendi ürününüzün içine koyar: uygulamanızdaki bir sohbet ekranı, SaaS panelinizdeki bir destek alanı ya da her kullanıcı için ChatGPT tarzı bir sohbet listesi. Yeni bir sohbetin ne zaman başlayacağına uygulamanız karar verir; her sohbet bir oturumdur (session). Oturumlar Inbox'ta diğer kanallardaki konuşmalar gibi API kanalıyla görünür; böylece ekibiniz takip edip devralabilir.
Temel adres: https://peki.ai/api
Kimlik doğrulama: Authorization: Bearer psk_… (Kimlik doğrulama)
Nasıl çalışır
- Sunucunuz kullanıcılarınızdan biri için, kendi kullanıcı kimliğinizle tanımlanan bir oturum başlatır.
- O kullanıcının mesajlarını oturuma gönderirsiniz.
- Agent'ın cevabını mesajları yoklayarak (polling) ya da yazıldıkça akış olarak alırsınız.
Oturumlar her zaman agent'ın yayındaki sürümüyle konuşur. Taslaktaki değişiklikler API'ye ancak yayınladıktan sonra ulaşır.
Uç noktalar
| Yöntem | Yol | Erişim | Ne yapar |
|---|---|---|---|
POST | /agents/{agentId}/sessions | Okuma ve yazma | Oturum başlatır |
GET | /agents/{agentId}/sessions | Yalnızca okuma | Oturumları listeler |
GET | /sessions/{id} | Yalnızca okuma | Bir oturumu getirir |
PATCH | /sessions/{id} | Okuma ve yazma | Oturumu yeniden adlandırır |
GET | /sessions/{id}/messages | Yalnızca okuma | Mesajları listeler |
POST | /sessions/{id}/messages | Okuma ve yazma | Mesaj gönderir (JSON ya da akış) |
POST | /sessions/{id}/files | Okuma ve yazma | Dosya yükler |
POST | /sessions/{id}/feedback | Okuma ve yazma | Oturumu puanlar |
POST | /sessions/{id}/resolve | Okuma ve yazma | Oturumu çözüldü olarak kapatır |
Bir mesajdaki dosyayı indirmek için GET /files/{id} kullanın.
Nesneler
Oturum
{
"id": "671c9f20a1b2c3d4e5f60718",
"agentId": "66f1c2a9e4b0a1d2c3e4f567",
"channel": "api",
"status": "open",
"title": "Siparişim nerede?",
"preview": "10482 numaralı sipariş hâlâ gelmedi",
"customer": { "id": "user-1042", "name": "Ayşe Yılmaz" },
"createdAt": "2026-10-26T14:03:11.520Z",
"lastMessageAt": "2026-10-26T14:05:40.102Z",
"feedback": "up"
}
| Alan | Tür | Açıklama |
|---|---|---|
id | string | Oturum kimliği. |
agentId | string | Oturumun konuştuğu agent. |
channel | string | Burada başlatılan oturumlar için api. Diğer kanallar (webchat, telegram) yalnızca onların bir konuşmasını kimliğiyle okuduğunuzda görünür. |
status | string | open ya da resolved. |
title | string | Verdiğiniz başlık ya da ilk mesajın ilk 60 karakteri. Başlık oluşana kadar yer almaz. |
preview | string | Son müşteri mesajının başı (en fazla 200 karakter). |
customer.id | string | Oturumu başlatırken gönderdiğiniz kendi kullanıcı kimliğiniz. |
customer.name | string | Biliniyorsa müşterinin adı. |
createdAt | string | Oturumun başladığı an. |
lastMessageAt | string | Oturumdaki son hareketin zamanı. |
feedback | string | Son puan, up ya da down. Puan verilene kadar yer almaz. |
Mesaj
{
"id": "671ca0b3a1b2c3d4e5f60722",
"role": "agent",
"text": "10482 numaralı siparişiniz dün depomuzdan çıktı, perşembe günü elinizde olur.",
"status": "done",
"createdAt": "2026-10-26T14:05:43.871Z"
}
| Alan | Tür | Açıklama |
|---|---|---|
id | string | Mesaj kimliği. |
role | string | customer (kullanıcınız), agent (yapay zekâ) ya da team (Inbox'ta devralan ekip üyeniz). |
text | string | Mesaj metni. Agent cevabı pending iken ya da failed olduğunda boştur. |
status | string | pending (agent hâlâ yazıyor), done ya da failed. |
createdAt | string | Mesajın oluşturulduğu an. |
author | string | team mesajlarında ekip üyesinin adı. |
attachments | array | Mesajdaki dosyalar: { id, name, contentType, size }. GET /files/{id} ile indirin. |
Oturum başlatma
POST /agents/{agentId}/sessions
Kullanıcılarınızdan biri için yeni, boş bir oturum başlatır. Uygulamanız her yeni sohbet açtığında yeni bir oturum başlatın; her mesaj için ayrı oturum gerekmez.
Gövde
| Alan | Tür | Zorunlu | Kurallar |
|---|---|---|---|
customer.id | string | Evet | Bu kullanıcı için kendi kimliğiniz. 1–128 karakter. Aynı kimlik Peki'de her zaman aynı müşteri demektir. |
customer.name | string | Hayır | 1–120 karakter. Peki bu müşteri kimliğini ilk gördüğünde kaydedilir; sonraki oturumlar onu değiştirmez. |
title | string | Hayır | 1–120 karakter. Verilmezse ilk mesaj başlık olur. |
curl -X POST https://peki.ai/api/agents/66f1c2a9e4b0a1d2c3e4f567/sessions \
-H "Authorization: Bearer $PEKI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"customer": {"id": "user-1042", "name": "Ayşe Yılmaz"}, "title": "Sipariş sorusu"}'
const res = await fetch(`https://peki.ai/api/agents/${agentId}/sessions`, {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.PEKI_API_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ customer: { id: 'user-1042', name: 'Ayşe Yılmaz' } }),
});
const session = await res.json(); // { id, agentId, channel: "api", status: "open", ... }
Yanıt 201, oturum nesnesiyle.
Hatalar
| Durum | Mesaj | Neden |
|---|---|---|
400 | Invalid body | customer.id eksik ya da bir alan çok uzun. |
404 | Agent not found | Agent kimliği yanlış ya da başka bir çalışma alanına ait. |
409 | Publish the agent first | Agent'ın henüz yayınlanmış bir sürümü yok. |
API'den gelen müşteriler Peki'de ayrı müşterilerdir; aynı kişi olsalar bile web sitesi sohbeti ve Telegram müşterilerinden ayrı tutulurlar.
Oturumları listeleme
GET /agents/{agentId}/sessions?customer={customerId}&limit={n}
Agent'ın API oturumlarını, en son hareket edene göre sıralı listeler. Bir kullanıcıya sohbet geçmişini göstermek için kullanın.
Sorgu parametreleri
| Parametre | Zorunlu | Açıklama |
|---|---|---|
customer | Hayır | Kullanıcı kimliğiniz. Yalnızca o kullanıcının oturumları döner. Bilinmeyen bir kimlik boş liste döner. |
limit | Hayır | Kaç tane döneceği, 1–100. Varsayılan 50. |
Yalnızca API üzerinden başlatılan oturumlar listelenir; web sitesi sohbeti ve Telegram konuşmaları ya da otomasyon çalıştırmaları listelenmez.
curl "https://peki.ai/api/agents/66f1c2a9e4b0a1d2c3e4f567/sessions?customer=user-1042&limit=20" \
-H "Authorization: Bearer $PEKI_API_KEY"
const url = new URL(`https://peki.ai/api/agents/${agentId}/sessions`);
url.searchParams.set('customer', 'user-1042');
const sessions = await (await fetch(url, { headers: { Authorization: `Bearer ${key}` } })).json();
Yanıt 200, oturum dizisiyle.
Sayfalama imleci yoktur: en fazla en son 100 oturumu alırsınız. Kullanıcılarınızın daha fazla oturumu varsa kendi listenizi tutun.
Bir oturumu getirme
GET /sessions/{id}
curl https://peki.ai/api/sessions/671c9f20a1b2c3d4e5f60718 \
-H "Authorization: Bearer $PEKI_API_KEY"
Yanıt 200, oturum nesnesiyle. Oturum çalışma alanınızda yoksa 404 Session not found.
Bu, çalışma alanınızın diğer kanallarından gelen konuşma kimlikleri için de çalışır; ör. bir otomasyonda aldığınız bir kimlik. Bunları okuyabilirsiniz, ama yalnızca API oturumları mesaj kabul eder.
Oturumu yeniden adlandırma
PATCH /sessions/{id}
| Alan | Tür | Zorunlu | Kurallar |
|---|---|---|---|
title | string | Evet | 1–120 karakter. |
curl -X PATCH https://peki.ai/api/sessions/671c9f20a1b2c3d4e5f60718 \
-H "Authorization: Bearer $PEKI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Geciken teslimat, sipariş 10482"}'
Yanıt 200, güncellenmiş oturum nesnesiyle.
Mesajları listeleme
GET /sessions/{id}/messages
Oturumun tüm mesajlarını en eskiden başlayarak döner: kullanıcınızın mesajları, agent'ın cevapları ve ekibinizin cevapları.
curl https://peki.ai/api/sessions/671c9f20a1b2c3d4e5f60718/messages \
-H "Authorization: Bearer $PEKI_API_KEY"
Yanıt 200:
[
{ "id": "671ca0a1a1b2c3d4e5f60720", "role": "customer", "text": "10482 numaralı sipariş hâlâ gelmedi", "status": "done", "createdAt": "2026-10-26T14:05:40.102Z" },
{ "id": "671ca0b3a1b2c3d4e5f60722", "role": "agent", "text": "", "status": "pending", "createdAt": "2026-10-26T14:05:40.230Z" }
]
Mesaj gönderme
POST /sessions/{id}/messages
Kullanıcınızın mesajını agent'a gönderir. Agent cevabını hemen yazmaya başlar.
Gövde
| Alan | Tür | Zorunlu | Kurallar |
|---|---|---|---|
text | string | İkisinden biri | En fazla 4.000 karakter. |
attachments | string[] | İkisinden biri | Dosya yükleme ile bu oturuma yüklediğiniz en fazla 5 dosya kimliği. |
Bir mesajda metin, dosya ya da ikisi birden olmalıdır. Çözüldü olarak kapatılmış bir oturuma mesaj göndermek onu yeniden açar.
Cevabı almanın iki yolu var.
Seçenek 1: JSON ve yoklama
Özel bir Accept başlığı olmadan çağrı hemen 202 ile döner:
{
"message": { "id": "671ca0a1a1b2c3d4e5f60720", "role": "customer", "text": "10482 numaralı sipariş hâlâ gelmedi", "status": "done", "createdAt": "2026-10-26T14:05:40.102Z" },
"reply": { "id": "671ca0b3a1b2c3d4e5f60722", "role": "agent", "text": "", "status": "pending", "createdAt": "2026-10-26T14:05:40.230Z" }
}
Ardından reply.id kimlikli mesajın status değeri done (ya da failed) olana kadar GET /sessions/{id}/messages uç noktasını yoklayın. 1–2 saniyede bir yoklamak yeterlidir; çoğu cevap birkaç saniye sürer, agent araç çağırdığında daha uzun.
Yapay zekâdan cevap gelmeyecekse reply değeri null olur: ekibinizden biri konuşmayı Inbox'ta devralmıştır. Onun cevapları team mesajları olarak gelir.
async function ask(sessionId, text) {
const headers = { Authorization: `Bearer ${process.env.PEKI_API_KEY}`, 'Content-Type': 'application/json' };
const base = 'https://peki.ai/api';
const sent = await (await fetch(`${base}/sessions/${sessionId}/messages`, {
method: 'POST', headers, body: JSON.stringify({ text }),
})).json();
if (!sent.reply) return null; // bir ekip üyesi devraldı
for (let i = 0; i < 60; i++) {
await new Promise((r) => setTimeout(r, 1500));
const messages = await (await fetch(`${base}/sessions/${sessionId}/messages`, { headers })).json();
const reply = messages.find((m) => m.id === sent.reply.id);
if (reply && reply.status !== 'pending') return reply;
}
throw new Error('Henüz cevap yok');
}
Seçenek 2: akış
Accept: text/event-stream gönderin; cevap aynı bağlantı üzerinden server-sent events olarak akar. Her olay, JSON nesnesi taşıyan tek bir data: satırıdır:
type | Alanlar | Ne zaman |
|---|---|---|
accepted | message | İlk olay: kullanıcınızın kaydedilen mesajı. |
start | Agent çalışmaya başladı. | |
tool | name, state (running, ok ya da error), durationMs | Agent araçlarından birini çağırıyor. "Siparişinize bakıyorum…" göstermek için kullanın. |
text | text | Şu ana kadarki cevap. Her text olayı yalnızca yeni kısmı değil, o ana kadarki metnin tamamını taşır: gösterdiğinizi değiştirin, sonuna eklemeyin. |
message | message | Kaydedilmiş son cevap. Bunu gösterin. |
done | timedOut (yalnızca true olduğunda) | Son olay. Bağlantı kapanır. |
curl -N -X POST https://peki.ai/api/sessions/671c9f20a1b2c3d4e5f60718/messages \
-H "Authorization: Bearer $PEKI_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-d '{"text": "10482 numaralı siparişim nerede?"}'
data: {"type":"accepted","message":{"id":"671ca0a1a1b2c3d4e5f60720","role":"customer","text":"10482 numaralı siparişim nerede?","status":"done","createdAt":"2026-10-26T14:05:40.102Z"}}
data: {"type":"start"}
data: {"type":"tool","name":"get_order_status","state":"running"}
data: {"type":"tool","name":"get_order_status","state":"ok","durationMs":412}
data: {"type":"text","text":"10482 numaralı siparişiniz"}
data: {"type":"text","text":"10482 numaralı siparişiniz dün depomuzdan çıktı"}
data: {"type":"message","message":{"id":"671ca0b3a1b2c3d4e5f60722","role":"agent","text":"10482 numaralı siparişiniz dün depomuzdan çıktı, perşembe günü elinizde olur.","status":"done","createdAt":"2026-10-26T14:05:40.230Z"}}
data: {"type":"done"}
const res = await fetch(`https://peki.ai/api/sessions/${sessionId}/messages`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.PEKI_API_KEY}`,
'Content-Type': 'application/json',
Accept: 'text/event-stream',
},
body: JSON.stringify({ text: '10482 numaralı siparişim nerede?' }),
});
if (!res.ok) throw new Error(`Peki API ${res.status}: ${await res.text()}`);
const reader = res.body.pipeThrough(new TextDecoderStream()).getReader();
let buffer = '';
for (;;) {
const { value, done } = await reader.read();
if (done) break;
buffer += value;
const events = buffer.split('\n\n');
buffer = events.pop();
for (const e of events) {
if (!e.startsWith('data: ')) continue;
const event = JSON.parse(e.slice(6));
if (event.type === 'text') render(event.text); // şu ana kadarki cevabın tamamı
if (event.type === 'message') render(event.message.text); // son metin
}
}
Akış hakkında bilmeniz gerekenler:
- Sızıntı guardrail'i açıkken
textolayı gelmez. Agent'ın Guardrails ayarları cevapları gönderilmeden önce sızıntıya karşı kontrol ediyorsa metin akamaz:start,toololaylarını ve ardından kontrol edilmişmessageolayını alırsınız. Bkz. Guardrails ve gizlilik. - Son
textolayını değil,messageolayını gösterin. Guardrails ve kişisel veri işleme son cevabı değiştirebilir.messageolayı kaydedilen ve ekibinizin Inbox'ta gördüğü cevaptır. - Ekip devralırsa akış
acceptedolayından hemen sonradoneile biter. - En fazla iki dakika. Cevap 120 saniye içinde bitmezse
{"type":"done","timedOut":true}alırsınız ve akış kapanır. Cevap yine de tamamlanır; onu Mesajları listeleme ile alın. - Bağlantınız koparsa cevap yine tamamlanır. Onu Mesajları listeleme ile alın.
- Agent'ın akıl yürütmesi (reasoning) asla akışa gönderilmez.
Hatalar
| Durum | Mesaj ya da code | Neden | Ne yapmalı |
|---|---|---|---|
400 | Write a message or attach a file | text boş ve attachments yok. | Metin ya da dosya gönderin. |
400 | Invalid body | text 4.000 karakteri aşıyor, 5'ten fazla ek var ya da bir kimlik dosya kimliği değil. | Gövdeyi kısaltın ya da düzeltin. |
404 | Session not found | Kimlik yanlış. | Kimliği kontrol edin. |
409 | Only sessions started through the API take messages here | Kimlik bir web sitesi sohbeti ya da Telegram konuşmasına ait. | Bir API oturumu başlatın. |
409 | file_taken | Bir ek kimliği yok, başka bir mesaja zaten eklenmiş, başka bir oturuma ait, başka bir anahtarla yüklenmiş ya da 24 saatten eski. | Aynı anahtarla yeniden yükleyin. |
429 | This assistant is very busy right now. Please try again later. | Çalışma alanı günlük yayındaki cevap sınırına ulaştı. | Daha sonra tekrar deneyin. |
503 | Could not accept the message right now. Please try again. | Mesaj kaydedilmedi. | Tekrar deneyin. |
Dosya yükleme
POST /sessions/{id}/files
Content-Type: multipart/form-data
Kullanıcınızdan oturuma bir dosya yükler. Ardından dönen id değerini bir mesajın attachments alanına koyarak dosyayı gönderin. Agent dosyayı mesajla birlikte okur.
Form alanları: file adlı tam olarak bir parça. Başka alan yok.
Kabul edilen dosyalar
| Tür | Biçimler |
|---|---|
| Fotoğraflar | JPEG, PNG, WebP, GIF |
| Belgeler | PDF, TXT, Markdown, CSV |
- Dosya başına en fazla 10 MB, mesaj başına 5 dosya ve konuşma başına 30 dosya.
- Tür, dosyanın adından değil içeriğinden kontrol edilir. SVG ve HTML asla kabul edilmez.
- Aynı API anahtarıyla yükleyin ve gönderin. Bir anahtarla yüklenen dosya başka bir anahtarla gönderilemez.
- 24 saat içinde bir mesajla gönderilmeyen dosya silinir.
curl -X POST https://peki.ai/api/sessions/671c9f20a1b2c3d4e5f60718/files \
-H "Authorization: Bearer $PEKI_API_KEY" \
-F "[email protected]"
const form = new FormData();
form.append('file', new Blob([bytes], { type: 'image/jpeg' }), 'hasarli-kutu.jpg');
const file = await (await fetch(`https://peki.ai/api/sessions/${sessionId}/files`, {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.PEKI_API_KEY}` },
body: form,
})).json();
await fetch(`https://peki.ai/api/sessions/${sessionId}/messages`, {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.PEKI_API_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ text: 'Kutu hasarlı geldi', attachments: [file.id] }),
});
Yanıt 201:
{ "id": "671ca1c4a1b2c3d4e5f60730", "name": "hasarli-kutu.jpg", "contentType": "image/jpeg", "size": 284113, "kind": "chat" }
Hatalar bir code taşır:
| Durum | code | Mesaj |
|---|---|---|
400 | Attach a file in the field "file" | |
400 | file_type | This kind of file is not accepted |
413 | file_size | The file is larger than 10 MB |
400 | conversation_full | This conversation already has 30 files |
409 | Only sessions started through the API take messages here |
Görselleri yalnızca bazı modeller görebilir. Kullanıcılarınız fotoğraf gönderecekse agent için Görsel okur işaretli bir model seçin. Diğer modellere yalnızca bir fotoğraf gönderildiği bilgisi gider. Bkz. Dosyalar ve ekler.
Oturumu puanlama
POST /sessions/{id}/feedback
Kullanıcınızın oturum için verdiği beğeni ya da beğenmemeyi kaydeder. Son verilen puan geçerlidir. Analytics'te müşteri memnuniyeti olarak görünür.
| Alan | Tür | Zorunlu | Kurallar |
|---|---|---|---|
rating | string | Evet | up ya da down. Başka alana izin verilmez. |
curl -X POST https://peki.ai/api/sessions/671c9f20a1b2c3d4e5f60718/feedback \
-H "Authorization: Bearer $PEKI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"rating": "up"}'
Yanıt 200, feedback alanı ayarlanmış oturum nesnesiyle. Diğer kanallar için 409 Only sessions started through the API take feedback here.
Oturumu çözüldü olarak kapatma
POST /sessions/{id}/resolve
Oturumu, Inbox'taki Çözüldü gibi çözüldü olarak işaretler. Kullanıcınız sohbeti kapattığında ya da sorunun çözüldüğünü söylediğinde çağırın. Kullanıcı aynı oturumda yeniden yazarsa oturum yeniden açılır.
Gövde yok.
curl -X POST https://peki.ai/api/sessions/671c9f20a1b2c3d4e5f60718/resolve \
-H "Authorization: Bearer $PEKI_API_KEY"
Yanıt 200, status değeri resolved olan oturum nesnesiyle.
Ekibiniz ve API oturumları
- API oturumları Inbox'ta API kanalıyla ve kullanıcınızın adı ya da kimliğiyle görünür.
- Agent'ınız bir kişiye devrettiğinde ekibinizden biri Devral ile konuşmayı alıp cevap verebilir. Onun cevapları Mesajları listeleme sonucunda
roledeğeriteamveauthoradıyla görünür. Oturum devralınmışken bunları yoklayın:replydeğerinullolur ve yapay zekâ cevabı gelmez. - AI'a geri ver sonrasında agent yeniden cevap verir.
Eksiksiz bir örnek
Uygulamanızın sohbet ekranının çağırdığı küçük bir Node.js sunucu uç noktası:
import express from 'express';
const app = express();
app.use(express.json());
const PEKI = 'https://peki.ai/api';
const AGENT_ID = process.env.PEKI_AGENT_ID;
const headers = { Authorization: `Bearer ${process.env.PEKI_API_KEY}`, 'Content-Type': 'application/json' };
// Kullanıcı yeni bir sohbet açtığında uygulamanız bunu çağırır.
app.post('/chat/sessions', async (req, res) => {
const r = await fetch(`${PEKI}/agents/${AGENT_ID}/sessions`, {
method: 'POST', headers,
body: JSON.stringify({ customer: { id: req.user.id, name: req.user.name } }),
});
res.status(r.status).json(await r.json());
});
// Uygulamanız her mesaj için bunu çağırır ve akışı okur.
app.post('/chat/sessions/:id/messages', async (req, res) => {
const r = await fetch(`${PEKI}/sessions/${req.params.id}/messages`, {
method: 'POST',
headers: { ...headers, Accept: 'text/event-stream' },
body: JSON.stringify({ text: req.body.text }),
});
res.status(r.status).set('content-type', r.headers.get('content-type'));
for await (const chunk of r.body) res.write(chunk);
res.end();
});
app.listen(3000);
İsteği iletmeden önce req.params.id değerinin giriş yapmış kullanıcıya ait olduğunu kontrol edin: API anahtarı çalışma alanınızdaki her oturuma erişebilir.