No results found.

Reseller mode (B2B-of-B2B-of-B2B)

Build SaaS / PaaS where your customers can resell your app to their own end-customers. WolfieAuth provides the primitives + SDKs.

Reseller mode — building B2B-of-B2B with WolfieAuth

TL;DR: any app registered with WolfieAuth can opt-in to reseller mode. Once enabled, the app’s owner-org admin can mark customer-orgs as resellers — they get a seat-pack, a dedicated signup link (and optionally a custom domain), and a panel to provision their own end-customer orgs. Three-level fee policy splits revenue between the platform (Wolfie), the app owner-org, and the reseller. SDKs in every framework expose isResellerOf(), useReseller(), drop-in components, and a typed API client so the app’s own admin UI shows up in 5 lines.

This page walks through how the model works and how to plug it into your app.

The hierarchy

                 WolfieAuth Platform (Wolfie)
                          │  Stripe Connect onboarding
                          │  + 5% platform-fee on reseller-subscriptions
                    ┌──── Owner-org of the OidcClient ────┐
                    │          (your app, e.g. acme-cloud)│
                    │                                     │
                    │   Sells seat-packs to resellers     │
                    │   (Stripe Connect on owner-org)     │
                    │                                     │
                    └──┬──────────────────────────────┬───┘
                       │                              │
                       ▼                              ▼
              Reseller-org A             Reseller-org B
              (Big Reseller Polska)      (some other partner)
                       │                              │
                       │  Distributes seats to          │
                       │  end-customers (out-of-band     │
                       │  invoicing — wolfieauth        │
                       │  doesn't pay reseller→client    │
                       │  transactions)                  │
                       ▼                              ▼
              ┌─ Bob's Pizza GmbH       ┌─ Other end-customer
              ├─ Tom's Café             └─ ...
              └─ Foo's Bar Sp. z o.o.

Three actors, three responsibilities

ActorOwnsDecides
Platform (Wolfie)WolfieAuth itselfDefault platform-fee BPS/flat for ALL reseller-subscriptions; per-app and per-reseller overrides; emergency suspend
Owner-org of the appOidcClient (acme-cloud-product)Whether to enable reseller-mode at all; which customer-orgs become resellers; seat-limits; per-reseller fees; signup-tokens; custom domains
Reseller-orgTheir reseller-subscription + their child-orgsWhich end-customers to provision (within the seat limit); when to suspend a customer; whether to charge them out-of-band

Three-level fee policy

WolfieAuth resolves a per-payment fee through this hierarchy (most-specific wins):

  1. Per-resellership override (AppReseller.platformFeeBpsOverride) — Wolfie negotiated a special rate with this specific reseller.
  2. Per-app override (OidcClient.platformResellerFeeBpsOverride) — Wolfie negotiated with the owner-org (e.g. Acme Cloud has 7% instead of 5%).
  3. Platform default (PlatformConfig.platformResellerFeeBps) — Wolfie’s global default, 500 bps (5%) out-of-the-box.

The owner-org’s own fee on the seat-pack subscription (the price they charge the reseller) sits separately on OidcClient.resellerFeeBps / AppReseller.feeBps. Both fees apply additively — reseller pays seat-pack price (= owner-org revenue) + a small platform-fee slice that goes to Wolfie.

Setup checklist (owner-org admin)

  1. Open /admin/clients/<your-app-id>Overview tab.
  2. Toggle 🤝 Tryb reseller / Reseller mode. Save.
  3. A new Resellers tab appears.
  4. Set the default resellerFeeBps / resellerFeeFlatCents (or leave 0 for “free seat-packs”).
  5. Click + Add reseller, pick a customer-org from the dropdown, set their seat-limit. A signup-token is generated and shown once — copy it.
  6. (Optional) Set a custom domain on that reseller (e.g. cloud.acme.com). Add the TXT record shown, click ✓ Verify, done.
  7. Hand the signup-token (or the custom-domain URL) to the reseller.

What changes for the reseller’s end-customers

A user signing up via:

https://auth.wolfieguard.com/signup?client_id=acme-cloud-product&reseller_token=<token>

…or via a verified custom domain:

https://cloud.acme.com/signup?client_id=acme-cloud-product

…gets a fresh Organization row with parentId = <reseller's orgId>. Their OIDC tokens carry:

  • wolfieauth_org_parentId = "<reseller-orgId>" (apps recognize this end-customer is “under” a reseller)
  • wolfieauth_org_id = the new child-org’s id (their own data scope)

The reseller’s admins log in to the owner-org’s admin UI (your app, e.g. Acme Cloud’s admin panel — built using SDK helpers below). Their tokens carry:

  • wolfieauth_reseller_apps[] = [{ client_id: "acme-cloud-product", org_id: "<reseller-orgId>", seat_limit: 100, seats_allocated: 17, status: "ACTIVE" }]

That’s the gating mechanism — your app sees the claim and shows the Reseller portfolio panel.

Wiring the panel in your app — per SDK

React / Next.js

import { RequireResellerAdmin, ResellerChildrenTable, useReseller } from "@wolfieauth/sdk-react";

// Drop-in (works out of the box):
<RequireResellerAdmin clientId="acme-cloud-product"
  fallback={<p>This page is for reseller admins only.</p>}>
  <ResellerChildrenTable clientId="acme-cloud-product" />
</RequireResellerAdmin>

// Or build your own UI from the hook:
function MyResellerDashboard() {
  const { isReseller, resellership, children, billing,
          provisionChild, suspendChild } = useReseller("acme-cloud-product");
  if (!isReseller) return null;
  return <div>{resellership.seats_allocated}/{resellership.seat_limit} seats  </div>;
}

The hook expects your app to mount a proxy at /api/wolfieauth/resellers/... that forwards to auth.wolfieguard.com/api/admin/resellers/... with the user’s OIDC bearer token. Your SDK middleware (handle.ts in SvelteKit, middleware in Next.js) typically already has this pattern — see your stack-specific guide.

SvelteKit

// +page.server.ts
import { loadResellerContext } from "@wolfieauth/sdk-sveltekit/server";

export const load = async ({ locals, fetch }) => {
  const reseller = await loadResellerContext({
    claims: locals.session?.claims ?? null,
    clientId: "acme-cloud-product",
    fetch,
  });
  return { reseller };
};
<!-- +page.svelte -->
<script>
  let { data } = $props();
</script>

{#if data.reseller.isReseller}
  <h1>Your customers ({data.reseller.children.length})</h1>
  {#each data.reseller.children as c}
    <div>{c.name}{c._count.members} members</div>
  {/each}
{/if}

Laravel / PHP

use WolfieAuth\Sdk\Resellers\Resellers;

public function dashboard(Request $request)
{
    $claims = $request->attributes->get('wolfieauth')['claims'] ?? null;
    if (!Resellers::isResellerOf($claims, 'acme-cloud-product')) {
        abort(403);
    }
    $seats = Resellers::getResellerSeatStatus($claims, 'acme-cloud-product');
    return view('reseller.dashboard', compact('seats'));
}

In Blade:

@if(\WolfieAuth\Sdk\Resellers\Resellers::isResellerOf($wolfieauth['claims'], 'acme-cloud-product'))
  <a href="/reseller">Manage your customers</a>
@endif

Django / DRF

from wolfieauth.resellers import IsResellerAdmin, is_reseller_of, wolfieauth_reseller_admin

# DRF
class ResellerDashboard(APIView):
    permission_classes = [IsResellerAdmin("acme-cloud-product")]
    def get(self, request):
        ...

# Plain Django
@wolfieauth_reseller_admin("acme-cloud-product")
def reseller_dashboard(request):
    ...

# In a template
{% if request.wolfieauth_claims|is_reseller_of:"acme-cloud-product" %}
  <a href="/reseller">Manage your customers</a>
{% endif %}

Building a “PaaS-of-PaaS” with WolfieAuth

Now that the primitives are in place, the wider pattern is: anything you build on top of WolfieAuth can be a multi-tier SaaS. Examples:

  • WolfieCloud (managed hosting): Wolfie runs the platform; resellers are agencies that bundle hosting for their clients; end-customers are the agency’s clients (each gets their own org with their own database, mailboxes, etc.)
  • Industry-specific CRM (e.g. dentist-CRM-as-a-service): Wolfie runs WolfieAuth; the platform owner-org sells a “Dental Network” seat-pack to large network-operators; each network has a reseller-status that lets them spawn an org per practice; each practice’s staff log in via a per-network branded subdomain.
  • White-label e-learning: Same pattern — corporate trainers buy seat-packs, distribute access to their internal “students” each in their own orgs.

The combination of:

  • per-OidcClient theme + reseller-scoped defaultChildTheme cascade,
  • per-org Stripe Connect for the owner-org,
  • per-resellership signup tokens + optional custom domain,
  • claims-based gating in the SDK,

means the platform owner doesn’t have to write three layers of code to support B2B-of-B2B-of-B2C — WolfieAuth handles identity, branding, fees, seats, and lifecycle. Your app just owns its own product.

API reference

All endpoints under /api/admin/resellers/... and /api/admin/clients/:id/resellers/... — see the REST API doc for full routes, query params, and response shapes.

Migration / rollout

  • Existing apps: opt-in. OidcClient.resellerMode defaults to false; existing customer-orgs continue to work unchanged.
  • Switching it on later is non-destructive: enabling reseller-mode doesn’t change anything about already-onboarded users; they remain root-level orgs.
  • Custom domains are optional: an app can use the standard auth.wolfieguard.com/signup?reseller_token=... link forever and never set up a custom domain.
  • Three fee policies are independent: the platform-fee + owner-org-fee + per-reseller override all stack additively. Setting any to 0 is supported (= no fee at that level).

Limits and caveats

  • Sub-resellers (3+ levels deep) are gated behind AppReseller.allowSubResellers and only SUPER_ADMIN of the platform can enable. Most cases are flat: platform → reseller → end-customer.
  • Each reseller-org needs its own Stripe Connect account if it wants to invoice its own end-customers via WolfieAuth — but reseller→end-customer transactions are out-of-scope for WolfieAuth’s billing (the reseller invoices their customers themselves, e.g. via their own wolfiecrm). WolfieAuth tracks only the seat-pack subscription (reseller → owner-org).
  • KSeF invoicing for reseller→end-customer is the reseller’s responsibility — their wolfiecrm or external accounting.

Last updated: