# Create Benefit Source: https://polar.sh/docs/api-reference/benefits/create post /v1/benefits/ Create a benefit. **Scopes**: `benefits:write` # Delete Benefit Source: https://polar.sh/docs/api-reference/benefits/delete delete /v1/benefits/{id} Delete a benefit. > [!WARNING] > Every grants associated with the benefit will be revoked. > Users will lose access to the benefit. **Scopes**: `benefits:write` # Get Benefit Source: https://polar.sh/docs/api-reference/benefits/get get /v1/benefits/{id} Get a benefit by ID. **Scopes**: `benefits:read` `benefits:write` # List Benefits Source: https://polar.sh/docs/api-reference/benefits/list get /v1/benefits/ List benefits. **Scopes**: `benefits:read` `benefits:write` # List Benefit Grants Source: https://polar.sh/docs/api-reference/benefits/list-grants get /v1/benefits/{id}/grants List the individual grants for a benefit. It's especially useful to check if a user has been granted a benefit. **Scopes**: `benefits:read` `benefits:write` # Update Benefit Source: https://polar.sh/docs/api-reference/benefits/update patch /v1/benefits/{id} Update a benefit. **Scopes**: `benefits:write` # Create Checkout Link Source: https://polar.sh/docs/api-reference/checkout-links/create post /v1/checkout-links/ Create a checkout link. **Scopes**: `checkout_links:write` Looking to create a single use checkout session? Checkout Links are probably **not** what you're looking for. Checkout Links are shareable links that generate checkout sessions when opened. They are very handy to start a purchase from your website or social media. However, if you want to start a checkout for one of your user **inside** your product, you should use the [Checkout Sessions API](/docs/api-reference/checkouts/create-session). # Delete Checkout Link Source: https://polar.sh/docs/api-reference/checkout-links/delete delete /v1/checkout-links/{id} Delete a checkout link. **Scopes**: `checkout_links:write` # Get Checkout Link Source: https://polar.sh/docs/api-reference/checkout-links/get get /v1/checkout-links/{id} Get a checkout link by ID. **Scopes**: `checkout_links:read` `checkout_links:write` # List Checkout Links Source: https://polar.sh/docs/api-reference/checkout-links/list get /v1/checkout-links/ List checkout links. **Scopes**: `checkout_links:read` `checkout_links:write` # Update Checkout Link Source: https://polar.sh/docs/api-reference/checkout-links/update patch /v1/checkout-links/{id} Update a checkout link. **Scopes**: `checkout_links:write` # Confirm Checkout Session from Client Source: https://polar.sh/docs/api-reference/checkouts/confirm-session-from-client post /v1/checkouts/client/{client_secret}/confirm Confirm a checkout session by client secret. Orders and subscriptions will be processed. # Create Checkout Session Source: https://polar.sh/docs/api-reference/checkouts/create-session post /v1/checkouts/ Create a checkout session. **Scopes**: `checkouts:write` # Get Checkout Session Source: https://polar.sh/docs/api-reference/checkouts/get-session get /v1/checkouts/{id} Get a checkout session by ID. **Scopes**: `checkouts:read` `checkouts:write` # Get Checkout Session from Client Source: https://polar.sh/docs/api-reference/checkouts/get-session-from-client get /v1/checkouts/client/{client_secret} Get a checkout session by client secret. # List Checkout Sessions Source: https://polar.sh/docs/api-reference/checkouts/list-sessions get /v1/checkouts/ List checkout sessions. **Scopes**: `checkouts:read` `checkouts:write` # Update Checkout Session Source: https://polar.sh/docs/api-reference/checkouts/update-session patch /v1/checkouts/{id} Update a checkout session. **Scopes**: `checkouts:write` # Update Checkout Session from Client Source: https://polar.sh/docs/api-reference/checkouts/update-session-from-client patch /v1/checkouts/client/{client_secret} Update a checkout session by client secret. # Create Custom Field Source: https://polar.sh/docs/api-reference/custom-fields/create post /v1/custom-fields/ Create a custom field. **Scopes**: `custom_fields:write` # Delete Custom Field Source: https://polar.sh/docs/api-reference/custom-fields/delete delete /v1/custom-fields/{id} Delete a custom field. **Scopes**: `custom_fields:write` # Get Custom Field Source: https://polar.sh/docs/api-reference/custom-fields/get get /v1/custom-fields/{id} Get a custom field by ID. **Scopes**: `custom_fields:read` `custom_fields:write` # List Custom Fields Source: https://polar.sh/docs/api-reference/custom-fields/list get /v1/custom-fields/ List custom fields. **Scopes**: `custom_fields:read` `custom_fields:write` # Update Custom Field Source: https://polar.sh/docs/api-reference/custom-fields/update patch /v1/custom-fields/{id} Update a custom field. **Scopes**: `custom_fields:write` # Get Customer Meter Source: https://polar.sh/docs/api-reference/customer-meters/get get /v1/customer-meters/{id} Get a customer meter by ID. **Scopes**: `customer_meters:read` # List Customer Meters Source: https://polar.sh/docs/api-reference/customer-meters/list get /v1/customer-meters/ List customer meters. **Scopes**: `customer_meters:read` # Get Source: https://polar.sh/docs/api-reference/customer-portal/downloadables/get get /v1/customer-portal/downloadables/{token} # List Downloadables Source: https://polar.sh/docs/api-reference/customer-portal/downloadables/list get /v1/customer-portal/downloadables/ **Scopes**: `customer_portal:read` `customer_portal:write` # Get Customer Source: https://polar.sh/docs/api-reference/customer-portal/get-customer get /v1/customer-portal/customers/me Get authenticated customer. **Scopes**: `customer_portal:read` `customer_portal:write` # Get Organization Source: https://polar.sh/docs/api-reference/customer-portal/get-organization get /v1/customer-portal/organizations/{slug} Get a customer portal's organization by slug. # Activate License Key Source: https://polar.sh/docs/api-reference/customer-portal/license-keys/activate post /v1/customer-portal/license-keys/activate Activate a license key instance. > This endpoint doesn't require authentication and can be safely used on a public > client, like a desktop application or a mobile app. > If you plan to validate a license key on a server, use the `/v1/license-keys/activate` > endpoint instead. You only need to use this endpoint if you have device **activations** enabled on the license key benefit. You then use this endpoint to reserve an allocation for a specific device. Storing the unique activation ID from the response on the device and using it as extra validation in the [/validate](/docs/api-reference/customer-portal/license-keys/validate) endpoint. Not using **activations**? Just use the [/validate](/docs/api-reference/customer-portal/license-keys/validate) endpoint directly instead. # Deactivate License Key Source: https://polar.sh/docs/api-reference/customer-portal/license-keys/deactivate post /v1/customer-portal/license-keys/deactivate Deactivate a license key instance. > This endpoint doesn't require authentication and can be safely used on a public > client, like a desktop application or a mobile app. > If you plan to validate a license key on a server, use the `/v1/license-keys/deactivate` > endpoint instead. # Get License Key Source: https://polar.sh/docs/api-reference/customer-portal/license-keys/get get /v1/customer-portal/license-keys/{id} Get a license key. **Scopes**: `customer_portal:read` `customer_portal:write` # List License Keys Source: https://polar.sh/docs/api-reference/customer-portal/license-keys/list get /v1/customer-portal/license-keys/ **Scopes**: `customer_portal:read` `customer_portal:write` # Validate License Key Source: https://polar.sh/docs/api-reference/customer-portal/license-keys/validate post /v1/customer-portal/license-keys/validate Validate a license key. > This endpoint doesn't require authentication and can be safely used on a public > client, like a desktop application or a mobile app. > If you plan to validate a license key on a server, use the `/v1/license-keys/validate` > endpoint instead. # Get Order Source: https://polar.sh/docs/api-reference/customer-portal/orders/get get /v1/customer-portal/orders/{id} Get an order by ID for the authenticated customer. # Get Order Invoice Source: https://polar.sh/docs/api-reference/customer-portal/orders/get-invoice get /v1/customer-portal/orders/{id}/invoice Get an order's invoice data. The invoice must be generated first before it can be retrieved. You should call the [`POST /v1/customer-portal/orders/{id}/invoice`](/docs/api-reference/customer-portal/orders/post-invoice) endpoint to generate the invoice. If the invoice is not generated, you will receive a `404` error. # Get Order Receipt Source: https://polar.sh/docs/api-reference/customer-portal/orders/get-receipt get /v1/customer-portal/orders/{id}/receipt Get a presigned URL to download an order's receipt PDF. # List Orders Source: https://polar.sh/docs/api-reference/customer-portal/orders/list get /v1/customer-portal/orders/ List orders of the authenticated customer. # Update Order Source: https://polar.sh/docs/api-reference/customer-portal/orders/patch patch /v1/customer-portal/orders/{id} Update an order for the authenticated customer. # Generate Order Invoice Source: https://polar.sh/docs/api-reference/customer-portal/orders/post-invoice post /v1/customer-portal/orders/{id}/invoice Trigger generation of an order's invoice. Once the invoice is generated, it's permanent and cannot be modified. Make sure the billing details (name and address) are correct before generating the invoice. You can update them before generating the invoice by calling the [`PATCH /v1/customer-portal/orders/{id}`](/docs/api-reference/customer-portal/orders/patch) endpoint. After successfully calling this endpoint, you get a `202` response, meaning the generation of the invoice has been scheduled. It usually only takes a few seconds before you can retrieve the invoice using the [`GET /v1/customer-portal/orders/{id}/invoice`](/docs/api-reference/customer-portal/orders/get-invoice) endpoint. If you want a reliable notification when the invoice is ready, you can listen to the [`order.updated`](/docs/api-reference/webhooks/order.updated) webhook and check the [`is_invoice_generated` field](/docs/api-reference/webhooks/order.updated#schema-data-is-invoice-generated). # Delete Customer Payment Method Source: https://polar.sh/docs/api-reference/customer-portal/payment-methods/delete delete /v1/customer-portal/customers/me/payment-methods/{id} Delete a payment method from the authenticated customer. # List Customer Payment Methods Source: https://polar.sh/docs/api-reference/customer-portal/payment-methods/list get /v1/customer-portal/customers/me/payment-methods Get saved payment methods of the authenticated customer. To change the default payment method, call the [`PATCH /v1/customer-portal/customers/me`](/docs/api-reference/customer-portal/update-customer) endpoint with the desired `default_payment_method_id`. # Assign Seat Source: https://polar.sh/docs/api-reference/customer-portal/seats/assign post /v1/customer-portal/seats # List Seats Source: https://polar.sh/docs/api-reference/customer-portal/seats/list get /v1/customer-portal/seats **Scopes**: `customer_portal:read` `customer_portal:write` # List Claimed Subscriptions Source: https://polar.sh/docs/api-reference/customer-portal/seats/list-subscriptions get /v1/customer-portal/seats/subscriptions List all subscriptions where the authenticated customer has claimed a seat. **Scopes**: `customer_portal:read` `customer_portal:write` # Resend Invitation Source: https://polar.sh/docs/api-reference/customer-portal/seats/resend post /v1/customer-portal/seats/{seat_id}/resend # Revoke Seat Source: https://polar.sh/docs/api-reference/customer-portal/seats/revoke delete /v1/customer-portal/seats/{seat_id} # Create Customer Session Source: https://polar.sh/docs/api-reference/customer-portal/sessions/create post /v1/customer-sessions/ Create a customer session. For organizations with `member_model_enabled`, this will automatically create a member session for the owner member of the customer. **Scopes**: `customer_sessions:write` # Cancel Subscription Source: https://polar.sh/docs/api-reference/customer-portal/subscriptions/cancel delete /v1/customer-portal/subscriptions/{id} Cancel a subscription of the authenticated customer. # Get Subscription Source: https://polar.sh/docs/api-reference/customer-portal/subscriptions/get get /v1/customer-portal/subscriptions/{id} Get a subscription for the authenticated customer. **Scopes**: `customer_portal:read` `customer_portal:write` # List Subscriptions Source: https://polar.sh/docs/api-reference/customer-portal/subscriptions/list get /v1/customer-portal/subscriptions/ List subscriptions of the authenticated customer. **Scopes**: `customer_portal:read` `customer_portal:write` # Update Subscription Source: https://polar.sh/docs/api-reference/customer-portal/subscriptions/update patch /v1/customer-portal/subscriptions/{id} Update a subscription of the authenticated customer. # Update Customer Source: https://polar.sh/docs/api-reference/customer-portal/update-customer patch /v1/customer-portal/customers/me Update authenticated customer. # Assign Seat Source: https://polar.sh/docs/api-reference/customer-seats/assign post /v1/customer-seats **Scopes**: `customer_seats:write` # Claim Seat Source: https://polar.sh/docs/api-reference/customer-seats/claim post /v1/customer-seats/claim # Get Claim Info Source: https://polar.sh/docs/api-reference/customer-seats/get-claim-info get /v1/customer-seats/claim/{invitation_token} # List Seats Source: https://polar.sh/docs/api-reference/customer-seats/list get /v1/customer-seats **Scopes**: `customer_seats:write` # Resend Invitation Source: https://polar.sh/docs/api-reference/customer-seats/resend post /v1/customer-seats/{seat_id}/resend **Scopes**: `customer_seats:write` # Revoke Seat Source: https://polar.sh/docs/api-reference/customer-seats/revoke delete /v1/customer-seats/{seat_id} **Scopes**: `customer_seats:write` # Create Customer Source: https://polar.sh/docs/api-reference/customers/create post /v1/customers/ Create a customer. **Scopes**: `customers:write` # Delete Customer Source: https://polar.sh/docs/api-reference/customers/delete delete /v1/customers/{id} Delete a customer. This action cannot be undone and will immediately: - Cancel any active subscriptions for the customer - Revoke all their benefits - Clear any `external_id` Use it only in the context of deleting a user within your own service. Otherwise, use more granular API endpoints to cancel a specific subscription or revoke certain benefits. Note: The customers information will nonetheless be retained for historic orders and subscriptions. Set `anonymize=true` to also anonymize PII for GDPR compliance. **Scopes**: `customers:write` # Delete Customer by External ID Source: https://polar.sh/docs/api-reference/customers/delete-external delete /v1/customers/external/{external_id} Delete a customer by external ID. Immediately cancels any active subscriptions and revokes any active benefits. Set `anonymize=true` to also anonymize PII for GDPR compliance. **Scopes**: `customers:write` # Get Customer Source: https://polar.sh/docs/api-reference/customers/get get /v1/customers/{id} Get a customer by ID. **Scopes**: `customers:read` `customers:write` # Get Customer by External ID Source: https://polar.sh/docs/api-reference/customers/get-external get /v1/customers/external/{external_id} Get a customer by external ID. **Scopes**: `customers:read` `customers:write` # List Customers Source: https://polar.sh/docs/api-reference/customers/list get /v1/customers/ List customers. **Scopes**: `customers:read` `customers:write` # List Customer Payment Methods Source: https://polar.sh/docs/api-reference/customers/list-payment-methods get /v1/customers/{id}/payment-methods Get saved payment methods of a customer. **Scopes**: `customers:read` `customers:write` # List Customer Payment Methods by External ID Source: https://polar.sh/docs/api-reference/customers/list-payment-methods-external get /v1/customers/external/{external_id}/payment-methods Get saved payment methods of a customer by external ID. **Scopes**: `customers:read` `customers:write` # Get Customer State Source: https://polar.sh/docs/api-reference/customers/state get /v1/customers/{id}/state Get a customer state by ID. The customer state includes information about the customer's active subscriptions and benefits. It's the ideal endpoint to use when you need to get a full overview of a customer's status. **Scopes**: `customers:read` `customers:write` # Get Customer State by External ID Source: https://polar.sh/docs/api-reference/customers/state-external get /v1/customers/external/{external_id}/state Get a customer state by external ID. The customer state includes information about the customer's active subscriptions and benefits. It's the ideal endpoint to use when you need to get a full overview of a customer's status. **Scopes**: `customers:read` `customers:write` # Update Customer Source: https://polar.sh/docs/api-reference/customers/update patch /v1/customers/{id} Update a customer. **Scopes**: `customers:write` # Update Customer by External ID Source: https://polar.sh/docs/api-reference/customers/update-external patch /v1/customers/external/{external_id} Update a customer by external ID. **Scopes**: `customers:write` # Create Discount Source: https://polar.sh/docs/api-reference/discounts/create post /v1/discounts/ Create a discount. **Scopes**: `discounts:write` # Delete Discount Source: https://polar.sh/docs/api-reference/discounts/delete delete /v1/discounts/{id} Delete a discount. **Scopes**: `discounts:write` # Get Discount Source: https://polar.sh/docs/api-reference/discounts/get get /v1/discounts/{id} Get a discount by ID. **Scopes**: `discounts:read` `discounts:write` # List Discounts Source: https://polar.sh/docs/api-reference/discounts/list get /v1/discounts/ List discounts. **Scopes**: `discounts:read` `discounts:write` # Update Discount Source: https://polar.sh/docs/api-reference/discounts/update patch /v1/discounts/{id} Update a discount. **Scopes**: `discounts:write` # Get Event Source: https://polar.sh/docs/api-reference/events/get get /v1/events/{id} Get an event by ID. **Scopes**: `events:read` `events:write` # Ingest Events Source: https://polar.sh/docs/api-reference/events/ingest post /v1/events/ingest Ingest batch of events. **Scopes**: `events:write` # List Events Source: https://polar.sh/docs/api-reference/events/list get /v1/events/ List events. **Scopes**: `events:read` `events:write` # Complete File Upload Source: https://polar.sh/docs/api-reference/files/complete-upload post /v1/files/{id}/uploaded Complete a file upload. **Scopes**: `files:write` # Create File Source: https://polar.sh/docs/api-reference/files/create post /v1/files/ Create a file. **Scopes**: `files:write` # Delete File Source: https://polar.sh/docs/api-reference/files/delete delete /v1/files/{id} Delete a file. **Scopes**: `files:write` # List Files Source: https://polar.sh/docs/api-reference/files/list get /v1/files/ List files. **Scopes**: `files:read` `files:write` # Update File Source: https://polar.sh/docs/api-reference/files/update patch /v1/files/{id} Update a file. **Scopes**: `files:write` # API Overview Source: https://polar.sh/docs/api-reference/introduction Base URLs, authentication, pagination, rate limits, and the difference between the Core API and the Customer Portal API `https://api.polar.sh/v1` `https://sandbox-api.polar.sh/v1` Use an **Organization Access Token (OAT)** in the `Authorization: Bearer` header Use a **Customer Access Token** created via `/v1/customer-sessions/` ## Base URLs | Environment | Base URL | Purpose | | ----------- | --------------------------------- | ------------------------------- | | Production | `https://api.polar.sh/v1` | Real customers & live payments | | Sandbox | `https://sandbox-api.polar.sh/v1` | Safe testing & integration work | The sandbox environment is fully isolatedโ€”data, users, tokens, and organizations created there do not affect production. Create separate tokens in each environment. Read more: [Sandbox Environment](/docs/integrate/sandbox) ## Authentication ### Organization Access Tokens (OAT) Use an **OAT** to act on behalf of your organization (manage products, prices, checkouts, orders, subscriptions, benefits, etc.). ```http theme={null} Authorization: Bearer polar_oat_xxxxxxxxxxxxxxxxx ``` Create OATs in your organization settings. See: [Organization Access Tokens](/docs/integrate/oat) Never expose an OAT in client-side code, public repos, or logs. If leaked, it will be revoked automatically by our secret scanning integrations. ### Customer Access Tokens Do **not** use OATs in the browser. For customer-facing flows, [generate a **Customer Session**](/docs/api-reference/customer-portal/sessions/create) server-side, then use the returned **customer access token** with the **Customer Portal API** to let a signed-in customer view their own orders, subscriptions, and benefits. ## Core API vs Customer Portal API | Aspect | Core API | Customer Portal API | | -------------------- | ------------------------------------------------------------------------ | ---------------------------------------------- | | Audience | Your server / backend | One of your customer | | Auth Type | Organization Access Token (OAT) | Customer Access Token | | Scope | Full org resources (products, orders, subscriptions, benefits, checkout) | Only the authenticated customerโ€™s data | | Typical Use | Admin dashboards, internal tools, automation, provisioning | Building a custom customer portal or gated app | | Token Creation | Via dashboard (manual) | Via `/v1/customer-sessions/` (server-side) | | Sensitive Operations | Yes (create/update products, issue refunds, etc.) | No (read/update only what the customer owns) | The Customer Portal API is a *restricted* surface designed for safe exposure in user-facing contexts (after exchanging a session). It cannot perform privileged org-level mutations like creating products or issuing refunds. ## Quick Examples ```bash curl (Production - Core API) theme={null} curl https://api.polar.sh/v1/products/ \ -H "Authorization: Bearer $POLAR_OAT" \ -H "Accept: application/json" ``` ```bash curl (Sandbox - Core API) theme={null} curl https://sandbox-api.polar.sh/v1/products/ \ -H "Authorization: Bearer $POLAR_OAT_SANDBOX" \ -H "Accept: application/json" ``` ```bash curl (Customer Portal API) theme={null} curl https://api.polar.sh/v1/customer-portal/orders/ \ -H "Authorization: Bearer $POLAR_CUSTOMER_TOKEN" \ -H "Accept: application/json" ``` ## Using SDKs All official SDKs accept a `server` parameter for sandbox usage: ```ts TypeScript theme={null} import { Polar } from "@polar-sh/sdk"; const polar = new Polar({ accessToken: process.env.POLAR_ACCESS_TOKEN!, server: "sandbox", // omit or use 'production' for live }); ``` ```py Python theme={null} from polar import Polar client = Polar( access_token=os.environ["POLAR_ACCESS_TOKEN"], server="sandbox", ) ``` ```go Go theme={null} s := polargo.New( polargo.WithServer("sandbox"), polargo.WithSecurity(os.Getenv("POLAR_ACCESS_TOKEN")), ) ``` ```php PHP theme={null} $sdk = Polar\Polar::builder() ->setServer('sandbox') ->setSecurity(getenv('POLAR_ACCESS_TOKEN')) ->build(); ``` ## Pagination List endpoints in the Polar API support pagination to help you efficiently retrieve large datasets. Use the `page` and `limit` query parameters to control pagination. ### Query Parameters | Parameter | Type | Default | Max | Description | | --------- | ------- | ------- | ----- | ------------------------------------------------ | | `page` | integer | `1` | - | Page number, starting from 1 | | `limit` | integer | `10` | `100` | Number of items to return per page (window size) | The `page` parameter works as a window offset. For example, `page=2&limit=10` means the API will skip the first 10 elements and return the next 10. ### Response Format All paginated responses include a `pagination` object with metadata about the current page and total results: | Field | Type | Description | | ------------- | ------- | ---------------------------------------------------------------- | | `total_count` | integer | Total number of items matching your query across all pages | | `max_page` | integer | Total number of pages available, given the current `limit` value | ### Example Let's say you want to fetch products with a limit of 100 items per page: ```bash Request theme={null} curl https://api.polar.sh/v1/products/?page=1&limit=100 \ -H "Authorization: Bearer $POLAR_OAT" \ -H "Accept: application/json" ``` ```json Response theme={null} { "items": [ { "id": "...", "name": "Product 1", ... }, ... ], "pagination": { "total_count": 250, "max_page": 3 } } ``` In this example: * `total_count=250` indicates there are 250 total products * `limit=100` means each page contains up to 100 products * `max_page=3` means you need to make 3 requests to retrieve all products (pages 1, 2, and 3) To retrieve all pages, increment the `page` parameter from `1` to `max_page`. Our SDKs provide built-in pagination helpers to automatically iterate through all pages. ## Rate Limits Polar API has rate limits to ensure fair usage and maintain performance. Limits differ between the **Sandbox** and **Production** environments. ### Production * **500 requests per minute** per organization/customer or OAuth2 Client. ### Sandbox * **100 requests per minute** per organization/customer or OAuth2 Client. Unauthenticated [validation](/docs/api-reference/customer-portal/license-keys/validate), [activation](/docs/api-reference/customer-portal/license-keys/activate), and [deactivation](/docs/api-reference/customer-portal/license-keys/deactivate) endpoints are limited to **3 requests per second** in both environments. If you exceed the rate limit, you will receive a `429 Too Many Requests` response. The response will include a `Retry-After` header indicating how long you should wait before making another request. Organizations requiring higher rate limits for production workloads may contact our support team to discuss elevated limits. # Activate License Key Source: https://polar.sh/docs/api-reference/license-keys/activate post /v1/license-keys/activate Activate a license key instance. **Scopes**: `license_keys:write` # Deactivate License Key Source: https://polar.sh/docs/api-reference/license-keys/deactivate post /v1/license-keys/deactivate Deactivate a license key instance. **Scopes**: `license_keys:write` # Get License Key Source: https://polar.sh/docs/api-reference/license-keys/get get /v1/license-keys/{id} Get a license key. **Scopes**: `license_keys:read` `license_keys:write` # Get Activation Source: https://polar.sh/docs/api-reference/license-keys/get-activation get /v1/license-keys/{id}/activations/{activation_id} Get a license key activation. **Scopes**: `license_keys:read` `license_keys:write` # List License Keys Source: https://polar.sh/docs/api-reference/license-keys/list get /v1/license-keys/ Get license keys connected to the given organization & filters. **Scopes**: `license_keys:read` `license_keys:write` # Update License Key Source: https://polar.sh/docs/api-reference/license-keys/update patch /v1/license-keys/{id} Update a license key. **Scopes**: `license_keys:write` # Validate License Key Source: https://polar.sh/docs/api-reference/license-keys/validate post /v1/license-keys/validate Validate a license key. **Scopes**: `license_keys:write` # Create Member Source: https://polar.sh/docs/api-reference/members/create post /v1/members/ Create a new member for a customer. Only B2B customers with the member management feature enabled can add members. The authenticated user or organization must have access to the customer's organization. **Scopes**: `members:write` # Delete Member Source: https://polar.sh/docs/api-reference/members/delete delete /v1/members/{id} Delete a member. The authenticated user or organization must have access to the member's organization. **Scopes**: `members:write` # Delete Member by External ID Source: https://polar.sh/docs/api-reference/members/delete-external delete /v1/members/external/{external_id} Delete a member by external ID. One of customer_id or external_customer_id must be specified. **Scopes**: `members:write` # Get Member Source: https://polar.sh/docs/api-reference/members/get get /v1/members/{id} Get a member by ID. The authenticated user or organization must have access to the member's organization. **Scopes**: `members:read` `members:write` # Get Member by External ID Source: https://polar.sh/docs/api-reference/members/get-external get /v1/members/external/{external_id} Get a member by external ID. One of customer_id or external_customer_id must be specified. **Scopes**: `members:read` `members:write` # List Members Source: https://polar.sh/docs/api-reference/members/list get /v1/members/ List members with optional customer ID filter. **Scopes**: `members:read` `members:write` # Update Member Source: https://polar.sh/docs/api-reference/members/update patch /v1/members/{id} Update a member. Only name and role can be updated. The authenticated user or organization must have access to the member's organization. **Scopes**: `members:write` # Update Member by External ID Source: https://polar.sh/docs/api-reference/members/update-external patch /v1/members/external/{external_id} Update a member by external ID. One of customer_id or external_customer_id must be specified. **Scopes**: `members:write` # Create Meter Source: https://polar.sh/docs/api-reference/meters/create post /v1/meters/ Create a meter. **Scopes**: `meters:write` # Get Meter Source: https://polar.sh/docs/api-reference/meters/get get /v1/meters/{id} Get a meter by ID. **Scopes**: `meters:read` `meters:write` # Get Meter Quantities Source: https://polar.sh/docs/api-reference/meters/get-quantities get /v1/meters/{id}/quantities Get quantities of a meter over a time period. **Scopes**: `meters:read` `meters:write` # List Meters Source: https://polar.sh/docs/api-reference/meters/list get /v1/meters/ List meters. **Scopes**: `meters:read` `meters:write` # Update Meter Source: https://polar.sh/docs/api-reference/meters/update patch /v1/meters/{id} Update a meter. **Scopes**: `meters:write` # Get Metrics Source: https://polar.sh/docs/api-reference/metrics/get get /v1/metrics/ Get metrics about your orders and subscriptions. Currency values are output in cents. **Scopes**: `metrics:read` # Get Metrics Limits Source: https://polar.sh/docs/api-reference/metrics/get-limits get /v1/metrics/limits Get the interval limits for the metrics endpoint. **Scopes**: `metrics:read` # Authorize Source: https://polar.sh/docs/api-reference/oauth2/connect/authorize get /v1/oauth2/authorize # Get User Info Source: https://polar.sh/docs/api-reference/oauth2/connect/get-user-info get /v1/oauth2/userinfo Get information about the authenticated user. # Introspect Token Source: https://polar.sh/docs/api-reference/oauth2/connect/introspect-token post /v1/oauth2/introspect Get information about an access token. # Request Token Source: https://polar.sh/docs/api-reference/oauth2/connect/request-token post /v1/oauth2/token Request an access token using a valid grant. # Revoke Token Source: https://polar.sh/docs/api-reference/oauth2/connect/revoke-token post /v1/oauth2/revoke Revoke an access token or a refresh token. # Get Order Source: https://polar.sh/docs/api-reference/orders/get get /v1/orders/{id} Get an order by ID. **Scopes**: `orders:read` # Get Order Invoice Source: https://polar.sh/docs/api-reference/orders/get-invoice get /v1/orders/{id}/invoice Get an order's invoice data. **Scopes**: `orders:read` The invoice must be generated first before it can be retrieved. You should call the [`POST /v1/orders/{id}/invoice`](/docs/api-reference/orders/post-invoice) endpoint to generate the invoice. If the invoice is not generated, you will receive a `404` error. # Get Order Receipt Source: https://polar.sh/docs/api-reference/orders/get-receipt get /v1/orders/{id}/receipt Get a presigned URL to download an order's receipt PDF. **Scopes**: `orders:read` # List Orders Source: https://polar.sh/docs/api-reference/orders/list get /v1/orders/ List orders. **Scopes**: `orders:read` # Update Order Source: https://polar.sh/docs/api-reference/orders/patch patch /v1/orders/{id} Update an order. **Scopes**: `orders:write` # Generate Order Invoice Source: https://polar.sh/docs/api-reference/orders/post-invoice post /v1/orders/{id}/invoice Trigger generation of an order's invoice. **Scopes**: `orders:read` Once the invoice is generated, it's permanent and cannot be modified. Make sure the billing details (name and address) are correct before generating the invoice. You can update them before generating the invoice by calling the [`PATCH /v1/orders/{id}`](/docs/api-reference/orders/patch) endpoint. After successfully calling this endpoint, you get a `202` response, meaning the generation of the invoice has been scheduled. It usually only takes a few seconds before you can retrieve the invoice using the [`GET /v1/orders/{id} /invoice`](/docs/api-reference/orders/get-invoice) endpoint. If you want a reliable notification when the invoice is ready, you can listen to the [`order.updated`](/docs/api-reference/webhooks/order.updated) webhook and check the [`is_invoice_generated` field](/docs/api-reference/webhooks/order.updated#schema-data-is-invoice-generated). # Create Organization Source: https://polar.sh/docs/api-reference/organizations/create post /v1/organizations/ Create an organization. **Scopes**: `organizations:write` # Get Organization Source: https://polar.sh/docs/api-reference/organizations/get GET /v1/organizations/{id} Get an organization by ID. **Scopes**: `organizations:read` `organizations:write` # List Organizations Source: https://polar.sh/docs/api-reference/organizations/list get /v1/organizations/ List organizations. **Scopes**: `organizations:read` `organizations:write` # Update Organization Source: https://polar.sh/docs/api-reference/organizations/update patch /v1/organizations/{id} Update an organization. **Scopes**: `organizations:write` # Create Product Source: https://polar.sh/docs/api-reference/products/create post /v1/products/ Create a product. **Scopes**: `products:write` # Get Product Source: https://polar.sh/docs/api-reference/products/get get /v1/products/{id} Get a product by ID. **Scopes**: `products:read` `products:write` # List Products Source: https://polar.sh/docs/api-reference/products/list get /v1/products/ List products. **Scopes**: `products:read` `products:write` # Update Product Source: https://polar.sh/docs/api-reference/products/update patch /v1/products/{id} Update a product. **Scopes**: `products:write` # Update Product Benefits Source: https://polar.sh/docs/api-reference/products/update-benefits post /v1/products/{id}/benefits Update benefits granted by a product. **Scopes**: `products:write` # Create Refund Source: https://polar.sh/docs/api-reference/refunds/create post /v1/refunds/ Create a refund. **Scopes**: `refunds:write` # List Refunds Source: https://polar.sh/docs/api-reference/refunds/list get /v1/refunds/ List refunds. **Scopes**: `refunds:read` `refunds:write` # Create Subscription Source: https://polar.sh/docs/api-reference/subscriptions/create post /v1/subscriptions/ Create a subscription programmatically. This endpoint only allows to create subscription on free products. For paid products, use the checkout flow. No initial order will be created and no confirmation email will be sent. **Scopes**: `subscriptions:write` # Get Subscription Source: https://polar.sh/docs/api-reference/subscriptions/get get /v1/subscriptions/{id} Get a subscription by ID. **Scopes**: `subscriptions:read` `subscriptions:write` # List Subscriptions Source: https://polar.sh/docs/api-reference/subscriptions/list get /v1/subscriptions/ List subscriptions. **Scopes**: `subscriptions:read` `subscriptions:write` # Revoke Subscription Source: https://polar.sh/docs/api-reference/subscriptions/revoke delete /v1/subscriptions/{id} Revoke a subscription, i.e cancel immediately. **Scopes**: `subscriptions:write` # Update Subscription Source: https://polar.sh/docs/api-reference/subscriptions/update patch /v1/subscriptions/{id} Update a subscription. **Scopes**: `subscriptions:write` # benefit.created Source: https://polar.sh/docs/api-reference/webhooks/benefit.created # benefit.updated Source: https://polar.sh/docs/api-reference/webhooks/benefit.updated # benefit_grant.created Source: https://polar.sh/docs/api-reference/webhooks/benefit_grant.created # benefit_grant.cycled Source: https://polar.sh/docs/api-reference/webhooks/benefit_grant.cycled # benefit_grant.revoked Source: https://polar.sh/docs/api-reference/webhooks/benefit_grant.revoked # benefit_grant.updated Source: https://polar.sh/docs/api-reference/webhooks/benefit_grant.updated # checkout.created Source: https://polar.sh/docs/api-reference/webhooks/checkout.created # checkout.updated Source: https://polar.sh/docs/api-reference/webhooks/checkout.updated # customer.created Source: https://polar.sh/docs/api-reference/webhooks/customer.created # customer.deleted Source: https://polar.sh/docs/api-reference/webhooks/customer.deleted # customer.state_changed Source: https://polar.sh/docs/api-reference/webhooks/customer.state_changed # customer.updated Source: https://polar.sh/docs/api-reference/webhooks/customer.updated # customer_seat.assigned Source: https://polar.sh/docs/api-reference/webhooks/customer_seat.assigned # customer_seat.claimed Source: https://polar.sh/docs/api-reference/webhooks/customer_seat.claimed # customer_seat.revoked Source: https://polar.sh/docs/api-reference/webhooks/customer_seat.revoked # Create Webhook Endpoint Source: https://polar.sh/docs/api-reference/webhooks/endpoints/create post /v1/webhooks/endpoints Create a webhook endpoint. **Scopes**: `webhooks:write` # Delete Webhook Endpoint Source: https://polar.sh/docs/api-reference/webhooks/endpoints/delete delete /v1/webhooks/endpoints/{id} Delete a webhook endpoint. **Scopes**: `webhooks:write` # Get Webhook Endpoint Source: https://polar.sh/docs/api-reference/webhooks/endpoints/get get /v1/webhooks/endpoints/{id} Get a webhook endpoint by ID. **Scopes**: `webhooks:read` `webhooks:write` # List Webhook Endpoints Source: https://polar.sh/docs/api-reference/webhooks/endpoints/list get /v1/webhooks/endpoints List webhook endpoints. **Scopes**: `webhooks:read` `webhooks:write` # Update Webhook Endpoint Source: https://polar.sh/docs/api-reference/webhooks/endpoints/update patch /v1/webhooks/endpoints/{id} Update a webhook endpoint. **Scopes**: `webhooks:write` # order.created Source: https://polar.sh/docs/api-reference/webhooks/order.created # order.paid Source: https://polar.sh/docs/api-reference/webhooks/order.paid # order.refunded Source: https://polar.sh/docs/api-reference/webhooks/order.refunded # order.updated Source: https://polar.sh/docs/api-reference/webhooks/order.updated # organization.updated Source: https://polar.sh/docs/api-reference/webhooks/organization.updated # product.created Source: https://polar.sh/docs/api-reference/webhooks/product.created # product.updated Source: https://polar.sh/docs/api-reference/webhooks/product.updated # refund.created Source: https://polar.sh/docs/api-reference/webhooks/refund.created # refund.updated Source: https://polar.sh/docs/api-reference/webhooks/refund.updated # subscription.active Source: https://polar.sh/docs/api-reference/webhooks/subscription.active # subscription.canceled Source: https://polar.sh/docs/api-reference/webhooks/subscription.canceled # subscription.created Source: https://polar.sh/docs/api-reference/webhooks/subscription.created # subscription.revoked Source: https://polar.sh/docs/api-reference/webhooks/subscription.revoked # subscription.uncanceled Source: https://polar.sh/docs/api-reference/webhooks/subscription.uncanceled # subscription.updated Source: https://polar.sh/docs/api-reference/webhooks/subscription.updated # Product Updates Source: https://polar.sh/docs/changelog/recent Stay up to date with the latest changes and improvements to Polar. ## Added support for new local payment methods We've enabled several additional local payment methods. These payment methods are automatically displayed at checkout when they're available for a customer's location and purchase. No action is required from merchants. Newly supported: * ๐Ÿ‡ง๐Ÿ‡ช Bancontact (EUR, one-time purchases) * ๐Ÿ‡ต๐Ÿ‡ฑ BLIK (EUR, one-time purchases) * ๐Ÿ‡ฆ๐Ÿ‡น EPS (EUR, one-time purchases) * ๐Ÿ‡ณ๐Ÿ‡ฑ iDEAL / Wero (EUR, one-time purchases) * ๐Ÿ‡ต๐Ÿ‡ฑ Przelewy24 (EUR, one-time purchases) * ๐Ÿ‡ช๐Ÿ‡ธ Bizum (EUR, one-time purchases) * ๐Ÿ‡ฎ๐Ÿ‡ณ UPI (INR, one-time purchases and recurring subscriptions) ## Pending Subscription Updates Now Surface in the UI Scheduled subscription changes (product swaps, price updates, seat-count changes) now appear in both the merchant dashboard and the customer portal. A clear "Scheduled Update" section shows what will change and when it applies, so there are no surprises at the next billing cycle. ## Filter License Keys and Benefit Grants by Status Merchants can now filter license keys and benefit grants by status (granted, revoked, disabled, pending) directly from the benefit detail page. The license key status filter is also exposed via the API. Finding revoked or pending entitlements across large customer bases is now a single dropdown click instead of scrolling pages of results. ## Invoice Support for Arabic, Hebrew, and CJK Invoice PDFs now render Arabic, Hebrew, and CJK (Chinese, Japanese, Korean) glyphs correctly, with proper right-to-left shaping for Arabic and Hebrew. Merchants invoicing customers in these languages get professional, readable documents out of the box. ## Expanded Currency Support Presentment currency coverage expanded by 80+ currencies, including EGP, KES, NGN, PKR, VND, UAH, and KZT. Merchants can now price products and display checkout in a much wider range of local currencies. ## Smarter Regional Currency Formatting Localized checkout now respects regional IETF subtags (e.g. `en-CA`, `en-AU`) while keeping the merchant's chosen language. Currency symbols render as `CA$` or `A$` instead of a generic `$`, so buyers see the right currency framing for their region. ## Meter Units Meters now support custom units with a value multiplier, so you can ingest raw counts (tokens, requests, bytes) and price in whatever unit makes sense for your customers (millions of tokens, GB, etc.). Configure the unit name and multiplier on each meter in the dashboard, and unit labels flow through to checkout, the customer portal, and invoices. ## Self-Service Payout Account Management Merchants can now create, switch between, and delete payout accounts directly from the Finance page. A new Manage modal lists every account on the user, lets you set the active one for the organization, and blocks deletion when an account still holds a balance or is linked to an organization. Useful for moving payouts to a new bank entity without contacting support. ## Customers Can Update Their Email Address Customers can now change the email address on their account directly from the customer portal, with a verification step on the new address. No more support tickets to fix a typo or move billing to a new mailbox. ## Cost Insights: Customer Ranking and Variance Detection Cost Insights now ranks customers by total cost so you can see which accounts are driving the bill, and a new variance API surfaces cost anomalies (sudden spikes or drops) on individual events. Cost values also render correctly on the leaf event detail page. An enhancement to the Cost Insights feature shipped last October. ## Customizable Dashboard Home Charts The dashboard home page now lets merchants choose which metrics they see on the overview chart and persist their selection. Tailor the first thing you see when you log in to whatever moves the business for you. ## Schedule Subscription Updates for Next Cycle Subscription changes (product swaps, price updates, seat changes) can now be scheduled to apply at the next billing cycle instead of taking effect immediately. The API exposes a new `next_period` proration behavior, and pending updates are returned on subscription objects and webhooks so external systems stay in sync. ## Tax-Inclusive Pricing Merchants can now set a default tax behavior on their organization and price products with tax included in the displayed amount. Useful for regions and product categories where buyers expect to see the all-in price up front. ## Renewal and Trial Conversion Reminders Customers on yearly or longer billing cycles now get an automatic reminder seven days before renewal. Trial subscribers get a heads-up three days before their trial ends (or one day for very short trials). Both reminders are toggleable per organization, helping reduce involuntary churn and surprise charges. ## GDPR Customer Data Export A new Privacy section in the customer portal lets buyers download a complete JSON export of their personal data, subscriptions, orders, and benefit grants in one click. Helps merchants satisfy data portability obligations without manual exports. ## Cancellation Reason Drill-Down Cancellation charts in the metrics dashboard are now interactive. Clicking on any reason or time bucket opens a detail modal showing the underlying subscriptions, customer comments, and dates. Merchants can finally connect a spike on the chart to the actual customers behind it. ## Korean Checkout Checkout is now translated into Korean. Merchants selling to Korean buyers get a native buying experience, with product details, payment fields, and order summary all rendering in Korean. ## Multi-Currency Fixed Discounts Fixed-amount discounts can now hold separate amounts per currency, matching the multi-currency pricing model that shipped in February. Run a \$10 / 10 EUR / 10 GBP coupon as a single discount instead of juggling three. ## Multi-Currency Product Pricing Merchants can now create products with prices in multiple currencies side by side. Set a USD price, an EUR price, a GBP price, and more on the same product, and customers see the right currency at checkout based on their region. A new default presentment currency setting at the organization level keeps things consistent across the dashboard. ## Italian and Portuguese Checkout Checkout, customer portal, and emails are now fully translated into Italian and Portuguese. Customers in Italy, Portugal, and Brazil get a native experience end to end, with currency formatting that matches their region. ## Feature Flag Benefit A new Feature Flag benefit type is available for SaaS merchants. Grant it to customers when they should unlock something in your product, then check entitlements through the Polar API or webhooks. JSON metadata stays invisible to the customer, keeping the experience clean for pure software subscriptions. ## Faster Wallet and Card Checkout When mobile wallets are available, they now appear at the top of the payment list instead of buried under the card form. The cardholder name field is hidden when paying with a wallet, since the name is pulled from the wallet token automatically. Fewer clicks, fewer fields, faster checkout. ## Team Ownership Transfer Team owners can now hand the owner role to another member directly from the customer portal, and admins can transfer ownership through the API. The previous owner is automatically demoted to billing manager so there is always exactly one owner. ## Seat Limits at Checkout Seat-based products now support `min_seats` and `max_seats` on the checkout. Lock customers to a minimum team size for plans that require it, or cap the upper bound for tiered packages, all enforced at the buying step. ## Name Your Webhook Endpoints Webhook endpoints can now have a friendly name like "Production Events" or "Analytics Pipeline". Names show up as the primary label in the dashboard, with the URL as secondary text, so managing more than a couple of endpoints stops feeling like reading a list of URLs. ## Return URL on Static Checkout Links Static checkout links created from the dashboard now support a custom return URL, matching what was already possible through the API. Send buyers back to a thank-you page, an upsell, or a specific app screen after they complete a purchase, no API call required. ## Team Member Management (B2B) Complete team member management system for B2B customers: * Owner and billing managers can add/remove team members * Role-based access control (owner, billing\_manager, member) * Member-specific benefit grants and portal access * Team portal page with member list and management * Member sessions (`polar_mst_`) preserve identity and permissions * Members tab on customer detail pages Perfect for B2B customers managing team access and permissions. ### Explicit Currency Display Currency symbols now show explicitly (e.g., `USD 123.45`) instead of just `$123.45` to prevent confusion for non-US dollar users. ## Event & Metering Enhancements ### Member-Level Event Attribution Events now support member tracking: * `member_id` and `external_member_id` fields * Member validation during event ingestion * B2B usage tracking at member level ### Timezone Handling Fixed timezone issues in meter quantities endpoint: * Added `timezone` parameter * Set database session timezone properly * Correct date grouping for non-UTC users ## Customer Portal Enhancements ### Benefit Grants UI * Complex benefit grants list with search and pagination * Better display for benefit details * Improved OAuth benefit grant buttons ### Payment Retry Fixes * 3DS modal display (popup instead of redirect) * Retry persistence after 3DS cancellation * Proper dunning management on manual retries ## Discount Management ### Redemption Count Optimization * Added `redemptions_count` column to discounts table ## System Events for Subscriptions Expanded system events to provide detailed subscription lifecycle tracking: * **Subscription cycled** - when billing periods roll over * **Subscription canceled** - when subscriptions are canceled * **Subscription past due** - when payment fails (separate from permanent revocation) * All events include product IDs and relevant metadata * Perfect for analytics and building custom workflows ## iOS Widget New iOS home screen and lock screen widget: * Real-time revenue display * Support for dark and light mode * Multiple widget sizes * Quick glance at your sales without opening the app Built with `@bacons/apple-targets` for native iOS integration. ## Product Management Improvements Major UX improvements for managing products: ### Sorting & Filtering * Sort products by name, creation date, update date, and end date * Custom pagination size (20, 50, 100 items per page) * Product rows are clickable links for cmd+click support ### Editing Experience * Unsaved changes alert prevents accidental data loss * Update button at top right for easier access * Auto-save for most settings * Fixed empty state flashing when sorting/filtering ### File Management * Client-side pagination for downloadable benefit files (10 per page) * Better file upload error messages * Fixed React state issues in benefit forms ### Product Duplication * Re-uploads images when duplicating to prevent shared references * Deleting an image from a duplicate no longer affects the original ## Search Improvements Significant search infrastructure updates: * Search vectors for full-text search * Union queries for better performance * PostgreSQL text search capabilities ## Member Management Enhanced member endpoint functionality: * Added `member_id` to relevant API endpoints * Improved B2B customer management ## Mobile App Improvements ### iOS Updates * iOS widget for home and lock screen * App rating prompts at appropriate times * Login screen animations (fade in, Ken Burns effect) * Fixed multi-account crash on settings * Fixed scroll issues with many organizations * Version bumped to 1.1.0 ### Android Support * Full Android build support * App store ready for both platforms * Consistent design system across iOS and Android ## Customer Meter Enhancements Significant improvements to customer meter calculation: * Speed improvements with optimized queries (avoided cartesian joins) * Better activation logic for meters with events * Fixed organization ID tracking * Increased lock time to 30s for slow queries * Debug information in window event spans * Proper external customer ID handling ## Webhook Management Better webhook health tracking: * Discord rate limit logging * Missing event types added to schema * Improved error handling * Repository-based webhook service ## Checkout Links * Added checkout links to mobile app * Better link management * Share and track checkout performance on mobile ## OAuth2 Improvements * First-party clients auto-save grants * No authorization prompt for trusted apps * Better user experience for internal tools ## Bug Fixes * Fixed undefined organization in dashboard sidebar * Fixed customer meter updates for soft-deleted customers * Fixed seat and trial end updates for seat-based products * Fixed tax calculation error handling * Fixed product empty state flashing * Fixed notifications on iOS simulator * Fixed Switch rendering on Android * Fixed Polar logo not linking to home * Fixed image duplication on product creation * Handle null Stripe refund reasons * Fixed subscription fields in system events ## Dispute & Chargeback Management Complete dispute tracking and prevention system: * Dispute model to track chargebacks from Stripe * Chargeback Stop integration for rapid dispute resolution (RDR) * Auto-refund on RDR disputes to prevent chargebacks * Link refunds to disputes for better tracking * Improved dispute event handling from Stripe webhooks Helps reduce chargeback fees and protect your account standing. ## Metrics API New metrics filtering system: * `metrics` query parameter to filter specific metrics * Removed deprecated `focus_metrics` parameter * Better performance by only calculating requested metrics ## iOS App Updates * App rating prompts at appropriate times * iOS upsell banner on homepage * Animations on login screen (fade in, Ken Burns effect, staggered elements) * Fixed multi-account crash on settings screen * Fixed onboarding redirect after org creation * Fixed scroll issue with many orgs * Version bumped to 1.1.0 ## Mobile App (Android) Added Android support: * Android targets in build scripts * App store ready for both iOS and Android ## Email Improvements * Updated Polar logo in all emails * Better email DNS validation * Fixed invalid email domain issues ## Worker Queue Management Better task prioritization: * Low, medium, and high priority queues * Separate workers for different priorities * Schedule tasks moved to high priority worker ## Downloads Page New dedicated downloads page at polar.sh/downloads for easy access to desktop and mobile apps. ## Customer Balance Refunds * Handle refunds on orders with customer balance applied * Proper balance credit restoration * Block refunds on specific orders via API flag ## Worker Priority Queues Introduced task prioritization system: * Low priority queue for background tasks * Medium priority queue for standard tasks * High priority queue for time-sensitive operations * Separate workers consuming from each queue Ensures critical tasks get processed first. ## Rate Limiting * New restricted rate limit group for abusive patterns * Better rate limit handling across endpoints ## Email Validation Added DNS validation for email addresses across checkout, customer creation, and user management to prevent typos and invalid domains. ## Event Query Optimization Massive performance improvements: * Removed unnecessary ordering from event queries (3x faster) * Better customer latest event queries * New indexes on events table * Optimized IN queries for customer meters ## Mobile App Improvements * Account deletion functionality * Proper logout and login flow fixes * Better error handling * Login screen animations with Ken Burns effect ## Bug Fixes * Fixed settings scroll with many organizations * Fixed app onboarding redirect * Fixed organization service name in Logfire * Fixed customer meter organization ID tracking * Fixed bitwise SQL operator parentheses ## Customer Balance Customer balance system built on top of wallet infrastructure: * Track customer credit balances * Apply balances automatically to invoices * API support for balance management * Foundation for store credit and refund credits ## Members API New members management system for B2B customers: * GET `/v1/members` endpoint with pagination * Filter members by customer ID * Auto-create owner member when customer is created * Override owner member details via API * Members included in customer endpoints * Proper organization scoping and access control Perfect for managing team members across B2B customer accounts. ## Sign in with Apple Added Apple OAuth for authentication: * Apple OAuth integration ## Ad-hoc Checkout Pricing Create checkouts with dynamic pricing that override catalog prices: * Useful for custom deals, usage-based pricing, or testing * Supports all price types (fixed, free, custom) * Full API and SDK support [Read more](/docs/features/checkout/session#ad-hoc-prices) ## Churn Rate New churn rate metric on analytics dashboard: * Track subscription cancellations over time * Understand retention patterns * Available on homepage and customers page ## Event Labels Events now have customizable labels: * System events get readable labels automatically * User events can have custom labels * Labels shown throughout events UI * Edit labels via event type settings Makes events more readable and meaningful for your team. ## Spans View New spans interface for hierarchical event tracking (hidden for now): * List and detail views for event spans * Charts and sparklines for visualization * Time-series data with proper timezone handling * Cost aggregation across event hierarchies ## Metrics Performance Significant performance improvements for analytics: * Optimized metrics queries with window functions * Better date range bounds handling * Indexes on subscription date columns * Cumulative revenue includes full history * Fixed timezone and interval issues ## Invoice Numbering Changed default invoice numbering to per-customer (was organization-wide): * Each customer starts invoice numbering at 1 * Hides sales volume from individual customers * Can still switch to organization-wide in settings ## Tax & Billing * Show taxable amount after discount in checkout * Fixed tax ID formatting (was showing raw strings) * Fixed Stripe coupon name length validation * Fixed conflicting invoice numbering issues ## Checkout Improvements * Persist business name across checkouts * Hide cookie consent banner when logging in via app * Fixed expired checkout intent handling ## Bug Fixes * Fixed 404 after deleting customers, benefits, or checkout links * Fixed product price configurator validation * Fixed custom field number constraint validation * Fixed benefit capitalization in customer portal * Fixed organization cache after profile picture update * Fixed social link parsing ## Mobile App * Continue with Apple support * Disabled tablet support (phone-only for now) * Fixed EAS build configuration * Removed demo mode * Better workspace integration ## Trial Abuse Prevention Prevent customers from redeeming multiple trials with email aliases or different payment methods: * Email normalization strips `+aliases` to detect duplicates * Payment method fingerprinting tracks card usage * Enable via Settings โ†’ Subscription โ†’ "Prevent trial abuse" * Blocked customers see error and can still subscribe without trial * Checkout parameter `allow_trial` to force disable trials Perfect for controlling trial costs while still allowing new customers to subscribe. ## Event Types & Aggregation New event types system for better organization and analytics: * Event types automatically created for events with the same name * Custom display names for better readability * Statistics endpoint for event aggregation * Event closure tables for efficient hierarchy queries ## Mobile App (Beta) Polar mobile app added to the monorepo: * React Native implementation with Expo * Expo workflow for development and builds * Shared workspace with web dashboard * Foundation for mobile-first experiences ## Webhook Events Skipped events now properly tracked: * Events skipped when endpoint disabled no longer count as failures * Prevents false positives in health checks * Better webhook reliability metrics ## File Uploads Large file upload support: * Handles files larger than memory limits * Multipart upload handling * SHA256 validation per part * Better progress tracking ## Subscription Management ### Billing Period Control New API to change current billing period end date: * Useful for custom billing arrangements * Prorations calculated automatically * Prevented on already-cancelled subscriptions ### Payout Improvements * Increased minimum payout amounts for certain currencies * Better error logging for Stripe payout failures ## UI & UX ### Analytics Improvements * Fixed timezone issues with date range selection * Better interval formatting in legends * Improved preset intervals ("this year", "last year") * Consistent date handling across dashboard ### Customer Portal * Fixed overflowing invoice action buttons * Improved mobile experience ### Settings * Auto-prefix social media URLs * Removed seat-based billing toggle (now standard) ## Bug Fixes * Fixed `allow_trial` parameter handling * Fixed dark mode colors across dashboard * Fixed tax rate limiting in sandbox environment * Fixed Stripe trial redemption error handling * Fixed member invitation dialog Enter key submission * Fixed large file upload memory issues * Fixed metrics organization filtering ## Hierarchical Events & Spans Events can now be organized hierarchically with parent-child relationships, perfect for tracking complex workflows: * Create event spans with `parent_id` reference * Use `external_id` as idempotency key to prevent duplicate events * View nested events in the dashboard * Navigate event hierarchies with dedicated event detail pages * Query transitive closure for efficient sub-hierarchy analysis This enables use cases like tracking LLM requests with multiple steps, or multi-phase order fulfillment workflows. ## Master-Detail Layout New master-detail layout pattern across the dashboard: * List view on the left, detail view on the right * Proper nested routing for direct linking * Better navigation and state management * Improved querystring handling Applied to customers, subscriptions, events, and more. ## Improved Date Range Picker Better date range selection throughout analytics: * Fixed "this year" and "last year" presets * Smart interval selection based on date range * Consistent behavior across all views * Better timezone handling ## Settings Auto-Save Settings now auto-save as you type instead of requiring manual save button clicks. Includes proper debouncing and error handling for a smoother experience. ## Customer Management Enhancements ### Product Subscription View Product overview now shows active subscriptions table, making it easier to see who's using each product. ### Customer Filtering Filter subscriptions by cancellation status - find subscriptions that will cancel at period end. ### Benefit Grants Added direct links to customers and subscriptions from benefit grants overview for easier navigation. ## Account Management * Email notifications when unlinking accounts from organizations * Fixed OAuth account disconnection for users with multiple accounts on the same platform ## Bug Fixes * Fixed customer update trying to set email to None when explicitly provided * Fixed payment method handling when soft-deleted methods are updated from Stripe * Added maximum length validation to customer names * Fixed customer portal theming * Fixed subscription double cycling with proper locking mechanisms * Fixed Mintlify docs CSP configuration ## Cost Insights We've launched Cost Insights - a powerful way to track and analyze costs associated with usage. Now you can: * Track costs per event with sub-cent precision * View cost analytics across your dashboard * See cost breakdowns by customer and product * Monitor cost trends with dedicated charts Perfect for understanding unit economics and profitability of your usage-based offerings. [Read more](/docs/features/usage-billing/cost-insights) ## Webhook Management ### Auto-Disable Failing Webhooks Webhooks that continuously fail (10+ consecutive failures by default) are now automatically disabled to save resources. You can manually re-enable them from the dashboard once the issue is fixed. ### Email Notifications for Webhook Failures When webhooks are automatically disabled due to continuous failures, all organization members receive an email notification with details and steps to re-enable. ## Benefit Revocation Grace Period Configure a grace period for benefit revocation when subscription payments fail. Instead of immediately revoking benefits, you can now give customers time to resolve payment issues: * Immediate revocation (default) * Grace period matching payment retry window This applies to both regular benefits and customer seat benefits. ## Customer Portal Enhancements ### Upcoming Charge Display Subscription detail pages now show the upcoming charge amount and date, including: * Trial end dates with first charge amount * Next billing cycle charges for active subscriptions * Automatic updates when subscriptions change ### Usage & Metering View Improved the customer usage view: * Respect archived meters - they no longer appear in usage tracking * Better empty states for customers with granted credits * Create customer meters automatically when meter credits are granted ### Navigation & UX Improvements * Fixed navigation highlighting on mobile * Better handling of expired login codes * Removed unnecessary scrolling * Can't switch plans during trial period (not supported yet) ## Seat Management Updates * API support for assigning seats programmatically * \$0/seat pricing now allowed for seat-based tiers * Fixed eager loading issues with customer seats * Better webhooks for customer seat events ## Invoice Numbering Two invoice numbering modes: * **Organization-wide** (default) - Sequential across all customers * **Per-customer** - Each customer has independent invoice numbering starting at 1 Useful when you want to hide sales volume from customers. ## System Events New system events automatically created for: * Orders paid * Orders refunded * Customer creation * Customer updates These events are available in the events explorer with cost tracking where applicable. ## Discount Management Improved discount code selection with a searchable combobox instead of a dropdown, making it easier to find specific codes. ## Bug Fixes * Fixed customer meter matching to include external customer IDs * Fixed Indian GST tax validation * Fixed subscription dunning process when payment is deleted * Fixed order confirmation emails for orders without products ## Seat-Based Pricing for One-Time Products You can now offer seat-based pricing for one-time purchases, not just subscriptions. Customers can select the number of seats during checkout, and you can manage seats for one-time orders through both the dashboard and API. This extends our seat management capabilities to all product types, giving you more flexibility in how you price and package your offerings. ## Ability to Change Subscription Seats Customers can now change the number of seats on their active subscriptions directly from the Customer Portal. The changes are prorated automatically, making it easy for teams to scale up or down as needed. ## Flexible Subscription Intervals We've added support for flexible subscription intervals beyond monthly and yearly. You can now create subscriptions with custom billing periods: * Daily subscriptions * Weekly subscriptions * Custom intervals (e.g., every 2 weeks, every 3 months) Configure the interval count when creating or updating subscription products through both the dashboard and API. ## Subscription Creation API We've launched a new API endpoint to create subscriptions programmatically. This enables you to set up subscriptions directly without requiring customers to go through the checkout flow. [Read more](/docs/api-reference/subscriptions/create) ## Enhanced Email Confirmations Order confirmation emails now include: * **Invoice PDFs attached** - Invoices are automatically generated and attached to confirmation emails for one-time purchases * **Order details** - Complete breakdown of what was purchased * **Benefits list** - Clear view of all benefits the customer will receive ## Customer Portal Improvements ### Seat Management Customers with seat-based subscriptions can now: * View all active seats * Add or remove seats (with automatic proration) * Manage team members directly from the portal ### URL Encoding Fix Fixed an issue where email addresses with special characters (like `+` signs in email aliases) weren't properly encoded in customer portal links, causing the wrong email to be pre-filled. ## Events & Metering ### Cost Tracking Events can now include cost metadata, enabling you to track costs associated with usage. Costs are displayed throughout the events explorer and analytics. ### Event Explorer Enhancements * Metadata filtering - Search and filter events by metadata fields * Customer filtering - Filter events by specific customers * Query search - Full-text search across event data * Improved event cards with better timestamps and cost visualization ## Order Export You can now export orders to CSV directly from the dashboard. Filter by product and the export will respect your current filters, making it easy to analyze sales data. ## OAuth2 Flow Improvements * If no scope is passed in the authorize request, the client's default scope is now used automatically * Improved organization selector UI in the OAuth2 authorization flow ## Checkout Improvements * Fixed seat selector visibility on mobile devices * Server-side prefilling of checkout link query parameters * Improved discount code validation flow ## Statement Descriptor Enhancement First payments after a trial period now include "TRIAL OVER" in the statement descriptor, making it clearer to customers why they're being charged. ## Bug Fixes * Fixed customer state API for trialing subscriptions * Fixed broken customer links in events explorer * Fixed discount duration calculation for very long durations (capped at 999 months) ## Ability to update subscription to an updated price of the product Merchants can now [update existing subscriptions](https://polar.sh/docs/api-reference/subscriptions/update#subscriptionupdateproduct) from archived pricing schemes to current ones within the same product. * Enables migration from grandfathered pricing to current pricing schemes * Prorations calculated using active subscription prices * In the dashboard, a small badge **Upgrade pricing** will indicate that you can update to the same product, but with the new pricing scheme. ## New IP ranges From **October 27th, 2025**, [new IP ranges](https://polar.sh/docs/integrate/webhooks/delivery#ip-allowlist) will be added. ## Improved Subscription Cancellation Flow [Benefits](https://polar.sh/docs/features/benefits/introduction) attached to the subscription [are now automatically revoked](https://github.com/polarsource/polar/pull/7271) when the subscription is canceled. ## Ability to specify External ID [External ID can now be specified](https://github.com/polarsource/polar/pull/7275) during creation of a customer via the dashboard. ## Ability to set Return URL for Checkouts and Customer Portal A Return URL can now be set while generating a [checkout session](https://polar.sh/docs/api-reference/checkouts/create-session#body-return-url-one-of-0) or a [customer portal session](https://polar.sh/docs/api-reference/customer-portal/sessions/create#body-one-of-0-return-url-one-of-0). This allows you to preserve the context for the end users who visit either if they wish to go back to the application. ## Ability to search Customers via their External ID The [List Customers API](https://polar.sh/docs/api-reference/customers/list) now accepts Customer's External ID [in the **query** parameter](https://polar.sh/docs/api-reference/customers/list#parameter-one-of-0). ## Ability to disable automatic customer emails Via the organization settings, you can now disable the emails we automatically send to customers on certain events. This gives you the ability to own the communications with the customers. ## Launched In-Product Chat Support for Merchants We're excited to introduce a chat widget to the Polar dashboard, making it easier than ever for merchants to get help directly within the product. ## Improved Checkout Flow for Invalid Discount Code Previously, if you entered an invalid discount code during checkout, you couldn't continue even after removing the code. Now, clearing the discount input lets you proceed smoothly with the checkout process. ## Improved Customer Portal Rate Limits We've made several improvements to the Customer Portal to handle authentication rate limits more gracefully: * The portal now clearly shows 401 and 429 errors on the OTP (one-time password) page. * If you hit a 429 (too many requests), you'll be redirected to a clear `/too-many-requests` page. ## Launched Subscription Trials You can now offer [trial periods](/docs/features/trials) for new subscriptions! This highly requested feature allows you to let customers experience your product before their first payment is due. Trials can be configured in both the dashboard and API when creating or updating subscription products. ## Always display taxes line item in the checkout We've improved our checkout experience to always display taxes (vs lazy loading them on country selection), making charges more transparent for your customers regardless of whether taxes apply to their purchase. ## Do not calculate taxes on free or zero-amount orders Orders with a zero amount (such as promotional products) will no longer have taxes calculated, resulting in a clearer and more accurate order summary for your customers. ## Add confirmation modal for deleting discounts When deleting a discount, you'll now see a confirmation modal to help prevent accidental deletions and provide extra clarity on the impact of your actions. ## Fix infinite rendering loop with date picker Resolved a bug where selecting dates in the date picker could cause an infinite rendering loop, improving reliability for date-related forms. ## Require opt-in if you will be charged immediately Users must now explicitly confirm immediate charges or credits when switching subscription intervals, with the UI providing clearer, contextual explanations of invoicing outcomes. ## Check for `expires_at` when activating license keys License key activation now correctly checks the `expires_at` date, ensuring that only valid, non-expired license keys can be activated. ## Fix customer state for trialing subscriptions The [Customer State API](/docs/api-reference/customers/state) now properly handles customers with `trialing` subscriptions, so your integrations and dashboards always show an accurate subscription status. ## Improved preview of next invoice in Customer Portal We've enhanced the Customer Portal to provide a clearer and more accurate preview of your next invoice. The overview now updates automatically after subscription changes, and you can preview upcoming charges with all relevant taxes and discounts included. ## Cancellation metrics We've added detailed cancellation metrics, giving you clearer insights into subscription cancellations and their impact on your business performance. ## Webhooks payload now includes timestamp We've updated our webhooks server implementation to [include a timestamp in each payload](https://github.com/polarsource/polar/pull/6770), in line with the Standard Webhooks specification. This change ensures that every webhook payload contains precise event timing, making it easier to trace and debug webhook deliveries, and to meet integration requirements for external platforms. ## Meter management improvements We've made it easier to manage your meters with new UI functionality for archiving and unarchiving meters directly from the dashboard. You can now archive meters that are no longer needed, which helps keep your meter list organized. Archived meters can be unarchived if you need to use them again. Note that meters cannot be archived if they are still attached to active products or referenced by active benefits. ## Metrics accuracy improvements We've improved the accuracy of our metrics by excluding unpaid orders from all calculations. Previously, orders in pending status were included in metrics, which could lead to inflated numbers. Now, only successfully paid and refunded orders are included in metrics calculations, giving you a more accurate view of your actual business performance. ## Enhanced customer email branding We've improved the branding of emails sent to your customers by using organization-specific 'From' and 'Reply-to' addresses. Customer emails now appear to come from your organization (e.g., "YourOrg (via Polar)") with replies directed to your organization's email address, providing a more professional and branded experience for your customers. ## Update subscription discount We've added the ability to update the discount on a subscription. This allows you to add, remove, or change the discount applied to a subscription at any time. This feature is both available through the [API](/docs/api-reference/subscriptions/update) and the dashboard. ## Payout Reverse Invoices We've added the ability to generate reverse invoices for payouts directly from the Payouts page. This feature allows you to easily create an invoice that details the sales made on your behalf, minus our fees. [Read more](/docs/features/finance/payouts#reverse-invoices) ## Business Purchase Option on Checkout We've added a new "I'm purchasing as a business" checkbox to the Checkout flow. When selected, customers are required to provide their business billing name and complete billing address. ## Enhanced Attribution for Checkout Links We've added support for `reference_id` and UTM parameters (`utm_source`, `utm_medium`, `utm_campaign`, `utm_content`, `utm_term`) as query parameters for Checkout Links. These parameters are automatically stored in the Checkout metadata, allowing you to track the source of your traffic and conversions more effectively. [Read more](/docs/features/checkout/links#store-attribution-and-reference-metadata) ## Checkouts and payments insights We've added a new **Checkouts** tab under the **Sales**, where you can review all the checkout sessions, successful or not. You can filter them by customer email, status, and product. You can also see the payment attempts for each checkout session, including the reason for any failed or declined payments. The payment attempts information is also available on each order. Besides, we've also added new analytics around checkouts: total number of checkouts, successful checkouts, and conversion rate. ## Zapier integration officially launched We're excited to announce the official launch of our [Zapier integration](https://zapier.com/apps/polar/integrations)! Get started now and connect Polar to 2,000+ other web services. We've focused on **triggers** (webhooks) for now, so you can react to events in Polar and trigger actions in other apps. Need to perform actions in Polar? Tell us about your use case [here](https://github.com/orgs/polarsource/discussions/new?category=integrations\&labels=integrations%2Fzapier) and we'll consider adding more actions in the future. ## Customer State Maybe one of our neatest features to date! Customer State is a concept allowing you to query for the current state of a customer, including their **active subscriptions** and **granted [benefits](/docs/features/benefits/introduction)**, in a single [API call](/docs/api-reference/customers/state-external) or single [webhook event](/docs/api-reference/webhooks/customer.state_changed). Combined with the [External ID](/docs/features/customer-management#external-id) feature, you can get up-and-running in minutes. [Read more](/docs/integrate/customer-state) ## Better Auth Plugin Integrating authentication and billing for your users has never been easier. [Better Auth](https://www.better-auth.com/) is an open source authentication framework for TypeScript that is quickly becoming a favorite amongst developers. Today, we're thrilled to have shipped a Polar plugin for Better Auth - in collaboration with them. Checkout our [integration guide](/docs/integrate/sdk/adapters/better-auth). ## Customer External ID We've added support for an `external_id` field on Customers. We believe this will greatly simplify the reconciliation process between your system and Polar. Previously, the recommended way to reconcile with your users was to use `metadata`. However, this was not always the most convenient method, especially if you needed to fetch a Customer from our API. With `external_id`, you can now fetch a Customer directly by their external ID through dedicated `GET`, `PATCH`, and `DELETE` endpoints. You don't even need to store Polar's internal ID in your system anymore! [Read more](/docs/features/customer-management#external-id) Of course, you can also directly preset `external_customer_id` when creating a Checkout Session, and it will automatically be set on the newly created Customer after a successful checkout. [Read more](/docs/features/checkout/session#external-customer-id) ## Polar's take on Product variants We've released big changes to how we handle products and pricing, allowing us to support a unique approach to what the industry typically calls **variants** ๐Ÿ”ฅ We believe having a single product with multiple pricing models and benefits adds unneccessary complexity to the user and to the API. Instead, we chose to treat everything as a product, giving you maximum flexibility about the pricing and benefits you want to offer. Thus, we introduce support for **multiple products** at checkout, allowing customers to switch between them before purchasing. Typically, you can offer a monthly and a yearly product, with specific pricing and benefits for each. This is available right now using the [Checkout Session API](/docs/features/checkout/session) and [Checkout Links](/docs/features/checkout/links). ### Depreciations * Products can no longer have both a monthly and yearly pricing. Existing products still work, but you'll see a warning like this when trying to edit their pricing: ### API changes * The `product_id` and `product_price_id` fields are deprecated in the [Checkout Session API](/docs/api-reference/checkouts/create-session). You should now use the `products` field to specify the products you want to include in the checkout. * The `type` and `recurring_interval` fields on `ProductPrice` are deprecated. `recurring_interval` is now set directly on `Product`. # Analytics Source: https://polar.sh/docs/features/analytics Understand how every metric in your dashboard is calculated. Polar ships with a built-in analytics dashboard so you can stay focused on growing the business rather than wiring up reporting. Each section below maps to one of the dashboards on the **Analytics** page and walks through what its metrics actually mean. ## How metrics are bucketed When you pick a date range, Polar splits it into equal-width **intervals** โ€” one hour, one day, one week, one month, or one year each โ€” and aggregates every metric inside each interval. Each interval (sometimes called a "bucket") is one point on the chart and one row in the API response. ## Subscriptions * **Monthly Recurring Revenue (MRR)** โ€” Sum of every ongoing subscription's net amount, normalized to a monthly rate (yearly plans are divided by 12). Trialing subscriptions are excluded. Paused and past-due subscriptions are included: a subscriber counts towards MRR until their subscription actually ends. * **Committed MRR** โ€” MRR restricted to subscriptions still inside a committed billing period. Subscriptions that have been canceled but are running out the clock until period end are excluded. As with MRR, paused and past-due subscriptions are included. * **Trial MRR Including Canceled Trials** โ€” Monthly-normalized amount of every subscription currently in a trial, including trials that have already been canceled. * **Trial Committed MRR** โ€” Trial MRR for trials still inside their committed billing period (i.e. canceled trials are excluded). * **Active Subscriptions** โ€” Count of ongoing subscriptions at the end of each interval, based on when each subscription started and ended rather than its current status. A subscription counts until it actually ends: those that have been canceled but are running out the clock until period end, as well as paused and past-due subscriptions, are all still included. * **New Subscriptions** โ€” Subscriptions whose first paid order falls inside the interval. Each subscription is counted once, at creation. * **Committed Subscriptions** โ€” Active subscriptions that have not been canceled, or whose cancellation date is after the interval. These are the subscriptions you can reasonably expect to renew. * **Renewed Subscriptions** โ€” Number of orders with billing reason `subscription_cycle` in the interval โ€” i.e. renewals on existing subscriptions. * **Average Revenue Per User (ARPU)** โ€” MRR divided by the count of distinct paying subscribers in the interval (trials excluded). Returns 0 when there are no paying subscribers. * **Lifetime Value (LTV)** โ€” Estimated revenue per customer over their lifetime: `(ARPU - cost per user) / churn rate`. Returns 0 when churn rate is 0. * **New Subscriptions Revenue** โ€” Gross revenue from the first paid order on every new subscription created in the interval. * **Renewed Subscriptions Revenue** โ€” Gross revenue from subscription renewal orders in the interval. ## Cancellations * **Canceled Subscriptions** โ€” Subscriptions where the customer triggered a cancellation in the interval. The subscription itself may still be active until the end of the paid period. * **Churned Subscriptions** โ€” Subscriptions whose paid period actually ended inside the interval. This is "real" churn โ€” the moment access stops. * **Churn Rate** โ€” Churned subscriptions in the interval divided by the active subscription base at the start of the interval, expressed as a percentage. * **Active Subscriptions** โ€” Same definition as in the [Subscriptions](#subscriptions) dashboard. Shown here to contextualise churn against the underlying base. * **Committed Subscriptions** โ€” Same definition as in the [Subscriptions](#subscriptions) dashboard. * **Cancellation reasons** โ€” Stacked chart of canceled subscriptions broken down by the reason the customer picked at cancellation: *too expensive*, *missing features*, *switched service*, *unused*, *customer service*, *low quality*, *too complex*, or *other* (free-form or no reason given). ## One-Time Products * **One-Time Products** โ€” Number of completed orders for non-recurring products in the interval. Subscription orders are not included. * **One-Time Products Revenue** โ€” Gross revenue (subtotal before fees) from those one-time product orders. ## Orders * **Revenue** โ€” Gross revenue from every completed order in the interval: one-time purchases, new subscription orders, and renewal orders. Computed on the order subtotal, before Polar fees and tax. * **Orders** โ€” Total number of completed orders in the interval, regardless of billing reason. * **Average Order Value (AOV)** โ€” Revenue divided by the number of orders. Returns 0 when there are no orders. * **Cumulative Revenue** โ€” Running total of revenue from the start of the selected range up to and including the current interval. ## Checkouts * **Checkouts Conversion** โ€” Succeeded checkouts divided by total checkouts created, as a percentage. Returns 0 when no checkouts were created. * **Checkouts** โ€” Every checkout session created in the interval, regardless of outcome (succeeded, expired, or abandoned). * **Succeeded Checkouts** โ€” Checkouts that reached the `succeeded` status, meaning they resulted in an order. ## Net Revenue * **Net Revenue** โ€” Revenue after deducting Polar's platform fee and the payment processor's fee on each order. This is the amount actually paid out to you. * **Net Average Order Value** โ€” Net revenue divided by the number of orders in the interval. * **Net Cumulative Revenue** โ€” Running total of net revenue from the start of the selected range up to the current interval. * **New Subscriptions Net Revenue** โ€” Net revenue from the first paid order on every new subscription. Shown when your organization sells recurring products. * **Renewed Subscriptions Net Revenue** โ€” Net revenue from subscription renewal orders. Shown when your organization sells recurring products. * **One-Time Products Net Revenue** โ€” Net revenue from one-time product orders. Shown when your organization sells one-time products. ## Costs The Costs dashboard is powered by [Cost Insights](/docs/features/cost-insights/introduction) โ€” you'll only see data here once you start sending events with a `_cost` annotation. * **Costs** โ€” Total operating costs ingested via Cost Insights in the interval. Stored at sub-cent precision so per-event costs (e.g. token-level inference costs) aren't rounded away. * **Cost Per User** โ€” Cumulative costs in the selected range divided by the count of active subscribers. Returns 0 when there are no active subscribers. * **Gross Margin** โ€” Cumulative revenue minus cumulative costs over the selected range. * **Gross Margin %** โ€” Gross margin expressed as a percentage of cumulative revenue. Returns 0 when cumulative revenue is 0. * **Cashflow** โ€” Revenue minus costs for the interval itself (not cumulative). Useful for spotting periods where costs outpaced revenue. # Credits Benefit Source: https://polar.sh/docs/features/benefits/credits Create your own Credits benefit The Credits benefit allows you to credit a customer's Usage Meter balance. ## Crediting Usage Meter Balance The Credits benefit will credit a customer's Usage Meter balance at different points in time depending on the type of product purchased. ### Subscription Products The customer will be credited the amount of units specified in the benefit at the beginning of every subscription cycle period โ€” monthly or yearly. ### One-Time Products The customer will be credited the amount of units specified in the benefit once at the time of purchase. ## Rollover unused credits You can choose to rollover unused credits to the next billing cycle. This means that if a customer doesn't use all of their credits in a given billing cycle, the remaining credits will be added to their balance for the next billing cycle. To enable this feature, check the "Rollover unused credits" checkbox when creating or editing the Credits benefit. If you change the rollover setting for a benefit, it will only apply to new credits issued after the change. Existing credits will not be affected. # Custom Benefit Source: https://polar.sh/docs/features/benefits/custom Create your own Custom benefit You can add a simple, custom benefit, which allows you to attach a note to paying customers. ## **Custom Notes** Secret message only customers can see, e.g [Cal.com](http://Cal.com) link, private email for support etc. For custom integrations you can also distinguish benefits granted to customers to offer even more bespoke user benefits. ## Sharing links and instructions after purchase Because the **Private note** supports Markdown, a Custom benefit is the simplest way to deliver post-purchase content without writing any code: * A private link (Calendly, Notion, โ€ฆ) * Onboarding instructions or a welcome message * A coupon code for a partner service Set the **Description** to the title customers will see (e.g. *"Your onboarding link"*), put the link or instructions in **Private note**, and attach the benefit to a product. The rendered Markdown appears on the checkout success page, in the purchase confirmation email, and in the [Customer Portal](/docs/features/customer-portal). Previously, we recommended using a Custom benefit without a note as a way to grant access to software or SaaS features โ€” decoupling entitlements from checking directly on products. We now recommend the [Feature Flag](/docs/features/benefits/feature-flags) benefit for this purpose, as it's purpose-built for feature gating and supports key-value metadata. # Automate Discord Invites & Roles Source: https://polar.sh/docs/features/benefits/discord-access Sell Discord access & roles with ease Automating Discord server invites and roles for customers or subscribers is super easy and powerful with Polar. * Fully automated Discord server invitations * You can even setup multiple Discord servers, or... * Offer different roles for different subscription tiers or products ## Create Discord Benefit Click on `Connect your Discord server`. You'll be redirected to Discord where you can grant the Polar App for your desired server. Next, you'll be prompted to approve the permissions our app requires to function. It needs all of them. ### **Manage Roles** Access to your Discord roles. You'll be able to select which ones to grant to your customers later. ### **Kick Members** Ability to kick members who have this benefit and connected Discord with Polar. ### **Create Invite** Ability to invite members who purchase a product or subscribes to a tier with this benefit. You're now redirected back to Polar and can finish setting up the Discord benefit on our end. ### **Connected Discord server** The Discord server you connected cannot be changed. However, you can create multiple benefits and connect more Discord servers if you want. ### **Granted role** Which Discord role do you want to grant as part of this benefit? ## Adding Benefit to Product Head over to the product you want to associate this new Discord benefit with. You should be able to toggle the benefit in the bottom of the Edit Product form. # Feature Flag Benefit Source: https://polar.sh/docs/features/benefits/feature-flags Gate access to features using simple, API-driven feature flags The Feature Flag benefit is a lightweight way to grant feature access to customers without any external service integration. If a customer has the benefit grant, they have access โ€” it's that simple. ## Use Cases * Gate premium features behind a subscription tier * Offer early access or beta features to select customers * Differentiate access levels across product tiers * Control API rate limit tiers or usage quotas in your application ## Create Feature Flag Benefit 1. Go to [`Benefits`](https://polar.sh/to/dashboard/products/benefits) 2. Click `+ New Benefit` to create a new benefit 3. Choose `Feature Flag` as the `Type` 4. Give it a short description (e.g. "Premium Features" or "Beta Access") ### Metadata You can optionally attach key-value metadata to a feature flag benefit. This is useful for passing additional context to your application, for example: * `role` โ†’ `editor` * `max_upload_size` โ†’ `10` * `priority` โ†’ `elevated` Metadata can be configured when creating or editing the benefit in the dashboard using the **Add Metadata** button. ## Integration The recommended way to check if a customer has a feature flag benefit is through the [Customer State](/docs/integrate/customer-state) API or the [`customer.state_changed`](/docs/api-reference/webhooks/customer.state_changed) webhook. The customer state object includes all granted benefits. Simply check if the customer has a benefit grant for your feature flag benefit to determine access. ### Lifecycle * **Subscriptions**: The feature flag is granted at the start of each subscription cycle and automatically revoked when the subscription is cancelled. * **One-time purchases**: The feature flag is granted at the time of purchase with lifetime access. # Automate Customer File Downloads Source: https://polar.sh/docs/features/benefits/file-downloads Offer digital file downloads with ease ## Sell Digital Products You can easily offer customers and subscribers access to downloadable files with Polar. * Up to 10GB per file * Upload any type of file - from ebooks to full-fledged applications * SHA-256 checksum validation throughout for you and your customers (if desired) * Customers get a signed & personal downloadable URL ## Create Downloadable Benefit 1. Go to [`Benefits`](https://polar.sh/to/dashboard/products/benefits) 2. Click `+ Add Benefit` to create a new benefit 3. Choose `File Downloads` as the `Type` You can now upload the files you want to offer as downloadables for customers. 1. Drag & drop files to the dropzone (`Feed me some bytes`) 2. Or click on that area to open a file browser ### Change filename Click on the filename to change it inline. ### Change order of files You can drag and drop the files in the order you want. ### Review SHA-256 checksum Click on the contextual menu dots and then `Copy SHA-256 Checksum` ### Delete a file Click on the contextual menu dots and then `Delete` in the menu. **Active subscribers & customers will lose access too!** Deleting a file permanently deletes it from Polar and our S3 buckets except for the metadata. Disable the file instead if you don't want it permanently deleted. ### Disable & Enable Files You can disable files at any point to prevent new customers getting access to it. **Existing customers retain their access** Customers who purchased before the file was disabled will still have access to legacy files. Only new customers will be impacted. **Enabling or adding files grants access retroactively** In case you add more files or re-enable existing ones, all current customers and subscribers with the benefit will be granted access. # Automate Private GitHub Repo(s) Access Source: https://polar.sh/docs/features/benefits/github-access Sell premium GitHub repository access with ease ## Sell GitHub Repository Access With Polar you can seamlessly offer your customers and subscribers automated access to private GitHub repositories. * Fully automated collaborator invites * Unlimited repositories (via multiple benefits) from your organization(s) * Users get access upon subscribing & removed on cancellation * Or get lifetime access upon paying a one-time price (product) ### **Use cases** * Sponsorware * Access to private GitHub discussions & issues for sponsors * Early access to new feature development before upstream push * Premium educational materials & code * Self-hosting products * Courses, starter kits, open core software & more... ## Create GitHub Repository Benefit 1. Go to [`Benefits`](https://polar.sh/to/dashboard/products/benefits) 2. Click `+ New Benefit` to create a new benefit 3. Choose `GitHub Repository Access` as the `Type` You first need to `Connect your GitHub Account` and install a dedicated Polar App for this benefit across the repositories you want to use it with. * Click `Connect your GitHub Account` **Why do I need to connect GitHub again and install a separate app?** This feature requires permission to manage repository collaborators. GitHub Apps does not support progressive permission scope requests. So instead of requesting this sensitive permission from all users (unnecessarily) in our core GitHub Login this feature uses a standalone app instead. Once you've authorized our dedicated GitHub App for this feature you'll be redirected back to Polar and the benefit form - now connected and updated. ### **Repository** Select the desired repository you want to automate collaborator invites for. **Why can I only connect organization repositories vs. personal ones?** GitHub does not support granular permissions for collaborators on personal repositories - granting them all write permissions instead. Since collaborators would then be able to push changes, releases and more, we do not support personal repositories by default.Want this still? Reach out to us and we can enable it. ### **Role** Select the role you want to grant collaborators. * **Read (Default & Highly recommended)** * Triage * Write * Maintain * Admin Read access (read-only) is what 99.9% of cases should use and the others are highly discouraged unless you have special use cases & absolutely know the impact of these permissions. Checkout the [GitHub documentation](https://docs.github.com/en/organizations/managing-user-access-to-your-organizations-repositories/managing-repository-roles/repository-roles-for-an-organization#permissions-for-each-role) for reference. Anyone with read access to a repository can create a pull request [(source)](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request). **Additional Costs for Paid GitHub Organizations** GitHub treats collaborators as a seat and they will incurr charges accordingly to your billing unless you're using a free GitHub organization plan. So make sure to confirm you're on a free plan OR charge sufficiently to offset the costs you'll need to pay to GitHub. # Automated Benefits Source: https://polar.sh/docs/features/benefits/introduction Polar offers built-in benefit (entitlements) automation for common upsells within the developer & designer ecosystem with more to come. * [**Credits**](/docs/features/benefits/credits). A simple benefit that allows you to credit a customer's Usage Meter balance. * [**License Keys**](/docs/features/benefits/license-keys). Software license keys that you can customize the branding of. * [**Feature Flags**](/docs/features/benefits/feature-flags). Simple, API-driven feature access flags with optional metadata. * [**File Downloads**](/docs/features/benefits/file-downloads). Downloadable files of any kind up to 10GB each. * [**GitHub Repository Access**](/docs/features/benefits/github-access). Automatically invite subscribers to private GitHub repo(s). * [**Discord Invite**](/docs/features/benefits/discord-access). Automate invitations and granting of roles to subscribers and customers. * [**Shared Slack Channel**](/docs/features/benefits/slack-shared-channel). Give customers a shared Slack channel via Slack Connect. ## Product & Subscription Benefits Product and subscription benefits are standalone resources in Polar - connected to one or many products or subscription tiers. This approach is a bit different from other platforms, but offers many advantages: * Easy to enable the same benefit across multiple products & subscriptions * You can change a benefit in one place vs. many * No duplicate data or work (error prone) * More intuitive UI for you and your customers **How customers get access to benefits:** * โœ… Active subscribers of tiers with the benefit enabled * โœ… Customers who bought a product with the benefit (lifetime access) * โŒ Subscribers with an expired subscription (cancelled) * โŒ Users who are not customers ## Creating & Managing Benefits You can manage benefits in two ways: 1. Directly within a product create/edit form 2. Or via `Benefits` in your dashboard # Automate Customer License Key Management Source: https://polar.sh/docs/features/benefits/license-keys Sell license key access to your service, software or APIs with ease You can easily sell software license keys with Polar without having to deal with sales tax or hosting an API to validate them in real-time. License keys with Polar come with a lot of powerful features built-in. * Brandable prefixes, e.g `POLAR_*****` * Automatic expiration after `N` days, months or years * Limited number of user activations, e.g devices * Custom validation conditions * Usage quotas per license key * Automatic revokation upon cancelled subscriptions ## Create License Key Benefit 1. Go to [`Benefits`](https://polar.sh/to/dashboard/products/benefits) 2. Click `+ New Benefit` to create a new benefit 3. Choose `License Keys` as the `Type` ### Custom Branding Make your license keys standout with brandable prefixes, e.g `MYAPP_` ### Automatic Expiration Want license keys to expire automatically after a certain time period from when the customer bought them? No problem. ### Activation Limits You can require license keys to be activated before future validation. A great feature in case you want to limit license key usage to a certain number of devices, IPs or other conditions. **Enable user to deactivate instances via Polar.** Instead of building your own custom admin for customers to manage their activation instances - leave it to Polar instead. ### Usage Limit Offering OpenAI tokens or anything else with a variable usage cost? You can set a custom usage quota per license key and increment usage upon validation. ## Customer Experience Once customers buy your product or subscribes to your tier, they will automatically receive a unique license key. It's easily accessible to them under their purchases page. Customers can: * View & copy their license key * See expiration date (if applicable) * See usage left (if applicable) * Deactivate activations (if enabled) ## Integrate API It's super easy and straightforward to integrate Polar license keys into your application, library or API. ### Activate License Keys (Optional) In case you've setup license keys to have a maximum amount of activation instances, e.g user devices. You'll then need to create an activation instance prior to validating license keys / activation. **No activation limit?** You can skip this step. ```bash Terminal theme={null} curl -X POST https://api.polar.sh/v1/customer-portal/license-keys/activate -H "Content-Type: application/json" -d '{ "key": "1C285B2D-6CE6-4BC7-B8BE-ADB6A7E304DA", "organization_id": "fda84e25-7b55-4d67-916d-60ead04ff61f", "label": "hello", "conditions": { "major_version": 1 }, "meta": { "ip": "84.19.145.194" } }' ``` Replace with the users license key (from input in your app). Replace with your organization ID here found in your settings. Set a label to associate with this specific activation. JSON object with custom conditions to validate against in the future, e.g IP, mac address, major version etc. JSON object with metadata to store for the users activation. #### **Response (200 OK)** ```json theme={null} { "id": "b6724bc8-7ad9-4ca0-b143-7c896fcbb6fe", "license_key_id": "508176f7-065a-4b5d-b524-4e9c8a11ed63", "label": "hello", "meta": { "ip": "84.19.145.194" }, "created_at": "2024-09-02T13:48:13.251621Z", "modified_at": null, "license_key": { "id": "508176f7-065a-4b5d-b524-4e9c8a11ed63", "organization_id": "fda84e25-7b55-4d67-916d-60ead04ff61f", "user_id": "d910050c-be66-4ca0-b4cc-34fde514f227", "benefit_id": "32a8eda4-56cf-4a94-8228-792d324a519e", "key": "1C285B2D-6CE6-4BC7-B8BE-ADB6A7E304DA", "display_key": "****-E304DA", "status": "granted", "limit_activations": 3, "usage": 0, "limit_usage": 100, "validations": 0, "last_validated_at": null, "expires_at": "2026-08-30T08:40:34.769148Z" } } ``` ### Validate License Keys For each session of your premium app, library or API, we recommend you validate the users license key via the [`/v1/customer-portal/license-keys/validate`](/docs/api-reference/customer-portal/license-keys/validate) endpoint. ```bash Terminal theme={null} curl -X POST https://api.polar.sh/v1/customer-portal/license-keys/validate -H "Content-Type: application/json" -d '{ "key": "1C285B2D-6CE6-4BC7-B8BE-ADB6A7E304DA", "organization_id": "fda84e25-7b55-4d67-916d-60ead04ff61f", "activation_id": "b6724bc8-7ad9-4ca0-b143-7c896fcbb6fe", "conditions": { "major_version": 1 }, "increment_usage": 15 }' ``` Replace with the users license key (from input in your app). Replace with your organization ID here found in your settings. The activation ID to validate - required in case activations limit is enabled and used (above). In case of activation instances. Same exact JSON object as upon registration of the activation. In case you want to increment usage upon validation. #### **Response (200 OK)** ```json theme={null} { "id": "508176f7-065a-4b5d-b524-4e9c8a11ed63", "organization_id": "fda84e25-7b55-4d67-916d-60ead04ff61f", "user_id": "d910050c-be66-4ca0-b4cc-34fde514f227", "benefit_id": "32a8eda4-56cf-4a94-8228-792d324a519e", "key": "1C285B2D-6CE6-4BC7-B8BE-ADB6A7E304DA", "display_key": "****-E304DA", "status": "granted", "limit_activations": 3, "usage": 15, "limit_usage": 100, "validations": 5, "last_validated_at": "2024-09-02T13:57:00.977363Z", "expires_at": "2026-08-30T08:40:34.769148Z", "activation": { "id": "b6724bc8-7ad9-4ca0-b143-7c896fcbb6fe", "license_key_id": "508176f7-065a-4b5d-b524-4e9c8a11ed63", "label": "hello", "meta": { "ip": "84.19.145.194" }, "created_at": "2024-09-02T13:48:13.251621Z", "modified_at": null } } ``` Validate `benefit_id` in case of multiple license keys We require `organization_id` to be provided to avoid cases of Polar license keys being used across Polar organizations erroneously. Otherwise, a valid license key for one organization could be used on another.However, you are required to validate and scope license keys more narrowly within your organization if necessary. Offering more than one type of license key? Be sure to validate their unique benefit\_id in the responses. # Shared Slack Channel Source: https://polar.sh/docs/features/benefits/slack-shared-channel Give customers a shared Slack channel via Slack Connect The Shared Slack Channel benefit automatically provisions a dedicated Slack channel for each customer and shares it with their workspace through [Slack Connect](https://slack.com/connect). * A new channel is created in your Slack workspace for every customer who gets the benefit * The channel is shared with the customer's own workspace โ€” no need for them to join yours * Channels can be archived automatically when the benefit is revoked The Shared Slack Channel benefit is currently in preview. If you're on a paid plan, you'll be able to create a Shared Slack Channel benefit. ## Create Shared Slack Channel Benefit 1. Go to [`Benefits`](https://polar.sh/to/dashboard/products/benefits) 2. Click `+ New Benefit` to create a new benefit 3. Choose `Shared Slack Channel` as the `Type` ### **Connect your Slack workspace** The first time you create this benefit, you'll be prompted to connect the Slack workspace where channels should be created. You can reuse the same workspace across multiple benefits. ### **Channel name template** Channel names are generated from a template, so every customer's channel follows the same naming convention. The template supports the following placeholders: * `{customer_name}` โ€” the customer's name * `{customer_email_local}` โ€” the local part of the customer's email (everything before the `@`) * `{metadata.}` โ€” any value stored in the customer's metadata, e.g. `{metadata.company}` For example, `support-{customer_email_local}` produces a channel like `support-jane` for `jane@acme.com`. ### **Other options** * **Private channel** โ€” create the channel as private. Recommended, and enabled by default. * **Welcome message** โ€” an optional message posted to the channel right after it's created. * **Team invitees** โ€” members of your Slack workspace to automatically invite to every channel created for this benefit. * **Archive on revoke** โ€” archive the channel when the benefit is revoked (for example, when a subscription is canceled). Enabled by default. ## Customer Experience When a customer is granted the benefit, they're asked for the email address of an admin in their own Slack workspace. Polar creates the channel, invites your team members, posts your welcome message, and sends a Slack Connect invitation to that admin. Once they accept, the shared channel appears in their workspace and you can start talking right away. # Embedded Checkout Source: https://polar.sh/docs/features/checkout/embed Embed our checkout directly on your site You can either copy and paste our code snippet to get up and running in a second or use our JavaScript library for more advanced integrations. Our embedded checkout allows you to provide a seamless purchasing experience without redirecting users away from your site. ## Code Snippet The code snippet can be used on any website or CMS that allows you to insert HTML. First, create a [Checkout Link](/docs/features/checkout/links) as described in the previous section. The code snippet can directly be copied from there by clicking on `Copy Embed Code`. The snippet looks like this: ```typescript theme={null} Purchase ``` This will display a `Purchase` link which will open an inline checkout when clicked. You can style the trigger element any way you want, as long as you keep the `data-polar-checkout` attribute. ## Import Library If you have a more advanced project in JavaScript, like a React app, adding the ` ``` The same script also powers embedded checkout triggers โ€” one tag covers every Polar embed. | Attribute | Value | Description | | ------------------------------------------ | --------------- | ------------------------------------------------------------------------------------------------- | | `data-polar-payment-method` | `string` | **Required.** The session token. Clicking the element opens the modal. | | `data-polar-payment-method-theme` | `light \| dark` | Optional theme override. | | `data-polar-payment-method-set-as-default` | `true \| false` | Optional. Default `true`. Pass `"false"` to add the card without overriding the existing default. | | `data-polar-payment-method-return-url` | `string` | Optional. Return URL for redirect-based payment methods. Defaults to the current page. | | `data-polar-payment-method-locale` | `string` | Optional. BCP47 locale (e.g. `'en'`, `'fr-FR'`). Unsupported locales fall back to English. | ## Localization The embed is fully localized, pass a BCP47 code via the `locale` option (or `data-polar-payment-method-locale` attribute): ```ts theme={null} const embed = await PolarEmbedPaymentMethod.create({ sessionToken: session.token, locale: "fr-FR", }); ``` When omitted, the embed defaults to English. Unsupported locales also fall back to English. See [Localization](/docs/features/checkout/localization) for the full list of supported languages. ## Events All events are dispatched as cancelable `CustomEvent`s on the `embed` instance. Call `event.preventDefault()` to opt out of the SDK's default action. | Event | Detail | Default action | | ----------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------- | | `loaded` | โ€” | Removes the loader spinner once the iframe is ready. | | `close` | โ€” | Tears down the iframe (unless locked by a pending `confirmed`). | | `confirmed` | โ€” | Marks the modal as non-closable while Stripe is processing. | | `success` | `{ paymentMethodId: string }` | **Auto-closes the modal.** Call `preventDefault()` to keep it open. | | `error` | `{ code: 'invalid_request' \| 'unauthorized' \| 'processing_failed' \| 'unknown' }` | Re-enables closing the modal after a failure. | ## Redirect-based payment methods Some payment methods authorise on the provider's own site. The browser navigates the whole tab away and back to `returnUrl` (defaults to the page the SDK was opened from), so the modal can't survive the round-trip. Read the outcome on the returned page with the static `getRedirectResult()`: ```ts theme={null} import { PolarEmbedPaymentMethod } from "@polar-sh/checkout/payment-method"; const result = PolarEmbedPaymentMethod.getRedirectResult(); // result: { status: 'succeeded' | 'failed' } | null if (result?.status === "succeeded") { // refresh the customer's payment methods } ``` In React, use the `usePaymentMethodRedirectResult` hook to avoid writing your own effect: ```tsx theme={null} import { usePaymentMethodRedirectResult } from "@polar-sh/checkout/react/payment-method"; usePaymentMethodRedirectResult({ onSuccess: () => console.log("Payment method added"), onError: () => console.error("Could not add payment method"), }); ``` Either way, the status query param is stripped from the URL so a refresh won't surface a stale result. Card payments (3DS) complete inside the modal and never trigger this path. # Checkout Links Source: https://polar.sh/docs/features/checkout/links Persistent URLs that create a Checkout Session on visit This is the simplest way to start selling. Configure once in the dashboard, share the URL anywhere (your website, social media, email, a button in your app), and Polar handles the rest. If you need to create Checkout Sessions programmatically (e.g. with per-customer data computed by your backend), use the [Checkout API](/docs/features/checkout/session) directly instead. ## How it works A Checkout Link is a long-lived URL tied to your organization. The link itself doesn't expire, and you can share it indefinitely. When a customer visits the link: 1. Polar reads the link's configuration (products, preset discount, metadata, etc.). 2. A new, short-lived Checkout Session is created from that configuration. 3. The customer is redirected to the session's checkout page to complete the purchase. Each visit produces a brand new Checkout Session. Always share the Checkout Link URL itself, never the URL of a generated Checkout Session, since those are temporary and will expire. ## Create a Checkout Link Checkout Links are managed from the [**Checkout Links**](https://polar.sh/to/dashboard/products/checkout-links) page. Click on **New Link** to create one. ### Products You can select one or several products. With several products, customers can switch between them on the checkout page before paying. Products are shown in the order you list them. Customers always purchase a single product per checkout. Selecting multiple products on a Checkout Link gives the customer a choice between them; it doesn't bundle them together. True multi-product checkout (multiple products in the same order) isn't supported yet. ### Discount You can preset a discount on the link. It will be **automatically applied** when the customer lands on the checkout page, with no action required from them. Useful for running promotions or offering special pricing through specific links. If **Allow discount codes** is enabled, customers can still manually enter another discount code during checkout. Discounts without a code can only be applied through Checkout Link presets or the [Checkout API](/docs/features/checkout/session). Learn more in the [Discounts documentation](/docs/features/discounts). ### Success URL URL where the customer is redirected after a successful payment. If left empty, the customer stays on the Polar-hosted confirmation page. You can add the `checkout_id={CHECKOUT_ID}` query parameter to the Success URL and Polar will substitute it with the actual Checkout Session ID at redirect time. This is handy to fetch the order details from your backend on the confirmation page. ### Return URL URL for the back button shown on the checkout page. When the customer clicks it, they're sent back to this URL (typically your pricing page or the previous page on your site). If not set, no back button is shown. ### Trial If the link's products are subscriptions, you can configure a trial period directly on the Checkout Link. When set, this trial **overrides** the default trial configured on the products themselves, but only for sessions created from this link. This is useful for running campaigns with longer (or shorter) trials than your default, without having to duplicate the product. If left empty, the product's own trial configuration is used. ### Seats If the link's products use seat-based pricing, you can preconfigure a fixed number of seats. When set, the checkout session is locked to that seat countโ€”the customer cannot change it. This is useful for creating links with a predetermined team size. If the products no longer support the configured seat count when the link is opened (e.g., tier limits changed), the value is ignored and the customer can select freely. ### Metadata An optional key-value object for storing extra information. Metadata set on the link is copied to the generated Checkout Session, and propagates to the resulting Order and/or Subscription on success. ## Query parameters Append query parameters to a Checkout Link URL to override or extend its preset configuration on a per-visit basis. ### Preselect a product When a link is configured with several products, point the customer directly to one of them using `product_id`. The customer can still switch products on the checkout page. ID of the product to preselect. Must be one of the products configured on the Checkout Link. ### Prefill fields Prefill the customer email. Prefill the customer name. Prefill the discount code input. Only works with discounts that have a code set. See the [Discounts documentation](/docs/features/discounts) for details. This is different from presetting a discount on the link. A preset discount is silently applied, whereas `discount_code` only fills the input field so the customer can see it. Prefill the amount when the product uses Pay-What-You-Want pricing. Force the checkout page language, given as an IETF BCP 47 language tag (e.g. `en`, `fr`, `pt-BR`). If omitted, the language is detected from the customer's browser. Checkout localization is in beta. See [Checkout Localization](/docs/features/checkout/localization) for details. Prefill custom field data, where `{slug}` is the slug of the custom field. Force the checkout page theme. Accepts `light` or `dark`. If omitted, the theme follows the customer's system preference. ### Attribution and reference metadata These parameters are automatically attached to the generated Checkout Session [`metadata`](/docs/api-reference/checkouts/get-session#response-metadata). Your own reference ID for the Checkout Session. UTM source of the Checkout Session. UTM medium of the Checkout Session. UTM campaign of the Checkout Session. UTM content of the Checkout Session. UTM term of the Checkout Session. # Checkout Localization Source: https://polar.sh/docs/features/checkout/localization Serve checkout in your customer's preferred language Checkout localization is currently in **beta**. To enable it, turn on the feature flag for your organization. ## How it works When checkout localization is enabled for your organization, the checkout page automatically detects the customer's preferred language from their browser settings and displays the checkout in that language. This includes translated labels, placeholders, and descriptions for all standard checkout fields. ## Overriding the language via API You can explicitly set the checkout language by passing the `locale` parameter when creating a checkout session via the API: ```bash theme={null} curl -X POST https://api.polar.sh/v1/checkouts/ \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "locale": "fr" }' ``` When a `locale` is set explicitly, it takes precedence over the browser's language preference. ## Overriding the language via querystring You can append `?locale=` to any checkout link and that will take precedence over the browser's language preference. ## Supported languages * English (en) (default) * Dutch (nl) * Spanish (es) * French (fr) * Swedish (sv) * German (de) * Hungarian (hu) * Italian (it) * Portuguese (Brazil) (pt) * Portuguese (Portugal) (pt-PT) * Korean (ko) * Japanese (ja) * Turkish (tr) * Polish (pl) Want to help us review translations for an upcoming language? Reach out at [translations@polar.sh](mailto:translations@polar.sh). ## Known limitations During the beta period, localization is scoped to the checkout page itself: * **Error messages** are displayed in English * **Transactional emails** (receipts, confirmations) are sent in English * There is **no language selector** on the checkout page โ€” language is determined automatically from the browser, via the API `locale` parameter or via a querystring `?locale`. ## Feedback We'd love to hear your feedback on checkout localization: * Open an issue on [GitHub](https://github.com/polarsource/polar) * Email us at [translations@polar.sh](mailto:translations@polar.sh) # Checkout API Source: https://polar.sh/docs/features/checkout/session Create checkout sessions programmatically for complete control If you want to integrate more deeply the checkout process with your website or application, you can use our dedicated API. The first step is to [create a Checkout session](/docs/api-reference/checkouts/create-session). For this you'll need at least your **Product ID**. You can retrieve your Product ID from Products in your dashboard, click on "context-menu" button in front of your product and click on Copy Product ID. The API will return you an object containing all the information about the session, including **an URL where you should redirect your customer** so they can complete their order. You can force the checkout page theme by appending `?theme=dark` or `?theme=light` to the session URL. Without it, the theme follows the customer's system preference. ## Multiple products You can create a checkout session with multiple products. This is useful if you want to allow your customers to choose between different products before they checkout. Products are shown in the order you pass them in the `products` array. ## Ad-hoc prices For advanced use cases where you need complete control over pricing, you can create ad-hoc prices directly when creating a checkout session. Ad-hoc prices are temporary prices that exist only for that specific checkout session and don't appear in your product's catalog. This is useful when you need to: * Apply dynamic pricing based on user-specific factors * Create custom pricing tiers for specific customers * Implement usage-based or calculated pricing that varies per checkout * Test pricing variations without modifying your product catalog When creating a checkout session, you can pass a `prices` parameter that maps product IDs to an array of price definitions. These prices will be created on-the-fly and associated with the checkout session. Ad-hoc prices are marked with `source: "ad_hoc"` in the API response, while catalog prices have `source: "catalog"`. Ad-hoc prices are temporary and specific to the checkout session. ### Example ```ts TypeScript theme={null} import { Polar } from "@polar-sh/sdk"; const polar = new Polar({ accessToken: process.env["POLAR_ACCESS_TOKEN"] ?? "", }); async function run() { const checkout = await polar.checkouts.create({ products: ["productId"], prices: { "productId": [ { amountType: "fixed", priceAmount: 10000, // $100.00 priceCurrency: "usd", } ] } }); console.log(checkout.url); } run(); ``` ```py Python theme={null} from polar_sdk import Polar with Polar( access_token="", ) as polar: checkout = polar.checkouts.create(request={ "products": [""], "prices": { "": [ { "amount_type": "fixed", "price_amount": 10000, # $100.00 "price_currency": "usd", } ] } }) print(checkout.url) ``` ### Price types Ad-hoc prices support all the same price types as catalog prices: * **Fixed**: A fixed amount price * **Custom**: Pay-what-you-want pricing * **Free**: No charge * **Seat-based**: Pricing based on number of seats * **Metered**: Usage-based pricing tied to a meter For the complete schema of each price type, refer to the [Checkout API reference](/docs/api-reference/checkouts/create-session). ## External Customer ID Quite often, you'll have your own users management system in your application, where your customer already have an ID. To ease reconciliation between Polar and your system, you can inform us about your customer ID when creating a checkout session through the [`external_customer_id`](/docs/api-reference/checkouts/create-session/) field. After a successful checkout, we'll create a Customer on Polar with the external ID you provided. It'll be provided through the `customer.external_id` property in webhooks you may have configured. When `customer_id` or `external_customer_id` is set, the customer's email is pre-filled and the email field is **disabled** on the checkout page. This ensures the order is always linked to the authenticated customer in your system. ## Customer IP address When you create a checkout session, Polar uses the IP address of the request to detect the customer's country. This drives features like: * **Currency auto-detection** for [products with multiple payment currencies](/docs/features/products) * **Pre-filling the billing country** on the checkout page, which is also used to compute taxes If you use [checkout links](/docs/features/checkout/links), this works automatically. But if you create sessions through the API from a **backend, proxy, or edge function** (e.g. your own API, a Cloudflare Worker, a Next.js route handler), Polar will see *your server's* IP โ€” not the customer's โ€” and the detection will be wrong. In that case, forward the customer's IP address as `customer_ip_address` in the request body: ```ts TypeScript theme={null} import { Polar } from "@polar-sh/sdk"; const polar = new Polar({ accessToken: process.env["POLAR_ACCESS_TOKEN"] ?? "", }); const checkout = await polar.checkouts.create({ products: ["productId"], customerIpAddress: request.headers.get("CF-Connecting-IP") ?? undefined, }); ``` ```py Python theme={null} from polar_sdk import Polar with Polar(access_token="") as polar: checkout = polar.checkouts.create(request={ "products": [""], "customer_ip_address": request.headers.get("True-Client-IP") or request.client.host, }) ``` The exact way to read the connecting IP depends on your runtime โ€” for example, `CF-Connecting-IP` on Cloudflare Workers, `x-forwarded-for` behind most proxies, or `True-Client-IP`/`request.client.host` in FastAPI. When `customer_ip_address` is provided, you don't need to set `customer_billing_address.country` yourself โ€” Polar will derive both the country and the currency from the IP. ## SDK examples Using our SDK, creating a checkout session is quite straightforward. ```ts TypeScript theme={null} import { Polar } from "@polar-sh/sdk"; const polar = new Polar({ accessToken: process.env["POLAR_ACCESS_TOKEN"] ?? "", }); async function run() { const checkout = await polar.checkouts.create({ products: ["productId"] }); console.log(checkout.url) } run(); ``` ```py Python theme={null} from polar_sdk import Polar with Polar( access_token="", ) as polar: checkout = polar.checkouts.create(request={ "allow_discount_codes": True, "product_id": "", }) print(checkout.url) ``` # Cost Events Source: https://polar.sh/docs/features/cost-insights/cost-events Track costs by adding cost metadata to your ingested events Cost Insights works by allowing you to add a special `_cost` property to any event you ingest through Polar's Event Ingestion API. These costs are then aggregated and made available through the Metrics API alongside revenue metrics. ## The `_cost` Property ### Basic Structure To track costs, add a `_cost` property to your event's metadata when ingesting events. ```typescript icon="square-js" TypeScript (SDK) theme={null} import { Polar } from "@polar-sh/sdk"; const polar = new Polar({ accessToken: process.env.POLAR_ACCESS_TOKEN, }); await polar.events.ingest({ events: [ { name: "llm.inference", externalCustomerId: "user_123", metadata: { _cost: { amount: 0.025, currency: "usd", }, }, }, ], }); ``` ```json cURL theme={null} POST https://api.polar.sh/v1/events/ingest Content-Type: application/json Authorization: Bearer YOUR_ACCESS_TOKEN { "events": [ { "name": "llm.inference", "external_customer_id": "user_123", "metadata": { "_cost": { "amount": 0.025, "currency": "usd" } } } ] } ``` ### Cost Metadata Schema The `_cost` property has the following structure: * **`amount`** (required): The cost amount in cents as a decimal number * Example: `0.025` for \$0.00025 (a fraction of a cent) * Example: `150` for \$1.50 * Supports up to 17 digits with 12 decimal places for precision * **`currency`** (required): The currency code * Currently only `"usd"` is supported **Amount must be in cents**: The `amount` field represents the cost in cents, not dollars. For example, `100 = \$1.00`, `0.5 = \$0.005` (half a cent), and `0.001 = \$0.00001` (one hundredth of a cent). ## Use Cases ### AI/LLM Applications Track the cost of LLM API calls per customer: ```typescript icon="square-js" TypeScript theme={null} import { Polar } from "@polar-sh/sdk"; import OpenAI from "openai"; const polar = new Polar({ accessToken: process.env.POLAR_ACCESS_TOKEN, }); const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY, }); // Make LLM API call const completion = await openai.chat.completions.create({ model: "gpt-4", messages: [{ role: "user", content: "Hello!" }], }); // Calculate cost (example: $0.03 per 1K input tokens, $0.06 per 1K output tokens) const inputCost = ((completion.usage.prompt * tokens) / 1000) * 3; // in cents const outputCost = ((completion.usage.completion * tokens) / 1000) * 6; // in cents const totalCost = inputCost + outputCost; // Track the cost in Polar await polar.events.ingest({ events: [ { name: "gpt4.completion", customerId: "cus_abc123", metadata: { _cost: { amount: totalCost, currency: "usd", }, _llm: { vendor: "openai", model: "gpt-4", input_tokens: completion.usage.prompt_tokens, output_tokens: completion.usage.completion_tokens, total_tokens: completion.usage.total_tokens, }, }, }, ], }); ``` ### Infrastructure Costs Track compute, storage, or API costs: ```json theme={null} { "events": [ { "name": "video.processing", "external_customer_id": "user_123", "metadata": { "_cost": { "amount": 45.5, // $0.455 "currency": "usd" }, "duration_seconds": 120, "resolution": "1080p" } } ] } ``` ### Third-Party Service Costs Track costs from external services: ```json theme={null} { "events": [ { "name": "email.sent", "external_customer_id": "user_123", "metadata": { "_cost": { "amount": 0.0001, // $0.000001 "currency": "usd" }, "provider": "sendgrid", "recipients": 1 } } ] } ``` ## Best Practices ### Track Costs in Real-Time Ingest cost events as they occur to maintain accurate, up-to-date metrics: ```typescript icon="square-js" TypeScript theme={null} import { Polar } from "@polar-sh/sdk"; const polar = new Polar({ accessToken: process.env.POLAR_ACCESS_TOKEN, }); // When making an LLM API call const completion = await openai.chat.completions.create({ model: "gpt-4", messages: [{ role: "user", content: "Hello!" }], }); const cost = calculateCost(completion.usage); // Cost should be in cents await polar.events.ingest({ events: [ { name: "llm.completion", externalCustomerId: "user_123", metadata: { _cost: { amount: cost, currency: "usd", }, }, }, ], }); ``` ### Use Precise Amounts The `amount` field supports up to 12 decimal places, perfect for tracking micro-costs: ```json theme={null} { "_cost": { "amount": 0.000125, // $0.00000125 "currency": "usd" } } ``` ### Add Context with Additional Metadata Combine `_cost` with other metadata to understand cost drivers: ```json theme={null} { "metadata": { "_cost": { "amount": 0.05, // $0.0005 "currency": "usd" }, "model": "gpt-4-turbo", "tokens": 1000, "feature": "chatbot" } } ``` # Cost Traces Source: https://polar.sh/docs/features/cost-insights/cost-traces Aggregate events by user sessions to calculate costs Cost Traces is a feature that allows you to define session boundaries and calculate costs for each session. Coming soon. # Introduction to Cost Insights Source: https://polar.sh/docs/features/cost-insights/introduction Track costs, profits, and customer lifetime value with event-based cost tracking Cost Insights is a powerful feature that enables you to calculate business-centric metrics like Costs, Profits, and Customer Lifetime Value (LTV) by annotating your events with cost data. ## Overview While Polar automatically tracks revenue from orders and subscriptions, Cost Insights allows you to track the costs associated with delivering your product or service. By combining revenue and cost data, you can gain deep insights into profitability on a per-customer basis. ## How It Works Cost Insights works in three simple steps: 1. **Ingest events with cost data**: Add a `_cost` property to events metadata when ingesting them through Polar's Event Ingestion API 2. **Automatic aggregation**: Polar automatically aggregates costs alongside your revenue data 3. **Query and analyze**: Use the Metrics API, or the Polar Dashboard to retrieve costs, profits, and customer LTV ### Quick Example ```json theme={null} { "events": [ { "name": "llm.inference", "external_customer_id": "user_123", "metadata": { "_cost": { "amount": 0.025, // $0.00025 in cents "currency": "usd" } } } ] } ``` ## Common Use Cases * **AI/LLM Applications**: Track the cost of API calls to OpenAI, Anthropic, or other LLM providers * **Infrastructure Costs**: Monitor compute, storage, or bandwidth costs per customer * **Third-Party Services**: Track costs from email providers, SMS gateways, or other external services * **Customer Profitability**: Calculate profit margins and LTV for each customer ## Documentation Learn how to track costs by adding the `_cost` property to your events metadata Query and analyze costs, profits, and customer lifetime value # Custom Fields Source: https://polar.sh/docs/features/custom-fields Learn how to add custom input fields to your checkout with Polar By default, the Checkout form will only ask basic information from the customer to fulfill the order: a name, an email address, billing information, etc. But you might need more! A few examples: * A checkbox asking the customer to accept your terms * An opt-in newsletter consent * A select menu to ask where they heard from you * ... With Polar, you can easily add such fields to your checkout using **Custom Fields**. ## Create Custom Fields Custom Fields are managed at an organization's level. To create them, go to [**Settings โ†’ Custom Fields**](https://polar.sh/to/dashboard/settings/custom-fields). You'll see the list of all the available fields on your organization. Click on **New Custom Field** to create a new one. You can also manage them programmatically using the [Custom Fields API](/docs/api-reference/custom-fields/create). ### Type The type of the field is the most important thing to select. It determines what type of input will be displayed to the customer during checkout. The type can't be changed after the field is created. We support five types of fields: #### Text This will display a simple text field to input textual data. By default, it'll render a simple input field but you can render a **textarea** by toggling the option under `Form input options`. Under `Validation constraints`, you can add minimum and maximum length validation. Underneath, the data will be stored as a string. #### Number This will display a number input field. Under `Validation constraints`, you can add minimum (`Greater than or equal`) and maximum (`Less than or equal`) validation. Underneath, the data will be stored as an integer. #### Date This will display a date input field. Under `Validation constraints`, you can add minimum and maximum validation. Underneath, the data will be stored as a string using the ISO 8601 format. #### Checkbox This will display a checkbox field. Underneath, the data will be stored as a boolean (`true` or `false`). #### Select This will display a select field with a predefined set of options. Each option is a pair of `Value` and `Label`, the first one being the value that'll be stored underneath and the latter the one that will be shown to the customer. At least one option is required, and you can drag options to reorder them. ### Slug and name The slug determines the key that'll be used to store the data inside objects related to the checkout, like Orders and Subscriptions. It must be unique across your organization and can only contain lowercase letters, numbers, hyphens and underscores. You can change it afterwards, we'll automatically update the data stored on existing Checkouts, Orders and Subscriptions to reflect the new slug. The name is what will be displayed to you to recognize the field across your dashboard. By default, it'll also be the label of the field displayed to the customer, unless you customize it under `Form input options`. ### Form input options Those options allow you to customize how the field is displayed to the customer. You can set: * The label, displayed above the field * The help text, displayed below the field * The placeholder, displayed inside the field when there is no value The label and help text support basic Markdown syntax, so you can add bold, italic or even links. ## Add Custom Field to Checkout Custom Fields are enabled on Checkout specifically on each **product**. While [creating or updating](/docs/features/products) a product, expand the **Checkout Page** section and select the fields you want to include under **Checkout Fields**. Note that you can make each field `Required` for that product. If you make a **checkbox** field **required**, customers will have to check the box before submitting the checkout. Very useful for terms acceptance! The fields are now added as part of the Checkout form for this product. When [creating a Checkout Session](/docs/api-reference/checkouts/create-session) from the API, you can also prefill the fields by setting the `custom_field_data` property, keyed by the **slug** of each field. ## Read data The values input by the customer are stored on the Order or Subscription resulting from the checkout. From your dashboard, open an order or subscription from the **Sales** section: the values are displayed under the **Custom Fields** section of the detail view. This data is also available from the [Orders](/docs/api-reference/orders/get) and [Subscriptions](/docs/api-reference/subscriptions/get) API, under the `custom_field_data` property. Each value is referenced by the **slug** of the field. ```json theme={null} { // ... "custom_field_data": { "terms": true, "source": "social_media" } } ``` # Customer Management Source: https://polar.sh/docs/features/customer-management Get insights on your customers and sales ## Managing Customers Polar has a built in feature to view and manage your Customers. Everyone who has ever purchased something from you will be recorded as a Customer to your Organization. Youโ€™re able to see past orders and their ongoing subscriptions, as well as some additional metrics. ## External ID Quite often, you'll have our own users management system in your application, where your customer already have an ID. To ease reconciliation between Polar and your system, we have a dedicated [`external_id`](/docs/api-reference/customers/get-external#response-external-id) field on Customers. It's unique across your organization and can't be changed once set. We have dedicated API endpoints that work with the `external_id` field, so you don't even have to store the internal Polar ID in your system. ## Metadata You may set additional metadata on Customers. This can be very useful to store additional data about your customer you want to be available through our API and webhooks. It can be set through the dashboard or through the [API](/docs/api-reference/customers/update#body-metadata). It can also be pre-set when creating a Checkout Session by using the [`customer_metadata`](/docs/api-reference/checkouts/create-session#body-customer-metadata) field. This way, after a successful checkout, the metadata will automatically be set on the newly created Customer. # Customer Portal Source: https://polar.sh/docs/features/customer-portal/introduction The self-service destination for your customers The Customer Portal is a hosted, self-service page where your customers can manage everything related to their relationship with your business โ€” without having to email your support team. ## What customers can do From the Customer Portal, your customers can: * View their **active subscriptions** and past **purchase history** * **Download and edit invoices** (e.g. add a company name, VAT number, or billing address) * **Download payment receipts** for every paid order, with the payment method and any refunds * **Access benefits** they're entitled to โ€” license keys, file downloads, Discord access, etc. * **Cancel active subscriptions** on their own * **Update their default payment method** โ€” the primary way for customers to recover from failed payments * Optionally, change their email address, switch subscription plans, manage seats, and view metered usage (see [Settings](/docs/features/customer-portal/settings)) ## Why it matters The Customer Portal isn't just a convenience feature โ€” it's a critical piece of your billing stack: * **Failed payment recovery.** When a subscription renewal fails, the portal is where customers update their card so they don't lose access. Because Polar is PCI-compliant, you never have to handle card details yourself. * **Self-service cancellations.** Some jurisdictions (notably California under the [Automatic Renewal Law](https://oag.ca.gov/consumers/auto-renewing-subscriptions)) legally require customers to be able to cancel a subscription the same way they signed up. The Customer Portal satisfies that requirement out of the box. * **Invoice and receipt access.** Customers retrieve and edit their invoices, and download a receipt for any paid order, without pulling you into a support thread. ## Next steps Use the default portal URL, generate authenticated links, or rely on the emails Polar already sends. Configure what your customers can do from the portal under Settings โ†’ Customer portal. Build your own portal experience on top of the Customer Portal API. ## FAQ **No.** The Customer Portal is always available for your customers, and it can't be turned off. This is a deliberate design decision. The portal is how we guarantee that your customers can always: * **Access their invoices and payment receipts** for tax and bookkeeping. * **Cancel their subscriptions on their own**, which is legally required in some jurisdictions (for example California's [Automatic Renewal Law](https://oag.ca.gov/consumers/auto-renewing-subscriptions), which requires that customers be able to cancel the same way they signed up). * **Update their payment method in a PCI-compliant way**, so they can recover from failed renewals without you having to handle card details. You can, however, [fine-tune what customers can do](/docs/features/customer-portal/settings) from the portal โ€” for example, disabling subscription plan changes or email edits. Not the hosted portal at `polar.sh//portal` โ€” it's intentionally consistent across all Polar organizations. If you need a branded experience, you can build your own portal on top of the [Customer Portal API](/docs/api-reference/customer-portal/get-customer), which covers the day-to-day actions: viewing subscriptions and orders, downloading invoices and receipts, managing benefits and seats, and reading meter usage. Not every action is exposed through the Customer Portal API. Most notably, **updating a default payment method** is only available from the hosted Customer Portal โ€” this is what keeps you PCI-compliant, since card details never touch your servers. Customers you send into a custom portal will still need the hosted one to recover from failed payments. By default, customers authenticate with the email address they used to purchase or subscribe โ€” Polar emails them a one-time code to confirm. You can also skip the email step entirely by generating a pre-authenticated link from your own application. See [Navigate customers to the portal](/docs/features/customer-portal/navigate-customers) for details. You don't have to. Polar already includes a link to the Customer Portal in the transactional emails it sends โ€” order confirmations, subscription renewal notices, failed payment alerts, and more. You may still want to link to it from your own app for convenience, but it's not required for customers to be able to reach it. The customer receives an email from Polar letting them know, with a link to the Customer Portal where they can update their default payment method. Once they do, Polar automatically retries the charge. This self-service flow is the primary way customers recover from failed renewals โ€” keep it in mind when you're thinking about churn. # Navigate Customers to the Portal Source: https://polar.sh/docs/features/customer-portal/navigate-customers Three ways your customers can reach their Customer Portal There are three ways your customers can land on the Customer Portal: the default URL, a pre-authenticated link you generate from your application, or the links Polar automatically includes in the emails it sends to your customers. ## 1. The default portal URL Every organization gets a Customer Portal hosted at: ``` https://polar.sh//portal ``` Customers authenticate by entering the email address they used to purchase or subscribe. Polar then emails them a one-time code to complete sign-in. This URL is a good choice to link from your marketing site, your app's help menu, or a support article โ€” anywhere you need a stable, shareable link. ## 2. Pre-authenticated portal links If your customer is already signed in to your application, you can generate an authenticated link that drops them directly into the portal โ€” no email code required. Under the hood, this calls the [Create Customer Session](/docs/api-reference/customer-portal/sessions/create) endpoint and redirects the user to the `customerPortalUrl` it returns. ```typescript theme={null} import { Polar } from "@polar-sh/sdk"; const polar = new Polar({ accessToken: process.env["POLAR_ACCESS_TOKEN"] ?? "", }); async function run() { const result = await polar.customerSessions.create({ customerId: "", }); redirect(result.customerPortalUrl); } run(); ``` If you're on Next.js, the `@polar-sh/nextjs` adapter wraps this into a single route handler: ```typescript theme={null} // app/portal/route.ts import { CustomerPortal } from "@polar-sh/nextjs"; export const GET = CustomerPortal({ accessToken: process.env.POLAR_ACCESS_TOKEN, getCustomerId: async (req) => "", server: "sandbox", // Use sandbox if you're testing Polar โ€” pass 'production' otherwise }); ``` Point a link in your app at `/portal` and your customer is one click away from managing their billing. Customer Session tokens are short-lived. Always generate a fresh link at the moment the customer clicks, rather than storing the URL. ## 3. Emails Polar sends to your customers You don't have to build anything to get customers to the portal โ€” Polar already includes a link to it in the transactional emails we send on your behalf, including: * **Order confirmation** emails sent after a successful checkout * **Subscription renewal** and **subscription updated** emails * **Failed payment** notifications, so customers can update their card This means that even if you never link to the portal from your own app, your customers already have a way to get back to it from their inbox. # Customer Portal Settings Source: https://polar.sh/docs/features/customer-portal/settings Configure what customers can do from the Customer Portal All Customer Portal settings live under [**Settings โ†’ Customer portal**](https://polar.sh/to/dashboard/settings) in your Polar dashboard. Each setting is a toggle that enables or disables a specific capability for your customers. ## Show metered usage Adds a **Usage** tab to the Customer Portal where customers can see their current consumption for each meter on their subscription. This is only relevant if you've configured [meters](/docs/features/usage-based-billing/meters) on your products. Disabling this toggle hides usage from the portal UI but has no effect on the Customer Portal API โ€” if you're building a custom usage surface in your own app, the [customer meters endpoints](/docs/api-reference/customer-meters/list) continue to work regardless. ## Enable subscription seat management Allows customers to change the number of seats on their active subscription, and to assign or revoke seats for their teammates, directly from the portal. This setting is only relevant when you offer [seat-based prices](/docs/features/seat-based-pricing). If you disable it, customers can't change seat counts or manage assignments from the portal โ€” you'll need to build that flow yourself against the [Customer Seats API](/docs/api-reference/customer-seats/assign). ## Allow email address changes Gives customers the option to change the email address associated with their customer record after purchasing. The new email must be verified through a confirmation link before the change takes effect. When this is enabled, you'll probably want to listen for the [`customer.updated`](/docs/api-reference/webhooks/customer.updated) webhook and sync the new email back into your own user system, so the two stay in sync. ## Enable subscription plan changes Lets customers switch between products on their own โ€” upgrading, downgrading, or moving to a different plan entirely โ€” without contacting you. Plan changes follow your [proration settings](/docs/features/subscriptions/proration). If you'd rather handle plan changes yourself (for example, because you gate them behind custom logic in your app), disable this and drive updates through the [Update Subscription](/docs/api-reference/subscriptions/update) endpoint instead. # Discounts Source: https://polar.sh/docs/features/discounts Create discounts on products and subscriptions Discounts are a way to reduce the price of a product or subscription. They can be applied to one-time purchasable products or subscriptions. ## Create a discount Go to the [**Discounts**](https://polar.sh/to/dashboard/products/discounts) page #### Name Displayed to the customer when they apply the discount. #### Code Optional code (case insensitive) that the customer can use to apply the discount. If left empty, the discount can only be applied through a Checkout Link or the API. #### Percentage Discount The percentage discount to apply to the product or subscription. #### Fixed Amount Discount The discount deducts a fixed amount from the price of the product or subscription. #### Recurring Discount The percentage discount to apply to the product or subscription. * **Once** The discount is applied once. * **Several Months** The discount is applied for a fixed number of months. * **Forever** The discount is applied indefinitely. #### Restrictions * **Products** The discount can only be applied to specific products. By default the discount can be applied to all products, also ones created after the discount was created. * **Starts at** The discount can only be applied after this date * **Ends at** The discount can only be applied before this date * **Maximum redemptions** The maximum number of times the discount can be applied. ## Apply a discount ### Auto-apply via Checkout Link When creating a [Checkout Link](/docs/features/checkout/links), you can preset a discount that will be automatically applied when customers land on the checkout page. This is useful for promotional campaigns or special offers where you want to guarantee the discount is applied without requiring customers to enter a code. Discounts without a code can only be auto-applied through Checkout Links or the API. ### Prefill via query parameter You can pass a `discount_code` query parameter to any Checkout Link URL to prefill a discount code in the checkout form. Note that this only prefills the fieldโ€”customers will still see the code and it will be visible in the form. ### Apply via API When creating a Checkout Session via the API, you can specify a discount to apply programmatically. See the [Checkout API documentation](/docs/features/checkout/session) for details. # Payout Accounts Source: https://polar.sh/docs/features/finance/accounts Connect a Stripe Connect Express account to receive your earnings A **payout account** is the external account Polar uses to send you your earnings. As the [Merchant of Record](/docs/merchant-of-record/introduction), Polar collects the money from your customers, then [transfers your balance](/docs/features/finance/payouts) โ€” minus our fees โ€” to your payout account. From there, the funds are deposited into your bank account. Polar uses [Stripe Connect Express](https://stripe.com/connect) for payout accounts. You need to connect a payout account **before Polar can accept money on your behalf**. We don't want to collect funds from your customers that we wouldn't be able to pay out to you, so a working payout account is a prerequisite to going live. ## Connecting a Payout Account 1. Open the [**Finance โ†’ Account**](https://polar.sh/to/dashboard/finance/account) page in your Polar dashboard. 2. Click **Continue with account setup**. 3. Select your country: * If this is a personal account, pick your **country of residence**. * If this is a business or organization, pick the **country of tax residency**. 4. You will be redirected to Stripe to complete onboarding. Stripe will ask for your identity details, business information (if applicable), and the bank account you want to be paid out to. 5. Once Stripe confirms your account, you're ready to receive payouts. The user who completes onboarding becomes the **owner** of the payout account. They own the underlying Stripe account and are the legal recipient of the funds. ## Reusing a Payout Account Across Organizations A single payout account can be linked to **multiple organizations**. If you run several organizations on Polar and want all of their earnings to settle into the same bank account, you don't need to repeat Stripe onboarding for each one. From the [**Finance โ†’ Account**](https://polar.sh/to/dashboard/finance/account) page of a new organization, you can either: * Connect a brand-new payout account, or * Select an existing payout account from one of your other organizations. Only one payout account is active per organization at a time, but you can switch the active account from the **Manage payout accounts** modal. The active account is the one that will receive the next payout from that organization. ## Managing Your Account From [**Finance โ†’ Account โ†’ Manage payout accounts**](https://polar.sh/to/dashboard/finance/account) you can: * **Open in Stripe** โ€” jump into the Stripe-hosted dashboard to update your bank account and business details. * **Make Active** โ€” switch the organization to a different payout account you already own. * **Add Payout Account** โ€” start onboarding for a new payout account (for example, in a different country). * **Delete** โ€” remove a payout account you no longer use. You can only delete an account that is not linked to any organization, has no pending payouts, and has a zero Stripe balance. ## Country & Currency Requirements Stripe Connect requires the bank account you connect to be in the **same country as the business** and to use that country's **local currency**. This is a Stripe (and underlying banking network) requirement that Polar cannot override. No. Stripe Connect requires a bank account in the same country as the business, in the local currency. An Irish company must connect an Irish (EUR) bank account; a US company must connect a US (USD) bank account, and so on. Only if the virtual account provides a real bank account in the same country as your registered business, in the local currency, and is accepted by Stripe. Most multi-currency or "borderless" accounts (Wise, Payoneer, Revolut, etc.) do not satisfy Stripe's verification for Connect payouts. If Stripe rejects the account during onboarding, you'll need to use a domestic bank account instead. Pick the country where the business is legally registered for tax purposes. For a personal account, pick your country of residence. Polar supports the countries listed in our [supported countries for payouts](/docs/merchant-of-record/supported-countries#payouts). If yours isn't listed during onboarding, we can't currently issue payouts there. # Account Balance & Transparent Fees Source: https://polar.sh/docs/features/finance/balance Monitor your Polar balance without hidden fees You can see your available balance for payout at any time under your `Finance` page. Your balance is all the earnings minus: 1. Any VAT we've captured for remittance, i.e balance is excluding VAT 2. Our revenue share (varies by plan โ€” see [fees](/docs/merchant-of-record/fees)) All historic transactions are available in chronological order along with their associated fees that have been deducted. Note: Upon [payout (withdrawal)](/docs/features/finance/payouts), Stripe incurs additional fees that will be deducted before the final payout of the balance. ## Multiple payment currencies orders and settlement When customers purchase your products in currencies other than USD, Polar automatically converts these amounts to USD (the settlement currency) for your account balance. This conversion ensures that all transactions are consolidated into a single currency for easier financial management and payout processing. The conversion process uses current exchange rates at the time of the transaction, and the converted USD amount is what appears in your account balance and is available for payout. ## Payouts in ISK, HUF, TWD, or UGX For accounts using Icelandic krรณna (ISK), Hungarian forint (HUF), New Taiwan dollar (TWD), or Ugandan shilling (UGX), Stripe requires payout amounts to be in whole currency units. This means any fractional amount (less than 1 ISK/HUF/TWD/UGX) will remain in your balance and be included in your next payout. # Payouts Source: https://polar.sh/docs/features/finance/payouts Withdraw money from your Polar account You can issue a withdrawal โ€” also called a payout โ€” once your balance meets the minimum threshold for your account currency. Polar transfers the balance, minus Stripe payout fees (see below), to your Stripe account, and Stripe issues the payout from there. ## Payout Delay Transactions are subject to a **7-day settlement delay** from the date they occur before the corresponding funds become available for payout. This delay applies by default to organizations created on or after May 12, 2026. Organizations created before this date continue to receive instant payouts. We may also extend this delay or hold specific transactions when it makes sense โ€” most commonly to align your settlement window with your refund policy, and occasionally for risk or compliance reasons. ## Minimum Payout Thresholds The minimum balance required to issue a payout varies based on the payout currency. Certain currencies have a higher limit due to Stripe requirements, and these limits can change. | Currency | Minimum Balance (USD) | | ---------------- | --------------------- | | USD | \$10.00 | | EUR | \$13.00 | | GBP | \$15.00 | | CHF | \$15.00 | | AOA | \$30.00 | | ALL | \$40.00 | | AMD | \$40.00 | | AZN | \$40.00 | | BAM | \$40.00 | | BOB | \$40.00 | | BTN | \$40.00 | | CLP | \$40.00 | | GHS | \$40.00 | | GMD | \$40.00 | | GYD | \$40.00 | | KHR | \$40.00 | | KRW | \$40.00 | | LAK | \$40.00 | | MDL | \$40.00 | | MGA | \$40.00 | | MKD | \$40.00 | | MNT | \$40.00 | | MYR | \$40.00 | | MZN | \$40.00 | | NAD | \$40.00 | | PYG | \$40.00 | | RSD | \$40.00 | | THB | \$40.00 | | TWD | \$40.00 | | UZS | \$40.00 | | COP | \$50.00 | | Other currencies | \$10.00 (default) | A handful of countries also enforce a higher minimum: | Country | Minimum Balance (USD) | | ----------- | --------------------- | | Bahamas | \$30.00 | | El Salvador | \$30.00 | | Panama | \$50.00 | ## Stripe Payout Fees Each payout to your Stripe account carries a fee: 1. \$2 per month in which you have at least one active payout 2. 0.25% + \$0.25 per payout 3. Cross-border fees (currency conversion): 0.25% within the EU, up to 1% elsewhere ## Manual Withdrawal Because of those fixed per-payout costs, withdrawals are initiated manually rather than on an automatic schedule. This way you decide when to incur the fees and can batch earnings into fewer, larger payouts instead of paying overhead on every transaction. ## Reverse invoices Polar invoices your customers directly as the Merchant of Record. For your own accounting, you'll need to invoice Polar in return for each payout. You can generate a **reverse invoice** โ€” detailing the sales we made on your behalf, minus our fees โ€” from the **Payouts** page under **Finance** in your dashboard. Click the ellipsis next to a payout and select **Download invoice**. A modal will open, allowing you to: * Set your billing name and address. * Add information shown below your billing address โ€” a good place for your VAT number or any other details your local tax rules require on an invoice. * Add notes shown at the bottom of the invoice. * Customize the invoice number. By default, we generate one like `POLAR-0001`, but you can change it to your own format and sequence. Once the reverse invoice is generated, it cannot be changed. Make sure to double-check the information before generating it. ### Sample Reverse Invoice