Leave Localhost logoLeave LocalhostDocs
Billing

Manual Grants

Manual grants let you assign capabilities to organizations without going through a payment provider. This is useful for:

Manual grants let you assign capabilities to organizations without going through a payment provider. This is useful for:

  • Granting Pro access to beta testers
  • Compensating users with free upgrades
  • Internal/demo workspaces
  • Testing billing flows in development

Creating a Manual Grant

Use the createManualGrant internal mutation from the Convex dashboard or a Convex action:

await ctx.runMutation(internal.billing.grants.createManualGrant, {
  organizationId: "org_123",
  planKey: "pro_monthly",
  // Optional: set an expiration
  expiresAt: Date.now() + 30 * 24 * 60 * 60 * 1000, // 30 days
  // Optional: custom source identifier
  source: "beta-program:org_123",
});

Parameters

ParameterRequiredDescription
organizationIdYesTarget workspace
planKeyYesPlan to grant (e.g. "pro_monthly")
expiresAtNoExpiration timestamp, null for permanent
sourceNoCustom source identifier (auto-generated if omitted)
eventIdNoCustom event ID for idempotency
eventTimestampNoCustom event timestamp

The mutation automatically resolves the plan's capabilities from the catalog and creates one grant row per capability.

Revoking a Manual Grant

Use the revokeManualGrant internal mutation with the source identifier:

await ctx.runMutation(internal.billing.grants.revokeManualGrant, {
  source: "beta-program:org_123",
});

This sets revokedAt on all active grants from that source.

Manual vs Provider Grants

Manual grants use provider: "manual" and sourceType: "manual". They coexist with provider grants — an organization can have both a paid subscription and manual grants. Capabilities are the union of all active grants.

Next Reads

On this page