Tenant Onboarding
PolarSharp.Onboarding provisions new Polar merchant tenants programmatically. Two co-existing flows:
| Flow | Use when | Entry point |
|---|---|---|
| Programmatic | Headless / B2B — host has all values upfront | IPolarOnboardingClient.OnboardProgrammaticallyAsync(request) |
| OAuth-linking | User-consent — merchant authorises via Polar | BuildAuthorizeUrl(...) → redirect → CompleteOAuthOnboardingAsync(callback) |
| Wizard | Interactive UI — step-by-step with resumable sessions | IOnboardingWizard.StartAsync() → submit steps → FinishAsync() |
All three converge on the same OnboardedTenantResult shape: OrganizationId, AccessToken, WebhookEndpointId, WebhookSecret, GrantedScopes, OnboardedAt.
Programmatic flow
var result = await client.OnboardProgrammaticallyAsync(new ProgrammaticOnboardingRequest
{
OrganizationName = "Acme",
OrganizationSlug = "acme",
Email = "ops@acme.example.com",
CountryCode = "US",
Currency = "USD",
WebhookCallbackUrl = "https://acme.example.com/hooks/polar",
WebhookEvents = ["order.created", "subscription.active"],
InitialAdminEmail = "admin@acme.example.com",
});
Under the hood: POST /v1/organizations/ → POST /v1/organization-access-tokens/ → POST /v1/webhooks/endpoints/ → persist via IOnboardedTenantSink → invoke post-processors (including TenantAdminAutoProvisioningPostProcessor when Identity is installed).
Wizard flow
The wizard exposes step-by-step methods backed by a persistent OnboardingSessionEntity row. Sessions are resumable — a user can close the browser and pick up where they left off within the configured TTL (default 7 days). Expired sessions are auto-pruned by OnboardingSessionExpirationCleaner (daily).
Step sequence
SubmitCompanyBasicsAsync— name, slug, email, country, currency, primary admin emailSubmitProductTypesAsync— what the tenant sells; answers drive conditional later stepsSubmitWebhookConfigAsync— callback URL + event subscriptionsSubmitTranslationConfigAsync(optional) — only surfaced whenProductTypes.RequiresMultiLanguage = true. API key is encrypted at rest immediately via the Data Protection API — plaintext never persisted.SubmitBankingHandoffAsync— acknowledges that Stripe Connect linking happens out-of-band in Polar's dashboardFinishAsync— commits; internally calls the programmatic API
Conditional next-steps
OnboardingStepResult.NextStep is computed from accumulated answers. For example, when ProductTypesStep.RequiresMultiLanguage = false, the TranslationConfig step is removed from RemainingSteps.
Auto-provisioned TenantAdmin
When PolarSharp.MultiTenant.Identity is installed and TenantAdminAutoProvisioningPostProcessor is registered (via .AddTenantAdminAutoProvisioning() on the Identity builder), every successful onboarding auto-creates a TenantAdmin membership for InitialAdminEmail (creating the user if needed, with a logged single-use reset token).