Developer Docs

The Starter to Pro Upgrade Flow allows Malet Owners to transition their personal Starter Malets into organizational Pro Malets. This process involves the payments, organizations, and malets subgraphs working together to ensure atomic provisioning and data integrity.

Upgrade & Transfer Architecture

When a user upgrades a Starter Malet, the backend must create an Organization, transfer the Malet to that Organization, and attach the Subscription & Billing Architecture โ€” all atomically.

The process leverages the same Stripe checkout.session.completed webhook pattern used in the initial Malet-First Ownership Architecture, but with the action set to upgrade_and_transfer.

1. Checkout Session Creation

The client requests an upgrade via the createUpgradeCheckoutSession GraphQL mutation. The payments service embeds the necessary context into the Stripe session metadata:

metadata: {
  action: 'upgrade_and_transfer',
  userId: 'user_123',
  planId: 'pro',
  maletId: 'malet_456', // The Starter Malet being upgraded
  orgName: 'Acme Corp',
  orgSlug: 'acme-corp'
}

2. Atomic Provisioning Webhook

When the user completes payment, Stripe fires checkout.session.completed. The WebhookService handles this by:

  1. Creating the Organization: An HTTP POST to the organizations subgraph (protected by x-internal-caller guard).
  2. Transferring the Malet: An HTTP POST to the malets subgraph to execute transferMaletToOrganization, changing ownership from the USER to the new ORGANIZATION.
  3. Updating the Subscription: Stripe subscription metadata is updated with the new orgId.
  4. Emitting Notifications: The org_upgraded_and_malet_transferred TCP event is dispatched.

NOTE

Idempotency: If the organization slug (acme-corp) is already taken, the webhook gracefully bails out before creating any entities, preventing orphan records.

Collaborator Migration

Starter Malets often have Outside Collaborators (users with sandboxed access). When upgrading to Pro, these collaborators must be converted into full Organization Members.

This is handled by the convertCollaboratorsToMembers mutation in the organizations subgraph.

Permission Mapping

The convertToMembers service translates sandbox permissions into formal Custom RBAC organization roles:

Collaborator Permission New OrgRole
FULL_ACCESS ADMIN
EDIT_CONTENT MEMBER
MANAGE_ORDERS MEMBER
VIEW_ONLY VIEWER

Tier Limits Enforcement

The migration process strictly enforces the Pro tier limit (maximum 10 members) using the centralized checkTierLimit configuration. If a conversion would exceed the limit, the specific collaborator is skipped, and an error is returned in the CollaboratorConversionResult.

type CollaboratorConversionResult {
  converted: Int!
  errors: [String!]!
}

Whenever a collaborator is successfully converted to a member, their old Collaborator record is marked as REVOKED, and an OrgAuditEventType.COLLABORATOR_CONVERTED_TO_MEMBER audit event is logged.

Deck UI

The upgrade flow is surfaced in The Deck โ€” the Malet Owner command center running on ngwenya-deck.

Upgrade CTA

On the /dashboard page, every personal Malet card (ownerType === 'USER') displays a green Upgrade button alongside the existing Manage, Blog, View, and Transfer actions. Clicking it opens the UpgradeModal.

UpgradeModal

The modal presents the Malet Owner with two paths:

  1. Upgrade & Transfer (recommended) โ€” The user enters an Organization name, URL handle (auto-derived slug), and optional category, selects Monthly or Yearly billing, and clicks "Upgrade & Transfer". This calls CREATE_UPGRADE_CHECKOUT_SESSION from @ngwenya/queries/subscriptions and redirects to Stripe Checkout.
  2. Create New Pro Malet โ€” Navigates to /create-malet?tier=pro. The original Starter Malet remains personal and unmodified.

The modal auto-derives the slug from the org name (same pattern as OrgDetailsStep.svelte in the create-malet wizard) and validates minimum lengths before enabling the submit button.

Upgrade Complete Page

After Stripe checkout succeeds, the user lands on /upgrade-complete?orgSlug={slug}. This is a simple confirmation page that:

  • Displays a success message with Pro feature highlights
  • Provides a CTA to navigate to the new Organization management page (/orgs/{slug}/manage)
  • Links back to The Deck dashboard

Shared Queries

The upgrade UI consumes two new GraphQL mutations from @ngwenya/queries:

Mutation Package Description
CREATE_UPGRADE_CHECKOUT_SESSION @ngwenya/queries/subscriptions Creates a Stripe Checkout session with upgrade_and_transfer metadata
CONVERT_COLLABORATORS_TO_MEMBERS @ngwenya/queries/organizations Converts sandbox collaborators into org members post-upgrade

NOTE

The PLAN_TIERS definition in @ngwenya/queries/subscriptions uses pro as the plan ID (renamed from business for consistency with the backend checkTierLimit configuration).