# Plan capabilities

> Which Hatched widgets, capabilities, and quotas land on which subscription tier. Auto-generated from packages/shared/src/pricing.ts.

Source: https://docs.hatched.live/docs/reference/plan-capabilities

import { Callout } from 'fumadocs-ui/components/callout';

This page is auto-generated from `packages/shared/src/pricing.ts`. The same
constant drives the backend `PlanGuard`, the pricing page, and dashboard
upgrade banners — so every gate you see in product is encoded below.

<Callout type="info">
  Plan keys (`starter`, `growth`, `pro`, `enterprise`) map to public names
  Free, Growth, Pro, Enterprise. Backend guards always use the keys.
</Callout>

## Widget × Plan

Which `data-hatched-mount="…"` values render successfully on each plan. A
gated widget mount still loads but the API will return `403 plan_feature_locked`
(with `details.required_plan` and `details.upgrade_url`) until the customer
upgrades. If the plan covers the feature but an operator has turned it off in
Settings, the API instead returns `403 capability_disabled`.

| Widget | Free (free) | Growth ($149/mo) | Pro ($499/mo) | Enterprise (custom) | Capability flag |
| --- | --- | --- | --- | --- | --- |
| `buddy` | ✓ | ✓ | ✓ | ✓ | _always available_ |
| `marketplace` | — | ✓ | ✓ | ✓ | `marketplace` |
| `leaderboard` | ✓ | ✓ | ✓ | ✓ | _always available_ |
| `badges` | ✓ | ✓ | ✓ | ✓ | `badges` |
| `streak` | ✓ | ✓ | ✓ | ✓ | `streaks` |
| `path` | — | — | ✓ | ✓ | `paths` |
| `tokens` | ✓ | ✓ | ✓ | ✓ | `tokens` |
| `kudos` | ✓ | ✓ | ✓ | ✓ | `kudos` |
| `group-quest` | — | ✓ | ✓ | ✓ | `group_quest` |
| `feed` | ✓ | ✓ | ✓ | ✓ | `seesaw_bump` |
| `mystery-box` | — | ✓ | ✓ | ✓ | `mystery_box` |
| `league` | — | ✓ | ✓ | ✓ | `leagues` |
| `council` | — | — | — | ✓ | `council` |
| `hexad-survey` | ✓ | ✓ | ✓ | ✓ | _always available_ |

A widget marked _always available_ has no capability flag — every tier
can mount it. `leaderboard` and `feed`-style surfaces fall here because
they read data the platform always produces.

## Capability × Plan

Raw `PlanFeatures` matrix. Backend `@RequiresCapability('foo')` decorators
gate on these keys; the dashboard reads the same map to render locked-state
banners. New capabilities must be added to `PlanFeatures` in `pricing.ts`
before they can be referenced anywhere.

| Capability | Free (free) | Growth ($149/mo) | Pro ($499/mo) | Enterprise (custom) |
| --- | --- | --- | --- | --- |
| `advanced_analytics` | — | — | ✓ | ✓ |
| `badges` | ✓ | ✓ | ✓ | ✓ |
| `boosters` | — | ✓ | ✓ | ✓ |
| `cause_counter` | ✓ | ✓ | ✓ | ✓ |
| `council` | — | — | — | ✓ |
| `evolution` | — | ✓ | ✓ | ✓ |
| `evolution_generative` | — | ✓ | ✓ | ✓ |
| `founding_cohort` | — | — | ✓ | ✓ |
| `gamification_planner` | ✓ | ✓ | ✓ | ✓ |
| `generative_media` | — | ✓ | ✓ | ✓ |
| `group_quest` | — | ✓ | ✓ | ✓ |
| `hosted_surface` | — | — | ✓ | ✓ |
| `kudos` | ✓ | ✓ | ✓ | ✓ |
| `leagues` | — | ✓ | ✓ | ✓ |
| `lottery` | — | ✓ | ✓ | ✓ |
| `marketplace` | — | ✓ | ✓ | ✓ |
| `mentorship` | — | — | ✓ | ✓ |
| `multi_audience` | — | — | ✓ | ✓ |
| `mystery_box` | — | ✓ | ✓ | ✓ |
| `paths` | — | — | ✓ | ✓ |
| `profile_pages` | — | — | ✓ | ✓ |
| `profile_pages_v2` | — | ✓ | ✓ | ✓ |
| `seesaw_bump` | ✓ | ✓ | ✓ | ✓ |
| `showroom` | — | — | ✓ | ✓ |
| `streaks` | ✓ | ✓ | ✓ | ✓ |
| `teams` | ✓ | ✓ | ✓ | ✓ |
| `tokens` | ✓ | ✓ | ✓ | ✓ |
| `wardrobe` | ✓ | ✓ | ✓ | ✓ |

## Quotas + support

| Plan | Events / month | Audiences | Welcome credits | Monthly credit grant | Support |
| --- | --- | --- | --- | --- | --- |
| Free (free) | 10,000 | 1 | 20 | 0 | community |
| Growth ($149/mo) | 500,000 | 1 | 0 | 50 | standard |
| Pro ($499/mo) | 5,000,000 | 3 | 0 | 250 | priority |
| Enterprise (custom) | Unlimited | Unlimited | 0 | Unlimited | sla |

Event quota counts inbound `POST /events` calls. Once a tenant hits the
monthly cap the API returns `402 event_quota_exceeded` carrying the reset
window (`details.reset_at`, the first of the next UTC month) and an upgrade
link. It is **not** a `429` — do not retry with backoff; back off until the
reset or upgrade the plan. SDK consumers catch this as
`EventQuotaExceededError` — see [Error handling](/docs/guides/best-practices#error-handling).

## When you change pricing

1. Edit `packages/shared/src/pricing.ts` — `PLAN_MATRIX`.
2. Run `pnpm --filter @hatched/docs generate:plan-matrix` to regenerate this page.
3. Commit both files in the same PR. `generate-plan-matrix.ts --check` is part
   of CI so drift fails the build.
