# Create Checkout Session Source: https://polar.sh/docs/api-reference/checkouts/create-checkout-session /openapi.json post /v1/checkouts/ Create a checkout session. **Scopes**: `checkouts:write` # Get Checkout Session Source: https://polar.sh/docs/api-reference/checkouts/get-checkout-session /openapi.json 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-checkout-session-from-client /openapi.json 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-checkout-sessions /openapi.json get /v1/checkouts/ List checkout sessions. **Scopes**: `checkouts:read` `checkouts:write` # Update Checkout Session Source: https://polar.sh/docs/api-reference/checkouts/update-checkout-session /openapi.json 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-checkout-session-from-client /openapi.json patch /v1/checkouts/client/{client_secret} Update a checkout session by client secret. # API Overview Source: https://polar.sh/docs/api-reference/introduction Official SDK quickstarts, base URLs, authentication, pagination, rate limits, and API concepts `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/` ## Official SDKs Use our new, fully typed SDKs to integrate with the Polar API from TypeScript or Python. The SDKs are currently in public preview. Install the pre-release explicitly to try them before the stable release. Create an [organization access token](/docs/integrate/oat), then install the SDK and make your first request: ```bash npm theme={null} npm install @polar-sh/sdk@next ``` ```typescript app.ts theme={null} import { createPolar } from "@polar-sh/sdk/2026-04"; const polar = createPolar({ accessToken: process.env.POLAR_ACCESS_TOKEN!, }); const customerState = await polar.customers.getStateExternal("customer_external_id"); console.log(customerState); ``` ```bash uv theme={null} uv add polar-sdk --prerelease allow ``` ```bash pip theme={null} pip install --pre polar-sdk ``` ```python main.py theme={null} import os from polar.v2026_04 import Polar polar = Polar(os.environ["POLAR_ACCESS_TOKEN"]) customer_state = polar.customers.get_state_external("customer_external_id") print(customer_state) ``` Both clients use production by default. Pass `environment="sandbox"` in Python or `environment: "sandbox"` in TypeScript to use the [sandbox environment](/docs/integrate/sandbox). ## 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-sessions/create-customer-session) 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" ``` ## 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/validate-license-key), [activation](/docs/api-reference/customer_portal/activate-license-key), and [deactivation](/docs/api-reference/customer_portal/deactivate-license-key) 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. # 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/customerstate_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/validate-license-key) 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-checkout-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-checkout-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-checkout-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-checkout-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-custom-field). ### 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-checkout-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-order) and [Subscriptions](/docs/api-reference/subscriptions/get-subscription) 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-customer-by-external-id#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-customer#body-metadata). It can also be pre-set when creating a Checkout Session by using the [`customer_metadata`](/docs/api-reference/checkouts/create-checkout-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, do more — change their email address, switch subscription plans, manage seats, pause and resume subscriptions, view metered usage, and so on — depending on which toggles you enable under [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-sessions/create-customer-session) 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-customer-meters) 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-seat). ## 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/customerupdated) 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-subscription) endpoint instead. ## Enable subscription pause Lets customers pause their own subscription from the portal and resume it later, without contacting you. A paused subscription stops billing at the end of the current period and revokes benefits until it resumes; resuming starts a new billing period and charges immediately. See [Pause and resume](/docs/features/subscriptions/manage#pause-and-resume) for the full behavior. This toggle only gates the portal. Pausing and resuming through the [Update Subscription](/docs/api-reference/subscriptions/update-subscription) endpoint is always available to you, so you can drive it from your own backend whether or not customers can self-serve. # 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, counting across all customers. * **Maximum redemptions per customer** The maximum number of times a single customer can apply the discount. See [Per-customer limits](#per-customer-limits). ## Per-customer limits A per-customer limit caps how many times a single buyer can redeem a code, while leaving the code open to everyone else. Share it publicly, set the limit to 1, and each buyer gets one redemption. Polar identifies a customer by three signals, and a match on any one is enough: * The (external) customer ID on the checkout or the subscription. * The email address (ignoring plus-aliases: `ada+polar@example.com` matches `ada@example.com`) * The payment card, so a new email address on the same card still counts. Free products and 100% forever subscriptions never ask for a card, so those match on ID and email alone. Refunding an order frees the customer's slot, and they can use the code again (note: a partial refund doesn't). [Polar-issued refunds as part of chargeback prevention](/docs/features/refunds) also don't free the customer's slot. ## 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