LastSaaS API Reference

Version 1.2

Authentication: Include an access token or API key as Authorization: Bearer <token>. Tenant-scoped routes require an X-Tenant-ID header. Admin keys (authority=admin) auto-resolve the root tenant. User keys require X-Tenant-ID. Admin routes require membership in the root tenant with admin or owner role.
Sections:
AuthenticationTenant MembersMessagesPlans & BillingAdmin — Dashboard & MonitoringAdmin — ConfigurationAdmin — TenantsAdmin — UsersAdmin — PlansAdmin — Credit BundlesAdmin — FinancialAdmin — API KeysAdmin — WebhooksSystemWebhook Events

Authentication

POST/api/auth/registerRegister a new userPublic

Creates a new user account with email and password. If an invitation token is provided, the user is automatically added to the inviting tenant. A personal tenant is always created for the new user. Returns access and refresh tokens plus the user profile and tenant memberships.

Request Body
{"email":"user@example.com","password":"secureP@ss1","displayName":"Jane Doe","invitationToken":"(optional)"}
Response
{"accessToken":"eyJ...","refreshToken":"eyJ...",
 "user":{"id":"...","email":"user@example.com","displayName":"Jane Doe","emailVerified":false,"isActive":true,"authMethods":[{"provider":"password"}],"createdAt":"...","updatedAt":"..."},
 "memberships":[{"tenantId":"...","tenantName":"Jane's Team","tenantSlug":"janes-team","role":"owner","isRoot":false}]}
POST/api/auth/loginAuthenticate and receive tokensPublic

Authenticates a user with email and password. Returns JWT access and refresh tokens. Account is locked for 15 minutes after 5 consecutive failed attempts.

Request Body
{"email":"user@example.com","password":"secureP@ss1"}
Response
{"accessToken":"eyJ...","refreshToken":"eyJ...","user":{...},"memberships":[...]}
POST/api/auth/refreshExchange refresh token for new access tokenPublic

Exchanges a valid refresh token for a new access/refresh token pair. The old refresh token is revoked (rotation). Use this when the access token expires.

Request Body
{"refreshToken":"eyJ..."}
Response
{"accessToken":"eyJ...","refreshToken":"eyJ...","user":{...},"memberships":[...]}
POST/api/auth/verify-emailVerify email addressPublic

Confirms the user's email address using a token sent via email. The token is single-use and expires after 24 hours.

Request Body
{"token":"verification-token-from-email"}
Response
{"message":"Email verified successfully"}
POST/api/auth/resend-verificationResend verification emailPublic

Sends a new verification email to the specified address. Rate-limited to one request per 60 seconds per email. Returns a success message regardless of whether the email exists (prevents enumeration).

Request Body
{"email":"user@example.com"}
Response
{"message":"If the email exists, a verification link has been sent"}
POST/api/auth/forgot-passwordRequest password reset emailPublic

Sends a password reset email to the specified address. Returns a success message regardless of whether the email exists (prevents enumeration). Only works for accounts with password authentication enabled.

Request Body
{"email":"user@example.com"}
Response
{"message":"If the email exists, a password reset link has been sent"}
POST/api/auth/reset-passwordReset password with tokenPublic

Resets the user's password using a token from the reset email. All existing refresh tokens are revoked (logs out all sessions). The token is single-use.

Request Body
{"token":"reset-token-from-email","newPassword":"newSecureP@ss1"}
Response
{"message":"Password reset successfully"}
GET/api/auth/googleInitiate Google OAuth flowPublic

Redirects the user to Google's OAuth consent screen. After authorization, Google redirects back to the callback URL. Only available when Google OAuth is configured.

GET/api/auth/google/callbackGoogle OAuth callbackPublic

Handles the OAuth callback from Google. Links the Google account to an existing user (matched by email) or creates a new account. Redirects to the frontend with tokens in the URL fragment: /auth/callback#access_token=...&refresh_token=...

Parameters
NameTypeRequiredDescription
statestringrequiredOAuth state parameter (verified against stored state)
codestringrequiredOAuth authorization code from Google
GET/api/auth/meGet current user profileBearer

Returns the authenticated user's profile and all tenant memberships. Use this to hydrate the session after login or page refresh.

Response
{"user":{"id":"...","email":"...","displayName":"...","emailVerified":true,"isActive":true,"authMethods":[...],"createdAt":"...","updatedAt":"...","lastLoginAt":"..."},"memberships":[{"tenantId":"...","tenantName":"...","tenantSlug":"...","role":"owner","isRoot":false}]}
POST/api/auth/logoutRevoke session tokensBearer

Revokes the current access token. If a refresh token is provided in the body, it is also revoked.

Request Body
{"refreshToken":"eyJ... (optional)"}
Response
{"message":"Logged out successfully"}
POST/api/auth/change-passwordChange passwordBearer

Changes the authenticated user's password. If the user already has a password, the current password must be provided. For Google-only accounts adding a password for the first time, the current password field can be omitted.

Request Body
{"currentPassword":"oldP@ss (required if password exists)","newPassword":"newSecureP@ss1"}
Response
{"message":"Password changed successfully"}
POST/api/auth/accept-invitationAccept a team invitationBearer

Accepts a pending invitation to join a tenant. The invitation token comes from the invitation email. The user is added to the tenant with the role specified in the invitation. Returns updated memberships.

Request Body
{"token":"invitation-token-from-email"}
Response
{"message":"Invitation accepted","memberships":[{"tenantId":"...","tenantName":"...","tenantSlug":"...","role":"user","isRoot":false}]}

Tenant Members

GET/api/tenant/membersList tenant membersBearer + Tenant

Returns all members of the current tenant with their roles and join dates. Any member of the tenant can call this endpoint.

Response
{"members":[{"userId":"...","email":"user@example.com","displayName":"Jane Doe","role":"owner","joinedAt":"2025-01-15T..."}]}
POST/api/tenant/members/inviteInvite a user by emailAdmin

Sends an invitation email to join the tenant. If the email belongs to an existing user, they receive a join link. If not, they receive a signup-and-join link. Invitations expire after 7 days. Only owners can invite admins; admins can only invite users. Subject to the plan's user limit.

Request Body
{"email":"newuser@example.com","role":"user"}
Response
{"message":"Invitation sent"}
DELETE/api/tenant/members/{userId}Remove a memberAdmin

Removes a member from the tenant. You cannot remove the owner or yourself. Admins can only remove regular users (not other admins).

Parameters
NameTypeRequiredDescription
userIdObjectIDrequiredThe user's ID
Response
{"message":"Member removed"}
PATCH/api/tenant/members/{userId}/roleChange a member's roleOwner

Changes a member's role to admin or user. Only the tenant owner can change roles. To transfer ownership, use the dedicated transfer endpoint instead.

Parameters
NameTypeRequiredDescription
userIdObjectIDrequiredThe target user's ID
Request Body
{"role":"admin"}
Response
{"message":"Role updated"}
POST/api/tenant/members/{userId}/transfer-ownershipTransfer tenant ownershipOwner

Transfers ownership of the tenant to another member. The current owner is demoted to admin. The target user must already be a member of the tenant. This action cannot be undone by the previous owner.

Parameters
NameTypeRequiredDescription
userIdObjectIDrequiredThe new owner's user ID
Response
{"message":"Ownership transferred"}

Messages

GET/api/messagesList messagesBearer

Returns all messages for the authenticated user, sorted by creation date (newest first). Messages include system notifications like invitation alerts.

Response
{"messages":[{"id":"...","userId":"...","type":"invitation","title":"...","body":"...","isRead":false,"createdAt":"..."}]}
GET/api/messages/unread-countGet unread countBearer

Returns the number of unread messages for the authenticated user. Use this for notification badges.

Response
{"count":3}
PATCH/api/messages/{messageId}/readMark as readBearer

Marks a specific message as read. Only the message owner can mark it as read.

Parameters
NameTypeRequiredDescription
messageIdObjectIDrequiredThe message ID
Response
{"message":"Marked as read"}

Plans & Billing

GET/api/plansList available plansBearer

Returns all subscription plans visible to the current user, along with the tenant's current plan, billing status, credits, and subscription interval. Requires the X-Tenant-ID header to determine the tenant's current state.

Response
{"plans":[{"id":"...","name":"Pro","description":"...","monthlyPriceCents":2900,"annualDiscountPct":20,"usageCreditsPerMonth":1000,"creditResetPolicy":"reset","bonusCredits":0,"userLimit":10,"entitlements":{...}}],
 "currentPlanId":"...","billingWaived":false,"tenantSubscriptionCredits":500,"tenantPurchasedCredits":0,
 "billingStatus":"active","billingInterval":"year","currentPeriodEnd":"2026-01-15T...","canceledAt":null}
GET/api/credit-bundlesList credit bundlesBearer

Returns all active credit bundles available for purchase, sorted by sort order.

Response
{"bundles":[{"id":"...","name":"500 Credits","credits":500,"priceCents":4900,"isActive":true,"sortOrder":1}]}
POST/api/billing/checkoutStart a checkout sessionBearer + Tenant

Creates a Stripe Checkout session for a plan subscription or credit bundle purchase. For free plans or billing-waived tenants, the plan is assigned immediately without Stripe. Specify either planId or bundleId, not both.

Request Body
{"planId":"ObjectID (or bundleId)","billingInterval":"year"}
Response
{"checkoutUrl":"https://checkout.stripe.com/..."}
POST/api/billing/portalOpen billing portalBearer + Tenant

Creates a Stripe Billing Portal session URL where the customer can manage payment methods, view invoices, and update billing details. The tenant must have an existing Stripe customer ID.

Response
{"portalUrl":"https://billing.stripe.com/..."}
GET/api/billing/transactionsList billing transactionsBearer + Tenant

Returns paginated billing transactions for the current tenant, sorted by date (newest first).

Parameters
NameTypeRequiredDescription
pageintoptionalPage number (default: 1)
perPageintoptionalItems per page, 1-100 (default: 20)
Response
{"transactions":[{"id":"...","tenantId":"...","description":"Pro Plan (Annual)","type":"subscription","amountCents":29900,"currency":"usd","invoiceNumber":"INV-0001","createdAt":"..."}],
 "total":15,"page":1,"perPage":20}
GET/api/billing/transactions/{id}/invoiceGet invoice detailsBearer + Tenant

Returns the full transaction record and tenant name for rendering an invoice view.

Parameters
NameTypeRequiredDescription
idObjectIDrequiredTransaction ID
Response
{"transaction":{...},"tenant":{"name":"Acme Corp"}}
GET/api/billing/transactions/{id}/invoice/pdfDownload invoice PDFBearer + Tenant

Generates and returns a PDF invoice for the specified transaction. The response Content-Type is application/pdf.

Parameters
NameTypeRequiredDescription
idObjectIDrequiredTransaction ID
POST/api/billing/cancelCancel subscriptionBearer + Tenant

Cancels the tenant's current subscription at the end of the billing period. The tenant retains access until the period ends. Returns the period end date.

Response
{"message":"Subscription will cancel at end of billing period","currentPeriodEnd":"2026-02-15T..."}
GET/api/billing/configGet billing configurationBearer + Tenant

Returns the Stripe publishable key for initializing Stripe.js on the frontend. Returns an empty string if Stripe is not configured.

Response
{"publishableKey":"pk_live_..."}

Admin — Dashboard & Monitoring

GET/api/admin/aboutGet system informationAdmin

Returns the current version and copyright information.

Response
{"version":"1.00","copyright":"..."}
GET/api/admin/dashboardGet dashboard metricsAdmin

Returns high-level system metrics including total user count, tenant count, and overall health status with any active issues.

Response
{"users":142,"tenants":38,"health":{"healthy":true,"issues":[]}}
GET/api/admin/logsList system logsAdmin

Returns paginated system audit logs with optional filtering by severity, user, or text search. Logs record authentication events, configuration changes, billing actions, and other system activity.

Parameters
NameTypeRequiredDescription
pageintoptionalPage number (default: 1)
perPageintoptionalItems per page, 1-100 (default: 50)
severitystringoptionalFilter by severity: critical, high, medium, low, debug
userIdObjectIDoptionalFilter by user ID
searchstringoptionalFull-text search in log messages
Response
{"logs":[{"id":"...","severity":"high","message":"Webhook created: Test → https://...","userId":"...","createdAt":"..."}],"total":256}
GET/api/admin/health/nodesList server nodesAdmin

Returns all known server nodes and their current status. In a multi-machine deployment, each machine registers as a separate node.

Response
{"nodes":[{"id":"...","hostname":"d892610f630968","region":"iad","lastSeen":"...","isHealthy":true}]}
GET/api/admin/health/metricsGet performance metricsAdmin

Returns time-series performance metrics (CPU, memory, request rate, latency) for a specific node or aggregated across all nodes.

Parameters
NameTypeRequiredDescription
nodeObjectIDoptionalNode ID (omit for aggregate)
rangestringoptionalTime range: 1h, 6h, 24h, 7d, 30d (default: 24h)
Response
{"metrics":[{"timestamp":"...","cpu":23.5,"memoryMB":128,"requestsPerMin":45,"avgLatencyMs":12}],"from":"...","to":"..."}
GET/api/admin/health/currentGet current node healthAdmin

Returns the latest health snapshot for each active node. Use this for real-time monitoring dashboards.

Response
{"metrics":[{"nodeId":"...","cpu":15.2,"memoryMB":96,"requestsPerMin":30,"avgLatencyMs":8}]}
GET/api/admin/health/integrationsCheck integration healthAdmin

Checks the connectivity and status of all external integrations: MongoDB, Stripe, Resend (email), and Google OAuth. Returns the check status and last 24h call count for each.

Response
{"integrations":[{"name":"mongodb","status":"healthy","lastCheck":"...","calls24h":1520},{"name":"stripe","status":"healthy",...},{"name":"resend","status":"not_configured",...}]}

Admin — Configuration

GET/api/admin/configList all config variablesAdmin

Returns all configuration variables as a map keyed by variable name. Includes system variables (read-only name/type) and user-created variables.

Response
{"configs":{"app.name":{"name":"app.name","type":"string","value":"LastSaaS","description":"Application name","isSystem":true,"options":""},...}}
POST/api/admin/configCreate a config variableAdmin

Creates a new user-defined configuration variable. Variable names must be unique. Types: string, numeric, enum (pipe-separated options), template (supports placeholders).

Request Body
{"name":"feature.max_uploads","description":"Maximum uploads per user","type":"numeric","value":"100","options":""}
Response
{"name":"feature.max_uploads","type":"numeric","value":"100","description":"Maximum uploads per user","isSystem":false,"options":""}
GET/api/admin/config/{name}Get a config variableAdmin

Returns a single configuration variable by name.

Parameters
NameTypeRequiredDescription
namestringrequiredConfig variable name
Response
{"name":"app.name","type":"string","value":"LastSaaS","description":"Application name","isSystem":true,"options":""}
PUT/api/admin/config/{name}Update a config variableAdmin

Updates the value (and optionally description/options) of a configuration variable. System variables only allow value changes. Enum variables validate against the options list.

Parameters
NameTypeRequiredDescription
namestringrequiredConfig variable name
Request Body
{"value":"200","description":"Updated description (optional)"}
Response
{"name":"feature.max_uploads","type":"numeric","value":"200",...}
DELETE/api/admin/config/{name}Delete a config variableAdmin

Deletes a user-created configuration variable. System variables cannot be deleted.

Parameters
NameTypeRequiredDescription
namestringrequiredConfig variable name
Response
{"message":"Config variable deleted"}

Admin — Tenants

GET/api/admin/tenantsList all tenantsAdmin

Returns all tenants with member counts and billing information. Includes the plan name, billing waived status, and credit balances.

Response
{"tenants":[{"id":"...","name":"Acme Corp","slug":"acme-corp","isRoot":false,"isActive":true,"memberCount":5,"planName":"Pro","billingWaived":false,"subscriptionCredits":1000,"purchasedCredits":200,"createdAt":"..."}]}
GET/api/admin/tenants/{tenantId}Get tenant detailsAdmin

Returns full tenant details including all members with roles and join dates.

Parameters
NameTypeRequiredDescription
tenantIdObjectIDrequiredTenant ID
Response
{"tenant":{"id":"...","name":"Acme Corp","slug":"acme-corp","isRoot":false,"isActive":true,"planId":"...","billingWaived":false,"subscriptionCredits":1000,"purchasedCredits":200,"stripeCustomerId":"cus_...","billingStatus":"active","billingInterval":"year","currentPeriodEnd":"...","createdAt":"...","updatedAt":"..."},
 "members":[{"userId":"...","email":"jane@acme.com","displayName":"Jane Doe","role":"owner","joinedAt":"..."}]}
PUT/api/admin/tenants/{tenantId}Update tenantOwner

Updates tenant properties. All fields are optional — only provided fields are changed. Can modify name, billing waived status, and credit balances.

Parameters
NameTypeRequiredDescription
tenantIdObjectIDrequiredTenant ID
Request Body
{"name":"New Name (optional)","billingWaived":true,"subscriptionCredits":5000,"purchasedCredits":100}
Response
{"message":"Tenant updated"}
PATCH/api/admin/tenants/{tenantId}/statusActivate or deactivate tenantOwner

Sets a tenant's active status. Deactivated tenants cannot access the application. The root tenant cannot be deactivated.

Parameters
NameTypeRequiredDescription
tenantIdObjectIDrequiredTenant ID
Request Body
{"isActive":false}
Response
{"message":"Tenant deactivated"}
PATCH/api/admin/tenants/{tenantId}/planAssign plan to tenantOwner

Directly assigns a plan to a tenant (bypasses Stripe). Can also toggle billing waived status. Send an empty planId or omit it to remove the plan.

Parameters
NameTypeRequiredDescription
tenantIdObjectIDrequiredTenant ID
Request Body
{"planId":"ObjectID (optional)","billingWaived":true}
Response
{"status":"updated"}
POST/api/admin/tenants/{tenantId}/cancel-subscriptionCancel subscription (admin override)Owner

Cancels a tenant's Stripe subscription. Set immediate to true to cancel now; otherwise cancels at the end of the billing period.

Parameters
NameTypeRequiredDescription
tenantIdObjectIDrequiredTenant ID
Request Body
{"immediate":false}
Response
{"message":"Subscription canceled"}
PATCH/api/admin/tenants/{tenantId}/subscriptionUpdate subscription detailsOwner

Manually updates subscription metadata such as the current period end date. Use this for correcting billing records.

Parameters
NameTypeRequiredDescription
tenantIdObjectIDrequiredTenant ID
Request Body
{"currentPeriodEnd":"2026-03-15T00:00:00Z"}
Response
{"message":"Subscription updated"}

Admin — Users

GET/api/admin/usersList all usersOwner

Returns all users with summary information including tenant count and last login time.

Response
{"users":[{"id":"...","email":"jane@example.com","displayName":"Jane Doe","emailVerified":true,"isActive":true,"tenantCount":2,"createdAt":"...","lastLoginAt":"..."}]}
GET/api/admin/users/{userId}Get user detailsOwner

Returns full user profile including authentication methods and all tenant memberships with billing details for each tenant.

Parameters
NameTypeRequiredDescription
userIdObjectIDrequiredUser ID
Response
{"user":{"id":"...","email":"jane@example.com","displayName":"Jane Doe","emailVerified":true,"isActive":true,"authMethods":[{"provider":"password"},{"provider":"google"}],"createdAt":"...","lastLoginAt":"..."},
 "memberships":[{"tenantId":"...","tenantName":"Acme Corp","tenantSlug":"acme-corp","isRoot":false,"role":"owner","joinedAt":"...","planId":"...","planName":"Pro","billingWaived":false,"subscriptionCredits":1000,"purchasedCredits":200}]}
PUT/api/admin/users/{userId}Update userOwner

Updates a user's email or display name. Both fields are optional — only provided fields are changed.

Parameters
NameTypeRequiredDescription
userIdObjectIDrequiredUser ID
Request Body
{"email":"new@example.com","displayName":"New Name"}
Response
{"message":"User updated"}
PATCH/api/admin/users/{userId}/statusActivate or deactivate userOwner

Sets a user's active status. Deactivated users cannot log in. Active sessions are not immediately terminated but will fail on the next API call.

Parameters
NameTypeRequiredDescription
userIdObjectIDrequiredUser ID
Request Body
{"isActive":false}
Response
{"message":"User deactivated"}
PATCH/api/admin/users/{userId}/role/{tenantId}Change user's role in tenantOwner

Changes a user's role within a specific tenant. Can set to owner, admin, or user. When changing to owner, the current owner is demoted to admin.

Parameters
NameTypeRequiredDescription
userIdObjectIDrequiredUser ID
tenantIdObjectIDrequiredTenant ID
Request Body
{"role":"admin"}
Response
{"message":"Role updated"}
GET/api/admin/users/{userId}/preflight-deletePreview delete effectsOwner

Returns a preview of what would happen if the user were deleted. Shows all tenants where the user is the owner and lists other members who could take ownership. Returns canDelete: false if the user is the sole owner of the root tenant.

Parameters
NameTypeRequiredDescription
userIdObjectIDrequiredUser ID
Response
{"canDelete":true,"ownerships":[{"tenantId":"...","tenantName":"Acme Corp","isRoot":false,"otherMembers":[{"userId":"...","email":"bob@acme.com","displayName":"Bob","role":"admin","joinedAt":"..."}]}]}
DELETE/api/admin/users/{userId}Delete userOwner

Permanently deletes a user account. For tenants where the user is the owner, specify a replacement owner or confirm tenant deletion. The request body must resolve all ownership conflicts identified by the preflight endpoint.

Parameters
NameTypeRequiredDescription
userIdObjectIDrequiredUser ID
Request Body
{"replacementOwners":{"tenantId":"newOwnerUserId"},"confirmTenantDeletions":["tenantId"]}
Response
{"message":"User deleted"}

Admin — Plans

GET/api/admin/plansList all plansAdmin

Returns all subscription plans with subscriber counts.

Response
{"plans":[{"id":"...","name":"Pro","description":"...","monthlyPriceCents":2900,"annualDiscountPct":20,"usageCreditsPerMonth":1000,"creditResetPolicy":"reset","bonusCredits":0,"userLimit":10,"entitlements":{"feature_x":{"type":"bool","boolValue":true,"description":"..."}},"isSystem":false,"createdAt":"..."}]}
GET/api/admin/plans/{planId}Get plan detailsAdmin

Returns full details for a single plan.

Parameters
NameTypeRequiredDescription
planIdObjectIDrequiredPlan ID
Response
{"id":"...","name":"Pro","description":"...","monthlyPriceCents":2900,...}
GET/api/admin/entitlement-keysList entitlement keysAdmin

Returns all unique entitlement keys currently in use across all plans, with their types and descriptions.

Response
{"keys":[{"key":"feature_x","type":"bool","description":"Enable feature X"}]}
POST/api/admin/plansCreate a planOwner

Creates a new subscription plan. Plan names must be unique. Credit reset policy can be reset (credits reset each month) or accrue (unused credits roll over). Set userLimit to 0 for unlimited users.

Request Body
{"name":"Enterprise","description":"For large teams","monthlyPriceCents":9900,"annualDiscountPct":25,"usageCreditsPerMonth":5000,"creditResetPolicy":"accrue","bonusCredits":1000,"userLimit":0,"entitlements":{"feature_x":{"type":"bool","boolValue":true,"description":"Enable feature X"}}}
Response
{"id":"...","name":"Enterprise",...}
PUT/api/admin/plans/{planId}Update a planOwner

Updates an existing plan. System plans (Free) cannot be renamed. All fields from the create endpoint are accepted.

Parameters
NameTypeRequiredDescription
planIdObjectIDrequiredPlan ID
Request Body
{"name":"Enterprise Plus","monthlyPriceCents":14900,...}
Response
{"id":"...","name":"Enterprise Plus",...}
DELETE/api/admin/plans/{planId}Delete a planOwner

Deletes a plan. System plans and plans with active subscribers cannot be deleted. Reassign subscribers first.

Parameters
NameTypeRequiredDescription
planIdObjectIDrequiredPlan ID
Response
{"status":"deleted"}

Admin — Credit Bundles

GET/api/admin/credit-bundlesList all credit bundlesAdmin

Returns all credit bundles (active and inactive), sorted by sort order.

Response
{"bundles":[{"id":"...","name":"500 Credits","credits":500,"priceCents":4900,"isActive":true,"sortOrder":1,"createdAt":"..."}]}
POST/api/admin/credit-bundlesCreate a credit bundleOwner

Creates a new credit bundle for purchase. Bundle names must be unique. Credits and price must be positive values.

Request Body
{"name":"1000 Credits","credits":1000,"priceCents":8900,"isActive":true,"sortOrder":2}
Response
{"id":"...","name":"1000 Credits","credits":1000,...}
PUT/api/admin/credit-bundles/{bundleId}Update a credit bundleOwner

Updates an existing credit bundle.

Parameters
NameTypeRequiredDescription
bundleIdObjectIDrequiredBundle ID
Request Body
{"name":"1000 Credits","credits":1000,"priceCents":7900,...}
Response
{"id":"...","name":"1000 Credits",...}
DELETE/api/admin/credit-bundles/{bundleId}Delete a credit bundleOwner

Permanently deletes a credit bundle.

Parameters
NameTypeRequiredDescription
bundleIdObjectIDrequiredBundle ID
Response
{"status":"deleted"}

Admin — Financial

GET/api/admin/financial/transactionsList all transactionsAdmin

Returns paginated billing transactions across all tenants. Supports filtering by tenant and text search across description, invoice number, plan name, and bundle name.

Parameters
NameTypeRequiredDescription
pageintoptionalPage number (default: 1)
perPageintoptionalItems per page, 1-100 (default: 50)
tenantIdObjectIDoptionalFilter by tenant
searchstringoptionalSearch description, invoice number, plan/bundle name
Response
{"transactions":[{"id":"...","tenantId":"...","description":"Pro Plan (Annual)","type":"subscription","amountCents":29900,"currency":"usd","invoiceNumber":"INV-0001","planName":"Pro","createdAt":"..."}],
 "total":150,"page":1,"perPage":50}
GET/api/admin/financial/metricsGet financial metricsAdmin

Returns time-series financial data for charting. Supported metrics: revenue (daily revenue), arr (annualized recurring revenue), dau (daily active users), mau (monthly active users).

Parameters
NameTypeRequiredDescription
rangestringoptionalTime range: 7d, 30d, 1y (default: 30d)
metricstringoptionalMetric type: revenue, arr, dau, mau (default: revenue)
Response
{"data":[{"date":"2026-02-01","value":15000},{"date":"2026-02-02","value":18500},...]}

Admin — API Keys

GET/api/admin/api-keysList active API keysAdmin

Returns all active API keys with metadata. The key hash is never returned — only the preview (last 8 characters) is shown.

Response
{"apiKeys":[{"id":"...","name":"CI/CD Pipeline","keyPreview":"x7k9m2pq","authority":"admin","createdBy":"...","createdAt":"...","lastUsedAt":"...","isActive":true}]}
POST/api/admin/api-keysCreate an API keyAdmin

Creates a new API key and returns the raw key value. The raw key is only returned once — it is stored as a SHA-256 hash and cannot be retrieved later. Authority levels: admin keys auto-resolve the root tenant and get admin-level access; user keys require an X-Tenant-ID header.

Request Body
{"name":"CI/CD Pipeline","authority":"admin"}
Response
{"apiKey":{"id":"...","name":"CI/CD Pipeline","keyPreview":"x7k9m2pq","authority":"admin",...},"rawKey":"lsk_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmno"}
DELETE/api/admin/api-keys/{keyId}Revoke an API keyAdmin

Soft-deletes an API key. The key immediately stops working for authentication. This cannot be undone.

Parameters
NameTypeRequiredDescription
keyIdObjectIDrequiredAPI key ID
Response
{"status":"deleted"}

Admin — Webhooks

GET/api/admin/webhooksList active webhooksAdmin

Returns all active webhook configurations sorted by creation date (newest first).

Response
{"webhooks":[{"id":"...","name":"Provisioning","description":"...","url":"https://example.com/webhook","secretPreview":"k9m2pqx7","events":["tenant.created"],"isActive":true,"createdBy":"...","createdAt":"..."}]}
GET/api/admin/webhooks/event-typesList available event typesAdmin

Returns all webhook event types that can be subscribed to, with descriptions.

Response
{"eventTypes":[{"type":"tenant.created","description":"Fired when a new tenant is created..."}]}
POST/api/admin/webhooksCreate a webhookAdmin

Creates a new webhook with an auto-generated signing secret (prefixed whsec_). The full secret is returned in the response — you can also retrieve it later from the detail endpoint. All deliveries include an X-Webhook-Signature header containing the HMAC-SHA256 signature of the payload.

Request Body
{"name":"Provisioning","description":"Provision new tenants","url":"https://example.com/webhook","events":["tenant.created"]}
Response
{"webhook":{"id":"...","name":"Provisioning",...},"secret":"whsec_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef"}
GET/api/admin/webhooks/{webhookId}Get webhook detailsAdmin

Returns full webhook configuration including the signing secret and the 20 most recent delivery attempts with their payloads and response details.

Parameters
NameTypeRequiredDescription
webhookIdObjectIDrequiredWebhook ID
Response
{"webhook":{"id":"...","name":"Provisioning",...},"secret":"whsec_...","deliveries":[{"id":"...","eventType":"tenant.created","payload":"{...}","responseCode":200,"responseBody":"ok","success":true,"durationMs":120,"createdAt":"..."}]}
PUT/api/admin/webhooks/{webhookId}Update webhookAdmin

Updates the webhook's name, description, URL, or subscribed events. The signing secret is not affected.

Parameters
NameTypeRequiredDescription
webhookIdObjectIDrequiredWebhook ID
Request Body
{"name":"Updated Name","description":"...","url":"https://new-url.com/webhook","events":["tenant.created"]}
Response
{"webhook":{"id":"...","name":"Updated Name",...}}
DELETE/api/admin/webhooks/{webhookId}Delete webhookAdmin

Soft-deletes a webhook. It immediately stops receiving event deliveries.

Parameters
NameTypeRequiredDescription
webhookIdObjectIDrequiredWebhook ID
Response
{"status":"deleted"}
POST/api/admin/webhooks/{webhookId}/testSend test eventAdmin

Delivers a test tenant.created event with sample data to the webhook URL. The delivery includes an X-Webhook-Test: true header so your handler can distinguish test deliveries. Returns the delivery result.

Parameters
NameTypeRequiredDescription
webhookIdObjectIDrequiredWebhook ID
Response
{"delivery":{"id":"...","eventType":"tenant.created","success":true,"responseCode":200,"durationMs":85,"createdAt":"..."}}
POST/api/admin/webhooks/{webhookId}/regenerate-secretRegenerate signing secretAdmin

Generates a new signing secret for the webhook. The old secret immediately stops working. Returns the new secret and preview.

Parameters
NameTypeRequiredDescription
webhookIdObjectIDrequiredWebhook ID
Response
{"secret":"whsec_NEWsecretABCDEFGHIJKLMNOPQRSTUV","secretPreview":"QRSTUV12"}

System

GET/api/versionGet API versionPublic

Returns the current application version. All API responses also include the version in the X-API-Version response header and a unique X-Request-ID header for tracing.

Response
{"version":"1.00"}
GET/healthHealth checkPublic

Returns a simple health status. Used by load balancers and monitoring services to verify the server is running.

Response
{"status":"ok"}
POST/api/billing/webhookStripe webhookStripe

Receives and processes Stripe webhook events. Authenticated via Stripe's webhook signature verification — not accessible with API keys or JWT tokens. Handles checkout completion, subscription updates, cancellations, and invoice events.

Webhook Events

Events that can be subscribed to via webhooks. Each delivery includes an X-Webhook-Signature header containing the HMAC-SHA256 hex digest of the JSON payload, computed with your webhook's signing secret.

subscription.activated
Fired when a subscription is activated after a successful checkout. Payload includes tenantId, planId, planName, billingInterval, and amountCents.
subscription.canceled
Fired when a subscription is canceled (by user, admin, or Stripe). Payload includes tenantId, tenantName, and reason (user_initiated, cancel_at_period_end, or subscription_ended).
payment.received
Fired when a recurring subscription payment succeeds (excludes the first payment which triggers subscription.activated). Payload includes tenantId, amountCents, currency, and planName.
payment.failed
Fired when a subscription payment fails. The tenant is moved to past_due status. Payload includes tenantId and tenantName.
member.invited
Fired when a team member is invited. Payload includes tenantId, tenantName, email, role, and invitedBy.
member.joined
Fired when a user joins a tenant by accepting an invitation. Payload includes tenantId, tenantName, userId, and role.
member.removed
Fired when a member is removed from a tenant by an admin. Payload includes tenantId, tenantName, userId, and removedBy.
member.role_changed
Fired when a member's role is changed within a tenant. Payload includes tenantId, tenantName, userId, oldRole, and newRole.
ownership.transferred
Fired when tenant ownership is transferred to another member. Payload includes tenantId, tenantName, fromUserId, and toUserId.
user.registered
Fired when a new user registers. Payload includes userId, email, and displayName.
user.verified
Fired when a user verifies their email address. Payload includes userId and email.
user.deactivated
Fired when an admin deactivates a user account. Payload includes userId.
credits.purchased
Fired when a credit bundle is purchased. Payload includes tenantId, bundleId, bundleName, credits, and amountCents.
plan.changed
Fired when a tenant's plan changes (upgrade, downgrade, or subscription end). Payload includes tenantId, planId, and planName.
tenant.created
Fired when a new tenant is created during registration. Payload includes tenantId, tenantName, tenantSlug, and userId.
tenant.deactivated
Fired when an admin deactivates a tenant. Payload includes tenantId and tenantName.
user.deleted
Fired when an admin deletes a user account. Payload includes userId and email.
tenant.deleted
Fired when a tenant is deleted (e.g. sole owner deleted). Payload includes tenantId, tenantName, and reason.
api_key.created
Fired when a new API key is created. Payload includes keyId, name, authority, and createdBy.
api_key.revoked
Fired when an API key is revoked/deleted. Payload includes keyId and revokedBy.