Webhooks
Erhalte Echtzeit-Benachrichtigungen über Spenden und Bestellungen in deinem System durch automatische HTTP-POST-Anfragen an deine konfigurierten Endpunkte.
Webhooks informieren dein System in Echtzeit über Spenden, Newsletter-Anmeldungen und Shop-Bestellungen. Sobald ein Ereignis eintritt, sendet Soulclick eine HTTP-POST-Anfrage mit den Daten an deinen Endpunkt.
Payload-Versionen
Jeder Endpunkt hat eine Payload-Version. Sie bestimmt, welche Felder er erhält. Eine Version liefert weiterhin genau die Felder, mit denen du integriert hast: Neue Felder kommen erst, wenn du im Admin unter «Webhook Endpunkte» auf eine neuere Version wechselst.
| Version | Enthält |
|---|---|
v1 | Die bisherige Payload, created_at in data in Schweizer Zeit mit Offset. Voreinstellung für neue Endpunkte. |
v2 | Zusätzlich donor.custom_fields: die Antworten auf die eigenen Felder des Spendenformulars, siehe Donor-Felder. Alle Zeitangaben in UTC. |
v2.1 | Zusätzlich positions in donation.completed: Ein Multi-Checkout kommt als ein einziges Event, siehe Multi-Checkout. amount und die Beträge der Positionen stehen in der Währung der Spende. Wählbar, sobald «Spendenshop Warenkorb Logik aktivieren» im Abschnitt «Warenkorb» unter Eigenschaften eingeschaltet ist. |
Für neue Integrationen empfehlen wir die neueste Version, die der Admin anbietet.
Einrichtung
- Öffne im Admin «Webhook Endpunkte». Pro Plattform gibt es einen Endpunkt.
- URL: Gib die vollständige HTTPS-URL deines Endpunkts ein.
- Aktiv: Setze den Endpunkt auf aktiv.
- Payload-Version: Wähle die Version, siehe Payload-Versionen.
- Ereignisse: Wähle, welche Events der Endpunkt erhält.
donation.completed,donation.recurring_completedundshop_order.completedsind voreingestellt.donation.reconciledundnewsletter.subscribedsind aus, auch bei bestehenden Endpunkten, bis du sie hier aktivierst. - Basic Auth (optional): Hinterlege Benutzername und Passwort, die Soulclick bei jeder Anfrage mitsendet.
Events
Jedes Event hat denselben Rahmen: id, event, created_at, environment und die eigentlichen Daten unter data, siehe Envelope.
Soulclick kann jederzeit neue Events einführen. Verarbeite nur die event-Werte, die du kennst, und beantworte alle anderen trotzdem mit 2xx, sonst wiederholt Soulclick die Zustellung.
Ein Endpunkt erhält nur die Events, die bei ihm unter «Ereignisse» ausgewählt sind, siehe Einrichtung.
donation.completed
donation.completedWird ausgelöst, wenn eine Spende erfolgreich abgeschlossen wurde. Enthält die Spende, die spendende Person, die Zahlung und bei QR-Rechnungen die Referenz.
Beispiel Payload (v1, Einzelspende):
{
"id": 123,
"event": "donation.completed",
"created_at": "2024-01-15T10:30:00Z",
"environment": "prod",
"data": {
"donation": {
"id": "150120240001",
"created_at": "2024-01-15T11:30:00+01:00",
"amount": 50.0,
"currency": "CHF",
"campaign": {
"id": 123,
"title": "Hilfsprojekt Afrika"
},
"purpose": "Unterstützung für Bildungsprojekte",
"donor": {
"gender": "male",
"first_name": "Max",
"last_name": "Mustermann",
"email": "[email protected]",
"company": "Beispiel AG",
"po_box": null,
"street": "Musterstrasse",
"street_number": "123",
"postal_code": "8000",
"city": "Zürich",
"country": "CH",
"language": "de",
"newsletter": true
},
"payment": {
"transaction_id": "TXN-ABC123",
"method": "VIS",
"status": "completed"
},
"invoice": null
}
}
}In v2 kommt donor.custom_fields dazu, in v2.1 zusätzlich positions.
Multi-Checkout
Legt eine Person mehrere Spenden in den Warenkorb, bezahlt sie diese mit einer einzigen Zahlung. Ab v2.1 sendet Soulclick dafür ein einziges donation.completed: id ist die Nummer des Multi-Checkouts, wie sie im Soulclick Manager steht, amount der Gesamtbetrag aller Positionen, und donor, payment und invoice gelten für die ganze Zahlung. campaign und purpose der Spende sind null, weil jede Position ihre eigenen trägt:
"donation": {
"id": "220920260006",
"amount": 41.45,
"currency": "CHF",
"campaign": null,
"purpose": null,
"positions": [
{"id": "220920260006", "amount": 10.35, "campaign": {"id": 68, "title": "Baumpatenschaft"}, "purpose": null},
{"id": "220920260007", "amount": 10.35, "campaign": {"id": 16, "title": "Nothilfe"}, "purpose": null},
{"id": "220920260008", "amount": 20.75, "campaign": {"id": 4, "title": "Wasserprojekt"}, "purpose": null}
]
}Eine Einzelspende führt sich selbst als einzige Position, campaign und purpose stehen dann wie gewohnt auch an der Spende. Die Felder einer Position stehen unter Positions-Felder.
Endpunkte auf v1 oder v2 erhalten wie bisher ein donation.completed pro Position, jedes mit Nummer, Betrag und Kampagne dieser Position.
Denselben Aufbau liefert die API ab /api/v2.1/ bei den Spenden eines Kontakts, siehe API-Referenz.
donation.recurring_completed
donation.recurring_completedWird bei jeder Folgezahlung einer Dauerspende ausgelöst. Die Payload hat denselben Aufbau wie donation.completed, ohne positions: Dauerspenden können nicht in den Warenkorb gelegt werden.
Online-Zahlung (Datatrans, Payrexx, Stripe)
Wird ausgelöst, sobald eine Folgezahlung per Karte, TWINT oder einer anderen Online-Zahlungsmethode erfolgreich war.
Beispiel Payload:
{
"id": 789,
"event": "donation.recurring_completed",
"created_at": "2024-02-15T08:00:00Z",
"environment": "prod",
"data": {
"donation": {
"id": "150220240001",
"created_at": "2024-02-15T08:00:00Z",
"amount": 50.0,
"currency": "CHF",
"campaign": {
"id": 123,
"title": "Hilfsprojekt Afrika"
},
"purpose": "Unterstützung für Bildungsprojekte",
"donor": {
"gender": "male",
"first_name": "Max",
"last_name": "Mustermann",
"email": "[email protected]",
"company": null,
"po_box": null,
"street": "Musterstrasse",
"street_number": "123",
"postal_code": "8000",
"city": "Zürich",
"country": "CH",
"language": "de",
"newsletter": true
},
"payment": {
"transaction_id": "240215100000",
"method": "VIS",
"status": "completed"
},
"invoice": null
}
}
}Rechnungszahlung (QR-Rechnung)
Wird ausgelöst, wenn eine wiederkehrende QR-Rechnung erstellt und versendet wurde.
Beispiel Payload:
{
"id": 790,
"event": "donation.recurring_completed",
"created_at": "2024-02-15T08:00:00Z",
"environment": "prod",
"data": {
"donation": {
"id": "150220240002",
"created_at": "2024-02-15T08:00:00Z",
"amount": 50.0,
"currency": "CHF",
"campaign": {
"id": 123,
"title": "Hilfsprojekt Afrika"
},
"purpose": "Unterstützung für Bildungsprojekte",
"donor": {
"gender": "male",
"first_name": "Max",
"last_name": "Mustermann",
"email": "[email protected]",
"company": null,
"po_box": null,
"street": "Musterstrasse",
"street_number": "123",
"postal_code": "8000",
"city": "Zürich",
"country": "CH",
"language": "de",
"newsletter": true
},
"payment": {
"transaction_id": "000000000150220240000200001",
"method": "INV",
"status": "pending"
},
"invoice": "000000000150220240000200001"
}
}
}Unterschiede zu donation.completed:
| Feld | donation.completed | donation.recurring_completed (Online) | donation.recurring_completed (Rechnung) |
|---|---|---|---|
payment.method | Zahlungsmethode, z.B. "VIS" oder "TWI" | Zahlungsmethode der Erstspende | "INV" |
payment.status | "completed" oder "pending" | "completed" | "pending" |
| Auslöser | Erstmalige Spende | Folgezahlung über Datatrans, Payrexx oder Stripe | Wiederkehrende QR-Rechnung |
donation.reconciled
donation.reconciledStandardmässig aus: Aktiviere «Spendengebühren abgeglichen» beim Endpunkt unter «Ereignisse».
Wird ausgelöst, sobald der Payment-Provider Gebühr und Auszahlungsbetrag einer Zahlung geliefert hat, siehe Reconciliation. Das gilt für Einzelspenden und Folgezahlungen von Dauerspenden über Stripe und Payrexx und ist Teil des Campaigner-Pakets. Jede Zahlung wird genau einmal gemeldet, je nach Payment-Provider direkt oder einige Stunden nach donation.completed bzw. donation.recurring_completed.
Die Payload ist dieselbe wie im Event, das die Zahlung gemeldet hat, payment enthält zusätzlich paid_out_amount, fee_amount und reconciled, siehe Payment-Felder. Die beiden Beträge stehen in der Auszahlungswährung des Payment-Providers, die von currency abweichen kann, zum Beispiel bei einer Spende in EUR, die in CHF ausbezahlt wird.
Wird das Event, das die Zahlung gemeldet hat, gerade wiederholt, kann donation.reconciled vorher ankommen. Lege die Spende darum über ihre id an oder aktualisiere sie, egal welches der beiden Events zuerst eintrifft.
Beispiel Payload (v1, Einzelspende):
{
"id": 124,
"event": "donation.reconciled",
"created_at": "2024-01-17T06:00:00Z",
"environment": "prod",
"data": {
"donation": {
"id": "150120240001",
"created_at": "2024-01-15T11:30:00+01:00",
"amount": 50.0,
"currency": "CHF",
"campaign": {
"id": 123,
"title": "Hilfsprojekt Afrika"
},
"purpose": "Unterstützung für Bildungsprojekte",
"donor": {
"gender": "male",
"first_name": "Max",
"last_name": "Mustermann",
"email": "[email protected]",
"company": null,
"po_box": null,
"street": "Musterstrasse",
"street_number": "123",
"postal_code": "8000",
"city": "Zürich",
"country": "CH",
"language": "de",
"newsletter": true
},
"payment": {
"transaction_id": "TXN-ABC123",
"method": "VIS",
"status": "completed",
"paid_out_amount": 48.55,
"fee_amount": 1.45,
"reconciled": true
},
"invoice": null
}
}
}Bei einem Multi-Checkout gilt dieselbe Aufteilung wie bei donation.completed: Ab v2.1 kommt ein einziges Event mit positions, paid_out_amount und fee_amount sind die Summen aller Positionen. Endpunkte auf v1 oder v2 erhalten ein Event pro Position mit ihrem Anteil an Gebühr und Auszahlungsbetrag.
newsletter.subscribed
newsletter.subscribedStandardmässig aus: Aktiviere «Newsletter-Anmeldung bestätigt» beim Endpunkt unter «Ereignisse».
Wird ausgelöst, sobald eine Person ihre Newsletter-Anmeldung über den Link in der Bestätigungsmail bestätigt (Double-Opt-in), egal ob sie sich im Spendenformular, über ein Formular oder über die API angemeldet hat. Ein weiterer Klick auf den Link löst kein neues Event aus.
Beispiel Payload (v2, v2.1):
{
"id": 812,
"event": "newsletter.subscribed",
"created_at": "2024-01-15T10:32:00Z",
"environment": "prod",
"data": {
"newsletter_subscription": {
"subscription_id": 2187,
"contact_id": 4711,
"gender": "male",
"first_name": "Max",
"last_name": "Mustermann",
"email": "[email protected]",
"language": "de",
"opt_in": true,
"opt_in_date": "2024-01-15T10:32:00Z"
}
}
}| Feld | Typ | Beschreibung |
|---|---|---|
subscription_id | integer | Nummer der Anmeldung |
contact_id | integer | Verknüpfter Kontakt, oder null |
gender | string | "male", "female", "prefer-not-to-say" oder "", wenn unbekannt |
first_name | string | Vorname, oder null |
last_name | string | Nachname, oder null |
email | string | E-Mail-Adresse |
language | string | Sprachcode (ISO 639-1) |
opt_in | boolean | Immer true |
opt_in_date | string | Zeitpunkt der Bestätigung in UTC. Endpunkte auf v1 erhalten ihn in Schweizer Zeit mit Offset, z.B. "2024-01-15T11:32:00+01:00" |
Ist die Anmeldung mit einem Kontakt verknüpft, zum Beispiel bei einer Anmeldung im Spendenformular, stammen Anrede, Name, E-Mail und Sprache von diesem Kontakt. Was dem Kontakt fehlt, kommt aus der Anmeldung. newsletter_subscription ist derselbe Eintrag, den GET /api/v2.1/crm/newsletter-subscriptions/ liefert.
shop_order.completed
shop_order.completedWird ausgelöst, wenn eine Shop-Bestellung erfolgreich abgeschlossen wurde, für registrierte Kund:innen wie für Gastbestellungen.
Beispiel Payload:
{
"id": 456,
"event": "shop_order.completed",
"created_at": "2024-01-15T11:00:00Z",
"environment": "prod",
"data": {
"shop_order": {
"id": "ORDER-2024-001",
"created_at": "2024-01-15T11:00:00Z",
"customer": {
"gender": "female",
"first_name": "Anna",
"last_name": "Beispiel",
"email": "[email protected]",
"phone": "+41 44 123 45 67",
"company": "Firma GmbH",
"department": "Marketing",
"care_of": null,
"po_box": null,
"street": "Beispielweg",
"street_number": "456",
"postal_code": "3000",
"city": "Bern",
"country": "CH",
"language": "de"
},
"invoice_address": {
"first_name": "Anna",
"last_name": "Beispiel",
"company": "Firma GmbH",
"department": "Marketing",
"care_of": null,
"po_box": null,
"street": "Beispielweg",
"street_number": "456",
"postal_code": "3000",
"city": "Bern",
"country": "CH"
},
"delivery_address": {
"first_name": "Anna",
"last_name": "Beispiel",
"company": "Firma GmbH",
"department": "Marketing",
"care_of": null,
"po_box": null,
"street": "Beispielweg",
"street_number": "456",
"postal_code": "3000",
"city": "Bern",
"country": "CH"
},
"items": [
{
"article_number": "ART-001",
"quantity": 2,
"unit_price_net": 25.0,
"unit_price_gross": 27.0,
"discount_percentage": 0,
"total_price_gross": 54.0
}
],
"currency": "CHF",
"total_price_gross": 59.0,
"total_vat": 4.0,
"total_discount": 0.0,
"payment": {
"transaction_id": "TXN-DEF456",
"method": "PAYPAL",
"status": "completed"
},
"shipping": {
"method": "standard",
"cost": 5.0
}
}
}
}Zustellung
Soulclick stellt Events automatisch zu. Eine Antwort mit einem 2xx-Statuscode gilt als zugestellt. Jeder andere Statuscode, ein Verbindungsfehler oder eine Antwort nach mehr als 30 Sekunden gilt als fehlgeschlagen.
Wiederholungen
Ein fehlgeschlagenes Event wird mit wachsendem Abstand erneut gesendet, mit derselben id:
| Fehlgeschlagener Versuch | Nächster Versuch nach |
|---|---|
| 1 | 5 Minuten |
| 2 | 15 Minuten |
| 3 | 1 Stunde |
| 4 | 4 Stunden |
Schlägt auch der fünfte Versuch fehl, erhält das Event den Status «Maximale Wiederholungen erreicht» und wird nicht mehr gesendet.
Event-Status
Im Admin unter «Webhook Events» siehst du jedes Event mit seinem Status:
| Status | Bedeutung |
|---|---|
| Wartend | Wartet auf die Zustellung |
| Zugestellt | Dein Endpunkt hat mit 2xx geantwortet |
| Fehlgeschlagen | Zustellung fehlgeschlagen, wird wiederholt |
| Maximale Wiederholungen erreicht | Fünf Versuche fehlgeschlagen, keine weitere Zustellung |
Troubleshooting
Events werden nicht gesendet:
- Prüfe im Admin unter «Webhook Endpunkte», ob ein Endpunkt eingerichtet ist.
- Stelle sicher, dass der Webhook-Endpunkt aktiv ist.
- Prüfe, ob das Event beim Endpunkt unter «Ereignisse» ausgewählt ist.
Events schlagen fehl:
- Prüfe «Letzter Response Code» und «Letzter Response Body» des Events im Admin.
- Stelle sicher, dass dein Endpunkt aus dem Internet erreichbar ist und mit 2xx antwortet.
- Prüfe Firewall und Netzwerkeinstellungen.
Bei anhaltenden Problemen hilft dir der Soulclick-Support.
Endpunkt implementieren
Beispiel-Handler (Python/Flask)
from flask import Flask, jsonify, request
app = Flask(__name__)
@app.route('/webhook', methods=['POST'])
def handle_webhook():
event = request.get_json()
event_type = event['event']
data = event['data']
if event_type == 'donation.completed':
donation = data['donation']
# Ab v2.1 listet positions jede Position, davor ist die Spende selbst die Position
for position in donation.get('positions', [donation]):
print(f"Position {position['id']}: {position['amount']} {donation['currency']}")
elif event_type == 'donation.recurring_completed':
donation = data['donation']
print(f"Folgezahlung: {donation['amount']} {donation['currency']}, {donation['payment']['status']}")
elif event_type == 'donation.reconciled':
payment = data['donation']['payment']
print(f"Gebühren abgeglichen: {payment['paid_out_amount']} ausbezahlt, {payment['fee_amount']} Gebühr")
elif event_type == 'newsletter.subscribed':
subscription = data['newsletter_subscription']
print(f"Newsletter: {subscription['email']} seit {subscription['opt_in_date']}")
elif event_type == 'shop_order.completed':
order = data['shop_order']
print(f"Bestellung {order['id']}: {order['total_price_gross']} {order['currency']}")
# Alle anderen Events bestätigen und ignorieren
return jsonify({'status': 'success'}), 200Best Practices
- Idempotenz: Ein Event kann mehrfach ankommen. Erkenne Wiederholungen an der
iddes Events. - Schnell antworten: Bestätige mit 2xx innerhalb von 30 Sekunden und verarbeite aufwendige Logik danach asynchron, zum Beispiel über eine Queue.
- Unbekanntes ignorieren: Beantworte ein
event, das du nicht verarbeitest, mit 2xx und ignoriere es. Verlasse dich nicht auf die Reihenfolge der Felder und lehne keine Payload wegen eines zusätzlichen Feldes ab. - Absichern: Verwende HTTPS und Basic Auth.
- Protokollieren: Logge eingehende Events, damit du Fehler nachvollziehen kannst.
Datenfelder-Referenz
Envelope
| Feld | Typ | Beschreibung |
|---|---|---|
id | integer | Eindeutige Nummer des Events, bei Wiederholungen gleich |
event | string | donation.completed, donation.recurring_completed, donation.reconciled, newsletter.subscribed oder shop_order.completed. Weitere können dazukommen. |
created_at | string | Zeitpunkt des Events |
environment | string | "prod", "stage" oder "test" |
data | object | Die Daten des Events unter donation, newsletter_subscription oder shop_order |
Donor-Felder
| Feld | Typ | Beschreibung |
|---|---|---|
gender | string | "male", "female" oder "prefer-not-to-say" |
first_name | string | Vorname |
last_name | string | Nachname |
email | string | E-Mail-Adresse |
company | string | Firmenname (optional) |
po_box | string | Postfach (optional) |
street | string | Strasse |
street_number | string | Hausnummer |
postal_code | string | Postleitzahl |
city | string | Ort |
country | string | Ländercode (ISO 3166-1 alpha-2) |
language | string | Sprachcode (ISO 639-1) |
newsletter | boolean | Einwilligung in den Newsletter |
custom_fields | object | Antworten auf die eigenen Felder des Spendenformulars (ab v2) |
Beispiel custom_fields:
"custom_fields": {
"trauerfall": "Anna Muster",
"newsletter_print": true
}Die Schlüssel sind die im Admin vergebenen Feldnamen, die Werte Text oder bei Checkboxen true und false. Hat eine Spende keine eigenen Felder, fehlt custom_fields ganz.
Positions-Felder
positions gibt es ab v2.1 in donation.completed, siehe Multi-Checkout.
| Feld | Typ | Beschreibung |
|---|---|---|
id | string | Nummer der Position. Die erste trägt die Nummer des Multi-Checkouts. |
amount | number | Betrag dieser Position |
campaign | object | Kampagne dieser Position mit id und title, oder null |
purpose | string | Spendenzweck dieser Position, oder null |
Payment-Felder
| Feld | Typ | Beschreibung |
|---|---|---|
transaction_id | string | Eindeutige Transaktions-ID der Zahlung |
method | string | Code der Zahlungsmethode, siehe Zahlungsmethoden |
status | string | "completed" oder "pending" |
paid_out_amount | number | Nur in donation.reconciled: Betrag, den der Payment-Provider nach Abzug seiner Gebühr auszahlt, in seiner Auszahlungswährung |
fee_amount | number | Nur in donation.reconciled: Gebühr des Payment-Providers, in seiner Auszahlungswährung |
reconciled | boolean | Nur in donation.reconciled: true, sobald Gebühr und Auszahlungsbetrag aller Positionen bekannt sind |
Invoice-Feld
invoice ist in allen Spenden-Events vorhanden und enthält die Referenz, die auf der QR-Rechnung steht. Bei Rechnungszahlungen ("INV") ist das je nach Konfiguration:
- QR-Referenznummer aktiv (Standard): die 27-stellige QR-Referenznummer, z.B.
"000000000150220240000200001" - Benutzerdefinierter Text: der konfigurierte Text, z.B.
"Spende für Kinderhilfe" - Spendenzweck: der Spendenzweck des Produkts, z.B.
"Zweck: Bildung"
Bei allen anderen Zahlungsmethoden ist invoice null.
Datenformate
- Zeitangaben: ISO 8601 in UTC, z.B.
"2024-01-15T10:30:00Z". Endpunkte aufv1erhaltencreated_atundopt_in_dateindatain Schweizer Zeit mit Offset, z.B."2024-01-15T11:30:00+01:00" - Beträge: was bezahlt wurde, inklusive der Gebühren, die die spendende Person übernommen hat, als Zahl mit bis zu 2 Nachkommastellen, z.B.
50oder50.5für CHF 50.00 bzw. CHF 50.50 - Währungen: ISO 4217, z.B.
"CHF","EUR" - Länder: ISO 3166-1 alpha-2, z.B.
"CH","DE" - Sprachen: ISO 639-1, z.B.
"de","fr"
Zahlungsmethoden
Die häufigsten Codes in payment.method:
| Code | Zahlungsmethode |
|---|---|
ECA | Mastercard |
VIS | Visa |
TWI | TWINT |
PFC | PostFinance Card |
PEF | PostFinance E-Finance |
PAP | PayPal |
INV | QR-Rechnung |
Die vollständigen Listen stehen in der Datatrans-Dokumentation und der Payrexx-Dokumentation.
Updated about 6 hours ago
