Geliştiriciler
NotusCRM API ve webhook'lar
Dış sistemlerinizi CRM'e bağlayın: REST API ile kayıtları okuyun ve yazın, webhook'larla fırsat, kişi, şirket ve görev değişikliklerini anında alın.
REST API
- Anahtarı workspace sahibi ya da yöneticisi Ayarlar → API ve webhook'lar'dan oluşturur. Anahtar yalnızca oluşturulduğu anda gösterilir ve ncrm_live_ ile başlar.
- İsteklerde Authorization: Bearer <anahtar> başlığını gönderin. Workspace'i anahtar taşır; ayrıca başlık gerekmez.
- Anahtar, onu oluşturan kişinin yetkisiyle çalışır. Varsayılan olarak salt okunurdur; kayıt oluşturmak ve güncellemek için yazma izni verilir (ücretsiz planda salt okunur).
- Anahtarla kayıt silinemez; anahtar, bağlantı ve webhook yönetimi, e-posta gönderimi, dışa aktarma ve workspace kapatma da kapalıdır. Her yazma denetim kaydına "API · anahtar adı" olarak düşer.
- Hatalar application/problem+json biçiminde döner: 400 geçersiz istek, 401 anahtar geçersiz, 403 izin yok, 404 bulunamadı, 409 işlem geçerli değil.
Tüm uçlar ve şemalar OpenAPI belgesinde: https://api.notuscrm.com/openapi/v1.json
curl https://api.notuscrm.com/api/v1/deals \
-H "Authorization: Bearer ncrm_live_…"Webhook'lar
- Uçları yine Ayarlar → API ve webhook'lar'dan ekleyin: HTTPS adres, abone olunacak olaylar. Gizli imza anahtarı (whsec_) yalnızca oluşturulduğunda ve yenilendiğinde gösterilir.
- Her olay imzalı bir POST isteğiyle gider. 10 saniye içinde 2xx dönen teslim başarılıdır; diğerleri 1 dk, 5 dk, 30 dk, 2 sa, 6 sa, 12 sa ve 24 sa aralıklarla, toplam 8 kez denenir.
- Art arda 50 başarısız teslimde uç kendiliğinden kapanır. Teslim günlüğü 30 gün saklanır; teslimleri oradan yeniden gönderebilir, "Test gönder" ile ping olayı yollayabilirsiniz.
- Aynı olay birden çok kez gelebilir (yeniden deneme): gövdedeki id ile tekrarları ayıklayın. Olayların sırası garanti değildir; createdAt'e bakın.
- data kaydın alanlarını taşır; silme olaylarında yalnızca id gelir. Aşama değişiminde previousStageId de vardır. İç ağ adreslerine gönderim yapılmaz.
Olaylar
- deal.created
- deal.updated
- deal.stage_changed
- deal.deleted
- person.created
- person.updated
- person.deleted
- company.created
- company.updated
- company.deleted
- task.created
- task.updated
- task.completed
- task.deleted
- custom_record.created
- custom_record.updated
- custom_record.deleted
POST /your/endpoint
Content-Type: application/json
X-Notus-Event: deal.stage_changed
X-Notus-Delivery: 0199c3a1-…
X-Notus-Signature: t=1790000000,v1=5f2b…
{
"id": "0199c3a0-…",
"type": "deal.stage_changed",
"createdAt": "2026-09-26T09:12:44Z",
"workspaceId": "01a0c31a-…",
"data": { "id": "…", "title": "…", "stageId": "…", "previousStageId": "…", "amount": 120000, "currency": "TRY" }
}İmzayı doğrulama
X-Notus-Signature başlığı t (Unix zaman) ve v1 (HMAC-SHA256) değerlerini taşır. İmza, gizli anahtarla t değeri, bir nokta ve ham gövdenin birleşiminden (t.gövde) hesaplanır. Gövdeyi yeniden biçimlendirmeden doğrulayın ve eski zaman damgalı istekleri reddedin.
import crypto from "node:crypto";
// rawBody: the request body exactly as received (do not re-serialize JSON)
export function verify(rawBody, header, secret, toleranceSeconds = 300) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) <= toleranceSeconds;
return fresh && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}using System.Security.Cryptography;
using System.Text;
static bool Verify(string rawBody, string header, string secret)
{
var parts = header.Split(',').Select(p => p.Split('=', 2)).ToDictionary(p => p[0], p => p[1]);
byte[] hash = HMACSHA256.HashData(Encoding.UTF8.GetBytes(secret), Encoding.UTF8.GetBytes($"{parts["t"]}.{rawBody}"));
bool fresh = Math.Abs(DateTimeOffset.UtcNow.ToUnixTimeSeconds() - long.Parse(parts["t"])) <= 300;
return fresh && CryptographicOperations.FixedTimeEquals(
Encoding.ASCII.GetBytes(Convert.ToHexStringLower(hash)), Encoding.ASCII.GetBytes(parts["v1"]));
}