# Web kancaları

> CRM'de bir şey olduğunda (fırsat kazanıldı, yeni firma, form başvurusu…) seçtiğiniz adrese imzalı JSON gönderin. Zapier, Make, n8n ve Pipedream aynı mekanizmayı kullanır.

Web kancası (giden kanca), CRM'deki olayları sizin sisteminize **itme** yöntemidir: sürekli API'yi yoklamak yerine olay olduğunda bir HTTP `POST` alırsınız. Kurulum **Ayarlar → Uygulamalar** sayfasından yapılır; kod yazmak gerekmez.

## Kurulum

:::steps
### Bağlantıyı açın

**Ayarlar → Uygulamalar**'da **Web kancası**'nı (ya da Zapier, Make, n8n, Pipedream kartını) seçin ve **Bağla**'ya basın.

### Adresi girin

Olayların gönderileceği `https://` adresini yapıştırın. Zapier'de "Webhooks by Zapier → Catch Hook", Make'te "Webhooks → Custom webhook", n8n'de "Webhook" düğümü, Pipedream'de "HTTP / Webhook" tetikleyicisi size bu adresi verir.

### Olayları seçin

Hangi olaylarda gönderim yapılacağını işaretleyin. Bağlantı kartı bir **imza anahtarı** gösterir; alıcı tarafta isteğin gerçekten CRM'den geldiğini doğrulamak için saklayın.

### Deneyin

**Bağlantıyı dene** alıcınıza `event: "test"` olan bir deneme gönderir. Son gönderimin sonucu (HTTP durum kodu ya da hata) kartta görünür.
:::

## Olaylar

| Olay | Ne zaman | `data` alanları |
|---|---|---|
| `opp_created` | Yeni fırsat açıldı | `id`, `rid`, `name`, `customer`, `customer_id`, `stage`, `stage_label`, `value`, `currency`, `owner` |
| `opp_stage` | Fırsatın aşaması değişti (başlık eski → yeni aşamayı yazar) | fırsat alanları |
| `opp_won` | Fırsat kazanıldı (`Win`) | fırsat alanları |
| `opp_lost` | Fırsat kaybedildi (`Lost`) | fırsat alanları |
| `customer_created` | Yeni firma | `id`, `rid`, `name`, `status`, `source`, `city` |
| `contact_created` | Yeni kişi (e-postadan otomatik açılanlar hariç) | `id`, `name`, `email`, `phone`, `title`, `customer`, `customer_id` |
| `lead_created` | Yeni fuar leadi | `id`, `company`, `contact`, `email`, `interest` |
| `visit_created` | Görüşme / ziyaret kaydı | `id`, `customer`, `type`, `date`, `next_action`, `note` |
| `ticket_created` | Yeni destek talebi | `id`, `subject`, `priority`, `who` |
| `form_submitted` | Site formu / Typeform / Tally başvurusu | `form`, `company`, `name`, `email`, `phone`, `customer_id`, `task_id`, `new_customer` |

Olaylar web arayüzünden, mobil uygulamadan, API'den, MCP'den, iş akışlarından ve gelen kancalardan doğan değişikliklerin hepsinde gönderilir. İki istisna:

- **Örnek veri** kayıtları olay göndermez.
- Tek bir işlemde 25'ten fazla olay doğarsa (ör. Excel'den toplu içe aktarma) olaylar gönderilmez; denetim günlüğüne not düşülür.

## Gövde

```http
POST /sizin/adresiniz HTTP/1.1
Content-Type: application/json
X-Solk-Event: opp_won
X-Solk-Signature: sha256=5d1c0a3e9b…
```

```json
{
  "event": "opp_won",
  "title": "Fırsat kazanıldı",
  "text": "Kuzey Plastik Sanayi · Streç film tedariki · 338.400 € · Sorumlu: Deniz Aksoy",
  "url": "https://ornek.solk.app/opportunities/53",
  "data": {
    "id": 53,
    "rid": "006Xq3LmT0aZb9K",
    "name": "Streç film tedariki",
    "customer": "Kuzey Plastik Sanayi",
    "customer_id": 31,
    "stage": "Win",
    "stage_label": "Win",
    "value": 338400.0,
    "currency": "EUR",
    "owner": "Deniz Aksoy"
  },
  "workspace": "Örnek Kimya",
  "app": "Solk CRM",
  "sent_at": "2026-10-02T14:05:11"
}
```

| Alan | Açıklama |
|---|---|
| `event` | Olay adı (yukarıdaki tablo; denemede `test`). |
| `title`, `text` | İnsan okuyacak başlık ve özet — bildirim kanallarına doğrudan basılabilir. |
| `url` | Kaydın CRM'deki adresi. |
| `data` | Olaya özgü yapısal alanlar. Kaydın tamamı gerekiyorsa `data.id` ile [REST API](/rest-api/overview)'den isteyin. |
| `workspace`, `app` | Kurulumun firma adı ve uygulama adı. |
| `sent_at` | Gönderim zamanı (kurulumun yerel saati). |

## İmzayı doğrulama

`X-Solk-Signature`, istek gövdesinin (ham bayt olarak) imza anahtarıyla HMAC-SHA256 özetidir. Gövdeyi JSON olarak ayrıştırmadan **önce** doğrulayın:

:::code
```python Python
import hmac, hashlib

def dogrula(govde_bytes: bytes, imza: str, anahtar: str) -> bool:
    beklenen = "sha256=" + hmac.new(anahtar.encode(), govde_bytes, hashlib.sha256).hexdigest()
    return hmac.compare_digest(beklenen, imza or "")

# Flask
@app.post("/solk")
def solk():
    if not dogrula(request.get_data(), request.headers.get("X-Solk-Signature"), ANAHTAR):
        abort(401)
    olay = request.get_json()
    ...
```
```javascript Node.js
import crypto from "node:crypto";
import express from "express";

const app = express();
app.post("/solk", express.raw({ type: "application/json" }), (req, res) => {
  const beklenen = "sha256=" + crypto.createHmac("sha256", process.env.SOLK_SIGNING_KEY)
    .update(req.body).digest("hex");
  const imza = req.get("X-Solk-Signature") || "";
  if (imza.length !== beklenen.length ||
      !crypto.timingSafeEqual(Buffer.from(imza), Buffer.from(beklenen))) {
    return res.sendStatus(401);
  }
  const olay = JSON.parse(req.body);
  res.sendStatus(204);
});
```
```php PHP
$govde = file_get_contents('php://input');
$beklenen = 'sha256=' . hash_hmac('sha256', $govde, getenv('SOLK_SIGNING_KEY'));
if (!hash_equals($beklenen, $_SERVER['HTTP_X_SOLK_SIGNATURE'] ?? '')) {
    http_response_code(401); exit;
}
$olay = json_decode($govde, true);
```
:::

## Teslim

- Gönderim istekten bağımsız, arka planda yapılır; CRM kullanıcısı beklemez.
- Zaman aşımı **8 saniyedir**. Alıcınız `2xx` döndürmelidir; uzun işleri kuyruğa alıp hemen yanıt verin.
- **Yeniden deneme yoktur.** Başarısız gönderim bağlantı kartında son durum olarak görünür. Olay kaçırmamanız gereken durumlarda periyodik olarak API'den eşitleyin (ör. `GET /api/v1/opportunities?sort=update`).
- Sıra garanti değildir; aynı kayıt için gelen olayları `sent_at` ile sıralayın.

## Kanallar

Slack, Microsoft Teams, Google Chat, Discord ve Telegram bağlantıları aynı olayları kanalın kendi mesaj biçiminde (Slack metni, Teams uyarlanabilir kartı…) gönderir; imza başlığı yalnız web kancası, Zapier, Make, n8n ve Pipedream'de vardır. Notion, Airtable, Google Sheets, Asana, ClickUp, Linear ve Mailchimp bağlantıları ise olaydan o uygulamada kayıt açar. Ayrıntı: [Uygulamalar](/guides/apps).

## CRM'e geri yazmak

Akışınız CRM'de kayıt açacak ya da güncelleyecekse (ör. Zapier'de "fatura ödendi → görevi tamamla") **Ayarlar → Geliştiriciler**'den bir API anahtarı açıp Zapier / Make / n8n'in HTTP adımında `Authorization: Bearer sk_…` başlığıyla [REST API](/rest-api/overview)'yi çağırın. Dışarıdan veri itmenin kodsuz yolu ise [gelen kancalardır](/guides/inbound).