Zum Inhalt springen

Billing API

Drei Endpunkte erlauben es Agenten und Integrationen, mit dem Abrechnungsstatus zu arbeiten, den Ihr Dashboard verwaltet.

Alle Endpunkte erfordern einen Bearer-API-Key (siehe Authentifizierung).

GET /me/billing/state

Gibt eine Momentaufnahme des Abonnements Ihres Tenants zurück.

Terminal-Fenster
curl https://api.saiku.bi/me/billing/state \
-H "Authorization: Bearer $SAIKU_API_KEY"

Antwort:

{
"tenantId": "81e301f2-…",
"tier": "team",
"stripeCustomerId": "cus_…",
"subscriptionStatus": "active",
"currentPeriodEnd": "2026-06-23T00:00:00Z",
"trialEndsAt": null,
"billedFeaturesActive": true
}

Felder:

  • tierstarter, team, business oder enterprise.
  • subscriptionStatus — der Status von Stripe wortwörtlich. Die Werte, die Sie in der Praxis sehen, sind trialing, active, past_due, unpaid, canceled oder leer (free / pre-billing).
  • currentPeriodEnd — wann Ihre aktuelle Abrechnungsperiode endet. Bei aktivem Abo erfolgt zu diesem Zeitpunkt die Verlängerung.
  • trialEndsAt — nur gesetzt, solange subscriptionStatus = trialing.
  • billedFeaturesActivetrue, solange Sie Zugriff auf abgerechnete Features haben. Andernfalls false. Verwenden Sie dies, um Ihr eigenes UI zu gaten.

POST /me/billing/checkout-session

Erzeugt eine einmalige URL zum von Stripe gehosteten Checkout, um eine Testphase zu starten oder einen Plan zu abonnieren.

Terminal-Fenster
curl -X POST https://api.saiku.bi/me/billing/checkout-session \
-H "Authorization: Bearer $SAIKU_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tier": "team",
"email": "alice@acme.com",
"name": "Acme Corp",
"successUrl": "https://app.acme.com/billing/success",
"cancelUrl": "https://app.acme.com/billing/cancel"
}'

Antwort:

{
"url": "https://checkout.stripe.com/c/pay/cs_test_a1…",
"sessionId": "cs_test_a1…"
}

Senden Sie den Browser des Benutzers per 303-Redirect an url. Die URL ist einmalig und verfällt nach wenigen Minuten — nicht cachen.

successUrl und cancelUrl sind absolute URLs, an die Stripe den Benutzer nach Abschluss oder Abbruch des Checkouts zurücksendet. Beide müssen HTTPS sein.

POST /me/billing/portal-session

Erzeugt eine einmalige URL zum Stripe Customer Portal. Das Portal ist die von Stripe gehostete Seite zur Verwaltung des Abonnements: Karte aktualisieren, Plan ändern, Rechnungen herunterladen, kündigen.

Terminal-Fenster
curl -X POST https://api.saiku.bi/me/billing/portal-session \
-H "Authorization: Bearer $SAIKU_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "returnUrl": "https://app.acme.com/billing" }'

Antwort:

{ "url": "https://billing.stripe.com/p/session/test_…" }

Liefert 409 no_customer, wenn der Tenant noch nie ein Checkout abgeschlossen hat — es gibt keinen Stripe-Kunden zu verwalten. Führen Sie zuerst POST /me/billing/checkout-session aus.

Ein durchgängiges Beispiel — eingebetteter Abonnementstatus

Wenn Sie ein Admin-UI bauen, das Saiku Cloud umschließt, und Ihren Benutzern den aktuellen Plan inline anzeigen möchten:

// pseudocode
const state = await fetch('https://api.saiku.bi/me/billing/state', {
headers: { Authorization: `Bearer ${saikuApiKey}` }
}).then(r => r.json());
if (!state.billedFeaturesActive) {
// user has no active subscription or trial — show "upgrade" CTA
const session = await fetch(
'https://api.saiku.bi/me/billing/checkout-session',
{
method: 'POST',
headers: {
Authorization: `Bearer ${saikuApiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
tier: 'team',
email: user.email,
name: org.name,
successUrl: `${window.location.origin}/billing/success`,
cancelUrl: `${window.location.origin}/billing/cancel`
})
}
).then(r => r.json());
window.location.href = session.url;
} else {
// user has access — show current plan + manage button
renderPlan(state.tier, state.currentPeriodEnd);
const portal = await fetch(
'https://api.saiku.bi/me/billing/portal-session',
{
method: 'POST',
headers: {
Authorization: `Bearer ${saikuApiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ returnUrl: `${window.location.origin}/billing` })
}
).then(r => r.json());
renderManageButton(portal.url);
}