Veranstaltung absagen
Veranstaltung über die öffentliche API absagen und zahls.ch-Zahlungen optional erstatten
Eine Absage über die API macht genau dasselbe wie das Dashboard: die Veranstaltung wird auf CANCELLED gesetzt, ihre offenen und abgeschlossenen Bestellungen werden storniert, Kontingente werden freigegeben und ausgegebene Tickets entwertet. Die Rückerstattung des Geldes ist eine getrennte, ausdrückliche Entscheidung.
Rückerstattungen nur auf Wunsch
refund ist standardmässig false. Eine Anfrage ohne Body oder mit "refund": false sagt die Veranstaltung ab und bewegt kein Geld. Nur "refund": true stellt Rückerstattungen in die Warteschlange.
Authentifizierung und Scopes
Alle drei Operationen nutzen denselben Organisations-API-Schlüssel wie der Rest der API — siehe Authentifizierung.
| Operation | Methode und Pfad | Scope |
|---|---|---|
| Veranstaltung absagen | POST /events/{eventId}/cancel | events:cancel |
| Rückerstattungsstatus lesen | GET /events/{eventId}/refunds | events:read |
| Fehlgeschlagene Rückerstattungen erneut versuchen | POST /events/{eventId}/refunds/retry | events:cancel |
Ein Schlüssel erreicht nur Veranstaltungen der eigenen Organisation. Eine fremde Veranstaltung antwortet mit 404, nie mit 403, damit sich keine Veranstaltungs-IDs erraten lassen.
Veranstaltung absagen
Idempotency-Key ist Pflicht, siehe Retries und Idempotenz weiter unten.
curl -X POST https://api.cowtic.com/api/v1/events/evt_2f8a/cancel \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
-H "Content-Type: application/json" \
-d '{ "refund": true }'{
"eventId": "evt_2f8a",
"status": "CANCELLED",
"cancelledOrderIds": ["ord_91c", "ord_44b", "ord_07e"],
"failedOrderIds": [],
"refunds": {
"queued": 2,
"queuedOrderIds": ["ord_91c", "ord_44b"],
"skipped": {
"refundable": 0,
"cancelled_earlier": 1,
"unpaid": 3,
"free": 1,
"manual_payment": 0,
"refunded": 0,
"refund_pending": 0,
"refund_failed": 0,
"refund_unknown": 0,
"refund_unsupported": 0
}
},
"refundStatusUrl": "/api/v1/events/evt_2f8a/refunds"
}| Feld | Bedeutung |
|---|---|
cancelledOrderIds | Bestellungen, die diese Anfrage storniert hat. Bei einer Wiederholung leer — sie waren bereits storniert. |
failedOrderIds | Bestellungen, die nicht storniert werden konnten. Sie behalten ihre Tickets und werden nie erstattet: zeigen Sie sie einer Person an. |
refunds | null, wenn refund false war. Sonst das, was diese Anfrage an die Rückerstattungs-Warteschlange übergeben hat. |
refunds.queuedOrderIds | Bestellungen, deren Rückerstattung jetzt in der Warteschlange steht. |
refunds.skipped | Übergangene Bestellungen, gezählt nach Eignung. |
refundStatusUrl | Pfad auf diesem API-Host, unter dem Sie das Ergebnis pro Bestellung abfragen. |
Ohne Rückerstattung ist der Aufruf kürzer:
curl -X POST https://api.cowtic.com/api/v1/events/evt_2f8a/cancel \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Idempotency-Key: 4f1d0f8c-7a1e-4d51-9a1b-0d1f0b2d9a77"{
"eventId": "evt_2f8a",
"status": "CANCELLED",
"cancelledOrderIds": ["ord_91c", "ord_44b", "ord_07e"],
"failedOrderIds": [],
"refunds": null,
"refundStatusUrl": "/api/v1/events/evt_2f8a/refunds"
}Rückerstattungen laufen asynchron
POST /events/{eventId}/cancel antwortet, sobald die Bestellungen storniert und die Rückerstattungen eingereiht sind. Danach schickt ein Hintergrund-Worker jede Rückerstattung an zahls.ch. Ein 200 bedeutet eingereiht, nicht erstattet — das Ergebnis lesen Sie unter refundStatusUrl.
Welche Bestellungen erstattet werden
Nur Bestellungen, die tatsächlich über zahls.ch bezahlt und noch nicht erstattet wurden. Jede andere Bestellung erscheint mit ihrem Grund unter refunds.skipped:
| Eignung | Bedeutung |
|---|---|
refundable | Abgeschlossene Bestellung mit eingezogener zahls.ch-Zahlung. Wird eingereiht. |
cancelled_earlier | Vor der Veranstaltung storniert, Zahlung weiterhin eingezogen. Wird nie automatisch erstattet — sie kann bereits von Hand erstattet worden sein. Erstatten Sie sie bei Bedarf über die Bestellung im Dashboard. |
unpaid | Checkout nie abgeschlossen. Nichts zu erstatten. |
free | Gratis oder vollständig rabattiert. Nichts zu erstatten. |
manual_payment | Ausserhalb von zahls.ch als bezahlt markiert. Erstatten Sie über den Kanal, der das Geld entgegengenommen hat. |
refunded | Bereits erstattet. |
refund_pending | Eine Rückerstattung ist bereits eingereiht oder unterwegs. |
refund_failed | zahls.ch hat die Rückerstattung abgelehnt. Erneut versuchbar. |
refund_unknown | zahls.ch hat nie geantwortet, die Rückerstattung kann ausgeführt worden sein oder nicht. Wird nie automatisch wiederholt — prüfen Sie zuerst die Transaktion auf zahls.ch. |
refund_unsupported | Die Zahlungsart lässt sich über zahls.ch nicht erstatten. |
Retries und Idempotenz
Idempotency-Key ist bei POST /events/{eventId}/cancel und POST /events/{eventId}/refunds/retry erforderlich; fehlt der Header, antwortet die API mit 400.
- Dieselbe Anfrage mit demselben Schlüssel und demselben Body liefert die gespeicherte Antwort und führt nichts erneut aus. Die Wiederholung trägt
Idempotency-Replayed: true. - Derselbe Schlüssel mit anderem Body ergibt
409. - Nutzen Sie einen neuen Schlüssel für einen wirklich neuen Versuch, etwa nachdem Sie Ihre zahls.ch-Verbindung repariert haben.
Die Operation ist auch mit neuem Schlüssel gefahrlos wiederholbar: eine bereits abgesagte Veranstaltung wird nicht erneut abgesagt, eine bereits stornierte Bestellung bleibt unangetastet, und ein bereits angelegter Rückerstattungssatz wird nie dupliziert — keine Bestellung wird zweimal erstattet. Die allgemeinen Regeln stehen unter Idempotenz.
Rückerstattungsstatus lesen
curl https://api.cowtic.com/api/v1/events/evt_2f8a/refunds \
-H "X-Api-Key: YOUR_API_KEY"{
"eventStatus": "CANCELLED",
"total": 2,
"counts": { "PENDING": 0, "SUCCEEDED": 1, "FAILED": 1, "UNSUPPORTED": 0 },
"stalePending": 0,
"unknownOutcome": 0,
"refundedAmount": 50,
"currency": "CHF",
"refunds": [
{
"id": "prf_7d1",
"orderId": "ord_91c",
"orderStatus": "CANCELLED",
"customer": { "name": "Ada Guest", "email": "guest@example.com" },
"status": "SUCCEEDED",
"currency": "CHF",
"paidAmount": 50,
"requestedAmount": 50,
"refundedAmount": 50,
"failureReason": null,
"attempts": 1,
"lastAttemptAt": "2026-09-14T09:12:04.881Z",
"updatedAt": "2026-09-14T09:12:05.102Z",
"stale": false,
"outcomeUnknown": false,
"providerTransactionId": "5012345",
"providerRefundId": "9001"
},
{
"id": "prf_7d2",
"orderId": "ord_44b",
"orderStatus": "CANCELLED",
"customer": { "name": "Blaise Guest", "email": "blaise@example.com" },
"status": "FAILED",
"currency": "CHF",
"paidAmount": 12.5,
"requestedAmount": 12.5,
"refundedAmount": 0,
"failureReason": "Refund limit reached for this account",
"attempts": 1,
"lastAttemptAt": "2026-09-14T09:12:04.902Z",
"updatedAt": "2026-09-14T09:12:04.960Z",
"stale": false,
"outcomeUnknown": false,
"providerTransactionId": "5012346",
"providerRefundId": null
}
]
}| Feld | Bedeutung |
|---|---|
counts | Rückerstattungssätze nach Status. PENDING heisst eingereiht oder unterwegs. |
stalePending | PENDING-Sätze, die seit einer Weile kein Worker angefasst hat. Ein erneuter Versuch nimmt sie mit. |
unknownOutcome | Rückerstattungen, die zahls.ch nie bestätigt hat. Prüfen Sie die Transaktion auf zahls.ch, bevor Sie es erneut versuchen. |
refundedAmount / paidAmount | Beträge in der Währung der Bestellung, nicht in kleinsten Einheiten. |
failureReason | Die Meldung von zahls.ch. Nur Anbietertext — Zugangsdaten sind nie enthalten. |
providerTransactionId / providerRefundId | zahls.ch-Referenzen für den Abgleich. |
Fragen Sie diesen Endpunkt ab, bis counts.PENDING bei 0 liegt. Wenige Sekunden Abstand genügen; beachten Sie die Rate Limits.
Fehlgeschlagene Rückerstattungen erneut versuchen
Reiht die Sätze der Veranstaltung mit FAILED, UNSUPPORTED und liegen gebliebene Sätze erneut ein. Die Veranstaltung muss bereits abgesagt sein, sonst antwortet der Aufruf mit 400 und dem Detail EVENT_NOT_CANCELLED.
curl -X POST https://api.cowtic.com/api/v1/events/evt_2f8a/refunds/retry \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Idempotency-Key: 9a3cbe44-2f9d-4f9e-8b4f-3c2d1a7b5e60"{
"queued": 1,
"queuedOrderIds": ["ord_44b"],
"skipped": {
"refundable": 0,
"cancelled_earlier": 0,
"unpaid": 0,
"free": 0,
"manual_payment": 0,
"refunded": 1,
"refund_pending": 0,
"refund_failed": 0,
"refund_unknown": 0,
"refund_unsupported": 0
},
"refundStatusUrl": "/api/v1/events/evt_2f8a/refunds"
}Sätze unter refund_unknown bleiben bewusst aussen vor: zahls.ch kann sie bereits ausgeführt haben, deshalb muss eine Person nachsehen, bevor sie erneut gesendet werden.
Fehler
| Status | type-Endung | detail | Was zu tun ist |
|---|---|---|---|
400 | validation-error | refund ist kein Boolean | Senden Sie true oder false, oder lassen Sie den Body weg |
400 | bad-request | Idempotency-Key header is required for this endpoint | Header ergänzen |
400 | bad-request | EVENT_NOT_CANCELLED | Veranstaltung absagen, bevor Sie ihre Rückerstattungen erneut versuchen |
401 | unauthorized | API-Schlüssel fehlt oder ist ungültig | Schlüssel prüfen |
403 | forbidden | API key lacks scope events:cancel | Scope vergeben oder Schlüssel damit rotieren |
404 | not-found | EVENT_NOT_FOUND | Die Veranstaltung gibt es in dieser Organisation nicht |
409 | idempotency-conflict | Schlüssel mit anderem Body wiederverwendet | Neuen Schlüssel nutzen |
503 | service-unavailable | EVENT_PAYMENT_CREDENTIALS_REQUIRED | zahls.ch im Dashboard verbinden oder ohne refund absagen |
Scheitert eine Anfrage an Validierung, Berechtigung oder der zahls.ch-Prüfung, wird nichts abgesagt. Fehler einzelner Bestellungen sind keine Fehlerantworten: sie stehen in failedOrderIds und im Rückerstattungsstatus. Das gemeinsame Format beschreibt Fehler.
Mit dem SDK
import { Cowtic } from "@cowtic/sdk";
const cowtic = new Cowtic({ apiKey: process.env.COWTIC_API_KEY! });
const { data: cancellation } = await cowtic.events.cancel("evt_2f8a", { refund: true });
console.log(cancellation.refunds?.queuedOrderIds);
const { data: status } = await cowtic.events.refunds("evt_2f8a");
if (status.counts.FAILED > 0) {
await cowtic.events.retryRefunds("evt_2f8a");
}Das SDK erzeugt pro Aufruf einen Idempotency-Key; mit { idempotencyKey } geben Sie einen eigenen mit, damit eine Wiederholung die erste Antwort liefert.