Files
2026-04-12 01:06:31 +07:00

8.2 KiB

Polar Benefits

Automated benefit delivery system for digital products.

Philosophy

Configure once, automatic delivery. Polar handles granting and revoking based on subscription state.

Benefit Types

1. License Keys

Auto-generate unique keys with customizable branding.

Create:

const benefit = await polar.benefits.create({
  type: "license_keys",
  organization_id: "org_xxx",
  description: "Software License",
  properties: {
    prefix: "MYAPP",
    expires: false,
    activations: 1,
    limit_usage: false
  }
});

Validation API (unauthenticated):

const validation = await polar.licenses.validate({
  key: "MYAPP-XXXX-XXXX-XXXX",
  organization_id: "org_xxx"
});

if (validation.valid) {
  // Grant access
}

Activation/Deactivation:

await polar.licenses.activate(licenseKey, {
  label: "User's MacBook Pro"
});

await polar.licenses.deactivate(activationId);

Auto-revoke: On subscription cancellation or refund

2. GitHub Repository Access

Auto-invite to private repos with permission management.

Create:

const benefit = await polar.benefits.create({
  type: "github_repository",
  organization_id: "org_xxx",
  description: "Access to private repo",
  properties: {
    repository_owner: "myorg",
    repository_name: "private-repo",
    permission: "pull" // or "push", "admin"
  }
});

Multiple Repos:

{
  properties: {
    repositories: [
      { owner: "myorg", name: "repo1", permission: "pull" },
      { owner: "myorg", name: "repo2", permission: "push" }
    ]
  }
}

Behavior:

  • Auto-invite on subscription activation
  • Permission managed by Polar
  • Auto-revoke on cancellation

3. Discord Access

Server invites and role assignment.

Create:

const benefit = await polar.benefits.create({
  type: "discord",
  organization_id: "org_xxx",
  description: "Premium Discord role",
  properties: {
    guild_id: "123456789",
    role_id: "987654321"
  }
});

Multiple Roles:

{
  properties: {
    guild_id: "123456789",
    roles: [
      { role_id: "role1", name: "Premium" },
      { role_id: "role2", name: "Supporter" }
    ]
  }
}

Requirements:

  • Polar Discord app must be added to server
  • Configure in Polar dashboard

Behavior:

  • Auto-invite to server
  • Assign roles automatically
  • Remove roles on cancellation

4. Downloadable Files

Secure file delivery up to 10GB each.

Create:

const benefit = await polar.benefits.create({
  type: "downloadable",
  organization_id: "org_xxx",
  description: "Premium templates",
  properties: {
    files: [
      { name: "template1.zip", size: 5000000 },
      { name: "template2.psd", size: 10000000 }
    ]
  }
});

Upload Files:

  • Via Polar dashboard
  • Secure storage
  • Access control

Customer Access:

  • Download links in customer portal
  • Secure, time-limited URLs
  • Multiple files supported

5. Meter Credits

Pre-purchased usage for usage-based billing.

Create:

const benefit = await polar.benefits.create({
  type: "custom",
  organization_id: "org_xxx",
  description: "10,000 API credits",
  properties: {
    meter_id: "meter_xxx",
    credits: 10000
  }
});

Automatic Application:

  • Credits added on subscription start
  • Balance tracked via API
  • Depletes with usage

Balance Check:

const balance = await polar.meters.getBalance({
  customer_id: "cust_xxx",
  meter_id: "meter_xxx"
});

6. Custom Benefits

Flexible placeholder for manual fulfillment.

Create:

const benefit = await polar.benefits.create({
  type: "custom",
  organization_id: "org_xxx",
  description: "Priority support via email",
  properties: {
    note: "Email support@example.com with your order ID for priority support"
  }
});

Use Cases:

  • Cal.com booking links
  • Email support access
  • Community forum access
  • Manual onboarding

Benefit Grants

Link between customer and benefit.

States

  • created - Grant created
  • active - Benefit delivered
  • revoked - Access removed

Webhooks

  • benefit_grant.created - Grant created
  • benefit_grant.updated - Status changed
  • benefit_grant.revoked - Access revoked

Auto-revoke Triggers

  • Subscription canceled
  • Subscription revoked
  • Refund processed
  • Product changed (if benefit not on new product)

Querying Grants

const grants = await polar.benefitGrants.list({
  customer_id: "cust_xxx",
  benefit_id: "benefit_xxx",
  is_granted: true
});

Attaching Benefits to Products

Via API

await polar.products.updateBenefits(productId, {
  benefits: [benefitId1, benefitId2, benefitId3]
});

Via Dashboard

  1. Navigate to product
  2. Benefits tab
  3. Select benefits to attach
  4. Save

Order

  • Benefits granted in order attached
  • Customers see in that order
  • Reorder via dashboard or API

Customer Experience

Viewing Benefits

  • Customer portal shows all active benefits
  • Clear instructions for each type
  • Download links for files
  • License keys displayed

Accessing Benefits

// Generate customer portal link
const session = await polar.customerSessions.create({
  external_customer_id: userId
});

// Customer sees:
// - Active subscriptions
// - Granted benefits
// - Download links
// - License keys
// - Instructions

Implementation Patterns

License Key Validation

// In your application
async function validateLicense(key) {
  try {
    const result = await polar.licenses.validate({
      key: key,
      organization_id: process.env.POLAR_ORG_ID
    });

    if (!result.valid) {
      return { valid: false, reason: 'Invalid license' };
    }

    if (result.limit_usage && result.usage >= result.limit_usage) {
      return { valid: false, reason: 'Usage limit exceeded' };
    }

    return { valid: true, customer: result.customer };
  } catch (error) {
    console.error('License validation failed:', error);
    return { valid: false, reason: 'Validation error' };
  }
}

GitHub Access Check

// Listen to benefit grant webhook
app.post('/webhook/polar', async (req, res) => {
  const event = validateEvent(req.body, req.headers, secret);

  if (event.type === 'benefit_grant.created') {
    const grant = event.data;

    if (grant.benefit.type === 'github_repository') {
      // Update user's GitHub access in your system
      await updateGitHubAccess(grant.customer.external_id, true);
    }
  }

  res.json({ received: true });
});

Discord Role Sync

// Monitor benefit grants
if (event.type === 'benefit_grant.created') {
  const grant = event.data;

  if (grant.benefit.type === 'discord') {
    // Notify user to connect Discord
    await sendDiscordInvite(grant.customer.email);
  }
}

if (event.type === 'benefit_grant.revoked') {
  const grant = event.data;

  if (grant.benefit.type === 'discord') {
    // Roles removed automatically by Polar
    await notifyRoleRemoval(grant.customer.external_id);
  }
}

Best Practices

  1. Benefit Selection:

    • Choose appropriate benefit types
    • Consider automation capabilities
    • Plan for revocation scenarios
  2. License Keys:

    • Set appropriate activation limits
    • Monitor usage patterns
    • Provide clear validation errors
    • Allow customers to manage activations
  3. GitHub Access:

    • Set minimum required permissions
    • Use separate repos for different tiers
    • Monitor repository access
    • Communicate access removal
  4. Discord Roles:

    • Clear role hierarchy
    • Meaningful role names
    • Separate roles per product tier
    • Welcome messages for new members
  5. Files:

    • Organize files clearly
    • Provide README/instructions
    • Keep files updated
    • Version control important files
  6. Credits:

    • Clear credit value communication
    • Usage tracking and display
    • Alerts near depletion
    • Easy credit top-up
  7. Custom Benefits:

    • Clear, actionable instructions
    • Provide contact information
    • Set expectations for timing
    • Track manual fulfillment
  8. Customer Communication:

    • Welcome email with benefit access info
    • Instructions for each benefit type
    • Support contact for issues
    • Revocation warnings before cancellation