Chargeless Webhook'larını Almak: events/v1

Bu rehber, Chargeless webhook'larını kendi sistemlerinde (yardım masası, CRM, sipariş sistemi ya da veri ambarı) karşılayan işletme mühendisleri için hazırlanmıştır. Her olayın ne anlama geldiğini, gövdesinde neler bulunduğunu, nasıl doğrulanacağını ve yeniden denemeler, yinelenen teslimatlar ve sıralama konusunda nelerle karşılaşacağınızı anlatır.

Sözleşme, Chargeless'ın size sağladığı dosyalardan oluşur. Bu rehberle dosyalar arasında fark varsa dosyalar esas alınır.

Dosya İçeriği
schemas/events/v1/handoff.json Tüm olayların ortak zarfı ve başlıkları; handoff.requested, action.failed, ticket.handed_to_team, ticket.message_received, ping
schemas/events/v1/action-outcome.json action.executed, refund.reversed
schemas/events/v1/customer-request.json customer_request.decided
schemas/events/v1/ticket-status.json ticket.status_changed
schemas/events/v1/payment-alert.json payment_alert.opened, payment_alert.resolved
schemas/events/examples/ Her olay için geçerli bir örnek
CHANGELOG.md Sözleşmede yapılan tüm değişiklikler

Her şema, $id değeri https://schemas.supportroute.dev/events/v1/<name> olan bir JSON Schema draft 2020-12 belgesidir. Başlık adlarında ve şema kimliklerinde geçen "SupportRoute", Chargeless'ın önceki çalışma adıdır; istek ve yanıtlarda (wire format) bu ad değişmeden kalır.

1. Webhook'lar Nedir, Ne Değildir

  • Bildirimdir, asla soru değildir. Chargeless hiçbir zaman yanıtınızı beklemez. 2xx yanıtı Chargeless'a yalnızca teslimatın ulaştığını bildirir, başka bir anlam taşımaz. Verdiğiniz hiçbir yanıt bir şeyi onaylamaz, reddetmez, yeniden denemez ya da geri almaz.
  • İki olay grubu.
    • Devir olayları (handoff.requested, action.failed, ticket.handed_to_team) ekibinizin işi devralması gerektiğini bildirir. Yalnızca devir hedefiniz webhook'u içerdiği sürece gönderilir (Ayarlar → Genel → Devirlerin Gideceği Yer: Yalnızca webhook ya da Ekip kuyruğu ve webhook). Bu ayar webhook'u içerdiği sürece üç olayın her birini en az bir etkin uç nokta almalıdır: panel, olaylardan birini alıcısız bırakacak bir değişikliği kabul etmez.
    • Bildirimler (action.executed, refund.reversed, customer_request.decided, ticket.status_changed, payment_alert.opened, payment_alert.resolved) olanları kayıt altına alır. Her uç noktada ayrı ayrı seçilir (opt-in) ve devir hedefiniz ne olursa olsun, yalnızca ekip kuyruğu seçili olsa bile gönderilir.
  • Gövdede neler bulunur. Devir olayları, ekibinizin müşteriye yanıt verebilmesi için gereken bilgileri taşır: talep sahibinin adresi ve adı ile yazışmadaki en son mesajlar (en fazla 20). Bu olayları kişisel veri olarak işleyin. Bildirimler yalnızca kimlik, kod, tutar ve zaman damgası içerir; adres, ad ya da mesaj içermez. Hiçbir olayda kart numarası, güvenlik kodu ya da gizli anahtar bulunmaz; mesaj metinlerindeki kart numaraları maskelenir.
  • Esas kayıt paneldir. Webhook'lar en az bir kez teslim edilir ve gecikebilir (bölüm 5). Mutabakat için cases.read yetkisi olan bir API anahtarıyla CasesService.ListActions ve ListCases prosedürlerini çağırın ya da vakayı veya talebi numarasıyla panelde açın.

2. Uç Nokta Kurulumu

Panelde: Ayarlar → Entegrasyonlar → Webhook'lar → Webhook Uç Noktası Ekle (handoff.manage yetkisi ve doğrulama uygulamanızdan bir kod gerekir).

  • Uç nokta URL'si: 443 numaralı portta herkese açık bir https adresi. Chargeless özel ve dahili adresleri reddeder, bağlanırken adresi yeniden denetler ve yönlendirmeleri hiçbir zaman izlemez. Yanıtı doğrudan bu URL'den verin.
  • Etiket: uç noktaya kendinizin verdiği ad.
  • Aldığı Olaylar: her uç nokta için ayrı ayrı seçilir. Yeni bir olay türü mevcut bir uç noktaya hiçbir zaman kendiliğinden eklenmez; almaya hazır olduğunuzda o türe abone olun.
  • Bir çalışma alanında en fazla 10 uç nokta olabilir. Her olay, ona abone olan tüm etkin uç noktalara, her birine ayrı bir teslimat olarak gider.
  • Uç noktanın imza anahtarı (srwh_…) yalnızca bir kez, uç noktayı oluşturduğunuzda gösterilir. Anahtarı gizli anahtar yöneticinizde (secret manager) saklayın.
  • Deneme Olayı Gönder, abonelikleri ne olursa olsun yalnızca o uç noktaya bir ping gönderir (uç nokta başına saatte en fazla 10). Ping hiçbir zaman yeniden denenmez.

Eski bir çalışma alanındaki tek devir webhook'undan oluşturulan bir uç noktanın teslimatları, anahtarı bir kez yenileyene (rotate) kadar o çalışma alanının devir imza anahtarıyla imzalanır.

3. İstek

Her teslimat, JSON gövdeli bir HTTP POST isteğidir; Content-Type: application/json ve aşağıdaki başlıkları taşır:

Başlık Değer
X-SupportRoute-Event Olay türü; gövdedeki type ile aynıdır
X-SupportRoute-Delivery Teslimat kimliği: her olay ve uç nokta için bir tane; aynı teslimatın tüm yeniden denemelerinde aynıdır
X-SupportRoute-Timestamp Bu denemenin imzalandığı an, Unix saniyesi olarak (her denemede yenilenir)
X-SupportRoute-Signature v1= ön eki ve ardından {timestamp}.{body} metninin küçük harfli onaltılık (hex) HMAC-SHA256 özeti
X-SupportRoute-Signature-Previous Aynı imza, önceki anahtarla; yalnızca anahtar yenilendikten sonraki 7 gün boyunca
User-Agent SupportRoute-Handoff/1 (ödeme alarmlarında: SupportRoute-Alerts/1)

Gövde, tüm olaylarda aynı olan zarftır (envelope):

json
{
  "id": "019a4f20-3c00-7d21-8e33-4f5a6b7c8d01",
  "type": "ticket.status_changed",
  "tenant_id": "0199a1b0-0000-7000-8000-000000000001",
  "created_at": "2026-11-04T10:12:05Z",
  "data": { "...": "depends on type" }
}
  • id: olayın kimliği. Tüm yeniden denemelerde ve olayı alan tüm uç noktalarda aynıdır. Yinelenen teslimatları bu değerle ayıklayın.
  • type: olay türü. Yönlendirmeyi başlığa göre değil, gövdedeki type değerine göre yapın.
  • tenant_id: çalışma alanınız.
  • created_at: olayın oluşturulduğu an (RFC 3339, UTC). Her olayın data nesnesinde, olayın konusu olan gelişmenin kendi zamanı bulunur (changed_at, executed_at, decided_at, opened_at…); sıralamayı bu zamana göre yapın.
  • data: olaya özgü nesne (bölüm 7).

4. İmzayı Doğrulama

Her teslimatı okumadan önce doğrulayın:

  1. JSON olarak ayrıştırmadan ya da yeniden kodlamadan önce ham gövdeyi bayt olarak okuyun.
  2. X-SupportRoute-Timestamp ile sizin saatiniz arasındaki fark 5 dakikayı aşıyorsa teslimatı reddedin.
  3. v1= + hex(HMAC-SHA256(anahtar = UTF-8 baytları olarak srwh_… anahtarınız, mesaj = {timestamp}.{raw body})) değerini hesaplayın.
  4. Bu değer, sabit zamanlı (constant-time) karşılaştırmada X-SupportRoute-Signature ya da X-SupportRoute-Signature-Previous ile eşleşiyorsa teslimatı kabul edin.

Anahtar yenilemeyi güvenli kılan, iki başlığı birden kontrol etmenizdir: eski anahtarı kullandığınız sürece imzanız -Previous başlığıyla, yenisini devreye aldığınızda ise ana başlıkla eşleşir.

Node:

js
import crypto from 'node:crypto';

function verify(rawBody, headers, secret) {
  const ts = headers['x-supportroute-timestamp'];
  if (!/^\d+$/.test(ts ?? '') || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
  const expected = 'v1=' + crypto.createHmac('sha256', secret).update(`${ts}.`).update(rawBody).digest('hex');
  return [headers['x-supportroute-signature'], headers['x-supportroute-signature-previous']].some(
    (sig) => typeof sig === 'string' && sig.length === expected.length &&
      crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected)));
}

Go:

go
func verify(h http.Header, rawBody []byte, secret string, now time.Time) bool {
    ts := h.Get("X-SupportRoute-Timestamp")
    sec, err := strconv.ParseInt(ts, 10, 64)
    if err != nil || now.Sub(time.Unix(sec, 0)).Abs() > 5*time.Minute {
        return false
    }
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write([]byte(ts + "."))
    mac.Write(rawBody)
    expected := []byte("v1=" + hex.EncodeToString(mac.Sum(nil)))
    for _, sig := range []string{h.Get("X-SupportRoute-Signature"), h.Get("X-SupportRoute-Signature-Previous")} {
        if sig != "" && hmac.Equal([]byte(sig), expected) {
            return true
        }
    }
    return false
}

Python:

python
import hashlib, hmac, time

def verify(raw_body: bytes, headers, secret: str) -> bool:
    ts = headers.get("X-SupportRoute-Timestamp", "")
    if not ts.isdigit() or abs(time.time() - int(ts)) > 300:
        return False
    expected = "v1=" + hmac.new(secret.encode(), ts.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    return any(sig and hmac.compare_digest(sig, expected)
               for sig in (headers.get("X-SupportRoute-Signature"), headers.get("X-SupportRoute-Signature-Previous")))

Anahtarı yenileme. Uç noktadaki Anahtarı Döndür düğmesi yeni anahtarı bir kez gösterir. Sonraki 7 gün boyunca her teslimat iki anahtarla birden imzalanır; böylece yeni anahtarı kendi takviminize göre devreye alabilirsiniz. Yeni anahtarı devreye aldıktan sonra Öncekini Emekliye Ayır ile bu geçiş süresini erken bitirebilirsiniz.

5. Yanıt, Yeniden Denemeler, Yinelenen Teslimatlar ve Sıralama

Yanıtınızı yavaş işlere başlamadan, 5 saniye içinde herhangi bir 2xx koduyla verin: gövdeyi kaydedin, yanıt verin, ardından işleyin. Yanıt gövdesi dikkate alınmaz. (Bir deneme ödeme alarmlarında en fazla 5 saniye, test olayında 10, diğer tüm olaylarda 15 saniye bekler; dolayısıyla 5 saniye her teslimat için yeterlidir.)

Yanıtınız Chargeless ne yapar
2xx Teslim edildi. Bir daha asla gönderilmez
Zaman aşımı, bağlantı hatası, 408, 429 ya da 5xx Giderek uzayan aralıklarla yeniden denenir
Diğer tüm durum kodları (400, 401, 403, 404, 410, 3xx…) Kesin sonuç: başarısız olarak kaydedilir, bir daha asla gönderilmez

İlk yeniden deneme 30 saniye sonra yapılır; bekleme süresi her seferinde iki katına çıkar ve iki deneme arasında en fazla 4 saat olur:

Deneme İlk denemeden yaklaşık ne kadar sonra
2 30 sn
3–6 1,5 dk, 3,5 dk, 7,5 dk, 15,5 dk
7–10 31 dk, 1 sa, 2 sa, 4 sa 15 dk
11, 12 8 sa 15 dk, 12 sa 15 dk: devir olayları ve bildirimler için son deneme
13–15 16, 20, 24 sa: yalnızca ödeme alarmlarında; son deneme ilkinden yaklaşık bir gün sonra yapılır

Son denemeden sonra teslimat başarısız olarak kaydedilir ve panelin bildirimlerinde teslimatı başarısız olan uç nokta belirtilir. Ping hiçbir zaman yeniden denenmez.

Yinelenen teslimatlar. Teslimat en az bir kez yapılır: gövdeyi işledikten sonra sizin tarafınızda zaman aşımı olursa aynı olay yeniden gelir. İşlediğiniz id değerlerini saklayın (bir hafta yeterlidir) ve daha önce gördüğünüz bir olayı atlayın.

Sıralama. Ne olay türleri arasında ne de aynı türün içinde bir sıra garanti edilir: yeniden denenen bir action.executed, aynı işlemin refund.reversed olayından sonra gelebilir; bir talebin iki durum değişikliği de herhangi bir sırayla gelebilir. Bir olayı yalnızca zamanı (changed_at, executed_at, decided_at, opened_at, resolved_at) o talep, işlem ya da alarm için elinizdeki kayıttan daha yeniyse uygulayın.

Eksik olaylar. Bir olay hiç gelmeyebilir:

  • Kısıtlama (throttling). Uç nokta başına en fazla 250 bildirim (action.executed, refund.reversed, customer_request.decided, ticket.status_changed) ve en fazla 25 ödeme alarmı bekleyebilir. Sınır aşıldıktan sonra kaydedilen olay başarısız olarak işaretlenir ve hiç gönderilmez; panelin bildirimlerinde ilgili uç nokta belirtilir. Devir olayları hiçbir zaman kısıtlanmaz.
  • Vazgeçilen teslimatlar. 24 saat sonra hâlâ bekleyen bir bildirimden ya da 25 saat sonra hâlâ bekleyen bir ödeme alarmından vazgeçilir.
  • Kesin yanıtlar. 408 ve 429 dışındaki her 4xx yanıtı o teslimatı sonlandırır.

İşlemlerin ve vakaların mutabakatını CasesService.ListActions ve ListCases ile yapın. Bir payment_alert.resolved olayı, payment_alert.opened olayı olmadan gelebilir: bu durumda onu alarmın tamamı olarak değerlendirin.

Bir uç noktayı devre dışı bıraktığınızda ya da sildiğinizde bekleyen teslimatları durur. Bu durumda devir olayları ve bildirimler, o anda kendi türüne abone olan uç noktalar için aynı gövde ve id ile yeniden kaydedilir. Ödeme alarmları ise gönderilmeden bırakılır.

6. V1 Kapsamındaki Değişiklikler

Chargeless, yeni bir sürüm çıkarmadan v1'e olay türleri, enum değerleri ve isteğe bağlı özellikler ekler ve her eklemeyi CHANGELOG.md dosyasında listeler. Bir özelliği kaldırmak, yeniden adlandırmak, türünü değiştirmek ya da zorunlu hale getirmek ise yeni bir sürüm (events/v2) gerektirir ve önceden duyurulur.

Bu nedenle alıcınız:

  • bilmediği bir olay türünü yok sayar ve 2xx ile yanıtlar;
  • bilmediği bir özelliği yok sayar;
  • bilmediği bir enum değerini belirtilmemiş sayar: bilinmeyen bir talep kanalını "herhangi bir kanal", bilinmeyen bir neden kodunu "neden yok", bilinmeyen bir durumu "değişti" olarak yorumlar;
  • gövdede yeni bir şey var diye asla 4xx döndürmez. Bu yanıt kesindir ve olay kaybolur.

Şemalar kapalıdır (additionalProperties: false). Bu kısıt Chargeless için bağlayıcıdır: Chargeless şemada bulunmayan bir özelliği ya da değeri asla göndermez. Gövdeleri bu şemalarla doğruluyorsanız sözleşmedeki güncel dosyaları kullanın; doğrulama başarısız olursa teslimatı reddetmek yerine hatayı günlüğe kaydedin.

7. Olaylar

Tür Grup Ne zaman gönderilir Şema
handoff.requested devir Bir vaka ekibinize devredildi: politika, yanıt denetimi (gate) ya da otomasyon işi bir kişinin devralmasına karar verdi handoff
action.failed devir Onaylanan bir iade ya da iptal gerçekleştirilemedi handoff
ticket.handed_to_team devir Bir talep, kendisine ait bir işlem olmadan otomasyondan ekibinize geçti handoff
action.executed bildirim Bir iade ya da iptal ödeme sağlayıcısında başarıyla gerçekleşti action-outcome
refund.reversed bildirim Başarıyla gerçekleşmiş bir iade, sağlayıcı tarafından sonradan başarısız sayıldı action-outcome
customer_request.decided bildirim Kendi formunuzdan gelen bir talep karara bağlandı customer-request
ticket.status_changed bildirim Bir talebin durumu değişti (değiştiren kim olursa olsun) ticket-status
payment_alert.opened bildirim Bir ödeme izleyicisi alarm açtı payment-alert
payment_alert.resolved bildirim Bir ödeme alarmı çözüldü payment-alert
ping test Deneme Olayı Gönder'i kullandınız handoff
ticket.message_received — Tanımlıdır ve panelde listelenir, ancak henüz gönderilmez handoff

Tüm tutarlar en küçük para birimi cinsinden (USD için sent, JPY için yen) { "amount_minor": <integer>, "currency": "<ISO 4217>" } biçimindedir; asla ondalıklı sayı (float) değildir. Alan, sağlayıcıya ait bir kimlik olduğunu belirtmedikçe tüm kimlikler UUID'dir.

handoff.requested ve action.failed

data, işlemi ve onunla ilgili her şeyi açıklar:

  • action: id, type (refund, cancel_subscription, handoff, dispute_accept, dispute_submit_evidence), status, target_id (kayıt defterindeki ödeme, abonelik ya da itiraz; ya da null), amount (ya da null), reason_code, failure (action.failed olayında başarısızlığın nedeni; asla kart verisi içermez), requested_by, created_at.
  • case: vaka (id, type, status, customer_id, transaction_id, subscription_id, zaman damgaları) ya da null. Vakanın type değeri chat_refund, chat_cancellation, fraud_alert, dispute, ticket ya da staff_action (bir ekip üyesinin panelden yaptığı iade ya da iptal) olabilir.
  • decision: outcome (allow, deny, handoff), sıralı gerekçeler reasons (code, İngilizce text ve kendi kurallarınız için rule_id), değerlendirme izi trace, uygulanan güvenlik sınırları limits_applied ve policy_version (v7).
  • timeline: vakanın geçmişi, en eskiden en yeniye.
  • ticket_id, ticket ve thread_excerpt: devrin ilgili olduğu talep ve yardım masanızın bu talebi yanıtlamak için ihtiyaç duyduğu bilgiler (aşağıda); talebe bağlı olmayan bir devirde (erken dolandırıcılık uyarısı, itiraz) null ve boştur.

Devri data.action.id ile tanımlayın (anahtar olarak bunu kullanın). Ekibin devralmasına bir işlem yol açtıysa, talep için o işlemin handoff.requested ya da action.failed olayı gönderilir; bunun yanında asla bir ticket.handed_to_team gönderilmez.

ticket.handed_to_team

json
{
  "id": "0199a3d0-1c2e-7f10-8a55-3b9e6d2c4f05",
  "type": "ticket.handed_to_team",
  "tenant_id": "0199a1b0-0000-7000-8000-000000000001",
  "created_at": "2026-09-25T13:41:07Z",
  "data": {
    "ticket_id": "0199a3f1-0b00-7a01-9b22-1c3d4e5f6a74",
    "reason": "customer_requested",
    "thread_excerpt": [
      { "author": "customer", "text": "My subscription renewed but I cancelled it last month.", "at": "2026-09-25T13:39:12Z" },
      { "author": "agent_llm", "text": "I can see the renewal on your account. Could you tell me when you cancelled?", "at": "2026-09-25T13:39:20Z" },
      { "author": "customer", "text": "I'd like someone from your team to look at this, please.", "at": "2026-09-25T13:41:02Z" }
    ],
    "ticket": {
      "number": 57,
      "subject": "Renewal after cancellation",
      "channel": "widget_message",
      "requester_email": "[email protected]",
      "requester_name": "Sam Rivera",
      "requester_verified": true,
      "identity_method": "email_otp"
    }
  }
}
  • reason: sabit bir koddur, asla serbest metin değildir. Olası değerlerden bazıları: customer_requested, out_of_scope, unable_to_resolve, needs_identity, sender_unverified (göndereni doğrulanamayan bir e-posta; müşteriye hiçbir şey gönderilmedi), automation_stalled (otomasyon müşterinin mesajını bir saat boyunca yanıtsız bıraktı), automation_failed, yanıt bütçelerine ait *_budget_exhausted ve daily_automation_cap_exhausted, request_disowned ve identity_not_on_file (form talepleri, aşağıda) ve kendi ödeme sisteminiz ya da yardım masanızla ilgili provider_unavailable, target_not_found, ownership_not_established, ambiguous_customer, ambiguous_target, helpdesk_taken_over. Bu liste zamanla genişler; bilinmeyen bir kodu unspecified olarak yorumlayın.
  • thread_excerpt: en son mesajlar (en fazla 20), en eskiden en yeniye; her birinde author (customer, otomasyon için agent_llm, staff, system), text (kart numaraları maskelenmiş, 4.000 karakterde kesilmiş) ve at bulunur. Dahili notlar hiçbir zaman eklenmez.
  • ticket: number (panelde göründüğü haliyle), subject, channel (email, widget_message, web_form, chat_handoff, helpdesk, merchant_form), requester_email (yanıtların gideceği adres ya da null), requester_name (talep sahibinin yazdığı haliyle, doğrulanmamış), requester_verified ve identity_method (none, signed_token, email_otp, email_link). Hesap ayrıntılarını yalnızca doğrulanmış bir adrese yazın. helpdesk taleplerinde ayrıca external_ref bulunur: bağlayıcı, talebin yardım masanızdaki kimliği ve bağlantısı.

Her talep için bir kez gönderilir; devralmayla aynı veritabanı işleminde (transaction) kaydedilir.

action.executed ve refund.reversed

json
{
  "id": "019a3410-2b00-7c11-8d22-3e4f5a6b7c01",
  "type": "action.executed",
  "tenant_id": "0199a1b0-0000-7000-8000-000000000001",
  "created_at": "2026-10-30T09:14:07Z",
  "data": {
    "action_id": "019a3410-1f00-7a01-9b22-1c3d4e5f6a90",
    "action_type": "refund",
    "source": "merchant_user",
    "status": "executed",
    "amount": { "amount_minor": 2499, "currency": "EUR" },
    "connection_id": "0199a1b0-0000-8000-8000-000000000001",
    "target": { "type": "transaction", "id": "019a33f0-7a10-7b3e-9d41-5f2a8c6e1b02", "provider_id": "ch_3QxZ8aLkdIwHu7ix0abcDEF1", "public_ref": "PAY-7K3F9QS" },
    "provider_operation_id": "re_3QxZ8aLkdIwHu7ix0ghiJKL2",
    "case": { "id": "019a33fe-0000-7c0d-8e0f-101112131417", "number": 482 },
    "ticket_id": "019a33fd-ff02-7a01-9b22-1c3d4e5f6a73",
    "policy_version": "v7",
    "manual_override": false,
    "actor_user_id": "0199b2c0-4d5e-7f60-8a71-92a3b4c5d6e7",
    "executed_at": "2026-10-30T09:14:06Z",
    "revised_from_failed": false
  }
}
  • Kaynağı ne olursa olsun, her işlem ve tür için en fazla bir kez gönderilir: widget sohbeti (chat), otomasyonun bir talebe verdiği yanıt (ticket), bir ekip üyesi (merchant_user, actor_user_id ile birlikte), erken dolandırıcılık uyarısı ve itiraz iş akışları (fraud_alert, dispute) ve API üzerinden arka uç sisteminiz (system).
  • amount ödemenin kendi para birimindedir ve asla dönüştürülmez; iptallerde ve itiraz işlemlerinde null olur.
  • target.provider_id (Stripe'ta ch_…, sub_… ya da dp_…; kendi sisteminizde sizin referansınız) yalnızca kendi ödeme bağlantısı içinde benzersizdir: connection_id ile birlikte anahtar olarak kullanın. target.public_ref, ödemenin ya da aboneliğin kısa referansıdır ("PAY-…", "SUB-…"; ADR 0042); çalışma alanınızda benzersizdir ve müşterilerinizin gördüğü referans budur; itirazlarda null olur. events/v1/handoff aynı değerleri action.target_public_ref, case.transaction_public_ref ve case.subscription_public_ref alanlarında taşır. Bu alanlar 2026-11-08'de v1'e eklendi: gövdeleri şemanın bir kopyasıyla doğruluyorsanız kopyanızı güncelleyin (nesneler bilinmeyen özelliklere izin vermez).
  • Hiçbir politika çalışmadıysa policy_version null olur; bir ekip üyesi politika dışı onayla işlem yaptıysa manual_override true olur.
  • revised_from_failed (action.executed olayında): işlem önce başarısız olarak kaydedilmiş, ardından sağlayıcıda gerçekleşen başarı bu kaydı düzeltmiştir. Bu işlemin action.failed olayına göre bir adım attıysanız, yaptığınızı gözden geçirin.
  • refund.reversed aynı alanları ve ek olarak reversed_at alanını taşır; işlem executed durumunda kalır. Geri alma (reversal) bir kişinin ilgilenmesini gerektiriyorsa, devir hedefinize bağlı olarak bir handoff.requested da gelebilir: o olay ekibinizden harekete geçmesini ister, bu olay ise yalnızca durumu kayda geçirir.

customer_request.decided

Kendi formunuzun RequestIntakeService.SubmitCustomerRequest ile gönderdiği bir talep karara bağlandığında gönderilir: bir kez talep ulaştığında, bir kez de müşteri talebi talep sayfasında onayladığında.

json
{
  "id": "019a3b14-2b00-7c11-8d22-3e4f5a6b7c12",
  "type": "customer_request.decided",
  "tenant_id": "0199a1b0-0000-7000-8000-000000000001",
  "created_at": "2026-11-01T09:31:40Z",
  "data": {
    "request_id": "order-1001-refund",
    "ticket": { "id": "019a3b10-1f00-7a01-9b22-1c3d4e5f6a01", "number": 231 },
    "action_id": "019a3b14-1f00-7a01-9b22-1c3d4e5f6a03",
    "action_type": "refund",
    "decision": "after_confirmation",
    "outcome": "pending",
    "reason_code": null,
    "decided_at": "2026-11-01T09:31:39Z"
  }
}
  • request_id: talebe sizin verdiğiniz kimlik, gönderdiğiniz şekliyle.
  • decision: first ya da after_confirmation.
  • outcome: olay oluşturulduğu anda talebin durumu: executed, pending (onaylandı ve sağlayıcıya iletiliyor; başarılı olduğunda action.executed olayı gelir), denied, handoff ya da identity_required (adresin sahibinin talep sayfasında onay vermesi bekleniyor).
  • reason_code: kararı belirleyen gerekçe; executed ve pending için null.

Müşterinin sahiplenmediği ("This wasn't me") ya da kayıtlarda kimseye ait olmayan bir adresten onaylanan bir talep yeniden karara bağlanmaz: talep ekibinize geçer ve bunu ticket.handed_to_team olayı bildirir (request_disowned, identity_not_on_file).

ticket.status_changed

Değişikliği kim yaparsa yapsın, her talebin her durum değişikliğinde gönderilir: ekibinizden bir üye, çözülmüş bir talebe yeniden yazan müşteri ya da Chargeless'ın kendisi.

json
{
  "id": "019a4f20-3c00-7d21-8e33-4f5a6b7c8d01",
  "type": "ticket.status_changed",
  "tenant_id": "0199a1b0-0000-7000-8000-000000000001",
  "created_at": "2026-11-04T10:12:05Z",
  "data": {
    "ticket": { "id": "019a4f1e-2b00-7c11-8d22-3e4f5a6b7c10", "number": 412 },
    "channel": "merchant_form",
    "handling": "automation",
    "from": "new",
    "to": "solved",
    "actor": { "kind": "system", "user_id": null },
    "reason": "request_finished",
    "changed_at": "2026-11-04T10:12:04Z"
  }
}
  • from, to: new, open, waiting_on_customer, on_hold, solved, closed.

  • handling: değişiklikten sonra talebi kimin yürüttüğü: automation, team (paneldeki ekibiniz) ya da external (kendi sisteminiz; talep Chargeless'ta salt okunur bir kayıt olarak durur).

  • actor.kind: staff (değişikliği yapan üye actor.user_id içinde), customer (müşterinin yanıtı çözülmüş bir talebi yeniden açtı) ya da system.

  • reason (yalnızca sistemin yaptığı değişikliklerde; diğerlerinde null):

    Neden Değişiklik
    request_finished Otomasyon talebi sonuçlandırdı (bir işlem gerçekleşti ya da kesin olarak reddedildi) ve müşteriye bildirdi: solved durumuna geçer
    customer_quiet Otomasyon yanıt verdi, müşteri ise çalışma alanının bekleme süresi (varsayılan 72 saat) içinde geri yazmadı: solved durumuna geçer
    solved_quiet Talep, çalışma alanının kapanma süresi (varsayılan 7 gün) boyunca çözüldü durumunda kaldı: closed durumuna geçer
    team_entry Talep ekibinizin kuyruğuna geldi: new durumuna geçer
    provider_outcome_changed Talep çözüldükten ya da kapatıldıktan sonra ödeme sağlayıcısı, talebin vakasındaki bir sonucu değiştirdi: talep yeniden açılır
  • closed durumundaki bir talep de böyle bir sağlayıcı değişikliğiyle yeniden açılabilir; Chargeless hiçbir talebi silmez.

Bir talebin birden fazla değişikliği art arda gelebilir (örneğin team_entry ve hemen ardından bir üyenin solved değişikliği): bunları changed_at değerine göre sıralayın.

payment_alert.opened ve payment_alert.resolved

Bir ödeme izleyicisi (Ödeme Analizi → alarmlar) bir alarm açtığında ya da çözdüğünde gönderilir. Gövde yalnızca kendi ödeme denemelerinize ait toplu sayıları içerir: müşteri, kart ya da tutar bilgisi yoktur.

  • alert_id: bir alarmın iki olayında da aynıdır; anahtar olarak bunu kullanın.
  • monitor: auth_rate_drop, failure_category_spike, fraud_warning_rate, dispute_rate, network_programme.
  • status (open, resolved), sensitivity, scope (kart ülkesi, kart markası ya da hata kategorisi; daraltılmamışsa her biri null), connection (id, label; yalnızca gösterim amaçlı).
  • current ve baseline dönemleri (period, from, to, deneme sayısı, numerator, denominator, baz puan cinsinden rate_bp), uygulanan eşik threshold ve change_bp, multiple_milli, z_milli.
  • network_programme (yalnızca bu izleyicide): program (visa_vamp_merchant, mastercard_ecm, mastercard_hecm ya da custom), kart ağı, kademe (approaching, projected, breached), ay ve sayılar.
  • resolution (resolved olayında): recovered, monitor_disabled, escalated, programme_exited, limit_removed.
  • panel_url: Ödeme Analizi sayfası (panele giriş yapmanız gerekir).

Açık bir alarmın sonraki değerlendirmeleri gönderilmez. Çözülen bir alarmın olayı, açılış olayı olmadan da gelebilir.

ping

json
{
  "id": "019a2f4c-7d10-7a3e-9c41-5e6f708192a3",
  "type": "ping",
  "tenant_id": "0199a1b0-0000-7000-8000-000000000001",
  "created_at": "2026-10-28T10:15:00Z",
  "data": { "endpoint_id": "019a2f40-1b2c-7d3e-8f40-516273849506" }
}

Diğer tüm olaylar gibi imzalanır. Abone olmadan önce URL'nizi, TLS yapılandırmanızı ve imza doğrulama kodunuzu denemek için kullanın.

8. Abone Olmadan Önce Kontrol Listesi

  1. URL, 443 numaralı portta herkese açık bir https adresidir ve yönlendirme olmadan doğrudan yanıt verir.
  2. İşleyici ham gövdeyi okur ve imzayı, 5 dakikalık zaman damgası aralığıyla, iki imza başlığına karşı da doğrular.
  3. 5 saniye içinde 2xx döndürür, işlemeyi sonra yapar.
  4. Daha önce işlediği bir id değerini atlar.
  5. Olayları geliş sırasına göre değil, olayın kendi zamanına göre sıralar.
  6. Bilinmeyen bir türe, özelliğe ya da değere 2xx ile yanıt verir ve şema doğrulaması nedeniyle hiçbir teslimatı reddetmez.
  7. Devir olayları kişisel veri olarak saklanır: talep sahibinin adresini, adını ve mesajlarını taşırlar.
  8. Deneme Olayı Gönder ile gönderilen ping, işleyiciniz tarafından kabul edilir.
  9. Panelin bildirimlerinde başarısız teslimatları takip eden biri vardır; böyle bir bildirim göründüğünde ListActions ile mutabakat yaparsınız.