Eklentiniz, tenant'ın bağladığı bildirim sağlayıcısı eklenti (push/Telegram/dahili ekran) üzerinden PERSONELE bildirim gönderebilir — sağlayıcının kimliğini bilmeden. messaging.send ile aynı eklentiler-arası capability altyapısını kullanır; fark yalnızca hedef ({role|userId}, müşteri değil) ve payload (title/body).
Tüketici bildirim ister, sağlayıcıyı tenant seçer (Android chooser modeli). Hedef { role | userId } bir personel referansıdır (PII değil — opak); sağlayıcı bunu kendi users:read'iyle kullanıcıya çözer. Scope'lar: capability:notify.staff:consume (tüketici) · capability:notify.staff:provide (sağlayıcı).
provides:[{capability:"notify.staff"}] (sağlayıcı) veya consumes:[{capability:"notify.staff"}] (tüketici) beyan edin; türetilmiş scope otomatik eklenir.import { RestomenumClient } from '@restomenum/plugin-sdk'; // v1.10.0+
// TÜKETİCİ (scope: capability:notify.staff:consume) — jenerik capability yolu.
// Sonuç tipi (NotifyStaffResult) capability id'den OTOMATİK türer; elle <> tip argümanı GEREKMEZ.
const r = await client.capabilities.invoke('notify.staff', {
to: { role: 'manager' }, // { role: 'manager'|'staff' } VEYA { userId } (en az biri)
title: 'Stok uyarısı',
body: 'Domates kritik seviyede (2 kg).',
idempotencyKey: `stock-alert-${date}`, // ZORUNLU — çift bildirim koruması
});
// r = { requestId, status: 'accepted'|'sent'|'failed', providerMessageId?, idempotentReplay? }
// Sağlayıcı bağlı değilse ApiError('plugin.notify.noProvider', 424) → özelliği gizle.import { verifyAndParseCapability, capabilityResponse } from '@restomenum/plugin-sdk';
import type { NotifyStaffPayload } from '@restomenum/plugin-sdk';
// SAĞLAYICI (scope: capability:notify.staff:provide) — actionUrl'e gelen imzalı type:"capability" POST:
const req = await verifyAndParseCapability<NotifyStaffPayload>(rawBody, headers['x-restomenum-signature'], {
getSecret: (tenantId) => installStore.find(tenantId)?.webhookSecret, // webhook ile AYNI imza şeması
});
if (!req) return res.status(401).json({ error: 'invalid_signature' });
// ZORUNLU dedupe: aynı req.requestId tekrar gelirse YENİDEN GÖNDERME (önceki yanıtı dön).
// Hedefi ÇÖZ: { role|userId } — personel listesini KENDİ users:read'inle çek (rol→kullanıcı):
const { to, title, body } = req.payload;
// const users = await client.users.get(); // {role:'manager'} → authority filtrele; {userId} → doğrudan
// … push/Telegram/dahili ekran ile personele ilet …
res.json(capabilityResponse('accepted', { providerMessageId }));title/body serbest metindir — gereksiz kişisel/müşteri verisi koymayın; UI'da text olarak basın (HTML/XSS yok). Hedefi çözmek için users:read+ (personel adı için) PII consent gerekir; userId/role'ün kendisi PII değildir.| Kod | HTTP | Anlamı |
|---|---|---|
| plugin.notify.noProvider | 424 | Tenant bildirim sağlayıcısı bağlamamış — önkoşul eksik. Özelliği gizle; retry etme. |
| plugin.notify.providerUnavailable | 503 | Sağlayıcı inaktif/askıda/breaker-open/ulaşılamaz. |
| plugin.notify.timeout | 504 | Sağlayıcı 10 sn içinde yanıtlamadı. AYNI idempotencyKey ile retry. |
| plugin.notify.invalidTarget | 400 | to yalnız {role: manager|staff, userId} alır; ham personel PII (name/email) yasak. |
| plugin.notify.idempotencyKeyRequired | 400 | idempotencyKey eksik/geçersiz (zorunlu, ≤64). |
| plugin.notify.titleTooLong | 400 | title 200 karakteri aşıyor. |
| plugin.notify.consumerBlocked | 403 | Tenant, eklentinizin bildirim göndermesini engellemiş. |
| plugin.notify.suspended | 403 | Eklentiniz kill-switch ile askıya alınmış — bildirim gönderemez. |
| plugin.notify.duplicateInProgress | 409 | Aynı idempotencyKey eşzamanlı işleniyor. |
| plugin.notify.idempotencyKeyReused | 409 | Aynı key FARKLI içerikle kullanıldı. |
| plugin.notify.selfTarget | 409 | Tüketici == bağlı sağlayıcı (kendine yönlendirme; binding çakışması). |
| plugin.notify.providerChanged | 409 | Belirsiz sonuçtan (timeout) sonra tenant sağlayıcıyı değiştirdi — çift bildirime karşı bu key ile retry engellendi. |
| plugin.capability.notFound | 404 | Bilinmeyen capability (jenerik /capabilities/{cap}/* ucunda). |
notify.staff fire-and-forget'tir — messaging'in aksine asenkron teslim durumu (DLR) yoktur; accepted sağlayıcının kuyruğa aldığını gösterir.