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
| Parameter | Required | Description |
|---|---|---|
organizationId | Yes | Target workspace |
planKey | Yes | Plan to grant (e.g. "pro_monthly") |
expiresAt | No | Expiration timestamp, null for permanent |
source | No | Custom source identifier (auto-generated if omitted) |
eventId | No | Custom event ID for idempotency |
eventTimestamp | No | Custom 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
- Entitlements and Grants — how the grant system works.
- Billing Catalog — plan definitions.