Introduction

Einführung

KD Push ist ein vollständig verwalteter Push-Dienst für Plattformen und ihre Händler. Händler berühren nie OneSignal, Firebase oder Apple-Zertifikate: Sie fügen ihre App im Portal hinzu, nehmen einen App Key und einen Server Key, und ihre Plattform sendet über diese API.

Your platform
→
KD Push API
→
Queue & engine
→
APNs / FCM
→
Customers' devices

Alle Anfragen und Antworten sind JSON über HTTPS, Datumsangaben sind ISO 8601 und öffentliche IDs sind ULIDs.

Quick start

Schnellstart

  1. 1
    App anlegen und Schlüssel holen
    Im Händlerportal: Neue App → App Key und Server Key kopieren (wird nur einmal angezeigt).
  2. 2
    Gerät aus der App registrieren
    cURL
    curl -X POST https://push.kd-app.com/api/v1/sdk/devices \
      -H "X-KD-App-Key: kdp_app_…" \
      -H "Content-Type: application/json" \
      -d '{
        "platform": "android",
        "token": "fcm-registration-token",
        "installation_id": "3f0c9a2e-6b1d-4c8e-9f7a-2d5e8b1c4a90",
        "android": { "fcm_project_id": "neon-push" },
        "external_user_id": "42",
        "user_signature": "9f2c4e…",
        "locale": "ar",
        "app_version": "2.1.0"
      }'
  3. 3
    Von Ihrem Server senden
    cURL
    curl -X POST https://push.kd-app.com/api/v1/messages \
      -H "Authorization: Bearer kdp_live_…" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: app-created-1234" \
      -d '{
        "title": "تطبيق جديد",
        "body": "تمت إضافة واتساب الذهبي للمتجر",
        "url": "neonstore://app/1234",
        "image_url": "https://cdn.example.com/icons/1234.png",
        "data": { "app_id": "1234" },
        "audience": { "type": "all" }
      }'
Authentication

Authentifizierung

Jede App hat zwei Arten von Schlüsseln, jede mit genau einem Platz:

App Key
kdp_app_…
X-KD-App-Key: kdp_app_…
Wo:
In der mobilen App (öffentlich)
Kann:
Geräte registrieren, Konfiguration lesen, Öffnungen melden
Kann niemals senden
Server Key
kdp_live_…
Authorization: Bearer kdp_live_…
Wo:
Nur auf dem Server Ihrer Plattform (geheim)
Kann:
Senden, verfolgen, stornieren, Statistiken
Wird einmal angezeigt – wir speichern nur einen Hash
Der Schlüssel bestimmt die App. Kein Endpoint akzeptiert eine App- oder Händler-ID im Body, daher kann der Schlüssel einer App nie die Geräte oder Nachrichten einer anderen erreichen – er bekommt einfach 404.
Errors

Fehler

Jeder Fehler hat dieselbe Form und verrät nie Interna:

422 Unprocessable Entity
{
  "error": {
    "code": "validation_failed",
    "message": "The body field is required.",
    "fields": {
      "body": [
        "The body field is required."
      ]
    }
  }
}
401invalid_app_key
Unbekannter App Key oder deaktivierte App.
401invalid_server_key
Unbekannter oder widerrufener Server Key.
402subscription_inactive
Der Händler hat kein aktives Abonnement.
402quota_exceeded
Das monatliche Benachrichtigungskontingent des Plans ist aufgebraucht.
403invalid_user_signature
Die Signatur passt nicht zu external_user_id.
403web_disabled · origin_not_allowed
Web ist für die App ausgeschaltet oder die Seite liegt auf keiner ihrer Websites.
422invalid_subscription · provider_mismatch
Unbrauchbare Abonnement-Schlüssel des Browsers oder Plattform und Anbieter passen nicht zusammen.
403app_disabled · account_banned
App deaktiviert oder Konto gesperrt.
404not_found
Nicht gefunden – oder gehört zu einer anderen App.
409not_cancelable
Die Benachrichtigung ist bereits abgeschlossen.
422validation_failed
Ungültige Eingaben; Details in fields.
422invalid_token
Fehlerhaft formatierter Geräte-Token.
429rate_limited
Zu viele Anfragen; gemäß Retry-After warten.
500server_error
Interner Fehler – später erneut versuchen.
Rate limits

Ratenlimits

60
Anfr. / Min.
App-Endpoints, pro IP
20,000
Anfr. / Min.
App-Endpoints, pro App
600
Anfr. / Min.
Server-Endpoints, pro Server Key

Über dem Limit erhalten Sie 429 rate_limited mit einem Retry-After-Header. Das Senden an viele Geräte ist eine einzige Anfrage – die Engine verteilt sie intern.

Idempotency

Idempotenz

Senden Sie bei jedem Versand einen Idempotency-Key-Header mit (z. B. Ihre Ereignis-ID). Eine Wiederholung mit demselben Schlüssel innerhalb von 24 h liefert dieselbe Benachrichtigung mit 200, statt eine neue anzulegen – ein erneuter Versuch nach einem Netzwerkfehler ist also immer sicher.

Header
Idempotency-Key: app-created-1234-v2.1.0
Mobile app

Konfiguration

GET/sdk/configApp Key – Mobile App

Beim App-Start abrufen. Android liest hier seine Firebase-Optionen, sodass ein Wechsel des Firebase-Projekts oder Anbieters nie einen Neubau der App erfordert.

Darf zwischengespeichert werden – die Antwort trägt Cache-Control: max-age=300.
Request
curl https://push.kd-app.com/api/v1/sdk/config \
  -H "X-KD-App-Key: kdp_app_…"
200 OK
{
  "app": {
    "id": "01JB2Q6V8K4Y3N5M7R9T1W0XZC",
    "name": "My Store"
  },
  "identity_verification": "required",
  "android": {
    "provider": "fcm",
    "firebase": {
      "project_id": "neon-push",
      "app_id": "1:123456789:android:abcdef",
      "api_key": "AIza…",
      "sender_id": "123456789",
      "storage_bucket": null
    }
  },
  "ios": {
    "provider": "apns"
  },
  "web": {
    "provider": "webpush",
    "vapid_public_key": "BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U",
    "icon_url": null,
    "prompt": {
      "enabled": true,
      "delay_seconds": 6,
      "title": null,
      "text": null
    }
  }
}
Mobile app

Gerät registrieren

POST/sdk/devicesApp Key – Mobile App

Bei jedem App-Start, Token-Wechsel, Login und Logout aufrufen. Beim ersten Mal 201, danach 200.

FeldBeschreibung
platformios | android | weberforderlich
Plattform des Geräts.
tokenstringerforderlich
APNs-Token (hex) oder FCM-Registrierungstoken; bei einem Browser der Endpoint des Abonnements.
installation_iduuidoptional
Eine stabile ID, die Sie einmal erzeugen und in der App pro App Key speichern. Ordnet Token-Wechsel demselben Gerät zu.
providerapns | fcm | webpushoptional
Wird aus der Plattform abgeleitet, wenn es fehlt.
ios.bundle_idstringoptional
Der Wert von Bundle.main.bundleIdentifier.
ios.profile_app_idstringoptional
application-identifier aus embedded.mobileprovision, ohne Team-Präfix.
ios.team_idstring(10)optional
TeamIdentifier aus derselben Datei.
ios.environmentproduction | sandboxoptional
Der Wert von aps-environment.
android.fcm_project_idstringoptional
Das Firebase-Projekt, das den Token ausgestellt hat (aus /sdk/config).
web.p256dh · web.authbase64urlje nach Fall
Die Abonnement-Schlüssel des Browsers (aus PushSubscription.toJSON()). Für Web erforderlich; nur von einer Seite auf einer der Websites der App akzeptiert.
external_user_idstring | nulloptional
Ihre Nutzer-ID. Beim Logout null senden.
user_signaturehexje nach Fall
Mit external_user_id erforderlich, solange die Identitätsprüfung aktiv ist. So wird sie berechnet
tagsobjectoptional
Bis zu 20 Tags für die Zielgruppenauswahl; Schlüssel [A-Za-z0-9_.-], kurze skalare Werte.
locale · timezone · app_version · os_version · device_model · sdk_versionstringoptional
Wird im Händlerportal angezeigt.
Ein Token gehört zu genau einer App. Registriert eine andere App ihn (z. B. die APK eines anderen Shops über Ihrer installiert), wechselt das Gerät dorthin und Nutzer und Tags werden gelöscht – Ihre Benachrichtigungen erreichen nie den Kunden eines anderen Shops.
Request
curl -X POST https://push.kd-app.com/api/v1/sdk/devices \
  -H "X-KD-App-Key: kdp_app_…" \
  -H "Content-Type: application/json" \
  -d '{
    "platform": "android",
    "token": "fcm-registration-token",
    "installation_id": "3f0c9a2e-6b1d-4c8e-9f7a-2d5e8b1c4a90",
    "android": { "fcm_project_id": "neon-push" },
    "external_user_id": "42",
    "user_signature": "9f2c4e…",
    "locale": "ar",
    "app_version": "2.1.0"
  }'
201 Created
{
  "device": {
    "installation_id": "3f0c9a2e-6b1d-4c8e-9f7a-2d5e8b1c4a90",
    "platform": "android",
    "status": "active",
    "external_user_id": "42"
  }
}
403 Forbidden
{
  "error": {
    "code": "invalid_user_signature",
    "message": "user_signature does not match external_user_id."
  }
}
Mobile app

Abmelden

POST/sdk/devices/unsubscribeApp Key – Mobile App

Wenn der Nutzer Benachrichtigungen in der App ausschaltet. Das Gerät empfängt nichts mehr, bis es sich erneut registriert.

FeldBeschreibung
installation_iduuiderforderlich
Dieselbe ID wie bei der Registrierung.
Request
curl -X POST https://push.kd-app.com/api/v1/sdk/devices/unsubscribe \
  -H "X-KD-App-Key: kdp_app_…" \
  -H "Content-Type: application/json" \
  -d '{ "installation_id": "3f0c9a2e-6b1d-4c8e-9f7a-2d5e8b1c4a90" }'
200 OK
{
  "unsubscribed": true
}
Mobile app

Öffnen einer Benachrichtigung melden

POST/sdk/eventsApp Key – Mobile App

Wenn der Nutzer auf eine Benachrichtigung tippt. Einmal pro Gerät und Nachricht gezählt, im Zähler „geöffnet“ angezeigt.

FeldBeschreibung
type"opened"erforderlich
Ereignistyp.
message_idULIDerforderlich
Die kd_message_id aus dem Payload.
installation_iduuiderforderlich
Installations-ID des Geräts.
Request
curl -X POST https://push.kd-app.com/api/v1/sdk/events \
  -H "X-KD-App-Key: kdp_app_…" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "opened",
    "message_id": "01JB2QA4N7C2X9V5B8M3K6P1RD",
    "installation_id": "3f0c9a2e-6b1d-4c8e-9f7a-2d5e8b1c4a90"
  }'
202 Accepted
{
  "accepted": true
}
Web

Website-Benachrichtigungen (Browser)

Dieselben Benachrichtigungen erreichen die Besucher Ihrer Website im Browser – ohne App, ohne Firebase, ohne Zertifikate: Jede App hat ihren eigenen VAPID-Schlüssel, erstellt von KD Push. Funktioniert in Chrome, Edge, Firefox, Samsung Internet und Safari auf dem Mac sowie auf iPhone und iPad, sobald die Website zum Home-Bildschirm hinzugefügt wurde (iOS 16.4+).

  1. Im Händlerportal: Ihre App → Web, Website-Benachrichtigungen einschalten und Ihre Domain hinzufügen (oder von Ihrem Server mit PUT /app/web).
  2. Legen Sie /kdpush-sw.js im Stammverzeichnis Ihrer Website mit dieser einen Zeile an:
Node.js
importScripts('https://push.kd-app.com/sdk/web/v1/kdpush-sw.js');
  1. Fügen Sie das SDK-Skript in jede Seite ein, vor </head>:
Text
<script src="https://push.kd-app.com/sdk/web/v1/kdpush.js"
        data-app-key="kdp_app_…" async></script>

Das war’s: Eine kleine Karte bittet Besucher, Benachrichtigungen einzuschalten (Text und Verzögerung legen Sie im Portal fest), der Browser wird automatisch registriert und meldet sich einmal am Tag. Ein Klick auf eine Benachrichtigung öffnet ihren Link und zählt als geöffnet.

Optionen und API

Node.js
<!-- data-* options -->
data-app-key        required
data-service-worker /kdpush-sw.js (default)
data-prompt         "off" hides the built-in card
data-lang           ar | en | es | fr | de (default: the page language)
data-user-id        your user id   + data-user-signature (identity verification)
data-tags           JSON, e.g. '{"plan":"vip"}'

// JavaScript
KDPush.subscribe()            // ask (from a click) and subscribe → "subscribed" | "denied" | "default" | "unsupported" | "disabled"
KDPush.unsubscribe()
KDPush.isSubscribed()         // Promise<boolean>
KDPush.login('42', signature) // link the browser to your user
KDPush.logout()
KDPush.setTags({ plan: 'vip' })
Jeder Browser sendet den Tag os von selbst (ios · android · desktop), sodass Sie z. B. eine iPhone-App nur Besuchern auf iPhones ankündigen können: {"platforms":["web"],"audience":{"type":"tags","tags":{"os":"ios"}}}. Hat Ihre Website schon einen eigenen Service Worker, fügen Sie ihm die importScripts-Zeile hinzu und richten Sie data-service-worker auf ihn.
Web

Web-Einstellungen

PUT/app/webServer Key – Ihr Server

Für eine Plattform, die die Websites ihrer Händler verbindet: Web einschalten und die Domains setzen, wenn sich ein Shop verbindet oder die Domain wechselt. Das Einschalten braucht mindestens eine Domain und erstellt den VAPID-Schlüssel der App. Weggelassene Felder behalten ihren Wert.

FeldBeschreibung
enabledbooleanerforderlich
Web ein- oder ausschalten.
originsstring[] ≤ 20je nach Fall
Websites, die Besucher abonnieren dürfen, z. B. shop.example.com (gespeichert als https://…).
icon_urlhttps URLoptional
Symbol jeder Benachrichtigung.
default_urlURLoptional
Klickziel, wenn eine Nachricht keinen Web-Link hat.
prompt{enabled, delay_seconds, title, text}optional
Die Berechtigungskarte.
Request
curl -X PUT https://push.kd-app.com/api/v1/app/web \
  -H "Authorization: Bearer kdp_live_…" \
  -H "Content-Type: application/json" \
  -d '{"enabled":true,"origins":["shop.example.com"],"icon_url":"https://cdn.example.com/logo.png"}'
200 OK
{
  "web": {
    "enabled": true,
    "origins": [
      "https://shop.example.com",
      "https://www.shop.example.com"
    ],
    "vapid_public_key": "BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U",
    "subscribers": 0,
    "icon_url": "https://cdn.example.com/logo.png",
    "default_url": null,
    "prompt": {
      "enabled": true,
      "delay_seconds": 6,
      "title": null,
      "text": null
    }
  }
}
Platform backend

Benachrichtigung senden

POST/messagesServer Key – Ihr Server

Sendet (oder plant) eine Benachrichtigung nur an die Geräte Ihrer App. Die Antwort kommt sofort mit dem Status queued; die Zustellung läuft im Hintergrund – Status und Zähler verfolgen Sie mit „Benachrichtigung abrufen“.

FeldBeschreibung
bodystring ≤ 2000erforderlich
Text der Benachrichtigung.
titlestring ≤ 255optional
Titel.
urlstringoptional
Wird beim Antippen geöffnet: ein https://-Link oder ein In-App-Link wie neonstore://app/1234.
image_urlhttps URLoptional
Großes Bild (Android; iOS braucht eine Erweiterung in der App).
dataobject ≤ 30optional
Zusätzliche Daten, als Text zugestellt. Schlüssel, die mit kd_ beginnen, sind reserviert.
audienceobjectoptional
{"type":"all"} (Standard) · users + external_user_ids · devices + installation_ids · tags + tags – bis zu 1000 IDs.
platforms["ios","android","web"]optional
Auf bestimmte Plattformen beschränken oder für alle weglassen.
scheduled_atISO 8601optional
Planung innerhalb der nächsten 30 Tage.
ttl_secondsintoptional
Wie lange sie für Offline-Geräte aufbewahrt wird (Standard 3 Tage, max. 28).
priorityhigh | normaloptional
Standard: high.
collapse_idstring ≤ 64optional
Eine neuere Benachrichtigung mit derselben ID ersetzt die ältere auf dem Gerät.

Header

FeldBeschreibung
Idempotency-Keystring ≤ 128optional
Dringend empfohlen. Warum?
Erfordert ein aktives Händler-Abonnement, sonst 402 subscription_inactive. Ohne Abonnement funktioniert nichts: keine Konfiguration (/sdk/config), keine Geräteregistrierung und keine Änderung der App-Einstellungen von Ihrem Server.
Request
curl -X POST https://push.kd-app.com/api/v1/messages \
  -H "Authorization: Bearer kdp_live_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: app-created-1234" \
  -d '{
    "title": "تطبيق جديد",
    "body": "تمت إضافة واتساب الذهبي للمتجر",
    "url": "neonstore://app/1234",
    "image_url": "https://cdn.example.com/icons/1234.png",
    "data": { "app_id": "1234" },
    "audience": { "type": "all" }
  }'
202 Accepted
{
  "message": {
    "id": "01JB2QA4N7C2X9V5B8M3K6P1RD",
    "status": "queued",
    "source": "api",
    "title": "تطبيق جديد",
    "body": "تمت إضافة واتساب الذهبي للمتجر",
    "image_url": "https://cdn.example.com/icons/1234.png",
    "url": "neonstore://app/1234",
    "data": {
      "app_id": "1234"
    },
    "audience": {
      "type": "all"
    },
    "platforms": null,
    "scheduled_at": null,
    "counts": {
      "targeted": 0,
      "sent": 0,
      "failed": 0,
      "invalid": 0,
      "opened": 0
    },
    "error": null,
    "failures": [],
    "created_at": "2026-10-03T12:00:00+00:00",
    "started_at": null,
    "finished_at": null
  }
}
402 Payment Required
{
  "error": {
    "code": "subscription_inactive",
    "message": "No active KD Push subscription for this account."
  }
}
Platform backend

Benachrichtigung abrufen

GET/messages/{id}Server Key – Ihr Server

Status und Zähler. Jedes Feld erklärt das Nachrichtenobjekt.

Request
curl https://push.kd-app.com/api/v1/messages/01JB2QA4N7C2X9V5B8M3K6P1RD \
  -H "Authorization: Bearer kdp_live_…"
200 OK
{
  "message": {
    "id": "01JB2QA4N7C2X9V5B8M3K6P1RD",
    "status": "sent",
    "source": "api",
    "title": "تطبيق جديد",
    "body": "تمت إضافة واتساب الذهبي للمتجر",
    "image_url": "https://cdn.example.com/icons/1234.png",
    "url": "neonstore://app/1234",
    "data": {
      "app_id": "1234"
    },
    "audience": {
      "type": "all"
    },
    "platforms": null,
    "scheduled_at": null,
    "counts": {
      "targeted": 1240,
      "sent": 1227,
      "failed": 3,
      "invalid": 10,
      "opened": 214
    },
    "error": null,
    "failures": [],
    "created_at": "2026-10-03T12:00:00+00:00",
    "started_at": "2026-10-03T12:00:01+00:00",
    "finished_at": "2026-10-03T12:00:04+00:00"
  }
}
Platform backend

Benachrichtigungen auflisten

GET/messagesServer Key – Ihr Server

Der Verlauf Ihrer App, neueste zuerst.

FeldBeschreibung
statusstringoptional
scheduled · queued · sending · sent · failed · canceled
per_page1–100optional
Standard 20.
Request
curl "https://push.kd-app.com/api/v1/messages?status=sent&per_page=20" \
  -H "Authorization: Bearer kdp_live_…"
200 OK
{
  "data": [
    {
      "id": "01JB2QA4N7C2X9V5B8M3K6P1RD",
      "status": "sent",
      "source": "api",
      "title": "تطبيق جديد",
      "body": "تمت إضافة واتساب الذهبي للمتجر",
      "image_url": "https://cdn.example.com/icons/1234.png",
      "url": "neonstore://app/1234",
      "data": {
        "app_id": "1234"
      },
      "audience": {
        "type": "all"
      },
      "platforms": null,
      "scheduled_at": null,
      "counts": {
        "targeted": 0,
        "sent": 0,
        "failed": 0,
        "invalid": 0,
        "opened": 0
      },
      "error": null,
      "failures": [],
      "created_at": "2026-10-03T12:00:00+00:00",
      "started_at": null,
      "finished_at": null
    }
  ],
  "meta": {
    "current_page": 1,
    "per_page": 20,
    "total": 1,
    "last_page": 1
  }
}
Platform backend

Benachrichtigung stornieren

POST/messages/{id}/cancelServer Key – Ihr Server

Storniert eine geplante, wartende oder gerade laufende Benachrichtigung. Noch nicht gesendete Pakete stoppen sofort.

Request
curl -X POST https://push.kd-app.com/api/v1/messages/01JB2QA4N7C2X9V5B8M3K6P1RD/cancel \
  -H "Authorization: Bearer kdp_live_…"
200 OK
{
  "message": {
    "id": "01JB2QA4N7C2X9V5B8M3K6P1RD",
    "status": "canceled",
    "source": "api",
    "title": "تطبيق جديد",
    "body": "تمت إضافة واتساب الذهبي للمتجر",
    "image_url": "https://cdn.example.com/icons/1234.png",
    "url": "neonstore://app/1234",
    "data": {
      "app_id": "1234"
    },
    "audience": {
      "type": "all"
    },
    "platforms": null,
    "scheduled_at": null,
    "counts": {
      "targeted": 0,
      "sent": 0,
      "failed": 0,
      "invalid": 0,
      "opened": 0
    },
    "error": null,
    "failures": [],
    "created_at": "2026-10-03T12:00:00+00:00",
    "started_at": null,
    "finished_at": null
  }
}
409 Conflict
{
  "error": {
    "code": "not_cancelable",
    "message": "Only scheduled, queued or sending messages can be canceled."
  }
}
Platform backend

App und Abonnement

GET/appServer Key – Ihr Server

Prüfen Sie den Schlüssel und ob Senden möglich ist, bevor Sie automatische Benachrichtigungen einschalten: sending.can_send berücksichtigt das Abonnement und das Kontingent dieses Monats, sending.reason nennt den Grund mit denselben Codes wie POST /messages (subscription_inactive · quota_exceeded). plan und usage zeigen den Stand des Händlers; portal verlinkt dorthin, wo er ihn behebt.

Request
curl https://push.kd-app.com/api/v1/app \
  -H "Authorization: Bearer kdp_live_…"
200 OK
{
  "app": {
    "id": "01JB2Q6V8K4Y3N5M7R9T1W0XZC",
    "name": "My Store",
    "platform": "neon",
    "status": "active",
    "ios_bundle_id": "com.store.app",
    "android_package": "com.store.app"
  },
  "subscription": {
    "status": "active",
    "expires_at": "2026-11-02T12:00:00+00:00",
    "grace_until": null,
    "days_left": 19,
    "can_send": true
  },
  "plan": {
    "name": "Pro",
    "period": "monthly",
    "monthly_messages": 100000,
    "max_devices": null,
    "max_apps": 3
  },
  "usage": {
    "deliveries_this_month": 18240,
    "remaining_this_month": 81760,
    "active_devices": 1240
  },
  "sending": {
    "can_send": true,
    "reason": null
  },
  "portal": {
    "plans": "https://push.kd-app.com/portal/plans",
    "subscription": "https://push.kd-app.com/portal/subscription"
  },
  "devices": {
    "active": 1240,
    "ios": 800,
    "android": 440,
    "web": 0
  },
  "web": {
    "enabled": false,
    "origins": [],
    "vapid_public_key": null,
    "subscribers": 0,
    "icon_url": null,
    "default_url": null,
    "prompt": {
      "enabled": true,
      "delay_seconds": 6,
      "title": null,
      "text": null
    }
  }
}
Platform backend

Android-Paket

PUT/app/androidServer Key – Ihr Server

Für eine Plattform, bei der der Händler das Android-Paket wählt: Senden Sie das Paket, mit dem die App des Shops gebaut wird, sobald es sich ändert – dann zeigt das Portal es an und eine später hochgeladene Firebase-Datei wird damit geprüft.

FeldBeschreibung
packagestringerforderlich
Zum Beispiel com.store.app.
Hat der Händler für diese App ein eigenes Firebase-Projekt gewählt, das für ein anderes Paket registriert ist, wird der Aufruf mit 409 package_mismatch abgelehnt und firebase_package nennt das registrierte Paket – damit Android-Benachrichtigungen nie unbemerkt ausfallen.
Request
curl -X PUT https://push.kd-app.com/api/v1/app/android \
  -H "Authorization: Bearer kdp_live_…" \
  -H "Content-Type: application/json" \
  -d '{"package":"com.store.app"}'
200 OK
{
  "app": {
    "id": "01JB2Q6V8K4Y3N5M7R9T1W0XZC",
    "android_package": "com.store.app"
  }
}
409 Conflict
{
  "error": {
    "code": "package_mismatch",
    "message": "The app's Firebase project is registered for com.store.app; upload one for com.other.app first.",
    "firebase_package": "com.store.app"
  }
}
Platform backend

Gerätestatistik

GET/devices/statsServer Key – Ihr Server

Aktive Geräte pro Plattform.

Request
curl https://push.kd-app.com/api/v1/devices/stats \
  -H "Authorization: Bearer kdp_live_…"
200 OK
{
  "devices": {
    "active": 1240,
    "ios": 800,
    "android": 440,
    "web": 0
  }
}
Connect

Mit KD Push verbinden

Verbinden Sie den Shop eines Händlers mit einem Klick mit einer KD-Push-App, ohne dass jemand einen Schlüssel kopiert: Der Händler stimmt auf einer KD-Push-Seite zu und Ihre Plattform erhält die Schlüssel von Server zu Server (OAuth 2 Authorization Code mit PKCE).

  1. Wir registrieren Ihre Plattform und geben Ihnen client_id und client_secret, mit den exakt erlaubten Redirect-URIs.
  2. Schicken Sie den Händler mit den Parametern unten zu /portal/connect/authorize.
  3. Der Händler meldet sich an oder erstellt ein Konto (vorausgefüllt mit Ihren Angaben), wählt oder erstellt die App und stimmt zu.
  4. Wir leiten ihn zurück zu redirect_uri?code=…&state=… – oder error=access_denied, wenn er ablehnt.
  5. Ihr Server tauscht den Code mit POST /connect/token gegen die Schlüssel.

Autorisierungsparameter

FeldBeschreibung
response_typecodeerforderlich
Immer code.
client_idstringerforderlich
Die ID Ihrer Plattform.
redirect_urihttps URLerforderlich
Muss exakt einer registrierten URI entsprechen.
statestring ≤ 255erforderlich
Ein Zufallswert, den Sie speichern und bei der Rückkehr prüfen.
code_challengebase64urlerforderlich
BASE64URL(SHA256(code_verifier)); den code_verifier (43–128 Zeichen) bei sich behalten.
code_challenge_methodS256erforderlich
Immer S256.
app_namestring ≤ 120optional
Name für eine neue App, falls der Händler noch keine hat.
ios_bundle_idstringoptional
Die passende App wird zuerst angeboten und die ID automatisch bei Apple registriert.
android_packagestringoptional
Das Paket, mit dem die Android-App gebaut wird; eine bestehende App ohne Paket übernimmt es.
external_refstring ≤ 191optional
Ihre ID für den Shop; eine erneute Verbindung desselben Shops ersetzt seinen bisherigen Schlüssel.
external_labelstring ≤ 255optional
Was der Händler sieht (z. B. die Shop-Domain).
hint_name · hint_email · hint_company · hint_phonestringoptional
Füllen das Registrierungsformular vor.
Prüfen Sie zuerst state. Der Code gilt 10 Minuten und nur einmal; wird er zweimal vorgelegt, wird widerrufen, was er erzeugt hat. Weder code_verifier noch client_secret erreichen je den Browser.
Request
// 1. Send the merchant to KD Push (keep $verifier and $state in their session)
$verifier = Str::random(64);
$state = Str::random(40);
session(['kdpush' => compact('verifier', 'state')]);

return redirect('https://push.kd-app.com/portal/connect/authorize?' . http_build_query([
    'response_type' => 'code',
    'client_id' => config('services.kdpush.connect_client_id'),
    'redirect_uri' => 'https://your-platform.example/kdpush/callback',
    'state' => $state,
    'code_challenge' => rtrim(strtr(base64_encode(hash('sha256', $verifier, true)), '+/', '-_'), '='),
    'code_challenge_method' => 'S256',
    'app_name' => $store->name,           // optional: prefills a new app
    'ios_bundle_id' => $store->bundleId,  // optional
    'android_package' => $store->androidPackage, // optional
    'external_ref' => $store->id,         // optional: your id for the store
    'hint_email' => $owner->email,        // optional: prefills sign-up
], '', '&', PHP_QUERY_RFC3986));
Connect

Code einlösen

POST/connect/tokenclient_id + client_secret
FeldBeschreibung
grant_typeauthorization_codeerforderlich
Fest.
client_id · client_secretstringerforderlich
Die Zugangsdaten Ihrer Plattform.
codestringerforderlich
Aus der Weiterleitung.
redirect_uristringerforderlich
Dieselbe URI wie in der Anfrage.
code_verifierstringerforderlich
Der Wert, den Sie gespeichert haben.

Sie erhalten den app_key (für die App), einen für diese Verbindung neu ausgestellten server_key (wird einmal angezeigt), das identity_secret und den Stand des Händlers in account – im selben Format wie GET /app.

Der Server Key ist die Verbindung: Ein Widerruf auf einer der beiden Seiten beendet sie, und eine erneute Verbindung desselben Shops ersetzt ihn, statt Schlüssel anzuhäufen.
Request
curl -X POST https://push.kd-app.com/api/v1/connect/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "authorization_code",
    "client_id": "kdp_cli_…",
    "client_secret": "kdp_cs_…",
    "code": "kdp_code_…",
    "redirect_uri": "https://your-platform.example/kdpush/callback",
    "code_verifier": "…"
  }'
200 OK
{
  "connection": {
    "id": "01JB2QX5V8K4Y3N5M7R9T1W0XZ",
    "connected_at": "2026-10-14T12:00:00+00:00"
  },
  "app": {
    "id": "01JB2Q6V8K4Y3N5M7R9T1W0XZC",
    "name": "My Store",
    "platform": "neon",
    "ios_bundle_id": "com.store.app",
    "android_package": null
  },
  "merchant": {
    "name": "Store owner",
    "email": "[email protected]"
  },
  "app_key": "kdp_app_…",
  "server_key": "kdp_live_…",
  "identity_secret": "…",
  "api_base": "https://push.kd-app.com/api/v1",
  "account": {
    "subscription": {
      "status": "active",
      "expires_at": "2026-11-02T12:00:00+00:00",
      "grace_until": null,
      "days_left": 19,
      "can_send": true
    },
    "plan": {
      "name": "Pro",
      "period": "monthly",
      "monthly_messages": 100000,
      "max_devices": null,
      "max_apps": 3
    },
    "usage": {
      "deliveries_this_month": 18240,
      "remaining_this_month": 81760,
      "active_devices": 1240
    },
    "sending": {
      "can_send": true,
      "reason": null
    },
    "portal": {
      "plans": "https://push.kd-app.com/portal/plans",
      "subscription": "https://push.kd-app.com/portal/subscription"
    }
  }
}
400 Bad Request
{
  "error": {
    "code": "invalid_grant",
    "message": "Authorization code already used."
  }
}
Connect

Trennen

POST/connect/revokeServer Key – Ihr Server

Der gesendete Server Key funktioniert sofort nicht mehr; App, Geräte und Verlauf bleiben beim Händler. Der Händler kann auch in seinem Panel trennen – Ihr Schlüssel antwortet dann mit 401 invalid_server_key; behandeln Sie das als getrennt.

Request
curl -X POST https://push.kd-app.com/api/v1/connect/revoke \
  -H "Authorization: Bearer kdp_live_…"
200 OK
{
  "revoked": true
}
Reference

Das Nachrichtenobjekt

scheduled→queued→sending→sent·failedcanceled
FeldBeschreibung
idULIDoptional
ID der Benachrichtigung.
statusstringoptional
sent, sobald mindestens ein Gerät sie angenommen hat, failed, wenn keines (Grund in error).
sourceapi | portal | adminoptional
Von wo sie gesendet wurde.
counts.targetedintoptional
Angesprochene Geräte.
counts.sentintoptional
Von Apple/Google zur Zustellung angenommen.
counts.invalidintoptional
Tote Geräte (App deinstalliert, …) – danach automatisch ausgeschlossen.
counts.failedintoptional
Aus anderen Gründen nach Wiederholungen fehlgeschlagen.
counts.openedintoptional
Gemeldete Öffnungen.
failures[{provider, code, http_status, count}]optional
Warum manche Geräte sie nicht erhielten, häufigste zuerst: der Code von Apple oder Firebase wie geliefert (z. B. TopicDisallowed), der HTTP-Status und die Anzahl der Geräte.
created_at · started_at · finished_at · scheduled_atISO 8601optional
Zeitstempel des Lebenszyklus.
Guide

Prüfung der Nutzeridentität

Um ein Gerät mit einem Nutzer zu verknüpfen (für persönliche Benachrichtigungen wie „Ihr Abo endet bald“), braucht die App eine Signatur, die Ihr Server nach dem Login berechnet – sonst könnte jeder sein Telefon als anderer Nutzer registrieren und dessen Benachrichtigungen erhalten.

Formula
user_signature = hex( HMAC-SHA256( external_user_id , identity_secret ) )
Auf Ihrem Server
// After the user logs in — on your backend, never in the app
$signature = hash_hmac('sha256', (string) $user->id, config('services.kdpush.identity_secret'));

return ['user_id' => $user->id, 'push_signature' => $signature];
Das Geheimnis steht im Portal: App → Schlüssel → Identitätsgeheimnis. Niemals in die App einbauen.
Guide

Was die App empfängt

iOS — APNs
{
  "aps": {
    "alert": {
      "title": "تطبيق جديد",
      "body": "تمت إضافة واتساب الذهبي للمتجر"
    },
    "sound": "default",
    "mutable-content": 1
  },
  "kd_message_id": "01JB2QA4N7C2X9V5B8M3K6P1RD",
  "kd_url": "neonstore://app/1234",
  "kd_image": "https://cdn.example.com/icons/1234.png",
  "app_id": "1234"
}
Android — FCM
{
  "notification": {
    "title": "تطبيق جديد",
    "body": "تمت إضافة واتساب الذهبي للمتجر",
    "image": "https://cdn.example.com/icons/1234.png"
  },
  "data": {
    "kd_message_id": "01JB2QA4N7C2X9V5B8M3K6P1RD",
    "kd_url": "neonstore://app/1234",
    "app_id": "1234"
  }
}

Beim Antippen kd_url öffnen und kd_message_id an /sdk/events senden. Werte in data kommen als Text an.

Flutter
FirebaseMessaging.onMessageOpenedApp.listen((message) {
  final url = message.data['kd_url'];
  final id = message.data['kd_message_id'];
  if (url != null) openDeepLink(url);   // e.g. neonstore://app/1234
  if (id != null) reportOpened(id);     // POST /sdk/events
});
Guide

Integrations-Checkliste für Plattformen

  1. 1Der Händler legt die App im Portal an und fügt App Key und Server Key in das Panel Ihrer Plattform ein.
  2. 2Ihre Plattform baut die App mit dem App Key – die App ruft /sdk/config auf und registriert dann das Gerät.
  3. 3Nach dem Login gibt Ihr Server der App eine user_signature für die Nutzer-ID.
  4. 4Ihre Plattform sendet automatische Benachrichtigungen (neue App, Update) mit einem Idempotency-Key pro Ereignis.
  5. 5Wird ein Katalog zum ersten Mal verbunden, nicht pro App benachrichtigen – eine Zusammenfassung senden.
  6. 6Vor dem Einschalten automatischer Benachrichtigungen subscription.can_send über /app prüfen.
Tools

Postman

Fertige Postman-Sammlungv2.1Jede Anfrage mit Beispielen und Antworten, Authentifizierung schon eingerichtet – Schlüssel eintragen und in Sekunden loslegen.
  1. Sammlung herunterladen und in Postman importieren (Import → Datei wählen).
  2. In den Variablen der Sammlung app_key, server_key und einen Test-Geräte-Token in device_token setzen.
  3. Führen Sie Register device und dann Send a notification aus – die ID wird für die folgenden Anfragen in message_id gespeichert.
  4. Optional: external_user_id und identity_secret setzen, dann berechnet die Sammlung user_signature selbst.