REST · v1

API dokümantasyonu

Nekna360'a bağlı WhatsApp, Instagram, Facebook ve Google kanallarını kendi yazılımınızdan kullanın: mesaj gönderin, sohbetleri okuyun, olayları webhook ile anında alın. NeknaCRM ve NeknaAI de bu API'yi kullanır.

Başlangıç

Tüm istekler JSON alır ve döndürür. Temel adres:

https://nekna360.com/api/v1
  1. Panelde Geliştirici sayfasından bir API anahtarı oluşturun ve gereken yetkileri seçin.
  2. Kanallar sayfasından en az bir kanal bağlayın (ör. WhatsApp).
  3. Olayları almak istiyorsanız aynı sayfadan bir webhook adresi ekleyin.

Anahtar ve webhook oluşturmak için panelde Geliştirici sayfası.

Kimlik doğrulama

Anahtarı her isteğe Authorization başlığıyla ekleyin. Anahtarlar yalnızca oluşturulurken bir kez gösterilir; sunucu tarafında saklayın, tarayıcıya veya mobil uygulamaya koymayın.

curl https://nekna360.com/api/v1/me \
  -H "Authorization: Bearer n360_xxxxxxxxxxxxxxxx"

Yetkiler

AlanAçıklama
channels:readHesap bilgisi (/me), kanal listesi ve kanal durumu (QR kodu dahil)
channels:writeWhatsApp QR kanalı açma, yeniden bağlama, bağlantıyı kesme
contacts:readKişileri listeleme
contacts:writeKişi ekleme/güncelleme, toplu aktarım
messages:readSohbet ve mesajları okuma
messages:sendMesaj gönderme
conversations:writeSohbet durumu, atama, okundu işaretleme
webhooks:manageWebhook adreslerini yönetme

Hatalar ve limitler

Hatalar HTTP durum kodu ve makine tarafından okunabilir bir error alanıyla döner. Anahtar başına dakikada 300 istek sınırı vardır; aşıldığında 429 ve retry-after başlığı döner.

HTTP/1.1 403 Forbidden
{ "error": "insufficient_scope", "required": "messages:send" }
HTTPerrorAnlamı
401missing_api_key / invalid_api_keyAnahtar yok, hatalı veya iptal edilmiş
403insufficient_scopeAnahtarın bu işlem için yetkisi yok (required alanına bakın)
403api_not_in_plan / organization_inactivePaketinizde API yok veya hesap askıda
403message_quota_reached / contact_limit_reachedPaket limiti doldu
403qr_channel_limit_reached / channel_limit_reachedWhatsApp QR hesabı veya toplam kanal sınırı doldu (limit alanında sayı)
404conversation_not_found, channel_not_found…Kayıt bulunamadı ya da başka bir firmaya ait
409channel_not_connectedKanal bağlı değil
409outside_24h_windowResmi WhatsApp API: 24 saat geçti, şablon gönderin
409contact_do_not_contactKişi aranmasın listesinde
422invalid_bodyGövde geçersiz; issues alanında ayrıntı var
429rate_limitedÇok fazla istek; retry-after kadar bekleyin

Sayfalama

Liste uç noktaları imleçle sayfalanır. Yanıttaki next_cursor (mesajlarda next_before) değerini bir sonraki istekte gönderin; null ise son sayfadasınız.

GET https://nekna360.com/api/v1/conversations?limit=50
→ { "data": [ … ], "next_cursor": "cmv…" }

GET https://nekna360.com/api/v1/conversations?limit=50&cursor=cmv…

Hesap ve kanallar

GET/mechannels:read

Anahtarın bağlı olduğu firma, ekip üyeleri (atama için kullanıcı id'leri) ve anahtarın yetkileri.

{
  "organization": { "id": "cmv…", "name": "Atlas Yapı", "status": "ACTIVE" },
  "members": [ { "id": "cmu…", "name": "Can Demir", "email": "can@atlas.com", "role": "agent" } ],
  "key": { "name": "NeknaCRM", "scopes": ["messages:read", "messages:send"] }
}
GET/channelschannels:read

Bağlı kanallar. Mesaj gönderirken channel_id olarak kullanılır.

{ "data": [
  { "id": "cmvp…", "type": "WHATSAPP_QR", "name": "WhatsApp +905321234567",
    "status": "connected", "phone": "+905321234567", "connected_at": "2026-10-27T09:12:00.000Z" }
] }
POST/channels/whatsapp-qrchannels:write

Yeni bir WhatsApp QR kanalı açar ve eşleştirmeyi başlatır. Paketteki WhatsApp QR hesabı sınırı (qr_channels) dolmuşsa 403 qr_channel_limit_reached döner. Bağlantısı kesilmiş kanallar sınıra sayılmaz.

curl -X POST https://nekna360.com/api/v1/channels/whatsapp-qr \
  -H "Authorization: Bearer n360_…" -H "Content-Type: application/json" -d '{ "name": "Satış hattı" }'
→ 201 { "data": { "id": "cmvq…", "type": "WHATSAPP_QR", "status": "pending", "qr": null, … } }
GET/channels/{id}channels:read

Kanal durumunu döndürür. Eşleşme beklenirken qr alanında güncel kod bulunur: value (kendi QR çiziminiz için) ve hazır svg. Kod yaklaşık 20 saniyede bir yenilenir; ekranınızda 3–5 saniyede bir sorgulayın. status "connected" olunca qr null olur ve phone dolar. Ayrıca channel.status webhook'u gelir.

{ "data": {
  "id": "cmvq…", "type": "WHATSAPP_QR", "name": "Satış hattı", "status": "pending", "phone": null,
  "qr": { "value": "2@Xk3…", "svg": "<svg xmlns=…>…</svg>", "generated_at": "2026-10-27T10:02:11.000Z" }
} }
POST/channels/{id}/connectchannels:write

Bağlantısı kesilmiş veya oturumu kapanmış bir QR kanalında eşleştirmeyi yeniden başlatır (sohbet geçmişi korunur).

DELETE/channels/{id}channels:write

WhatsApp QR kanalının bağlı cihaz oturumunu kapatır. Sohbetler silinmez; aynı kanal /connect ile yeniden bağlanabilir.

Kişiler

GET/contacts?q=&limit=100&cursor=contacts:read

Arama (q: ad, telefon, e-posta) ve imleçli listeleme.

POST/contactscontacts:write

Telefona göre ekler veya günceller. opt_in: true, kişinin pazarlama mesajı izni verdiğini kaydeder (kampanyalar için).

curl -X POST https://nekna360.com/api/v1/contacts \
  -H "Authorization: Bearer n360_…" -H "Content-Type: application/json" \
  -d '{ "name": "Ayşe Yılmaz", "phone": "05321234567", "tags": ["web"], "opt_in": true }'
POST/contacts/bulkcontacts:write

Tek istekte 1000 kişiye kadar ekleme/güncelleme (telefona göre). opt_in her kişi için ayrı verilir; yanıttaki ids dizisi girdi sırasını izler (null = atlandı).

{ "contacts": [ { "name": "Ayşe", "phone": "05321234567", "opt_in": true }, { "name": "Mert", "phone": "05339876543" } ], "tags": ["fuar-2026"] }
→ { "created": 2, "updated": 0, "skipped": 0, "invalid": 0, "ids": ["cmv…", "cmv…"] }

Mesaj gönderme

POST/messagesmessages:send

Mesaj kuyruğa alınır ve 202 döner. Gönderim sonucu message.sent / message.failed webhook'u ile gelir; isterseniz GET /messages/{id} ile de sorgulayabilirsiniz.

AlanTürAçıklama
conversation_idstringVar olan bir sohbete cevap (her kanal türünde çalışır).
channel_id + tostringTelefon numarasıyla WhatsApp sohbeti başlatır veya sürdürür. Kişi yoksa oluşturulur. Yalnızca WhatsApp kanalları.
namestring?Yeni kişi oluşturulursa adı.
textstring?En fazla 4000 karakter.
mediaobject?{ type: image|video|audio|document, data: base64, mimetype, filename } — en fazla 16 MB.
templateobject?Resmi WhatsApp API şablonu: { name, language, params[] }.
client_refstring?Sizin kimliğiniz. Aynı değerle tekrar gönderirseniz yeni mesaj oluşmaz, ilk mesaj döner (duplicate: true).
WhatsApp — text
curl -X POST https://nekna360.com/api/v1/messages \
  -H "Authorization: Bearer n360_…" -H "Content-Type: application/json" \
  -d '{ "channel_id": "cmvp…", "to": "05321234567", "name": "Ayşe Yılmaz",
        "text": "Merhaba Ayşe Hanım, teklifiniz hazır.", "client_ref": "crm-offer-8812" }'
Reply in a conversation — document
{ "conversation_id": "cmvq…",
  "text": "Teklif ektedir.",
  "media": { "type": "document", "data": "<base64>", "mimetype": "application/pdf", "filename": "teklif.pdf" } }
Official WhatsApp API — template
{ "channel_id": "cmvc…", "to": "+905321234567",
  "template": { "name": "randevu_hatirlatma", "language": "tr", "params": ["Ayşe", "28 Ekim 14:00"] } }
202 Accepted
{ "data": {
  "id": "cmvq0jw9o…", "conversation_id": "cmvpyt25…", "channel_id": "cmvp…", "channel_type": "WHATSAPP_QR",
  "contact_id": "cmvpw239…", "direction": "out", "type": "text", "text": "Merhaba Ayşe Hanım, teklifiniz hazır.",
  "media": null, "status": "pending", "error": null, "external_id": null, "source": "api",
  "client_ref": "crm-offer-8812", "created_at": "2026-10-27T09:30:11.388Z"
} }

Mesaj durumları: pending → sent → delivered → read, ya da failed (error alanında sebep).

GET/messages/{id}messages:read

Sohbetler

GET/conversationsmessages:read

Son etkinliğe göre sıralı. Filtreler: status (open|pending|closed), channel_id, contact_id, phone, updated_after (ISO tarih).

GET https://nekna360.com/api/v1/conversations?status=open&phone=05321234567

{ "data": [ {
  "id": "cmvq…", "channel_id": "cmvp…", "channel_type": "WHATSAPP_QR", "channel_name": "WhatsApp +9053…",
  "contact": { "id": "cmvp…", "name": "Ayşe Yılmaz", "phone": "+905321234567", "email": null },
  "status": "open", "assignee_id": null, "unread_count": 2,
  "last_message_at": "2026-10-27T09:31:02.000Z", "last_preview": "Fiyat alabilir miyim?",
  "last_inbound_at": "2026-10-27T09:31:02.000Z", "created_at": "2026-10-20T08:00:00.000Z"
} ], "next_cursor": null }
GET/conversations/{id}messages:read
PATCH/conversations/{id}conversations:write

Durum değiştirir veya temsilciye atar (assignee_id: /me içindeki üye id'si, null = atamayı kaldır).

{ "status": "closed" }        { "assignee_id": "cmu…" }
GET/conversations/{id}/messages?limit=50&before=messages:read

Mesajlar yeniden eskiye. Daha eskileri için before=<en eski mesaj id'si>. İç notlar için include_notes=true.

POST/conversations/{id}/readconversations:write

Okunmamış sayacını sıfırlar ve WhatsApp'ta okundu bilgisi (mavi tik) gönderir.

Webhook'lar

Webhook eklediğinizde Nekna360, seçtiğiniz olaylarda adresinize imzalı bir POST isteği atar. Adres https olmalı ve herkese açık bir sunucuda olmalıdır. 2xx dışındaki her yanıt başarısız sayılır ve yaklaşık bir saat boyunca artan aralıklarla 8 kez yeniden denenir; arka arkaya 50 olay teslim edilemezse webhook duraklatılır (panelden devam ettirebilirsiniz).

OlayNe zaman
message.receivedMüşteriden yeni mesaj geldi.
message.sentMesaj kanala iletildi (panelden, API'den, kampanyadan veya doğrudan telefondan yazılan).
message.failedMesaj gönderilemedi; message.error alanında sebep.
message.statusTeslim/okundu bilgisi değişti (delivered, read).
conversation.updatedSohbet kapatıldı, açıldı, beklemeye alındı veya atandı.
channel.statusKanalın bağlantı durumu değişti (ör. WhatsApp oturumu kapandı).
POST https://your-app.example.com/webhooks/nekna360 — message.received
{
  "id": "evt_4f0c2b0e9a6c4c8f9d3a1b7e5c2d8f10",
  "type": "message.received",
  "created_at": "2026-10-27T09:31:02.412Z",
  "org_id": "cmvpuinuk…",
  "data": {
    "message": {
      "id": "cmvq…", "conversation_id": "cmvq…", "channel_id": "cmvp…", "channel_type": "WHATSAPP_QR",
      "contact_id": "cmvp…", "direction": "in", "type": "text", "text": "Fiyat alabilir miyim?",
      "media": null, "status": "received", "external_id": "3EB0…", "source": "customer", "created_at": "2026-10-27T09:31:02.000Z"
    },
    "contact": { "id": "cmvp…", "name": "Ayşe Yılmaz", "phone": "+905321234567" }
  }
}

Başlıklar

AlanTürAçıklama
x-n360-eventstringOlay adı
x-n360-deliverystringOlay id'si (evt_…). Aynı olay yeniden denenirse aynı kalır; tekrarları bununla ayıklayın.
x-n360-attemptnumberKaçıncı deneme
x-n360-timestampunix snİmzanın zamanı
x-n360-signaturehexHMAC-SHA256(secret, timestamp + "." + gövde)
GET/webhookswebhooks:manage
POST/webhookswebhooks:manage
{ "url": "https://crm.example.com/webhooks/nekna360", "events": ["message.received", "message.status"], "description": "NeknaCRM" }
→ 201 { "data": { "id": "cmvq…", …, "secret": "whsec_…" } }
PATCH/webhooks/{id}webhooks:manage
{ "events": ["*"] }      { "active": false }
DELETE/webhooks/{id}webhooks:manage
POST/webhooks/{id}/testwebhooks:manage

İmza doğrulama

Gövdeyi ham haliyle (JSON'a çevirmeden önce) imzalayıp başlıktaki değerle sabit zamanlı karşılaştırın; 5 dakikadan eski zaman damgalarını reddedin. Gizli anahtar (whsec_…) webhook oluşturulurken bir kez gösterilir.

Node.js (Express)
import crypto from "node:crypto";

app.post("/webhooks/nekna360", express.raw({ type: "application/json" }), (req, res) => {
  const ts = req.get("x-n360-timestamp");
  const sig = req.get("x-n360-signature") || "";
  const expected = crypto.createHmac("sha256", process.env.N360_WEBHOOK_SECRET)
    .update(ts + "." + req.body).digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(ts)) < 300;
  if (!fresh || sig.length !== expected.length || !crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
    return res.sendStatus(401);
  }
  const event = JSON.parse(req.body);
  res.sendStatus(200);           // answer first…
  queue.add(event);              // …then process
});
PHP
$body = file_get_contents('php://input');
$ts  = $_SERVER['HTTP_X_N360_TIMESTAMP'] ?? '';
$sig = $_SERVER['HTTP_X_N360_SIGNATURE'] ?? '';
$expected = hash_hmac('sha256', $ts . '.' . $body, getenv('N360_WEBHOOK_SECRET'));
if (abs(time() - (int) $ts) > 300 || !hash_equals($expected, $sig)) {
    http_response_code(401);
    exit;
}
$event = json_decode($body, true);
http_response_code(200);
Python
import hmac, hashlib, time

def verify(raw_body: bytes, ts: str, sig: str, secret: str) -> bool:
    if abs(time.time() - int(ts)) > 300:
        return False
    expected = hmac.new(secret.encode(), ts.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, sig)

İş ortağı API'si

Nekna ürünleri (NeknaCRM, NeknaAI) ve iş ortakları, kendi müşterileri için Nekna360 firması açar ve yönetir. Platform anahtarı (np_…) Nekna ekibi tarafından verilir ve /api/partner/v1 altında kullanılır. Firma açıldığında o firmaya ait tam yetkili bir API anahtarı (n360_…) bir kez döner; müşterinin mesaj, sohbet ve kanal işlemleri bu anahtarla /api/v1 üzerinden yapılır.

  1. Müşteri ürününüzde WhatsApp'ı açmak istediğinde POST /organizations ile firmayı oluşturun (ref = sizin müşteri id'niz). Dönen api_key'i o müşteri için şifreli saklayın.
  2. POST /api/v1/channels/whatsapp-qr ile kanalı açın, GET /api/v1/channels/{id} ile QR kodunu kendi ekranınızda gösterin.
  3. Webhook ekleyin (POST /api/v1/webhooks), mesajları kendi arayüzünüzde gösterin; cevapları POST /api/v1/messages ile gönderin.
  4. Müşteri Nekna360 panelini de kullanacaksa owner_email ile tek kullanımlık davet bağlantısı alın.
https://nekna360.com/api/partner/v1
Authorization: Bearer np_…
POST/organizations
{ "ref": "crm-tenant-42", "name": "Atlas Yapı", "plan": "profesyonel", "trial_days": 14,
  "limits": { "qr_channels": 2, "monthly_messages": 5000 }, "owner_email": "ali@atlas.com" }

→ 201 { "data": { "id": "cmv…", "ref": "crm-tenant-42", "status": "trial", "plan": { "code": "profesyonel", … },
         "limits": { "channels": 10, "qr_channels": 2, … }, "usage": { "channels": 0, "qr_channels": 0 }, … },
        "created": true, "api_key": "n360_…", "invite_url": "https://nekna360.com/davet/…" }

limits alanı müşteri bazında paket limitlerini ezer: channels, qr_channels, seats, contacts, monthly_messages, monthly_ai_credits. -1 = sınırsız, null = paketteki değer.

GET/organizations · /organizations/{ref}
PATCH/organizations/{ref}
{ "status": "active", "plan": "kurumsal", "limits": { "qr_channels": 5 } }
POST/organizations/{ref}/keys
{ "revoke_others": true }   → 201 { "api_key": "n360_…" }
POST/organizations/{ref}/invites
{ "email": "ali@atlas.com", "role": "owner" }   → 201 { "invite_url": "https://nekna360.com/davet/…", "expires_in_days": 7 }

Kurallar ve iyi uygulamalar

  • WhatsApp QR bağlantısı resmi API değildir: birebir sohbet içindir. Toplu veya istenmeyen mesaj göndermeyin; numaranız kısıtlanabilir. Bildirim ve kampanyalar için resmi WhatsApp Business API kanalı kullanın.
  • Resmi WhatsApp API'de müşterinin son mesajından 24 saat sonra yalnızca onaylı şablonlar gönderilebilir.
  • Aynı mesajı iki kez göndermemek için her mesaja client_ref verin (ör. CRM'deki kayıt id'si).
  • Webhook'larınıza hızlıca 200 dönün, işi arka planda yapın; 10 saniyede cevap vermeyen istek başarısız sayılır.
  • Olaylar nadiren tekrar gelebilir veya sırası değişebilir: x-n360-delivery ile tekrarları, created_at ile sırayı kontrol edin.
  • API mesajları paketinizin aylık mesaj kotasından düşer; panelden gönderilenlerle aynı sayaçtır.

Sorunuz mu var? Bize yazın.