# Account reviews Source: https://staging-docs.crosspoint.finance/africa/account-reviews What we check before you can take payments, and how long it takes. Before you can take payments, our team reviews your account by hand, usually within one business day. This keeps Crosspoint safe for you, your customers and every other seller. ## What we check We look at four things: | What | What we're looking for | | - | - | | Your organization | Your details are complete and accurate. | | Your product | What you sell is clear and fits our [acceptable use policy](/merchant-of-record/acceptable-use/introduction). | | Your identity | The organization owner is [verified](/africa/identity-verification). | | Your payout account | Your [payout account](/africa/payout-accounts) is a bank account (Nigeria) or mobile money wallet in your own verified legal name, or an approved stablecoin wallet. | > [!TIP] > > **Set things up fully before you submit.** Clear product details, a website and a support email make the review faster. ## How identity verification fits in Identity verification is part of the review. Until your identity is verified and your organization has been reviewed, your account isn't activated: customers can't pay you yet. * **In Nigeria**, BVN verification is instant. * **In every other African country**, our team reviews your ID by hand. Your status shows **Pending** until then, and we email you when it's approved or rejected. See [Identity verification](/africa/identity-verification) for the steps. ## Submitting for review When everything on **Finance > Account** is ticked, click **Submit for review**. The page then says **Our team is reviewing your account**. We email you when you're approved, and you can start taking payments straight away. If we can't approve your account, we email you why. ## Later reviews As your sales grow, we take another look at your account now and then. You keep selling while we do. Payouts you request in the meantime are **held** until the review is done, then they move on like any other payout. See [Payouts](/africa/payouts) for the full list of payout statuses. ## Getting ready **1. Complete onboarding** Fill in your personal details, business details and product details. **2. Verify your identity** Use your BVN (Nigeria) or upload an ID (every other African country). You can do this from **Finance > Account**. **3. Add a payout account** Add a payout account from **Finance > Payouts**: a bank account (Nigeria) or mobile money wallet in your own name, or a stablecoin wallet. **4. Submit for review** Add your website and support email on **Finance > Account**, then click **Submit for review**. We email you when you're approved. ## FAQ > **How long does the review take?** > > Usually within one business day. We review every seller by hand. > **Can I take payments while I wait?** > > Not on your first review: checkout is turned on once you're approved. During later reviews you keep selling, and payouts are held until we're done. > **What if my product doesn't fit the acceptable use policy?** > > Read the [acceptable use policy](/merchant-of-record/acceptable-use/introduction) before you start. If you're not sure, email us first. ## Contact Questions about your review? Email [support@crosspoint.finance](mailto:support@crosspoint.finance). # Balance Source: https://staging-docs.crosspoint.finance/africa/balance What your Crosspoint balance is, when money becomes available, and how to withdraw it. Your balance is the money you've earned on Crosspoint that's waiting to be paid out to you. ## What's in your balance Your balance is your sales, minus: * Crosspoint's fees, and * any refunds or disputes. ## It's tracked in US dollars, paid in your currency Your balance is shown in US dollars so sales in different currencies add up in one place. If a customer pays in another currency, the order is converted to USD for your balance. You're never paid out in US dollars. When we send a payout, we convert it to your local currency at the exchange rate on the day (naira to a Nigerian bank account, your local currency to mobile money), or send USDT or USDC to your stablecoin wallet. ## Available vs. on the way Your balance has two parts: | Part | What it means | | - | - | | Available | Money you can withdraw now. | | On the way | Money from recent sales that isn't ready to withdraw yet. | Money from a sale becomes available after a settlement delay. Once it's available, you can withdraw it. ## Withdraw your balance You withdraw your balance with a [payout](/africa/payouts). A payout always takes your whole available balance and sends it to your [payout account](/africa/payout-accounts). How much you can withdraw depends on your verification tier. See [Payout limits](/africa/payout-limits). > [!NOTE] > > If a payout fails, the full amount goes back to your balance. Questions about your balance? Email [support@crosspoint.finance](mailto:support@crosspoint.finance). # Fees Source: https://staging-docs.crosspoint.finance/africa/fees One fee per payment, no plans, no monthly costs. You choose whether your customer or your store pays it. Crosspoint is free to use. You only pay when you get paid. ## The fee **6% + \$0.50** on every successful payment. That's it. There's no monthly subscription, no plan to pick, no extra fee for international cards and no fee for payouts. The fee is worked out on the price after any discount. In currencies other than US dollars, the fixed part is a set amount in that currency. ## Who pays it You choose in **Settings → Fees**. By default, your customer pays it. | A \$10 product | Customer pays the fee | Store pays the fee | | - | - | - | | Your customer pays | \$11.10 | \$10.00 | | You receive | \$10.00 | \$8.90 | | Checkout shows | One total (\$11.10) | \$10.00 | | Receipt and invoice | Subtotal, processing fee, total | The price | > [!NOTE] > > **Subscriptions:** your store always pays the fee, on the first payment and on every renewal, whatever the setting. A subscriber pays the plan price you set. ## Refunds and disputes * **Refunds:** the fee on a refunded payment isn't returned, because the card networks still charge for the original payment. See [Refunds](/features/refunds). * **Disputes:** when a customer disputes a payment with their bank, you can accept it or send your evidence from **Sales → Disputes**. ## Payouts Crosspoint doesn't charge a fee to withdraw your balance to your bank account, mobile money wallet or stablecoin wallet. You see the exact amount before you confirm a payout. See [Payouts](/africa/payouts). ## Questions Selling a lot, or have a special case? Email [support@crosspoint.finance](mailto:support@crosspoint.finance). # Verify your identity Source: https://staging-docs.crosspoint.finance/africa/identity-verification How identity verification works in Nigeria (BVN) and in every other African country (ID upload). Before we can pay you, we need to know who you are. Verifying your identity protects you, your customers and other sellers from fraud. It's also what unlocks your first payouts. Until your identity is verified and your organization has been [reviewed](/africa/account-reviews), your account isn't activated and you can't get paid out. ## Where to do it Verifying your identity is the last step of onboarding, after personal details, business details and product details. You can skip it and come back later from your dashboard: * **Finance > Account**, or * your account settings. > [!WARNING] > > Enter your legal name, date of birth and country **exactly as they appear on your official ID**. We check your ID against these details. If they don't match, verification fails. ## Nigeria: verify with your BVN If you live in Nigeria, you verify with your Bank Verification Number (BVN). It's instant. **1. Open Verify your identity** Go to **Finance > Account** in your dashboard, or finish the last step of onboarding. **2. Enter your BVN** Type in your 11-digit BVN. **3. We check it** We check your BVN with our verification partner against the legal first name, last name and date of birth you entered. **4. Get your result** You see the result straight away. If it matches, you're verified. What has to match: * **Name.** Every part of your legal name must be on your BVN. The order doesn't matter, and a middle name is fine. Capital letters and accents are ignored, but the spelling must match. * **Date of birth.** It must match exactly. A few rules: * One BVN can only verify one Crosspoint account. * You get up to 5 failed attempts every 24 hours. ## Other African countries: upload your ID If you live anywhere else in Africa, you upload a photo or scan of your ID. Our team reviews it by hand. **1. Open Verify your identity** Go to **Finance > Account** in your dashboard, or finish the last step of onboarding. **2. Choose your document** Pick one: * an **international passport**: upload the photo page, or * a **national ID card** or **driver's licence**: upload the front and the back. **3. Upload clear files** Use JPG, PNG, WebP or PDF, up to 8 MB each. Make sure all four corners are visible and your name and date of birth match your legal details. **4. Wait for our review** Your status shows **Pending** on your dashboard while we review it. We email you when it's approved or rejected. If your ID is rejected, you'll see the reason on your dashboard and you can upload again. ## What we store * **BVN:** we keep only the last 4 digits. We never store your full BVN. * **ID documents:** your images are stored privately. Only our review team can see them. ## Your legal details are locked once verified Once your identity is verified, or while your ID is being reviewed, you can't change your legal name, date of birth or country. If any of them are wrong, email [support@crosspoint.finance](mailto:support@crosspoint.finance). If your ID is rejected, these details unlock again so you can fix them. ## FAQ > **My name doesn't match my BVN** > > Go to **User settings** and update your legal name so every part of it is on your BVN, spelled the same way. Then try again. > **My date of birth doesn't match** > > Go to **User settings** and update your date of birth so it's exactly the same as on your BVN or ID. Then try again. > **My ID was rejected** > > Check the reason shown on your dashboard, fix the problem, and upload again. Common causes are a blurry photo, cut-off corners, or a name or date of birth that doesn't match your legal details. A rejected ID also unlocks your legal details, so you can correct them first. > **I don't know my BVN** > > Dial `*565*0#` from the phone number linked to your bank account. Your BVN will be shown to you. > **I've had too many failed attempts** > > You can try up to 5 times every 24 hours. Wait, check your legal details in **User settings**, then try again. > **Can I change my name after I'm verified?** > > Not yourself. Your legal name, date of birth and country are locked once you're verified or while your ID is in review. Email [support@crosspoint.finance](mailto:support@crosspoint.finance) and we'll help. ## Next steps Once you're verified, [add a payout account](/africa/payout-accounts) and check your [payout limits](/africa/payout-limits). # Crosspoint: get paid for what you build Source: https://staging-docs.crosspoint.finance/africa/introduction Sell software, courses and digital products to customers anywhere, from anywhere in Africa, and get paid out to your bank or mobile money. ![](https://mintcdn.com/crosspoint/yvxJ2OtopJFsT5kH/assets/welcome.png?fit=max\&auto=format\&n=yvxJ2OtopJFsT5kH\&q=85\&s=2127d7342a5ab44fbd81194d79969125) ## What is Crosspoint? Crosspoint lets you sell what you build to customers anywhere in the world, from anywhere in Africa. Your customers pay by card or wallet; you get paid out in your local currency, to your bank account in Nigeria or your mobile money wallet anywhere else in Africa, or in stablecoin. We're the [merchant of record](/africa/merchant-of-record): we take the payments, send the receipts, check for fraud and handle disputes, so you don't need a payment company of your own. > **Sell to anyone** > > One-time purchases, subscriptions and pay-what-you-want, with a checkout that works in your customer's language. > **Get paid at home** > > Withdraw in your local currency or in stablecoin. See [Payouts](/africa/payouts). ## What you can do with it > **[One-time purchases](/features/products)** > > Software, courses, templates and downloads. > **[Subscriptions](/features/subscriptions/introduction)** > > Recurring billing with automatic renewals. > **[Usage-based billing](/features/usage-based-billing/introduction)** > > Charge for what your customers use. ### Checkout ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/introduction/checkout.light.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=43af70d0ef949cd79c85a0be5e95b39f) > **[Checkout links](/features/checkout/links)** > > No code: create a link and share it anywhere. > **[Embedded checkout](/features/checkout/embed)** > > Let customers pay without leaving your website. > **[Checkout API](/api-reference)** > > Create checkouts from your own code. ### Deliver automatically Customers get what they paid for straight away, with no manual work. > **[License keys](/features/benefits/license-keys)** > > Generate and deliver software licenses. > **[File downloads](/features/benefits/file-downloads)** > > Deliver files up to 10GB. > **[GitHub access](/features/benefits/github-access)** > > Invite customers to private repositories. > **[Discord access](/features/benefits/discord-access)** > > Give customers a role on your server. ## Get started **1. Create your account** [Sign up for Crosspoint](https://staging.crosspoint.finance/signup) with GitHub or your email, and create your store. ![](https://mintcdn.com/crosspoint/FhlE9Zd-9_RH7whj/assets/create-org.light.png?fit=max\&auto=format\&n=FhlE9Zd-9_RH7whj\&q=85\&s=41f753665f4dab72f371ede67a2b6b3c) **2. Get verified** Verify your identity and we'll review your store, usually within one business day. See [Identity verification](/africa/identity-verification) and [Account reviews](/africa/account-reviews). **3. Add a product** Choose a one-time purchase or a subscription, set your price and pick what customers get. See [Products](/features/products). **4. Start selling** **No code** Create a [checkout link](/features/checkout/links) and share it on your website, social media or by message. **On your website** Add [embedded checkout](/features/checkout/embed) so customers pay without leaving your site. **With code** Use our [SDKs](/africa/sdks) or the [API](/api-reference) to create checkouts and manage customers from your app. **5. Stay in sync** Set up [webhooks](/integrate/webhooks/endpoints) to hear about purchases, subscription changes and refunds as they happen. > [!TIP] > > Want to try it first? The [sandbox](/africa/sandbox) is a full copy of Crosspoint with test money: build and test your integration before you go live. ## Pricing > **[6% + \$0.50 per payment](/africa/fees)** > > No plans, no monthly fee, no international card fee, no payout fee. Your customer pays the fee by default, or your store can pay it. ## Build with code > **[TypeScript / JavaScript](/africa/sdk-typescript)** > > For web and Node.js apps. > **[Python](/africa/sdk-python)** > > For Django, Flask and FastAPI. ## Need help? > **[Join our Slack community](https://join.slack.com/t/crosspoint-b0i2223/shared_invite/zt-4dtuza6ir-WFVJRxXK30GfQ5laIvq9JQ)** > > Meet other builders and ask questions. > **[Contact support](/support)** > > We're here to help. # IP allowlist Source: https://staging-docs.crosspoint.finance/africa/ip-allowlist Make an API token work only from your servers' IP addresses. An organization access token with an IP allowlist only works from those addresses. A request with the token from anywhere else is refused, so a leaked token can't be used. ## Set it up 1. Create a token in **Settings > Preferences > Developers**, if you haven't. 2. Open **Settings > IP allowlist**. 3. Next to the token, choose **Limit IPs** and enter your servers' addresses, one per line: ``` 41.58.12.7 41.58.12.0/24 2c0f:f248::/32 ``` Single addresses and ranges (CIDR), IPv4 and IPv6, up to 50 per token. **Allow any IP** removes the restriction. Only the store's owners and admins can change it, and only from the dashboard: a token can't change its own list. ## What a blocked request gets ```json theme={null} { "error": "TokenIPNotAllowed", "detail": "This API token can't be used from 196.1.1.1. Add the address to the token's allowed IPs (dashboard: Settings > IP allowlist)." } ``` with status `403`. The address in the message is the one Crosspoint saw, so you know what to add. > [!TIP] > > Hosts like Vercel or serverless functions call from changing addresses. Use an allowlist for servers with fixed outbound IPs, or ask your host for its egress ranges. ## Related * [SDKs](/africa/sdks) * [Sandbox](/africa/sandbox) # Merchant of Record Source: https://staging-docs.crosspoint.finance/africa/merchant-of-record Crosspoint sells to your customers on your behalf, so you can take payments from anywhere without setting up a payment company. When a customer buys from you through Crosspoint, Crosspoint is the **merchant of record**: the business that sells to your customer and takes their payment. You make the product; we handle the money side. ## What that means for you You don't need your own card processor or a business that card networks approve. You sign up, get your identity verified and your account reviewed, and you can start selling to customers anywhere in the world. | Crosspoint takes care of | You take care of | | - | - | | Taking payments by card and wallet (Apple Pay, Google Pay) | Your product and delivering what you promised | | Receipts and invoices for your customers | Supporting your customers with your product | | Checking payments for fraud | Your refund policy (we can still refund to stop fraud or a chargeback) | | Handling disputes with the card networks, with your evidence | Answering disputes in **Sales → Disputes** | | Paying your balance out in your local currency or in stablecoin | Keeping your payout account up to date | ## What your customers see * Their card statement shows **CROSSPOINT**, so it's clear who charged them. * Their receipt and invoice come from Crosspoint, with your store's name and product on them. * The checkout tells them their order is processed by Crosspoint as the reseller. ## What you can sell Digital products and services: software, apps, courses, templates, downloads, memberships and the like. Read the [acceptable use policy](/merchant-of-record/acceptable-use/introduction) before you start, and see [Account reviews](/africa/account-reviews) for how we check new stores. ## Learn more > **[Fees](/africa/fees)** > > 6% + \$0.50 per payment, nothing else. > **[Supported countries](/africa/supported-countries)** > > Sell from any of the 54 African countries. > **[Payouts](/africa/payouts)** > > Withdraw to your bank or mobile money. > **[Account reviews](/africa/account-reviews)** > > How we review new stores. # Payment Methods Source: https://staging-docs.crosspoint.finance/africa/payment-methods How your customers can pay you through Crosspoint Checkout Crosspoint Checkout shows each customer the ways they can pay, based on where they are, the checkout currency and their device. You don't need to switch anything on. ## Available now | Payment method | Who can use it | | - | - | | Debit and credit cards (Visa, Mastercard, American Express) | Customers anywhere in the world. One-time purchases and subscriptions | | Apple Pay | Customers using Safari on an Apple device with Apple Pay set up | | Google Pay | Customers using Chrome with Google Pay set up | > [!NOTE] > > On Crosspoint's hosted checkout, Apple Pay and Google Pay need no setup: they appear automatically when the customer's device supports them. > > If you use [Embedded Checkout](/features/checkout/embed#enable-wallets-in-embedded-checkout) on your own website, you'll need to validate your domain for wallets to show. ## Coming soon: local payments We're connecting local payment partners so your customers in Africa can pay the way they already do: | Payment method | Where | | - | - | | Bank transfer | Nigeria first, then more countries | | USSD | Nigeria | | Verve cards | Nigeria | | Mobile money (M-Pesa, MTN MoMo, Airtel Money and others) | Kenya, Ghana, Uganda, Tanzania and more | > [!NOTE] > > We'll announce each method when it goes live. We don't have dates to share yet. ## Getting paid Whichever way your customers pay, the money lands in your Crosspoint [balance](/africa/balance). You withdraw it with a [payout](/africa/payouts), in your local currency to your bank account (Nigeria) or mobile money wallet (every other African country), or in stablecoin. ## Other payment methods The list above is everything we support today. There's no setting or plan that unlocks more, and payment methods can't be turned on per organization. Want a payment method we don't have yet? Tell us at [support@crosspoint.finance](mailto:support@crosspoint.finance). # Payout accounts Source: https://staging-docs.crosspoint.finance/africa/payout-accounts Add where you want to receive your money: a bank account in Nigeria, a mobile money wallet in every other African country, or a stablecoin wallet. Your payout account is where we send your money. What you can add depends on where you are: | Where you are | Payout account | You receive | | - | - | - | | Nigeria | Bank account | Naira | | Every other African country | Mobile money wallet | Your local currency | | Anywhere in Africa | Stablecoin wallet (on request) | USDT or USDC | We never pay out in US dollars. Your balance is tracked in USD, and each payout is converted to your local currency at the exchange rate on the day we send it. ## Before you start * **Verify your identity first.** The organization owner must be [verified](/africa/identity-verification) before a payout account can be added. * **Bank and mobile money accounts must be in your own name.** We only pay out to accounts in your own verified legal name. Accounts in someone else's name, or a friend's or family member's wallet, won't work. * **It must be in Africa.** The bank or mobile money wallet must be in an African country. ## Add a payout account **1. Open payout settings** Go to **Finance > Payouts** (or **Finance > Account**) in your dashboard. **2. Choose your country** The kinds of payout account you can add depend on your country. **3. Choose how to get paid** A bank account (Nigeria), mobile money (every other African country), or stablecoin. **4. Enter your details and save** Fill in the details for that account type (see below). Check everything carefully: a wrong number can make a payout fail. ### Bank account (Nigeria) | Field | What to enter | | - | - | | Bank | Choose your bank from the list. You can search it. | | Account number | Your 10 digit account number (NUBAN). | | Account name | Filled in for you. Your bank tells us the name on the account. | The account name comes from your bank, and you can't edit it. It must match your verified legal first and last name. If it doesn't, for example because the account is in someone else's name, you'll see the name your bank has and the account can't be added. Add an account in your own name instead. ### Mobile money (every other African country) | Field | What to enter | | - | - | | Provider | Your mobile money provider | | Phone number | The number linked to your wallet, with country code, for example `+254712345678` | | Account name | The name on the wallet. It must match your legal name. | The providers you see depend on your country. They include: * M-Pesa * MTN MoMo * Airtel Money * Orange Money * Wave * Moov Money * EcoCash * Telebirr * Mixx by Yas (Tigo Pesa) * T-Money * Telecel Cash (Vodafone) * AT Money (AirtelTigo) * Vodafone Cash If yours isn't listed, choose **Other**. ### Stablecoin (anywhere in Africa) Stablecoin payouts are available on request. Choose **Stablecoin** when you add your payout account and click **Request access**. You can tell us a bit about your wallet. Our team looks at each request, and we email the organization owner when it's approved. If we can't turn it on, the email says why, and you can ask again. Once it's approved: | Field | What to enter | | - | - | | Coin and network | USDT on Tron, USDT on Solana, USDC on Base or USDC on Solana | | Wallet address | Your wallet address on that network. Copy it from your wallet or exchange. | > [!WARNING] > > Check the network. Coins sent to an address on the wrong network can't be recovered. We check that the address looks right for the network you chose, but only you can make sure it's your wallet. We check every wallet address against sanctions lists, and we can't pay out to a sanctioned wallet. ## Changing your payout account To switch to a different account or wallet, go back to **Finance > Payouts** (or **Finance > Account**) and update your payout account. The same rules apply. > [!TIP] > > Update your payout account before you request your next payout, not after. ## FAQ > **Can I use my business bank account?** > > We only pay out to accounts in your own verified legal name. If you have questions about your setup, email [support@crosspoint.finance](mailto:support@crosspoint.finance). > **My bank says the account is in a different name** > > In Nigeria, the account name comes straight from your bank. If it doesn't match your verified legal name, the account can't be added. Use an account in your own name, or contact support if your legal name has changed. > **Can I get paid to a bank account outside Nigeria?** > > Not at the moment. Outside Nigeria, payouts go to mobile money in your local currency, or to a stablecoin wallet. > **Will I get paid in US dollars?** > > No. You get your local currency (naira to a Nigerian bank account, your local currency to mobile money), or USDT or USDC to a stablecoin wallet. > **Why can't I add a payout account?** > > The organization owner has to be verified first. Finish [identity verification](/africa/identity-verification), then try again. > **My mobile money provider isn't listed** > > Choose **Other**, then enter your phone number with the country code. > **Can I get paid into an account outside Africa?** > > No. Your bank account or mobile money wallet must be in an African country. > **How long does a stablecoin request take?** > > Our team looks at each request by hand. You'll get an email as soon as it's done. Meanwhile you can add a bank or mobile money account and get paid there. > **What happens if I enter the wrong account number?** > > The transfer will fail. We mark the payout as failed with the reason, and the full amount goes back to your balance. Fix your payout account and request a new payout. ## Related * [Payouts](/africa/payouts) * [Payout limits](/africa/payout-limits) # Payout limits and verification tiers Source: https://staging-docs.crosspoint.finance/africa/payout-limits How much you can withdraw at each verification tier, and how to move up. How much you can withdraw depends on how much you've verified. There are four tiers. Each one has two limits: * a **per-payout limit**: the most you can withdraw in one payout, and * a **30-day limit**: the most you can withdraw over any rolling 30 days. Limits are measured in US dollars, the currency your balance is shown in. You're still paid in your local currency or in stablecoin. ## Tiers | Tier | What you need | Per payout | Per 30 days | | - | - | - | - | | Tier 0 | Not verified yet | No payouts | No payouts | | Tier 1 | Identity verified (BVN or approved ID) | \$1,000 | \$3,000 | | Tier 2 | Tier 1 + proof of address | \$5,000 | \$20,000 | | Tier 3 | Tier 2 + business registration | \$50,000 | \$250,000 | ## Moving up a tier ### Tier 1: verify your identity [Verify your identity](/africa/identity-verification) with your BVN (Nigeria) or an ID document (every other African country). Once you're verified, you're on tier 1. ### Tier 2: add proof of address Once your identity is verified, you can upload a proof of address. **1. Get a recent document** A utility bill, bank statement or government letter from the **last 3 months**. It must be in your legal name and show your address. **2. Upload it** Go to **Finance > Account** and open the **Verify your identity** page. Upload your document there. **3. Wait for our review** Our team checks it. We email you when it's approved or rejected. ### Tier 3: register your business Once you're on tier 2, the store owner can register the business in **Settings > Account tier**, in five short steps. You'll need: * **Business details**: the registered name, registration number (your RC or BN number in Nigeria), tax number (your TIN in Nigeria) and the business address. * **Registration documents**: * **Nigeria**: your CAC certificate, MEMART (business names can upload the BN1 form instead) and a CAC status report. * **Elsewhere**: your certificate of incorporation, constitution or articles of association, and a register of directors and shareholders. * **A company bank statement or proof of address** in the business name, from the **last 3 months** (for example a utility bill for the company address). * **Every director and shareholder**: full legal name, date of birth, role, how much of the business they own, their BVN if they have one (Nigeria), and a proof of address from the last 3 months. Files can be JPG, PNG, WebP or PDF, up to 8 MB each. Only our review team can see them. We email you when your business is approved (you move to tier 3) or if something needs fixing, and you can resubmit straight away. ## How limits are applied * **A payout always withdraws your whole available balance.** You can't choose a smaller amount. * If your balance is **more than your per-payout limit**, the payout is refused and you'll see why. * If the payout would take you **over your 30-day limit**, it's refused and you'll see why. * The 30-day window is rolling. It looks back 30 days from the moment you request a payout. * Payouts that are **Pending**, **Held for review**, **In Transit** or **Succeeded** count toward your 30-day limit. **Canceled** and **Failed** payouts don't. If a payout is refused because of your limits, move up a tier to withdraw it. ## FAQ > **My balance is over my per-payout limit. What can I do?** > > Because a payout always takes your whole balance, you'll need to move up a tier to withdraw it. Add [proof of address](#tier-2-add-proof-of-address) to reach tier 2. > **When does my 30-day limit reset?** > > It doesn't reset on a fixed date. It's a rolling window, so each payout stops counting 30 days after you requested it. > **Does a failed payout count toward my limit?** > > No. Failed and canceled payouts don't count. The money goes back to your balance. > **My proof of address was rejected** > > You'll get an email. Check that the document is from the last 3 months, is in your legal name and clearly shows your address, then upload again. ## Related * [Payouts](/africa/payouts) * [Payout accounts](/africa/payout-accounts) * [Identity verification](/africa/identity-verification) # Payouts Source: https://staging-docs.crosspoint.finance/africa/payouts Withdraw your balance in your local currency, to your bank account or mobile money wallet, or in stablecoin. A payout moves the money in your Crosspoint balance to your [payout account](/africa/payout-accounts): your bank account in Nigeria or your mobile money wallet elsewhere, in your local currency, or a stablecoin wallet in USDT or USDC. ## Before your first payout You need three things in place: 1. **Your identity is verified.** See [Identity verification](/africa/identity-verification). 2. **You've added a payout account.** See [Payout accounts](/africa/payout-accounts). 3. **Your account has been reviewed.** See [Account reviews](/africa/account-reviews). ## When money is ready to withdraw Money from a sale doesn't become available straight away. It becomes available to withdraw after a settlement delay. Your balance is tracked in US dollars and converted when we pay you. See [Balance](/africa/balance). ## Request a payout **1. Open Payouts** Go to **Finance > Payouts** in your dashboard. **2. Click Withdraw** A payout always withdraws your whole available balance. **3. Check the amount** You see the amount before you confirm. Any fees are shown here too. **4. Confirm** Your payout is created with the status **Pending**. > [!NOTE] > > You can request one payout every 24 hours, and a minimum payout amount applies. ## What happens next Our team checks each pending payout, converts it to your local currency at the exchange rate on the day, and sends it to your bank account or mobile money wallet through a payment partner. Stablecoin payouts are sent to your wallet in USDT or USDC. We pay pending payouts as soon as we can after checking them. Once the money is sent, we mark the payout as **Succeeded**. ## Payout statuses | Status | What it means | | - | - | | Pending | We've got your request and will send it as soon as we can. | | Held for review | Your account is still under review. The payout waits until the review is done. | | Succeeded | The money has been sent to your payout account. | | Canceled | The transfer didn't go through (for example, a wrong account number). The full amount goes back to your balance, and you can request a new payout. | ### If a payout fails A payout can fail if, for example, the account number is wrong. When that happens: * the payout is marked as failed, with the reason, * the full amount goes back to your balance, and * it doesn't count toward your 30-day limit. Fix the problem in your [payout account](/africa/payout-accounts), then request a new payout. ## Limits How much you can withdraw depends on your verification tier. If your balance is over your per-payout limit, or a payout would take you over your 30-day limit, the payout is refused and you'll see why. See [Payout limits](/africa/payout-limits). ## FAQ > **Can I withdraw only part of my balance?** > > No. A payout always withdraws your whole available balance. > **How long does a payout take?** > > We pay pending payouts as soon as we can after checking them. If your account is still under review, your payout is held until the review is done. > **Why was my payout refused?** > > Usually it's because of your limits: your balance is over your per-payout limit, or the payout would take you over your 30-day limit. Move up a [tier](/africa/payout-limits) to withdraw it. You can also only request one payout every 24 hours. > **My payout failed. Did I lose the money?** > > No. The full amount goes back to your balance. Check the reason, fix your payout account if needed, and request a new payout. > **Are there payout fees?** > > Any fees are shown before you confirm the payout. > **What currency will I receive?** > > Your local currency: naira to a Nigerian bank account, or your local currency to mobile money. With a stablecoin wallet, USDT or USDC. We never pay out in US dollars. Your balance is shown in US dollars, and we convert it at the exchange rate on the day we send your payout. Something not right? Email [support@crosspoint.finance](mailto:support@crosspoint.finance). # Sandbox Source: https://staging-docs.crosspoint.finance/africa/sandbox Build and test your integration with test money before you go live The sandbox is a separate copy of Crosspoint for building and testing. Payments use test cards, nothing is charged, and nothing you do there touches your live store. | | Live | Sandbox | | - | - | - | | Dashboard | [app.crosspoint.finance](https://app.crosspoint.finance) | [sandbox.crosspoint.finance](https://sandbox.crosspoint.finance/start) | | API | `https://api.crosspoint.finance` | `https://sandbox-api.crosspoint.finance` | | Money | Real | Test only | Accounts, stores, products, customers and access tokens are separate in each. A token from your live store doesn't work in the sandbox, and the other way round. ## Get started 1. In your Crosspoint dashboard, click **Sandbox** at the bottom of the sidebar. The sandbox opens in a new tab, already signed in with your Crosspoint account: there's no separate sign-up. You can also choose **I'm just exploring → Launch sandbox** when you first sign up. 2. Create a sandbox store. It's ready to sell straight away: no identity check, no review. 3. Create a product and an access token under **Settings**, the same way as on your live store. ## Test payments Pay at checkout with Stripe's [test cards](https://docs.stripe.com/testing#cards). For a successful payment, use: ``` 4242 4242 4242 4242 ``` with any future expiry date and any CVC. To place an order without a card, create a discount code worth 100% and enter it at checkout. ## Use the SDKs Set `CROSSPOINT_ENVIRONMENT=sandbox` next to your sandbox access token, and the SDKs talk to the sandbox. Every example in these docs then works unchanged. ```bash .env theme={null} CROSSPOINT_ACCESS_TOKEN=cxp_oat_... # created in the sandbox CROSSPOINT_ENVIRONMENT=sandbox ``` Or choose it in code: ```ts TypeScript theme={null} import { createCrosspoint } from "@crossdev/sdk"; const crosspoint = createCrosspoint({ environment: "sandbox" }); ``` ```python Python theme={null} from crosspoint import Crosspoint crosspoint = Crosspoint(server="sandbox") ``` Calling the API directly? Use `https://sandbox-api.crosspoint.finance` as the base URL. When you're ready to go live, create a store and an access token on [app.crosspoint.finance](https://app.crosspoint.finance), then set `CROSSPOINT_ENVIRONMENT=production` (or remove it). ## Embedded checkout The [embed script](/features/checkout/embed) opens sandbox checkout links too: use a checkout link from your sandbox store. Add your website under **Settings → Preferences → Embedding** in the sandbox, the same as on your live store. ## How the sandbox differs * **Identity.** There's no ID check: on **Finance → Account**, "Verify your identity" lets you mark yourself verified in one click. * **Payouts.** You can add any payout account, including a stablecoin wallet, without verifying your identity or asking for access. Splits are open too. A withdrawal is marked as paid straight away, and no money is sent. * **Emails to customers** (order confirmations, receipts) only go to members of your store, so test orders don't email strangers. Add teammates under **Settings → Members**. Addresses like `you+test@example.com` work. * **Split recipients.** [Recipients](/africa/splits) are approved as soon as you add them, so you can try splits end to end. No money is sent to them. * **Limits.** Payout limits don't apply. # Python SDK Source: https://staging-docs.crosspoint.finance/africa/sdk-python Use the Crosspoint Python SDK, crosspoint on PyPI ## Installation **uv** ```bash Terminal theme={null} uv add crosspoint ``` **pip** ```bash Terminal theme={null} pip install crosspoint ``` ## Quickstart Create an organization access token (**Settings > Developers**) and store it in `CROSSPOINT_ACCESS_TOKEN`. The client reads it from there: ```python main.py theme={null} from crosspoint import Crosspoint crosspoint = Crosspoint() for product in crosspoint.products.iter_list(limit=10): print(product) ``` You can also pass the token yourself: `Crosspoint("cxp_oat_...")`. Requests wait up to 30 seconds (Polar's SDK waits 5). Change it with `Crosspoint(timeout=60.0)`, in seconds. ## API versions `crosspoint` uses API version 2026-10. Pin a version with its import path: ```python theme={null} from crosspoint.v2027_01 import Crosspoint ``` Versions 2026-04 (`v2026_04`), 2026-10 (`v2026_10`) and 2027-01 (`v2027_01`) are available. ## Async client ```python async_main.py theme={null} import asyncio from crosspoint import CrosspointAsync async def main() -> None: async with CrosspointAsync() as crosspoint: customer_state = await crosspoint.customers.get_state_external( "customer_external_id" ) print(customer_state) asyncio.run(main()) ``` Both clients work as context managers (`with` / `async with`) and close their connections when the block ends. ## Transaction splits `crosspoint.splits` and `crosspoint.split_recipients` are Crosspoint's own: they share each sale with partners or vendors. See [Transaction splits](/africa/splits). Like the rest of the SDK, they take keyword arguments with the API's field names (`crosspoint.splits.create(name=..., shares=[...])`, `crosspoint.splits.update(id, is_default=True)`) and return objects (`split.id`, `recipient.owed`). ```python theme={null} recipients = crosspoint.split_recipients.list(status="pending") ``` Errors from these raise `CrosspointAPIError`, with `status_code` and `body`. ## Verifying webhooks ```python theme={null} import os from crosspoint.webhooks import WebhookVerificationError, validate_event try: event = validate_event(body, headers, os.environ["CROSSPOINT_WEBHOOK_SECRET"]) except WebhookVerificationError: ... # not from Crosspoint: reject it with a 403 ``` ## Sandbox Use the [sandbox](/africa/sandbox) while you build: set `CROSSPOINT_ENVIRONMENT=sandbox` with a sandbox access token, or pass the server: ```python theme={null} crosspoint = Crosspoint(server="sandbox") ``` ## Local development Point the client at a local API with `base_url`: ```python theme={null} crosspoint = Crosspoint(base_url="http://127.0.0.1:8000") ``` ## Next steps * [Webhook delivery](/integrate/webhooks/delivery): a full webhook handler * [API reference](/api-reference): every endpoint, with Python examples # TypeScript SDK Source: https://staging-docs.crosspoint.finance/africa/sdk-typescript Use the Crosspoint TypeScript SDK, @crossdev/sdk ## Installation **npm** ```bash Terminal theme={null} npm install @crossdev/sdk ``` **pnpm** ```bash Terminal theme={null} pnpm add @crossdev/sdk ``` **yarn** ```bash Terminal theme={null} yarn add @crossdev/sdk ``` **bun** ```bash Terminal theme={null} bun add @crossdev/sdk ``` ## Quickstart Create an organization access token (**Settings > Developers**) and store it in `CROSSPOINT_ACCESS_TOKEN`. The client reads it from there: ```ts index.ts theme={null} import { createCrosspoint } from "@crossdev/sdk"; const crosspoint = createCrosspoint(); for await (const product of crosspoint.products.iterList({ limit: 10 })) { console.log(product); } ``` You can also pass the token yourself: `createCrosspoint({ accessToken: "cxp_oat_..." })`. Requests wait up to 30 seconds (Polar's SDK waits 5). Change it with `createCrosspoint({ timeout: 60 })`, in seconds. ## API versions `@crossdev/sdk` uses API version 2026-10. Pin a version with its import path: ```ts theme={null} import { createCrosspoint } from "@crossdev/sdk/2027-01"; ``` Versions 2026-04, 2026-10 and 2027-01 are available. ## Creating a checkout ```ts theme={null} const checkout = await crosspoint.checkouts.create({ products: [""], success_url: "https://your-app.com/success?checkout_id={CHECKOUT_ID}", }); // send your customer to checkout.url ``` ## Transaction splits `crosspoint.splits` and `crosspoint.splitRecipients` are Crosspoint's own: they share each sale with partners or vendors. See [Transaction splits](/africa/splits). ```ts theme={null} const recipients = await crosspoint.splitRecipients.list({ status: "pending" }); ``` Like the rest of the SDK, they use the API's field names (`fee_bearer`, `product_ids`) and take IDs as plain arguments (`crosspoint.splits.update(id, { is_default: true })`). Errors from these come as `CrosspointAPIError`, with `statusCode` and `body`. ## Verifying webhooks ```ts theme={null} import { webhooks } from "@crossdev/sdk"; try { const event = await webhooks.validateEvent(body, headers, process.env.CROSSPOINT_WEBHOOK_SECRET!); // handle event.type } catch (error) { if (error instanceof webhooks.WebhookVerificationError) { // not from Crosspoint: reject it with a 403 } } ``` ## Sandbox Use the [sandbox](/africa/sandbox) while you build: set `CROSSPOINT_ENVIRONMENT=sandbox` with a sandbox access token, or pass the environment: ```ts theme={null} const crosspoint = createCrosspoint({ environment: "sandbox" }); ``` ## Local development Point the client at a local API with `baseUrl`: ```ts theme={null} const crosspoint = createCrosspoint({ baseUrl: "http://127.0.0.1:8000" }); ``` ## Next steps * [Webhook delivery](/integrate/webhooks/delivery): a full webhook handler * [API reference](/api-reference): every endpoint, with TypeScript examples # SDKs & compatibility Source: https://staging-docs.crosspoint.finance/africa/sdks Crosspoint's SDKs for TypeScript and Python, and how to call the API from anything else Install the Crosspoint SDK for your language and give it your access token. It already talks to Crosspoint's API, so every example in these docs works as written. ```ts TypeScript theme={null} // npm install @crossdev/sdk import { createCrosspoint } from "@crossdev/sdk"; // reads CROSSPOINT_ACCESS_TOKEN const crosspoint = createCrosspoint(); ``` ```python Python theme={null} # pip install crosspoint from crosspoint import Crosspoint # reads CROSSPOINT_ACCESS_TOKEN crosspoint = Crosspoint() ``` | Language | Package | Guide | | - | - | - | | TypeScript / JavaScript | `@crossdev/sdk` | [TypeScript SDK](/africa/sdk-typescript) | | Python | `crosspoint` | [Python SDK](/africa/sdk-python) | | Any other language | Plain HTTPS requests | [API reference](/api-reference) with base URL `https://api.crosspoint.finance` | Pin an API version with `@crossdev/sdk/2027-01` or `from crosspoint.v2027_01 import Crosspoint`. Versions 2026-04, 2026-10 (the default) and 2027-01 are available. ## Access tokens Create an **organization access token** from your dashboard (**Settings > Developers**) and keep it in an environment variable, `CROSSPOINT_ACCESS_TOKEN`. Never put it in code you publish. Crosspoint tokens start with `cxp_` (organization tokens start with `cxp_oat_`). If one ever leaks, revoke it from the same settings page and create a new one. ## Webhooks Store your endpoint's signing secret in `CROSSPOINT_WEBHOOK_SECRET` and check every event with the SDK's `webhooks.validateEvent` (TypeScript) or `validate_event` from `crosspoint.webhooks` (Python). A forged or changed event raises `WebhookVerificationError`. See [webhook delivery](/integrate/webhooks/delivery) for a full example. ## Already using the Polar SDK? Crosspoint's API is compatible with the open-source Polar SDKs, and our packages are built on them: same methods, same models. If you already use `@polar-sh/sdk` or `polar-sdk`, you can keep it. Pass `baseUrl: "https://api.crosspoint.finance"` (TypeScript) or `base_url="https://api.crosspoint.finance"` (Python), and don't use the `environment` option. > [!WARNING] > > Without that address, the Polar SDK talks to Polar instead of Crosspoint, and your Crosspoint token won't work there. ## Frameworks * **Next.js, Express, Hono, SvelteKit and the other framework adapters**: their **Webhooks** handlers work with Crosspoint (they only check signatures). Their **Checkout** and **Customer Portal** handlers are wired to Polar's servers and can't be pointed at Crosspoint yet, so create checkouts and customer portal sessions with the SDK instead. ## Testing Build and test in the [sandbox](/africa/sandbox): a separate copy of Crosspoint with test cards and no real money. Create a sandbox access token and set `CROSSPOINT_ENVIRONMENT=sandbox`; both SDKs then talk to `https://sandbox-api.crosspoint.finance`. # Transaction splits Source: https://staging-docs.crosspoint.finance/africa/splits Share each sale with a partner, co-founder or vendor: a percentage or a flat amount, paid to their own bank account or mobile money wallet. A split shares your sales with other people. Each person (a **recipient**) takes a **percentage** or a **flat amount** of every sale it applies to, and you keep the rest. ## Splits are on request Our team turns splits on for each store. Go to **Finance > Splits** and click **Request access**. You can tell us who you'll split sales with. We email the organization owner as soon as we've looked at it. If we can't turn splits on, the email says why, and you can ask again. Until splits are on, adding a recipient or a split, in the dashboard or through the API, fails with a `403` error. ## How it works * **Recipients are bank accounts or mobile money wallets.** They're paid in their local currency, and they don't need a Crosspoint account. Before their first payout, our team checks the account. Their shares are set aside from the first sale, so nothing is lost while we check. * **You choose who pays the fees**, per split: * `seller` (the default): recipients get their full share of the sale and you pay the fees. * `shared`: fees come off the sale first, then it's divided. * **Shares are worked out on the sale before tax.** A percentage of a $100 sale is a percentage of $100. * **Recipients never get more than you earned.** If the shares add up to more than the sale minus fees, each is reduced in proportion. * **Refunds and chargebacks are shared too.** Refund half a sale and each recipient gives back half of their share from that sale. ## Which split applies Most specific first: 1. **The checkout's split**, if you named one when creating the checkout (see [below](#use-a-split-for-one-checkout)). 2. **The product's split**, if the product is in a split's `product_ids`. 3. **Your default split**, the one with `is_default: true`. If none applies, the sale isn't split. Subscription renewals use the product's split or your default. ## Access You need an organization access token (**Settings > Developers**) with the **payouts** scopes: `payouts:read` to read splits and recipients, `payouts:write` to change them. Only owners and admins can manage splits from a personal account. ## Add a recipient ```ts TypeScript theme={null} import { createCrosspoint } from "@crossdev/sdk"; const crosspoint = createCrosspoint(); const partner = await crosspoint.splitRecipients.create({ method: "bank_account", name: "Co-founder", email: "ada@example.com", country: "NG", account_name: "Ada Obi", bank_name: "GTBank", account_number: "0123456789", }); // partner.status === "pending" until we've checked the account ``` ```python Python theme={null} from crosspoint import Crosspoint crosspoint = Crosspoint() partner = crosspoint.split_recipients.create( method="bank_account", name="Co-founder", email="ada@example.com", country="NG", account_name="Ada Obi", bank_name="GTBank", account_number="0123456789", ) # partner.status == "pending" until we've checked the account ``` ```bash cURL theme={null} curl -X POST https://api.crosspoint.finance/v1/split-recipients/ \ -H "Authorization: Bearer $CROSSPOINT_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "method": "bank_account", "name": "Co-founder", "country": "NG", "account_name": "Ada Obi", "bank_name": "GTBank", "account_number": "0123456789" }' ``` For mobile money, use `"method": "mobile_money"` with `provider` (for example `M-Pesa`) and `phone_number` in international format (`+254712345678`) instead of the bank fields. The account must be in an African country. | Status | What it means | | - | - | | `pending` | We're checking the account. Their shares are set aside. (In the [sandbox](/africa/sandbox), recipients are approved straight away.) | | `approved` | Checked. We pay them what they're owed. | | `rejected` | The details didn't check out (see `rejection_reason`). They stop taking shares, and what was set aside for them goes back to your balance. Add the recipient again with the right details. | ### Getting paid We check each account (for Nigerian banks, we ask the bank whose account it is) and then pay approved recipients what they're owed, to the account you added. Payouts to recipients come out of the shares set aside for them, not out of your balance. If a sale is refunded after a recipient was paid for it, their `owed` goes negative; their next shares make up for it before they're paid again. You can change a recipient's `name` and `email`. To change account details, add a new recipient and remove the old one: new details are checked again. Removing a recipient stops new shares; what they're already owed is still theirs. ## Create a split ```ts TypeScript theme={null} const split = await crosspoint.splits.create({ name: "Partners", fee_bearer: "seller", is_default: true, shares: [ { recipient_id: partner.id, type: "percent", percent: 20 }, { recipient_id: designer.id, type: "flat", amount: 500 }, // $5.00 per sale ], product_ids: [], // or the products that always use this split }); ``` ```python Python theme={null} split = crosspoint.splits.create( name="Partners", fee_bearer="seller", is_default=True, shares=[ {"recipient_id": partner.id, "type": "percent", "percent": 20}, {"recipient_id": designer.id, "type": "flat", "amount": 500}, ], ) ``` | Field | | | - | - | | `shares` | One per recipient. `percent` (up to 2 decimals; all percentages together at most 100) or `flat` with `amount` in USD cents. | | `fee_bearer` | `seller` or `shared`, see [How it works](#how-it-works). | | `is_default` | Use for every sale without a checkout or product split. Setting it takes it off your previous default. | | `product_ids` | Every sale of these products uses this split. A product has one split: adding it here takes it off any other. | Changes apply to sales from then on: earlier sales keep the shares they had. Deleting a split sends its products back to your default split. ## Use a split for one checkout Put the split's ID in the checkout's metadata under `crosspoint_split`: ```ts TypeScript theme={null} import { SPLIT_METADATA_KEY } from "@crossdev/sdk"; const checkout = await crosspoint.checkouts.create({ products: [""], metadata: { [SPLIT_METADATA_KEY]: split.id }, }); ``` ```python Python theme={null} from crosspoint import SPLIT_METADATA_KEY checkout = crosspoint.checkouts.create( products=[""], metadata={SPLIT_METADATA_KEY: split.id}, ) ``` This is how marketplaces send each sale to the right vendor. An unknown split ID is rejected with a `422`. The same metadata works on a **checkout link**, so every checkout opened from that link uses the split. Customers can't choose a split: the key is only read from metadata you set (a link's URL parameters are ignored). ## Endpoints | | | | - | - | | `GET /v1/split-recipients/` | List recipients. Filters: `organization_id`, `status`, `page`, `limit`. | | `POST /v1/split-recipients/` | Add a recipient. | | `GET /v1/split-recipients/{id}` | Get a recipient, with `owed`: what they're owed and not yet paid, in USD cents. | | `PATCH /v1/split-recipients/{id}` | Change `name` or `email`. | | `DELETE /v1/split-recipients/{id}` | Remove a recipient. | | `GET /v1/splits/` | List splits. Filters: `organization_id`, `page`, `limit`. | | `POST /v1/splits/` | Create a split. | | `GET /v1/splits/{id}` | Get a split. | | `PATCH /v1/splits/{id}` | Change any field; `shares` and `product_ids` replace the old ones. | | `DELETE /v1/splits/{id}` | Delete a split. | With a personal access token, pass `organization_id` when creating. An organization token already names the store. ## Related * [Payout accounts](/africa/payout-accounts) * [Payouts](/africa/payouts) * [Checkout sessions](/features/checkout/session) # Supported countries and currencies in Africa Source: https://staging-docs.crosspoint.finance/africa/supported-countries Who can sell with Crosspoint, the African countries we cover, how you get paid in each, and the currencies you can price in. Crosspoint is built for people selling from Africa. If you live in any of the 54 African countries, you can sign up, sell, and get paid out in your local currency or in stablecoin. ## Who can use Crosspoint Two things need to be true: * **You live in Africa.** The country in your personal details must be an African country. * **Your organization is in Africa.** The country of your organization must also be an African country. You can sell as an individual or as a registered business. Your customers can be anywhere. ## Countries All 54 African countries are supported. We group them by region below. | Region | Countries | | - | - | | North Africa (6) | Algeria, Egypt, Libya, Morocco, Sudan, Tunisia | | West Africa (16) | Benin, Burkina Faso, Cabo Verde, Côte d'Ivoire, The Gambia, Ghana, Guinea, Guinea-Bissau, Liberia, Mali, Mauritania, Niger, Nigeria, Senegal, Sierra Leone, Togo | | Central Africa (9) | Angola, Cameroon, Central African Republic, Chad, Republic of the Congo, Democratic Republic of the Congo, Equatorial Guinea, Gabon, São Tomé and Príncipe | | East Africa (18) | Burundi, Comoros, Djibouti, Eritrea, Ethiopia, Kenya, Madagascar, Malawi, Mauritius, Mozambique, Rwanda, Seychelles, Somalia, South Sudan, Tanzania, Uganda, Zambia, Zimbabwe | | Southern Africa (5) | Botswana, Eswatini, Lesotho, Namibia, South Africa | ## How you get paid You're paid in your local currency, or in stablecoin if you prefer. We never pay out in US dollars. | Where you are | Get paid to | | - | - | | Nigeria | Your bank account, in naira | | Every other African country | Your mobile money wallet, in your local currency | | Anywhere in Africa | A stablecoin wallet, in USDT or USDC (on request) | Your balance is tracked in US dollars so sales in different currencies add up in one place. When we send a payout, we convert it to your local currency at the exchange rate on the day we send it. Stablecoin payouts are sent in USDT or USDC. See [Payout accounts](/africa/payout-accounts). ## Currencies you can price in Each organization has a **default payment currency**: the currency your prices are set in and your customers are charged in. It can be US dollars (USD), which is the default, or one of the African currencies below. Whichever you choose, you're still paid out in your local currency. | Code | Currency | | - | - | | USD | US dollar (default) | | AOA | Angolan kwanza | | BIF | Burundian franc | | BWP | Botswana pula | | CDF | Congolese franc | | CVE | Cape Verdean escudo | | DJF | Djiboutian franc | | DZD | Algerian dinar | | EGP | Egyptian pound | | ETB | Ethiopian birr | | GMD | Gambian dalasi | | GNF | Guinean franc | | KES | Kenyan shilling | | KMF | Comorian franc | | LRD | Liberian dollar | | LSL | Lesotho loti | | MAD | Moroccan dirham | | MGA | Malagasy ariary | | MUR | Mauritian rupee | | MWK | Malawian kwacha | | MZN | Mozambican metical | | NAD | Namibian dollar | | NGN | Nigerian naira | | RWF | Rwandan franc | | SCR | Seychellois rupee | | SOS | Somali shilling | | SZL | Swazi lilangeni | | TZS | Tanzanian shilling | | UGX | Ugandan shilling | | XAF | Central African CFA franc | | XOF | West African CFA franc | | ZAR | South African rand | | ZMW | Zambian kwacha | ### Pricing in these currencies isn't available yet Card payments are processed in a fixed set of currencies, and these aren't in it yet, so they can't be your default payment currency: * Ghanaian cedi (GHS) * Sierra Leonean leone (SLE) * Tunisian dinar (TND) * Libyan dinar (LYD) * Sudanese pound (SDG) * South Sudanese pound (SSP) * Eritrean nakfa (ERN) * Mauritanian ouguiya (MRU) * São Tomé and Príncipe dobra (STN) * Zimbabwe gold (ZWG) > [!NOTE] > > These countries are fully supported. Sellers there sign up, sell and get paid like everyone else: set your prices in USD (or another currency from the list), and you're still paid out in your local currency, for example cedis in Ghana, or in stablecoin. Payout limits are measured in US dollars, whatever currency you're paid in. See [Payout limits](/africa/payout-limits) for the numbers. ## Next steps * [Verify your identity](/africa/identity-verification) so you can get paid out. * [Add a payout account](/africa/payout-accounts): a bank account in Nigeria, a mobile money wallet everywhere else, or a stablecoin wallet. * Read about the [account review](/africa/account-reviews) before your first payout. Questions? Email us at [support@crosspoint.finance](mailto:support@crosspoint.finance). # Analytics Source: https://staging-docs.crosspoint.finance/features/analytics Understand how every metric in your dashboard is calculated. Crosspoint 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, Crosspoint 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 Crosspoint 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 Crosspoint'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](/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://staging-docs.crosspoint.finance/features/benefits/credits Create your own Credits benefit The Credits benefit allows you to credit a customer's Usage Meter balance. ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/features/benefits/credits/credits.light.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=26efa0555d7d1d797f68919f67f31a00) ## 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. > [!NOTE] > > 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://staging-docs.crosspoint.finance/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](/features/customer-portal). > [!NOTE] > > 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](/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://staging-docs.crosspoint.finance/features/benefits/discord-access Sell Discord access & roles with ease ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/features/benefits/discord/hero.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=f7a0cf288b0fb494ef1e158d759404ba) Automating Discord server invites and roles for customers or subscribers is super easy and powerful with Crosspoint. * 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 ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/features/benefits/discord/create.light.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=3ceed95924f42acdfb9988118d6a5a32) Click on `Connect your Discord server`. You'll be redirected to Discord where you can grant the Crosspoint 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 Crosspoint. ### **Create Invite** Ability to invite members who purchase a product or subscribes to a tier with this benefit. You're now redirected back to Crosspoint 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://staging-docs.crosspoint.finance/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://staging.crosspoint.finance/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](/integrate/customer-state) API or the [`customer.state_changed`](/api-reference/current/customer_state_changed) webhook. The customer state object includes all granted benefits. Simply check if the customer has a benefit grant for your feature flag benefit to determine access. ### Lifecycle * **Subscriptions**: The feature flag is granted at the start of each subscription cycle and automatically revoked when the subscription is cancelled. * **One-time purchases**: The feature flag is granted at the time of purchase with lifetime access. # Automate Customer File Downloads Source: https://staging-docs.crosspoint.finance/features/benefits/file-downloads Offer digital file downloads with ease ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/features/benefits/file-downloads/hero.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=3645f3a1dd176e11570ab91c2926a5dd) ## Sell Digital Products You can easily offer customers and subscribers access to downloadable files with Crosspoint. * 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://staging.crosspoint.finance/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 (`Upload your files`) 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 Crosspoint 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://staging-docs.crosspoint.finance/features/benefits/github-access Sell premium GitHub repository access with ease ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/features/benefits/github/hero.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=0c2e063223ba9e66f35dfcf96ac885cc) ## Sell GitHub Repository Access With Crosspoint 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://staging.crosspoint.finance/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 Crosspoint App for this benefit across the repositories you want to use it with. * Click `Connect your GitHub Account` > [!NOTE] > > **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 Crosspoint and the benefit form - now connected and updated. ### **Repository** Select the desired repository you want to automate collaborator invites for. > [!NOTE] > > **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. > [!NOTE] > > 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). > [!NOTE] > > **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://staging-docs.crosspoint.finance/features/benefits/introduction Crosspoint offers built-in benefit (entitlements) automation for common upsells within the developer & designer ecosystem with more to come. * [**Credits**](/features/benefits/credits). A simple benefit that allows you to credit a customer's Usage Meter balance. * [**License Keys**](/features/benefits/license-keys). Software license keys that you can customize the branding of. * [**Feature Flags**](/features/benefits/feature-flags). Simple, API-driven feature access flags with optional metadata. * [**File Downloads**](/features/benefits/file-downloads). Downloadable files of any kind up to 10GB each. * [**GitHub Repository Access**](/features/benefits/github-access). Automatically invite subscribers to private GitHub repo(s). * [**Discord Invite**](/features/benefits/discord-access). Automate invitations and granting of roles to subscribers and customers. * [**Shared Slack Channel**](/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 Crosspoint - 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://staging-docs.crosspoint.finance/features/benefits/license-keys Sell license key access to your service, software or APIs with ease ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/features/benefits/license-keys/hero.jpeg?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=c8df77e649b97d7851481decbbdcfe63) You can easily sell software license keys with Crosspoint without hosting an API to validate them in real-time. License keys with Crosspoint come with a lot of powerful features built-in. * Brandable prefixes, e.g `MYAPP_*****` * 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://staging.crosspoint.finance/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 Crosspoint.** Instead of building your own custom admin for customers to manage their activation instances - leave it to Crosspoint 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) * Rotate their license key if it is exposed or compromised ### Rotate License Keys If a key is exposed or compromised, merchants can rotate it from the benefit license keys page in the dashboard, or via `POST /v1/license-keys/{id}/rotate`. Customers can rotate their own key from the customer portal, or via `POST /v1/customer-portal/license-keys/{id}/rotate`. Only keys in `granted` or `disabled` status can be rotated; rotating a revoked key returns a `400` error. Rotation generates a new key string on the same license key record. The previous key stops validating immediately. Status, usage, limits, expiry, and activations are preserved. After rotating, copy the new key from the customer portal or share it from the dashboard. ## Integrate API It's super easy and straightforward to integrate Crosspoint 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.crosspoint.finance/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" } }' ``` **key** `string` *required* Replace with the users license key (from input in your app). **organization\_id** `string` *required* Replace with your organization ID here found in your settings. **label** `string` *required* Set a label to associate with this specific activation. **conditions** `object` JSON object with custom conditions to validate against in the future, e.g IP, mac address, major version etc. **meta** `object` 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`](/api-reference/current/customer_portal/validate-license-key) endpoint. ```bash Terminal theme={null} curl -X POST https://api.crosspoint.finance/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 }' ``` **key** `string` *required* Replace with the users license key (from input in your app). **organization\_id** `string` *required* Replace with your organization ID here found in your settings. **activation\_id** `string` The activation ID to validate - required in case activations limit is enabled and used (above). **conditions** `object` In case of activation instances. Same exact JSON object as upon registration of the activation. **increment\_usage** `integer` 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 Crosspoint license keys being used across Crosspoint 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://staging-docs.crosspoint.finance/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 > [!NOTE] > > The Shared Slack Channel benefit is currently in preview. Email [support@crosspoint.finance](mailto:support@crosspoint.finance) to have it switched on for your store. ## Create Shared Slack Channel Benefit 1. Go to [`Benefits`](https://staging.crosspoint.finance/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. Crosspoint 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://staging-docs.crosspoint.finance/features/checkout/embed Embed our checkout directly on your site ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/features/checkout/embed/demo.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=7465eeafdc84bc04530445bbd6b09e6c) 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](/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-crosspoint-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 Crosspoint embed. | Attribute | Value | Description | | - | - | - | | `data-crosspoint-payment-method` | `string` | **Required.** The session token. Clicking the element opens the modal. | | `data-crosspoint-payment-method-theme` | `light \| dark` | Optional theme override. | | `data-crosspoint-payment-method-set-as-default` | `true \| false` | Optional. Default `true`. Pass `"false"` to add the card without overriding the existing default. | | `data-crosspoint-payment-method-return-url` | `string` | Optional. Return URL for redirect-based payment methods. Defaults to the current page. | | `data-crosspoint-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-crosspoint-payment-method-locale` attribute): ```ts theme={null} const embed = await CrosspointEmbedPaymentMethod.create({ sessionToken: session.token, locale: "fr-FR", }); ``` When omitted, the embed defaults to English. Unsupported locales also fall back to English. See [Localization](/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 { CrosspointEmbedPaymentMethod } from "@crossdev/checkout/payment-method"; const result = CrosspointEmbedPaymentMethod.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 "@crossdev/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://staging-docs.crosspoint.finance/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 Crosspoint handles the rest. > [!TIP] > > If you need to create Checkout Sessions programmatically (e.g. with per-customer data computed by your backend), use the [Checkout API](/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. Crosspoint 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://staging.crosspoint.finance/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. > [!NOTE] > > 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. > [!TIP] > > Discounts without a code can only be applied through Checkout Link presets or the [Checkout API](/features/checkout/session). Learn more in the [Discounts documentation](/features/discounts). ### Success URL URL where the customer is redirected after a successful payment. If left empty, the customer stays on the Crosspoint-hosted confirmation page. You can add the `checkout_id={CHECKOUT_ID}` query parameter to the Success URL and Crosspoint 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. **product\_id** `string` ID of the product to preselect. Must be one of the products configured on the Checkout Link. ### Prefill fields **customer\_email** `string` Prefill the customer email. **customer\_name** `string` Prefill the customer name. **discount\_code** `string` Prefill the discount code input. Only works with discounts that have a code set. See the [Discounts documentation](/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. **amount** `string` Prefill the amount when the product uses Pay-What-You-Want pricing. **locale** `string` 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](/features/checkout/localization) for details. **custom\_field\_data.\{slug}** `string` Prefill custom field data, where `{slug}` is the slug of the custom field. **theme** `string` 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`](/api-reference/current/checkouts/get-checkout-session#response-metadata). **reference\_id** `string` Your own reference ID for the Checkout Session. **utm\_source** `string` UTM source of the Checkout Session. **utm\_medium** `string` UTM medium of the Checkout Session. **utm\_campaign** `string` UTM campaign of the Checkout Session. **utm\_content** `string` UTM content of the Checkout Session. **utm\_term** `string` UTM term of the Checkout Session. # Checkout Localization Source: https://staging-docs.crosspoint.finance/features/checkout/localization Serve checkout in your customer's preferred language > [!NOTE] > > 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.crosspoint.finance/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 [support@crosspoint.finance](mailto:support@crosspoint.finance). ## 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: * Email us at [support@crosspoint.finance](mailto:support@crosspoint.finance) # Checkout API Source: https://staging-docs.crosspoint.finance/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](/api-reference/current/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. > [!TIP] > > 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. ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/features/checkout/session/checkout_multiple_products.light.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=abf01fc0d42e4d5e79f43e0fa6001539) ## 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. > [!NOTE] > > 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 { createCrosspoint } from "@crossdev/sdk/2026-10"; const crosspoint = createCrosspoint({ accessToken: process.env.CROSSPOINT_ACCESS_TOKEN!, }); async function run() { const checkout = await crosspoint.checkouts.create({ products: ["productId"], prices: { "productId": [ { amount_type: "fixed", price_amount: 10000, // $100.00 price_currency: "usd", } ] } }); console.log(checkout.url); } run(); ``` ```py Python theme={null} from crosspoint.v2026_10 import Crosspoint with Crosspoint("") as crosspoint: checkout = crosspoint.checkouts.create( 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](/api-reference/current/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 Crosspoint and your system, you can inform us about your customer ID when creating a checkout session through the [`external_customer_id`](/api-reference/current/checkouts/create-checkout-session) field. After a successful checkout, we'll create a Customer on Crosspoint with the external ID you provided. It'll be provided through the `customer.external_id` property in webhooks you may have configured. > [!NOTE] > > 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, Crosspoint 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](/features/products) * **Pre-filling the billing country** on the checkout page, which is also used to compute taxes If you use [checkout links](/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), Crosspoint 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 { createCrosspoint } from "@crossdev/sdk/2026-10"; const crosspoint = createCrosspoint({ accessToken: process.env.CROSSPOINT_ACCESS_TOKEN!, }); const checkout = await crosspoint.checkouts.create({ products: ["productId"], customer_ip_address: request.headers.get("CF-Connecting-IP") ?? undefined, }); ``` ```py Python theme={null} from crosspoint.v2026_10 import Crosspoint with Crosspoint("") as crosspoint: checkout = crosspoint.checkouts.create( 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. > [!NOTE] > > When `customer_ip_address` is provided, you don't need to set `customer_billing_address.country` yourself — Crosspoint 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 { createCrosspoint } from "@crossdev/sdk/2026-10"; const crosspoint = createCrosspoint({ accessToken: process.env.CROSSPOINT_ACCESS_TOKEN!, }); async function run() { const checkout = await crosspoint.checkouts.create({ products: ["productId"] }); console.log(checkout.url) } run(); ``` ```py Python theme={null} from crosspoint.v2026_10 import Crosspoint with Crosspoint("") as crosspoint: checkout = crosspoint.checkouts.create( allow_discount_codes=True, products=[""], ) print(checkout.url) ``` # Cost Events Source: https://staging-docs.crosspoint.finance/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 Crosspoint'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 { createCrosspoint } from "@crossdev/sdk/2026-10"; const crosspoint = createCrosspoint({ accessToken: process.env.CROSSPOINT_ACCESS_TOKEN!, }); await crosspoint.events.ingest({ events: [ { name: "llm.inference", external_customer_id: "user_123", metadata: { _cost: { amount: 0.025, currency: "usd", }, }, }, ], }); ``` ```json cURL theme={null} POST https://api.crosspoint.finance/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 > [!NOTE] > > **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 { createCrosspoint } from "@crossdev/sdk/2026-10"; import OpenAI from "openai"; const crosspoint = createCrosspoint({ accessToken: process.env.CROSSPOINT_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!" }], }); if (!completion.usage) { throw new Error("OpenAI did not return token usage"); } // 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 Crosspoint await crosspoint.events.ingest({ events: [ { name: "gpt4.completion", customer_id: "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 { createCrosspoint } from "@crossdev/sdk/2026-10"; const crosspoint = createCrosspoint({ accessToken: process.env.CROSSPOINT_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 crosspoint.events.ingest({ events: [ { name: "llm.completion", external_customer_id: "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://staging-docs.crosspoint.finance/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://staging-docs.crosspoint.finance/features/cost-insights/introduction Track costs, profits, and customer lifetime value with event-based cost tracking ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/features/cost/ingestion.light.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=aad5ce7b80fbefe2718811985a8356f9) 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 Crosspoint 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 Crosspoint's Event Ingestion API 2. **Automatic aggregation**: Crosspoint automatically aggregates costs alongside your revenue data 3. **Query and analyze**: Use the Metrics API, or the Crosspoint 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 > **[Cost Events](/features/cost-insights/cost-events)** > > Learn how to track costs by adding the `_cost` property to your events metadata > **[Cost Metrics](/features/analytics)** > > Query and analyze costs, profits, and customer lifetime value # Custom Fields Source: https://staging-docs.crosspoint.finance/features/custom-fields Learn how to add custom input fields to your checkout with Crosspoint 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 Crosspoint, 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://staging.crosspoint.finance/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](/api-reference/current/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](/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. > [!TIP] > > 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](/api-reference/current/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](/api-reference/current/orders/get-order) and [Subscriptions](/api-reference/current/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://staging-docs.crosspoint.finance/features/customer-management Get insights on your customers and sales ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/features/customer-management/details.light.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=c4acdcb88283de6e59a505099d4ea05a) ## Managing Customers Crosspoint 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 Crosspoint and your system, we have a dedicated [`external_id`](/api-reference/current/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 Crosspoint ID in your system. > **[Get Customer by External ID](/api-reference/current/customers/get-customer-by-external-id)** > **[Update Customer by External ID](/api-reference/current/customers/update-customer-by-external-id)** > **[Delete Customer by External ID](/api-reference/current/customers/delete-customer-by-external-id)** ## 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. ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/features/customer-management/edit.light.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=1cd242a362cbe926d1425652bac16c02) It can be set through the dashboard or through the [API](/api-reference/current/customers/update-customer#body-metadata). It can also be pre-set when creating a Checkout Session by using the [`customer_metadata`](/api-reference/current/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://staging-docs.crosspoint.finance/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. ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/features/customer-portal/overview.light.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=472bad6117da455d1e7bb5c0b72c3074) ## 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](/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 Crosspoint 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 > **[Navigate customers to the portal](/features/customer-portal/navigate-customers)** > > Use the default portal URL, generate authenticated links, or rely on the > emails Crosspoint already sends. > **[Customer portal settings](/features/customer-portal/settings)** > > Configure what your customers can do from the portal under Settings → Customer portal. > **[Customer Portal API](/api-reference/current/customer_portal/get-customer)** > > Build your own portal experience on top of the Customer Portal API. ## FAQ > **Can I disable the Customer Portal completely?** > > **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](/features/customer-portal/settings) > from the portal — for example, disabling subscription plan changes or email > edits. > **Can I customize the look and feel of the Customer Portal?** > > Not the hosted portal at `app.crosspoint.finance//portal` — it's > intentionally consistent across all Crosspoint organizations. > > If you need a branded experience, you can build your own portal on top of > the [Customer Portal API](/api-reference/current/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. > > > [!NOTE] > > > > 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. > **How do customers sign in to the portal?** > > By default, customers authenticate with the email address they used to > purchase or subscribe — Crosspoint 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](/features/customer-portal/navigate-customers) > for details. > **Do I need to send customers a link to the portal myself?** > > You don't have to. Crosspoint 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. > **What happens if a customer's payment method fails?** > > The customer receives an email from Crosspoint letting them know, with a link to > the Customer Portal where they can update their default payment method. > Once they do, Crosspoint 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://staging-docs.crosspoint.finance/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 Crosspoint automatically includes in the emails it sends to your customers. ## 1. The default portal URL Every organization gets a Customer Portal hosted at: ``` https://staging.crosspoint.finance//portal ``` Customers authenticate by entering the email address they used to purchase or subscribe. Crosspoint then emails them a one-time code to complete sign-in. ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/features/customer-portal/signin.light.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=3935f61f541419b37737f6ccfb7ff347) 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](/api-reference/current/customer-sessions/create-customer-session) endpoint and redirects the user to the `customer_portal_url` it returns. ```typescript theme={null} import { createCrosspoint } from "@crossdev/sdk/2026-10"; const crosspoint = createCrosspoint({ accessToken: process.env.CROSSPOINT_ACCESS_TOKEN!, }); async function run() { const result = await crosspoint.customerSessions.create({ customer_id: "", }); redirect(result.customer_portal_url); } run(); ``` Point a link in your app at `/portal` and your customer is one click away from managing their billing. > [!NOTE] > > Customer Session tokens are short-lived. Always generate a fresh link at the > moment the customer clicks, rather than storing the URL. ## 3. Emails Crosspoint sends to your customers You don't have to build anything to get customers to the portal — Crosspoint 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://staging-docs.crosspoint.finance/features/customer-portal/settings Configure what customers can do from the Customer Portal All Customer Portal settings live under [**Settings → Customer portal**](https://staging.crosspoint.finance/to/dashboard/settings) in your Crosspoint 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](/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](/api-reference/current/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](/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](/api-reference/current/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. > [!NOTE] > > When this is enabled, you'll probably want to listen for the > [`customer.updated`](/api-reference/current/customer_updated) webhook and > sync the new email back into your own user system, so the two stay in sync. ## Enable subscription plan changes Lets customers switch between products on their own — upgrading, downgrading, or moving to a different plan entirely — without contacting you. Plan changes follow your [proration settings](/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](/api-reference/current/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](/features/subscriptions/manage#pause-and-resume) for the full behavior. This toggle only gates the portal. Pausing and resuming through the [Update Subscription](/api-reference/current/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://staging-docs.crosspoint.finance/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. ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/features/discounts/create.light.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=0267be9696d66d335ea4381803b702f7) ## Create a discount Go to the [**Discounts**](https://staging.crosspoint.finance/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. Crosspoint 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). [Crosspoint-issued refunds as part of chargeback prevention](/features/refunds) also don't free the customer's slot. ## Apply a discount ### Auto-apply via Checkout Link When creating a [Checkout Link](/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. > [!NOTE] > > 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](/features/checkout/session) for details. # Orders Source: https://staging-docs.crosspoint.finance/features/orders Every paid transaction on Crosspoint is an order. An **order** is the record of a single paid transaction on Crosspoint. Every successful checkout, subscription cycle, and subscription change generates an order — it's where the money, the tax, the invoice, and the link back to the [product](/features/products) and [customer](/features/customer-management) live. If [subscriptions](/features/subscriptions/introduction) are the *relationship* with a customer, orders are the *individual payments* inside that relationship. ## When orders are created Crosspoint creates an order in all of these cases: * **One-time purchase** — when a customer checks out a non-recurring product. * **Subscription created** — the initial order generated when a customer subscribes. * **Subscription renewal** — every billing cycle of an active subscription. * **Subscription change** — when a plan or seat update is prorated with `invoice` behavior and charges the difference immediately. See [Proration](/features/subscriptions/proration). Each order carries a `billing_reason` that tells you which of these it is: `purchase`, `subscription_create`, `subscription_cycle`, or `subscription_update`. ## Arbitrary charges Sometimes you need to charge a customer an amount that doesn't originate from a checkout or a subscription renewal — a usage overage, a one-off professional-services fee, or a manual top-up. Crosspoint lets you run these **off-session charges** against a customer's saved payment method, without the customer being present. > [!NOTE] > > Off-session charges are currently in preview. Email [support@crosspoint.finance](mailto:support@crosspoint.finance) to have it switched on for your store. An off-session charge is a two-step flow: you create a **draft order**, then **finalize** it to attempt the charge. Both endpoints require the `orders:write` scope and the sales-management permission on the organization. ### 1. Create a draft order `POST /v1/orders/` creates an order in `draft` status with no invoice number — nothing is charged yet. You reference an existing customer and a one-time product, and optionally override the amount and description: **customer\_id** `string` *required* The customer to charge. Must belong to the organization. **product\_id** `string` *required* A one-time product to charge for. Only fixed-price, free and unit-based products are supported. **units** `integer` The number of units to charge for. Required for unit-based products, rejected for any other pricing. The quantity shows on the invoice line, e.g. `Acme Credits (500 credits)`, unless `description` replaces the text. **amount** `integer` A custom amount in the smallest currency unit (e.g. `2500` for \$25.00). Overrides the product's price; defaults to it. Can't be combined with `units`. **currency** `string` ISO 4217, lowercase (e.g. `usd`). Defaults to the organization's currency. **description** `string` The line-item text shown on the invoice and receipt. Defaults to the product name. **organization\_id** `string` Required unless you authenticate with an organization token. The customer must have a complete billing address (so Crosspoint can calculate tax) and at least one saved payment method. ```bash cURL theme={null} curl --request POST \ --url https://api.crosspoint.finance/v1/orders/ \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "customer_id": "", "product_id": "", "amount": 2500, "description": "5,000 extra tokens" }' ``` The response is the draft order, including its `id`. Creating a draft fires the `order.created` webhook but does not yet email the customer. ### 2. Finalize and charge `POST /v1/orders/{id}/finalize` synchronously attempts the off-session charge. By default it uses the customer's default payment method; pass `payment_method_id` to charge a specific one. ```bash cURL theme={null} curl --request POST \ --url https://api.crosspoint.finance/v1/orders//finalize \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{}' ``` On success, the order transitions to `paid`, an invoice number is assigned, any [benefits](/features/benefits/introduction) attached to the product are granted, the customer is emailed their confirmation, and the [`order.paid`](/api-reference/current/order_paid) webhook fires. If the charge fails, the API returns an error and the order is reverted to `draft` so you can fix the problem and finalize the same order again — no invoice number is consumed by a failed attempt: | Status | When | | - | - | | `402` | The card was declined, the customer has no payment method, or the charge needs a 3DS / SCA challenge that can't be completed off-session. | | `403` | Off-session charges aren't enabled for the organization, or its account can't currently accept payments. | | `412` | The order is no longer in `draft` status (for example, it was already finalized). | ## Order status An order moves through a small set of statuses over its lifetime: | Status | Meaning | | - | - | | `pending` | The order has been created and Crosspoint is attempting to collect payment. | | `paid` | The payment succeeded. | | `refunded` | The order has been fully refunded. | | `partially_refunded` | Part of the order has been refunded. See [Refunds](/features/refunds). | | `void` | The order will not be collected (for example, it's been voided after repeated failures). | Free orders — those with a total of zero, typically from a \$0 subscription or a 100% discount — are marked `paid` immediately with no payment step. ## What's on an order Every order carries: * **Amounts**: subtotal, discount, tax, net, and total, with the currency it was billed in. * **Billing details**: customer billing name and address (editable until the invoice is generated). * **Tax details**: taxability reason, tax rate, and the amount collected — useful for your own records. * **A link to the product and customer**, and to the subscription if the order came from a subscription. * **The invoice**, once it's been generated. * **[Custom field data](/features/custom-fields)** captured at checkout. ## Invoices Crosspoint generates a PDF invoice for every paid order. You can: * **Download it** from the order detail page in the dashboard. * **Trigger generation** programmatically via [Generate Order Invoice](/api-reference/current/orders/generate-order-invoice), then fetch the URL with [Get Order Invoice](/api-reference/current/orders/get-order-invoice). Customers can download and edit their own invoices — adding a company name, VAT number, or billing address — from the [Customer Portal](/features/customer-portal/introduction), without pulling you into a support thread. > [!NOTE] > > Once an invoice has been generated, its billing details are frozen. If you need to correct a name > or address after the fact, the customer should edit their invoice from the Customer Portal, which > regenerates it. ## Receipts A **receipt** is the proof of payment for an order — what was charged, how, and what's been refunded since. Crosspoint issues one for every paid order and assigns a per-customer number in the form `RCPT-{customer-id}-{NNNN}`. Each receipt includes: * The **payment method** used (e.g. `Visa — 4242`), the **date paid**, and the **amount**. * Any **customer balance** applied to the order. * Any **refunds** issued against the order, with dates and amounts. * The same line items, taxes, totals, and linked invoice number as the order's invoice. You can **download receipts** from the order detail page in the dashboard, and customers download receipts from the [Customer Portal](/features/customer-portal/introduction) — a **Download Receipt** button appears on each paid order. Programmatically, use [Get Order Receipt](/api-reference/current/orders/get-order-receipt) for the merchant API or its [customer-portal counterpart](/api-reference/current/customer_portal/get-order-receipt). The first request for a given order may return `202 Accepted` while the PDF renders. Retry shortly after for a presigned download URL. The Customer Portal handles this for you. ## Refunds Orders can be refunded in full or in part from the dashboard, or programmatically via the [Refunds API](/api-reference/current/refunds/create-refund). Refunds are a separate resource linked to the order — see [Refunds](/features/refunds) for the rules around what's refundable and how it interacts with payouts. ## Webhooks If you're integrating orders into your own system, Crosspoint emits an event on every state transition: * [`order.created`](/api-reference/current/order_created) — a new order exists (not necessarily paid yet). * [`order.paid`](/api-reference/current/order_paid) — the order has been collected. This is the one most integrations care about. * [`order.updated`](/api-reference/current/order_updated) — something changed on the order. * [`order.refunded`](/api-reference/current/order_refunded) — a refund was issued against the order. ## Next steps > **[Subscriptions](/features/subscriptions/introduction)** > > The recurring relationship that generates most orders. > **[Refunds](/features/refunds)** > > How to refund an order, fully or partially. > **[Orders API](/api-reference/current/orders/list-orders)** > > List and fetch orders, update billing details, and generate invoices. > **[Customer Portal](/features/customer-portal/introduction)** > > Where customers view their own orders and download invoices and receipts. # Products Source: https://staging-docs.crosspoint.finance/features/products Start selling digital products on Crosspoint in minutes. > [!NOTE] > > **Everything is a product** > > Subscriptions and one-time purchases are both products in Crosspoint — same API, same data model, just different pricing and billing logic. They live together in the Products dashboard, filterable by pricing model. ## Billing cycle A product is either a **one-time purchase** or **recurring**. One-time products charge the customer once and grant access forever. Recurring products bill on an interval — daily, weekly, monthly, or yearly — and you can extend any of those with an interval count to express things like "every 2 weeks" or "every 3 months". The billing cycle and recurring interval are locked in at creation. If you need to change them later, create a new product instead. ## Pricing Crosspoint supports several pricing models, and you pick one per product: * **Fixed price.** Set an amount and that's what customers pay. * **Pay what you want.** Customers choose the amount. You can set a minimum and a default that's pre-filled at checkout. * **Free.** No charge. Useful for lead magnets, free tiers, or gating benefits behind a sign-up. * **Metered pricing.** Charge based on usage — bill per API call, per token, per anything you can count. [Learn more about usage-based billing](/features/usage-based-billing/introduction). * **Seat-based pricing.** Sell a number of seats with optional volume tiers, and let the buyer assign them to teammates. [Learn more about seat-based billing](/features/seat-based-pricing). Metered prices stack on top of the others. You can pair one with a fixed base fee for a classic "base + usage" plan, and you can attach multiple metered prices to the same product if you want to bill on more than one dimension at once — say, per API call *and* per GB of storage. Pricing type is locked in at creation, but for fixed-price products you can change the amount at any time. Existing subscribers are **grandfathered** onto the price they signed up at, so a price change only affects new purchases. If you do want to migrate someone onto the new price, you can do it [per subscription](/features/subscriptions/manage) from the dashboard or the API. Whether the amount you enter includes tax or has tax added on top depends on your tax behavior setting. By default, Crosspoint picks the convention that matches the customer's country (inclusive in most of the world, exclusive in the US, Canada, and India). You can set your own default under **Settings**. See [Tax Inclusive Pricing](/features/tax-inclusive-pricing) for how the amount you set translates to what customers actually pay. ### Multiple payment currencies Products can be priced in several currencies at once so customers pay in their local currency. Your organization has a default payment currency that acts as the fallback, and you can add more on top. A price in the default currency is **required** — if you leave it empty, the product is treated as free. To price a product only in another currency, change your organization's default payment currency under **Settings** first. The price structure (price type, metered prices, etc.) must match across every currency you enable. Crosspoint picks the currency based on the customer's geolocation at checkout. If their currency isn't enabled on the product, it falls back to your organization's default. > [!WARNING] > > **Creating checkout sessions from a backend or proxy?** Crosspoint reads the customer's geolocation from the IP address of the request that creates the session. If you create sessions server-side (an API, a Cloudflare Worker, etc.), Crosspoint sees *your server's* IP and may pick the wrong currency. > > Forward the customer's IP as [`customer_ip_address`](/features/checkout/session#customer-ip-address) when creating the session. > **Supported currencies** > > Products can be priced in 130+ currencies. Your organization's default payment currency must be USD or an African currency, and a few African currencies, such as the Ghanaian cedi, can't be charged yet: see [Supported countries](/africa/supported-countries#currencies-you-can-price-in). > > | Code | Currency | > | - | - | > | `AED` | United Arab Emirates Dirham | > | `ALL` | Albanian Lek | > | `AMD` | Armenian Dram | > | `AOA` | Angolan Kwanza | > | `ARS` | Argentine Peso | > | `AUD` | Australian Dollar | > | `AWG` | Aruban Florin | > | `AZN` | Azerbaijani Manat | > | `BAM` | Bosnia-Herzegovina Convertible Mark | > | `BBD` | Barbadian Dollar | > | `BDT` | Bangladeshi Taka | > | `BIF` | Burundian Franc | > | `BMD` | Bermudan Dollar | > | `BND` | Brunei Dollar | > | `BOB` | Bolivian Boliviano | > | `BRL` | Brazilian Real | > | `BSD` | Bahamian Dollar | > | `BWP` | Botswanan Pula | > | `BZD` | Belize Dollar | > | `CAD` | Canadian Dollar | > | `CDF` | Congolese Franc | > | `CHF` | Swiss Franc | > | `CLP` | Chilean Peso | > | `CNY` | Chinese Yuan | > | `COP` | Colombian Peso | > | `CRC` | Costa Rican Colón | > | `CVE` | Cape Verdean Escudo | > | `CZK` | Czech Koruna | > | `DJF` | Djiboutian Franc | > | `DKK` | Danish Krone | > | `DOP` | Dominican Peso | > | `DZD` | Algerian Dinar | > | `EGP` | Egyptian Pound | > | `ETB` | Ethiopian Birr | > | `EUR` | Euro | > | `FJD` | Fijian Dollar | > | `FKP` | Falkland Islands Pound | > | `GBP` | British Pound | > | `GEL` | Georgian Lari | > | `GIP` | Gibraltar Pound | > | `GMD` | Gambian Dalasi | > | `GNF` | Guinean Franc | > | `GTQ` | Guatemalan Quetzal | > | `GYD` | Guyanaese Dollar | > | `HKD` | Hong Kong Dollar | > | `HNL` | Honduran Lempira | > | `HTG` | Haitian Gourde | > | `HUF` | Hungarian Forint | > | `IDR` | Indonesian Rupiah | > | `ILS` | Israeli New Shekel | > | `INR` | Indian Rupee | > | `ISK` | Icelandic Króna | > | `JMD` | Jamaican Dollar | > | `JPY` | Japanese Yen | > | `KES` | Kenyan Shilling | > | `KGS` | Kyrgystani Som | > | `KHR` | Cambodian Riel | > | `KMF` | Comorian Franc | > | `KRW` | South Korean Won | > | `KYD` | Cayman Islands Dollar | > | `KZT` | Kazakhstani Tenge | > | `LAK` | Laotian Kip | > | `LKR` | Sri Lankan Rupee | > | `LRD` | Liberian Dollar | > | `LSL` | Lesotho Loti | > | `MAD` | Moroccan Dirham | > | `MDL` | Moldovan Leu | > | `MGA` | Malagasy Ariary | > | `MKD` | Macedonian Denar | > | `MNT` | Mongolian Tugrik | > | `MOP` | Macanese Pataca | > | `MUR` | Mauritian Rupee | > | `MVR` | Maldivian Rufiyaa | > | `MWK` | Malawian Kwacha | > | `MXN` | Mexican Peso | > | `MYR` | Malaysian Ringgit | > | `MZN` | Mozambican Metical | > | `NAD` | Namibian Dollar | > | `NGN` | Nigerian Naira | > | `NIO` | Nicaraguan Córdoba | > | `NOK` | Norwegian Krone | > | `NPR` | Nepalese Rupee | > | `NZD` | New Zealand Dollar | > | `PAB` | Panamanian Balboa | > | `PEN` | Peruvian Sol | > | `PGK` | Papua New Guinean Kina | > | `PHP` | Philippine Peso | > | `PKR` | Pakistani Rupee | > | `PLN` | Polish Zloty | > | `PYG` | Paraguayan Guarani | > | `QAR` | Qatari Riyal | > | `RON` | Romanian Leu | > | `RSD` | Serbian Dinar | > | `RWF` | Rwandan Franc | > | `SAR` | Saudi Riyal | > | `SBD` | Solomon Islands Dollar | > | `SCR` | Seychellois Rupee | > | `SEK` | Swedish Krona | > | `SGD` | Singapore Dollar | > | `SHP` | St. Helena Pound | > | `SOS` | Somali Shilling | > | `SRD` | Surinamese Dollar | > | `SZL` | Swazi Lilangeni | > | `THB` | Thai Baht | > | `TJS` | Tajikistani Somoni | > | `TOP` | Tongan Paʻanga | > | `TRY` | Turkish Lira | > | `TTD` | Trinidad & Tobago Dollar | > | `TWD` | New Taiwan Dollar | > | `TZS` | Tanzanian Shilling | > | `UAH` | Ukrainian Hryvnia | > | `UGX` | Ugandan Shilling | > | `USD` | US Dollar | > | `UYU` | Uruguayan Peso | > | `UZS` | Uzbekistani Som | > | `VND` | Vietnamese Dong | > | `VUV` | Vanuatu Vatu | > | `WST` | Samoan Tala | > | `XAF` | Central African CFA Franc | > | `XCD` | East Caribbean Dollar | > | `XCG` | Caribbean Guilder | > | `XOF` | West African CFA Franc | > | `XPF` | CFP Franc | > | `YER` | Yemeni Rial | > | `ZAR` | South African Rand | > | `ZMW` | Zambian Kwacha | ## Trial period For recurring products, toggle **Enable trial period** to give customers a window where they aren't charged. Pick a number and a unit (days, weeks, months, or years) and Crosspoint handles the rest. [Learn more about trials](/features/subscriptions/trials). ## Metadata You can attach arbitrary key–value metadata to a product. It's not shown to customers, but it travels along on every order, subscription, and webhook tied to the product, which makes it useful for keeping track of internal IDs or categories that live outside Crosspoint. ## Automated Benefits Benefits are what your customers actually get when they buy: license keys, Discord roles, GitHub repository access, file downloads, feature flags, or a custom benefit you wire up yourself. Crosspoint grants and revokes benefits automatically as customers purchase, renew, or cancel. [Learn more about benefits](/features/benefits/introduction). ## Checkout Page How your product is represented during checkout. ### Description Optional copy that appears on the checkout page. Use it to pitch the product, list what's included, or anything else that helps the customer commit. Markdown is supported. ### Product media Upload images to display on the checkout page. Images can be up to 10MB each, and you can re-arrange or remove them at any time. ### Checkout fields Collect extra information from customers at checkout — phone numbers, terms-of-service agreements, custom data you need for fulfillment, anything you want. Fields are defined once at the organization level and then enabled per product, where you also choose whether each one is required. Supported field types: text, number, date, checkbox, and select. > [!NOTE] > > A required checkbox blocks confirmation until the customer ticks it. Handy for legal terms. The collected values show up on the resulting order or subscription. ## Update a product Most things on a product can be edited after the fact, except for the billing cycle and pricing type — those are locked in at creation. To change either, create a new product. A few things to know: * **Existing subscribers stay on their original price.** Changing a fixed price only affects new purchases. * **Benefit changes propagate.** Add a benefit and existing customers get it automatically. Remove one and they lose access. * **Need a similar product?** Use **Duplicate Product** from the product menu to clone an existing one as a starting point — handy for spinning up a yearly variant of a monthly plan, or for A/B testing pricing. ## Archive a product Products can be archived at any time. Click **Archive** from the product menu and the product disappears from new checkouts. Existing customers keep their access, and active subscriptions keep renewing. You can unarchive at any time from the same menu to make the product available again. ## Delete a product A product without an order, subscription, trial or discount can be permanently deleted. Archive it first, then click **Delete Permanently** from the product menu. The option only appears for products that aren't in use. ## FAQ > **What about variants? Can I sell a monthly and a yearly version of the same product?** > > Crosspoint takes a different approach to what the industry typically calls **variants**. Each product has a single pricing model, and instead of bolting variants onto one product, you create one product per pricing model and showcase them together at checkout. > > So a "monthly" and "yearly" plan are two products, each with their own pricing and benefits, presented side-by-side via [Checkout Links](/features/checkout/links) or the [Checkout Session API](/features/checkout/session). It keeps the API and the data model clean, and gives you full freedom over what each option includes. > **Can I change the price of a product after publishing it?** > > Yes, for fixed-price products. Existing subscribers are grandfathered onto their original price and only new purchases see the updated amount — but you can move individual subscribers onto the new price [per subscription](/features/subscriptions/manage) if you want. Billing cycle and pricing type can't change. > **Can I sell usage-based or metered products?** > > Yes. Set the price type to **Metered** and link it to a meter that tracks the events you care about. Full walkthrough in the [usage-based billing guide](/features/usage-based-billing/introduction). > **Can I sell team or per-seat plans?** > > Yes, via [seat-based pricing](/features/seat-based-pricing). The buyer purchases a number of seats and assigns them to teammates, who each get the product's benefits. > **Will customers see prices in their local currency?** > > If you've enabled additional payment currencies on the product, yes — Crosspoint matches the customer's geolocation to one of the enabled currencies. If there's no match, it falls back to your organization's default. See [Multiple payment currencies](#multiple-payment-currencies) above. > **Can I duplicate a product?** > > Yes. From the product list or the product menu, pick **Duplicate Product** to clone all the settings into a new draft you can tweak before saving. # Manage Refunds Source: https://staging-docs.crosspoint.finance/features/refunds You can easily refund orders on Crosspoint — both in full or in parts. No matter what refund policy you offer to customers, Crosspoint makes it easy to issue both full and partial refunds, so you can deliver the customer experience and refund policy you want. > [!NOTE] > > **Crosspoint can issue refunds on your behalf** > > To stop a chargeback or fraud, Crosspoint may refund an order on your behalf, which cancels any related subscription and revokes the customer's benefits. This applies even if you have a "no refunds" policy: keeping chargebacks low protects your account. See [Account reviews](/africa/account-reviews). ## What's refundable The maximum refundable amount on an order is the **net amount** (excluding tax) minus anything that's already been refunded, with any customer balance applied to the order taken into account. The corresponding **tax** is refunded automatically alongside it — fully on a full refund, prorated on a partial one. > [!WARNING] > > **Payment fees are not refunded** > > Credit card networks and payment processors charge us for the underlying transaction regardless of whether it's later refunded (industry standard). We therefore can't return our fees, since the cost remains. > > Example: when your store pays the fee, a \$30 order costs \$2.30 in fees (6% + \$0.50). You can still refund the customer \$30, but the \$2.30 fee stays deducted from your balance. See [Fees](/africa/fees). ## Issuing a refund 1. Go to the order details page for the specific order you want to refund. 2. Scroll down to the "Refunds" section. 3. Click "Refund order". ### Amount Specify the **net amount** to refund — i.e. the amount excluding tax. It defaults to the maximum refundable amount, but you can lower it to issue a partial refund. Crosspoint **automatically calculates the corresponding tax** to refund based on the order's tax rate. For partial refunds, tax is prorated against the refunded portion; for a full refund, the entire remaining tax is returned. ### Reason Select the reason for the refund — helpful for future reference. ### Revoking benefits **One-time purchases.** You can revoke the customer's access to product benefits — e.g. file downloads, license keys, or Discord/GitHub invites. This is selected by default, since we default to a full refund, but it can be disabled. **Subscriptions.** You can't revoke access by refunding an order tied to a subscription — refunding the order returns the money but does not end the relationship. To end access, [cancel the subscription](/features/subscriptions/manage) instead. Crosspoint revokes the associated benefits automatically once the subscription itself is revoked. # Seat-Based Pricing Source: https://staging-docs.crosspoint.finance/features/seat-based-pricing Sell team products with assignable seats and tiered pricing Seat-based pricing allows you to sell products where a billing manager purchases a specific number of seats and can assign them to team members. Each seat holder gets their own access to the product benefits, making it perfect for team subscriptions, perpetual licenses, and multi-user products. > [!NOTE] > > **Seat-based pricing is ideal for:** > > * Team subscriptions where one billing manager pays for multiple users > * Perpetual team licenses with one-time payment > * Organizational licenses with per-seat pricing > * Products with flat, graduated, or volume-discounted seat pricing ## Customers and members Seat-based products separate who pays from who uses, modeled through three entities: * A **Customer** is the billing entity — who pays. They own subscriptions, orders, and payment methods. On their first seat-based purchase, the customer is permanently upgraded to `type: "team"`, which enables members and team management. * A **Member** is a person under a customer — who uses. Each member has their own email, role (`owner`, `billing_manager`, or `member`), and receives benefit grants independently. The purchaser becomes an `owner` member. Both `owner` and `billing_manager` roles can manage seats and the subscription. * A **CustomerSeat** is the link between a product and a member. It tracks assignment status (`pending`, `claimed`, `revoked`) and holds the invitation token. Because benefits are granted to members, not to the billing customer, always identify the end user by their member — not the customer who paid. ## How it works With seat-based pricing, a billing manager purchases a product (subscription or one-time) with a specific number of seats. They can then: 1. **Assign seats** to team members via email or external customer ID 2. **Manage seats** by resending invitations or revoking access 3. **Scale up** by purchasing additional seats (or a new order for one-time products) 4. **Track usage** by viewing which seats are claimed, pending, or available Team members receive an invitation email with a claim link. Once they claim their seat, benefits are automatically granted. ### Subscriptions vs One-Time Purchases | Feature | Subscriptions | One-Time Purchases | | - | - | - | | **Payment** | Recurring (monthly/yearly) | Single payment | | **Seat Duration** | Active while subscribed | Perpetual (never expire) | | **Adding Seats** | Modify subscription | Purchase new order | | **Benefits** | While subscription active | Forever after claim | > [!TIP] > > Use **subscriptions** for ongoing team access. Use **one-time purchases** for perpetual team licenses. ## Creating a seat-based product **1. Create a new product** From your dashboard, [create a new product](https://staging.crosspoint.finance/to/dashboard/products/new). **2. Configure basic settings** Set your product name, description, and media as usual. **3. Select seat-based pricing** Under **Pricing**, select: * **Product type**: Subscription or One-time * **Billing cycle** (subscriptions only): Monthly or Yearly * **Pricing type**: Seat-based **4. Choose a tiering model** Under **Tiering model**, select how seats are priced: | Model | Description | | - | - | | **Fixed price per seat** | Every seat costs the same flat rate. Simple and predictable. | | **Graduated** | Seats are priced per tier range independently — seats in tier 1 cost one rate, seats in tier 2 cost another. Total price is the sum across all tiers. | | **Volume discounts** | The per-seat price is determined by the total seats purchased, and that rate applies to all seats. Crossing a tier threshold lowers the price for everyone. | **Fixed price per seat** is the default and the simplest option — just enter a single price per seat. For **Graduated** and **Volume discounts**, define tiers using a threshold (the seat count at which a new rate begins) and a price per seat for that range. **Example** with tiers at 1–10 seats: \$10/seat and 11+ seats: \$8/seat: * Graduated: 14 seats = 10 × \$10 + 4 × \$8 = \$132 — each range is billed at its own rate * Volume discounts: 14 seats = 14 × \$8 = \$112 — all seats use the lowest matching rate **5. Add benefits** Configure the benefits that seat holders will receive. These are only granted when a seat is claimed, not when purchased. > [!NOTE] > > When using the default Crosspoint confirmation page (no custom `success_url`), the buyer's seat is automatically claimed during checkout, granting them immediate access to benefits. Any remaining seats can be assigned to teammates. > > If you set a custom `success_url`, the buyer will need to manually assign themselves a seat through the Customer Portal or API if they also want benefits. ## Managing seats After purchase, the billing manager can assign and manage seats from the **Customer Portal** or via the API. ### Seat statuses * **Pending**: Seat assigned, invitation sent, awaiting claim * **Claimed**: Seat claimed by team member, benefits granted * **Revoked**: Seat revoked, benefits removed, can be reassigned ### Key actions * **Assign seats** by email, external customer ID, or existing Crosspoint customer ID * **Resend invitations** for pending seats if the link expired (valid for 24 hours) * **Revoke seats** to remove benefits and free the seat for reassignment * **Reduce seat count** to lower the number of seats on the subscription (triggers a prorated credit) > [!WARNING] > > **Revoking a seat** and **reducing the seat count** are different actions: > > * **Revoking a seat** removes a specific user's access and frees that seat for reassignment. It does **not** reduce the number of seats on the subscription, and the billing manager continues to pay for the same total. > * **Reducing the seat count** changes the subscription quantity itself, which results in a prorated credit for the remainder of the billing period. > > To stop paying for an unused seat, you must reduce the seat count — not just revoke the assignment. ### Proration and billing adjustments When the seat count on a subscription changes mid-billing cycle, charges are prorated automatically: * **Adding seats**: The billing manager is charged immediately for the new seats, prorated for the remainder of the current billing period. The full per-seat price applies from the next billing cycle onward. * **Reducing seat count**: A prorated credit is applied for the removed seats, covering the unused portion of the current billing period. > [!TIP] > > Proration ensures billing managers only pay for seats during the time they are active. Encourage customers to adjust their seat count rather than leaving unused seats idle. ## Limitations * Seats must be assigned individually (no bulk import via dashboard, use API instead) * Claim links expire after 24 hours * Billing manager does not receive product benefits * Maximum of 1,000 seats per subscription * Metadata limited to 10 keys and 1KB total size per seat ## Next steps For implementation details including API integration, webhook handling, and code examples, see the [Implementing Seat-Based Pricing](/guides/seat-based-pricing) guide. # Single Sign-On Source: https://staging-docs.crosspoint.finance/features/sso Let your team sign in to Crosspoint through your own identity provider, and become members automatically. > [!NOTE] > > Single Sign-On is switched on per store. Email [support@crosspoint.finance](mailto:support@crosspoint.finance) to have it switched on for your store. Single Sign-On connects your organization to your own identity provider. Your team signs in with the credentials they already use, and anyone who authenticates becomes a member of your organization. Crosspoint supports any provider that implements OpenID Connect, including Google Workspace, Okta, Microsoft Entra ID, and Keycloak. SSO lives under [**Settings → SSO**](https://staging.crosspoint.finance/to/dashboard/settings/sso) in your dashboard. ## How people become members Anyone who signs in through your connection becomes a member of your organization. You don't invite them first — authenticating against your identity provider is what grants membership. New members join with the **Member** role. To grant more access, [change their role](/features/team-management#changing-a-members-role) after their first sign-in. Your identity provider decides who can authenticate, so it also decides who can join. Grant and revoke access to the Crosspoint application there. > [!WARNING] > > **Removing a member in Crosspoint does not keep them out.** If they can still authenticate against your identity provider, they rejoin on their next sign-in. To remove someone's access, revoke it in your identity provider. Crosspoint only accepts an identity whose email address your provider marks as verified. If the email is unverified, the sign-in is refused and no account is created. ## Setting up a connection **1. Add the connection in Crosspoint** Go to [**Settings → SSO**](https://staging.crosspoint.finance/to/dashboard/settings/sso) and click **Add connection**. Pick **Google Workspace** or **Custom OIDC provider**. **2. Register the callback URL** Copy the **Callback URL** shown at the top of the form and register it as a redirect URI in your identity provider. **3. Fill in the provider details** Enter the **Issuer URL** and **Client ID** from your provider. You can paste the discovery URL into the issuer field — Crosspoint strips the `/.well-known/openid-configuration` suffix. For **Client secret** authentication, paste the secret. For **Private key JWT**, copy the **JWKS URL** into your provider so it can fetch Crosspoint's public keys. **4. Enable it** Connections are created disabled. Once your provider is configured, click **Enable** on the connection row. Share the **Login link** from the SSO settings page with your team. It takes them straight to your organization's sign-in page. ### Authorization parameters You can append extra parameters to the authorization request. The most common is `hd`, which pins a Google Workspace domain so users outside it can't sign in. Crosspoint sets `response_type`, `client_id`, `redirect_uri`, `scope`, `state`, `nonce`, and the PKCE parameters itself. You can't override them. ## Enforcing SSO By default, members can sign in either through SSO or through the standard methods — email code or GitHub. Enforcing SSO removes the alternatives: members reach your organization only through your identity provider. To enforce it, you need an enabled connection, and you must already be signed in through this organization's SSO. That proves the connection works before it becomes the only way in. > [!WARNING] > > Enforcing SSO disconnects every member and every authorized third-party app. They must sign in again through your identity provider. Enforcement makes your identity provider the only door. Anyone who can't authenticate there loses access, including members you invited by email — they keep their membership, but they can't reach the organization until you add them to your provider. You can stop enforcing at any time from the same screen. > [!NOTE] > > Enforcement applies to dashboard sessions. Personal access tokens and organization access tokens keep working, so scripts and integrations aren't interrupted. ## Frequently asked questions > **Do members need a Crosspoint account before their first sign-in?** > > No. Crosspoint creates the account on their first sign-in through your connection, and adds them to your organization. > **What happens to someone who already has a Crosspoint account?** > > They keep it. Signing in through your connection adds your organization to their existing account — it doesn't create a second one, and it doesn't give your organization access to anything else they own. > **Can I still invite members by email?** > > Yes, as long as you don't enforce SSO. [Invitations](/features/team-management#inviting-a-member) work alongside it, which is useful for contractors who aren't in your identity provider. > > Once you enforce SSO, invitations stop being a way in. The invited person becomes a member, but reaching your organization requires signing in through your identity provider. Add them there instead. > **Can I use more than one identity provider?** > > Yes. Add a connection per provider. Each one appears as a separate button on your organization's sign-in page, so give them names. > **How do I remove someone's access?** > > Revoke their access to the Crosspoint application in your identity provider. Removing them in Crosspoint alone doesn't stop them from rejoining on their next sign-in. > **What happens if SSO is switched off?** > > SSO turns off, and enforcement is lifted with it, so your members sign in with the standard methods again. Your connections are kept — switching SSO back on restores them as they were. Everyone who joined through SSO stays a member. # Recovering failed payments Source: https://staging-docs.crosspoint.finance/features/subscriptions/failed-payments How Crosspoint retries failed renewals before revoking a subscription. When a subscription renews, Crosspoint advances it to the next billing cycle first, then attempts to charge the customer's default payment method for the new order. If that charge fails, the subscription moves to `past_due` and enters Crosspoint's automated **payment recovery** (dunning) flow instead of being canceled straight away. This page explains exactly what happens during that window, how to give yourself breathing room before benefits are revoked, and what levers you have to help the customer recover. ## The retry schedule As soon as the first renewal charge fails: 1. The subscription's status moves from `active` to `past_due`, and `past_due_at` is stamped with the time of the failure. 2. Crosspoint emails the customer to let them know the charge failed and links them to the [Customer Portal](/features/customer-portal/introduction) so they can update their default payment method. 3. The renewal order stays open with `next_payment_attempt_at` set to the next retry time. Crosspoint then retries the charge on a fixed schedule, starting from the time of the first failure: | Attempt | Delay from previous | Cumulative time from first failure | | - | - | - | | 1st retry | 2 days | 2 days | | 2nd retry | 5 days | 7 days | | 3rd retry | 7 days | 14 days | | 4th retry | 7 days | 21 days | If a retry succeeds, the failed order is paid. The subscription returns to `active` once all of its pending orders have been paid (past-due subscriptions keep cycling, so there can be more than one). If all four retries fail — or the payment's decline code indicates the method will never succeed (for example, `lost_card`) — Crosspoint stops retrying and **revokes the subscription**. Its status moves to `canceled` and benefits are revoked (subject to the grace period below). ## Benefit revocation grace period By default, benefits follow the subscription's status strictly: the moment the subscription leaves `active`, benefits are revoked. For many businesses that's harsh — a single expired card shouldn't instantly lock a paying customer out while they update their details. Crosspoint has an organization-level **grace period** that holds off benefit revocation while a subscription is in `past_due`. You can set it under **Settings → Subscriptions → Grace period for benefit revocation**. The available values are: * **Immediately** (default) — revoke benefits as soon as the subscription leaves `active`. * **After 2 days** * **After 7 days** * **After 14 days** * **After 21 days** — benefits stay granted for the full length of the retry schedule. The grace period is measured from `past_due_at`. While it's in effect the subscription is still `past_due` (so you can differentiate it in your own app), but the customer retains access to their benefits. Once the grace period expires — or the subscription is revoked for good — benefits are revoked on the next check. > [!NOTE] > > The grace period only delays **benefit revocation**. It does not change the > retry schedule, and it does not keep the subscription `active`. If you want > to treat past-due subscribers specially (for example, with a banner or > a reduced feature set), listen for the > [`subscription.updated`](/api-reference/current/subscription_updated) webhook > and branch on `status === "past_due"`. ## Helping customers recover The most reliable way for a customer to get back into `active` is to update their default payment method from the [Customer Portal](/features/customer-portal/introduction). As soon as the payment method is updated, Crosspoint retries the charge immediately rather than waiting for the next scheduled attempt. Things you can do from your side: * Link prominently to the Customer Portal from your own app when a customer is `past_due`. * [Issue a refund](/features/refunds) on the original failed order if you want to credit the customer for the lost time while keeping the subscription. * [Reschedule the renewal](/features/subscriptions/manage#reschedule-the-next-renewal) to give the customer extra time before the next attempt. * [Revoke the subscription](/features/subscriptions/manage#revoke-immediately) manually if you've decided not to pursue recovery. # Subscriptions Source: https://staging-docs.crosspoint.finance/features/subscriptions/introduction Recurring revenue on top of your products. A **subscription** is the recurring relationship between a customer and one of your [products](/features/products). It's created automatically whenever a customer checks out a product that has a recurring price, and it keeps generating orders on each renewal until the customer or you decide to end it. ## How subscriptions work 1. **A customer subscribes.** When a customer completes a [checkout](/features/checkout/session) for a product that has a recurring price, Crosspoint creates a `subscription` and the first [order](/features/orders). 2. **Crosspoint renews it automatically.** At the end of each billing period, Crosspoint advances the subscription to the next cycle and creates a new order, then attempts to charge the customer's default payment method. If the payment fails, the subscription moves to `past_due` and Crosspoint runs an automated [payment recovery flow](/features/subscriptions/failed-payments). 3. **Benefits stay in sync.** As long as the subscription is active or trialing, the customer keeps access to all the [benefits](/features/benefits/introduction) attached to the product. When the subscription ends, those benefits are revoked. 4. **Customers self-serve from the portal.** The [Customer Portal](/features/customer-portal/introduction) lets customers update their payment method, download invoices, cancel, and — if you allow it — change plans or manage seats. ## Recurring pricing Subscriptions are driven entirely by the way you price the underlying product. Recurring pricing is configured when you [create a product](/features/products#pricing): * **Billing interval.** Daily, weekly, monthly or yearly. * **Interval count.** The number of units per cycle. Setting this to `2` with a `month` interval gives you "every 2 months". * **Pricing type.** Fixed, pay-what-you-want, or free. Free recurring products are still modeled as subscriptions — they just never generate a payment. * **Currency.** A product can have prices in multiple currencies; the subscription locks in the currency used at checkout. > [!NOTE] > > The billing interval and pricing type are fixed once a product is created. If > you need a different interval (for example, a yearly plan next to a monthly > one), create a separate product and show both at checkout. See > [Products → Variants](/features/products#variants). ## Renewal reminders For subscriptions on long billing cycles — six months or more — Crosspoint emails the customer **7 days before the renewal date** to let them know an upcoming charge is coming. This helps with regulations (like the EU's Consumer Rights Directive) that require advance notice on long-interval renewals, and avoids surprise charges that often end in chargebacks. The threshold is six months, however it is expressed: yearly plans, monthly plans with an interval of 6 or more, weekly plans of 25 or more, and daily plans of 180 or more all get the reminder. Reminders are skipped for free subscriptions and for subscriptions already scheduled to cancel at period end. You can turn renewal reminders off under [**Settings → Customer notifications**](https://staging.crosspoint.finance/to/dashboard/settings) if you prefer to handle that communication yourself. ## Cancellation A subscription can end in one of two ways, both available to you and your customers: * **Cancel at period end** — the subscription keeps working until `current_period_end`, then transitions to `canceled`. You can reverse this (uncancel) until the end date is reached. * **Revoke immediately** — the subscription moves to `canceled` right away and benefits are revoked. This is irreversible. See [Managing subscriptions](/features/subscriptions/manage) for how to perform these actions and everything else merchants can do from the dashboard or the API. ## Creating subscriptions There are two ways a subscription can come into existence: * **Through checkout.** The default. Works for any paid recurring product. See [Checkout](/features/checkout/session). * **Through the API, for free recurring products only.** You can call [Create Subscription](/api-reference/current/subscriptions/create-subscription) to subscribe an existing customer to a free product — no order, no email, no charge. This is useful for freemium onboarding flows where you want every signup to have a subscription tied to it from day one. Paid products always go through checkout so that Crosspoint can collect and validate the payment method. ## Next steps > **[Managing subscriptions](/features/subscriptions/manage)** > > Change plans, seats, trials, billing dates, cancel or revoke — from the > dashboard or the API. > **[Proration](/features/subscriptions/proration)** > > Control how the price difference is handled when a subscription is upgraded > or downgraded. > **[Recovering failed payments](/features/subscriptions/failed-payments)** > > How Crosspoint retries failed renewals and when benefits are revoked. > **[Subscriptions API](/api-reference/current/subscriptions/list-subscriptions)** > > List, create, update, and revoke subscriptions programmatically. ## FAQ > **What's the difference between a subscription and a product?** > > A product is the thing you're selling — name, description, benefits, > pricing. A subscription is one specific customer's ongoing relationship > with that product. > > Crosspoint treats one-time purchases and recurring subscriptions as the same > kind of object (both are "products"); the only difference is whether the > price is recurring. See [Products](/features/products) for the full model. > **Can a customer have more than one active subscription?** > > By default, no — a customer can only have one active subscription per > organization at a time. This keeps the common case simple. > > If your product genuinely needs multiple parallel subscriptions per > customer, you can opt in under **Organization Settings → Subscriptions** > by toggling on **Allow multiple subscriptions**. > **When is a customer actually charged?** > > At the end of each billing cycle. When a subscription renews, Crosspoint > advances the cycle, creates a new order with tax and any applicable > discount, and charges the customer's default payment method. If the > charge fails the subscription moves to `past_due` and Crosspoint starts the > [payment recovery flow](/features/subscriptions/failed-payments). > > The first charge happens at checkout (or when the [trial](/features/subscriptions/trials) > ends, if one is configured). > **Can I change the price of an existing subscription?** > > Not the amount on an existing subscription directly — subscriptions lock > in the price they were created with so existing subscribers aren't > surprised by changes. > > If you update the price on a product, the change applies only to **new** > subscriptions. To move an existing subscriber to different pricing, > either create a new product and run an [update to switch them to > it](/features/subscriptions/manage#change-the-plan), or ask them to > change plans themselves from the Customer Portal. > **What happens to benefits when a subscription is canceled?** > > Benefits are tied to the subscription's status, not to the cancellation > action itself: > > * If you **cancel at period end**, the customer keeps their benefits > until `current_period_end` — they paid for that period. > * If you **revoke immediately**, the subscription becomes `canceled` > right away and all benefits are revoked. > * If a renewal payment can't be recovered after Crosspoint's retries, the > subscription moves to `unpaid` and benefits are revoked the same way > as a revoke — subject to the optional > [grace period](/features/subscriptions/failed-payments#benefit-revocation-grace-period) > you can configure on your organization. # Managing subscriptions Source: https://staging-docs.crosspoint.finance/features/subscriptions/manage Everything you can do to a subscription after it's been created. Once a subscription exists, you'll want to adjust it over time — change plans, extend trials, tweak seats, or end it. This page covers everything a merchant can do to a subscription, both from the dashboard and through the API. All of these actions are available under **Sales → Subscriptions** in the dashboard, and via the [Update Subscription](/api-reference/current/subscriptions/update-subscription) endpoint. Customers can also perform a subset of them from the [Customer Portal](/features/customer-portal/introduction) — which ones is controlled by your [portal settings](/features/customer-portal/settings). ## Change the plan Switch a subscription to a different recurring product — the standard upgrade or downgrade flow. * When the change takes effect depends on the [proration behavior](/features/subscriptions/proration): `invoice` and `prorate` apply the new product immediately, while `next_period` schedules a pending update that's only applied at the start of the next billing cycle. * The new product must share the subscription's currency. * You can upgrade a non-seat subscription to a seat-based product — the billing customer is auto-promoted to a `team` customer and claims a seat — but you can't switch a seat-based subscription back to a non-seat product. Non-seat → seat changes must apply immediately, so `next_period` proration isn't allowed for that transition. * You can't change the plan on a subscription that's already canceled or scheduled to cancel — uncancel first. * Plan changes on a **trialing** subscription are allowed. The trial carries over with its end recomputed from the new product's trial length (anchored to the original `trial_start`). If the new product has no trial — or its trial would already have elapsed — the trial ends immediately and a fresh billing cycle starts on the new product. * Custom-priced products (pay-what-you-want) aren't valid destinations for a plan change. ```bash cURL theme={null} curl --request PATCH \ --url https://api.crosspoint.finance/v1/subscriptions/{subscription_id} \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "product_id": "", "proration_behavior": "prorate" }' ``` ```py Python theme={null} crosspoint.subscriptions.update( "", product_id="", proration_behavior="prorate", ) ``` Read more in [Proration](/features/subscriptions/proration). ## Change the number of seats For [seat-based subscriptions](/features/seat-based-pricing), update how many seats the customer is paying for. Like plan changes, when the seat count actually takes effect depends on the [proration behavior](/features/subscriptions/proration): immediate with `invoice` or `prorate`, scheduled for the next cycle with `next_period`. ```bash cURL theme={null} curl --request PATCH \ --url https://api.crosspoint.finance/v1/subscriptions/{subscription_id} \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "seats": 25, "proration_behavior": "invoice" }' ``` ## Apply or change a discount Attach a [discount](/features/discounts) to an active subscription, or remove the current one by passing `null`. The change is applied to the **next** billing cycle — it doesn't retroactively re-bill the current period. ```bash cURL theme={null} curl --request PATCH \ --url https://api.crosspoint.finance/v1/subscriptions/{subscription_id} \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "discount_id": "" }' ``` ## Manage the trial You can add, extend, or end a [trial](/features/subscriptions/trials) on any subscription: * **Add or extend** a trial by setting `trial_end` to a future date. If the subscription is currently active, its status switches to `trialing` and the next charge is postponed to the new date. * **End a trial immediately** by setting `trial_end` to `"now"`. Crosspoint will charge the first billing cycle and only change the subscription to `active` if payment succeeds. If payment fails, the subscription remains `trialing` with its original trial end. ```bash cURL theme={null} curl --request PATCH \ --url https://api.crosspoint.finance/v1/subscriptions/{subscription_id} \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "trial_end": "2026-06-01T00:00:00Z" }' ``` ## Reschedule the next renewal If you need to move a subscription's renewal date — for example, to align several subscriptions on the same day, or to extend the current period as a goodwill gesture — you can set a new `current_billing_period_end`. The new date has to be in the future, and this operation isn't available on revoked subscriptions. If the subscription is scheduled to cancel at period end, its `ends_at` moves with the new date: it ends then instead of on the original period end. ```bash cURL theme={null} curl --request PATCH \ --url https://api.crosspoint.finance/v1/subscriptions/{subscription_id} \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "current_billing_period_end": "2026-07-15T00:00:00Z" }' ``` ## Pause and resume Pausing stops billing without ending the subscription. Use it for seasonal plans or account "freezes", where a customer steps away for a while and comes back to the same subscription and payment method. ### Pause at period end Calling pause sets `pause_at_period_end = true`. The subscription stays **active** and keeps its benefits until its `current_period_end`, then it moves to the `paused` status: benefits are revoked and no further orders are generated. Pausing never charges the customer and never takes effect mid-period. Pass an optional `resumes_at` date to schedule an automatic resume. It has to be after the current period end. Leave it out to pause indefinitely and resume by hand later. ```bash cURL theme={null} curl --request PATCH \ --url https://api.crosspoint.finance/v1/subscriptions/{subscription_id} \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "pause_at_period_end": true, "resumes_at": "2026-11-01T00:00:00Z" }' ``` You can only pause a subscription that's active, with no pending cancellation or pause. To cancel a scheduled pause before it takes effect, set `pause_at_period_end` back to `false`. ```bash cURL theme={null} curl --request PATCH \ --url https://api.crosspoint.finance/v1/subscriptions/{subscription_id} \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "pause_at_period_end": false }' ``` ### Resume Resuming a paused subscription takes effect **right now**: status moves back to `active`, a new billing period starts from the resume date, and the customer is charged immediately. The payment method stays on file, so there's no second checkout. An automatic resume on the `resumes_at` date does exactly the same thing. ```bash cURL theme={null} curl --request PATCH \ --url https://api.crosspoint.finance/v1/subscriptions/{subscription_id} \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "resume": true }' ``` ## Cancel or revoke Crosspoint distinguishes between **canceling** and **revoking** a subscription. Both end the customer's access eventually — the difference is when. ### Cancel at period end Calling cancel (or toggling "Cancel at period end" in the dashboard) sets `cancel_at_period_end = true` and schedules the subscription to end on its `current_period_end`. Until then: * The subscription stays **active** and the customer keeps their benefits — they paid for that period. * No further orders are generated after the current one. * You can **uncancel** at any time before the end date, which reverts the scheduled cancellation. This is the gentler option and the one customers trigger themselves from the [Customer Portal](/features/customer-portal/introduction). It's also what satisfies the "cancel the way you signed up" requirement in jurisdictions like California's [Automatic Renewal Law](https://oag.ca.gov/consumers/auto-renewing-subscriptions). ```bash cURL theme={null} curl --request PATCH \ --url https://api.crosspoint.finance/v1/subscriptions/{subscription_id} \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "cancel_at_period_end": true, "customer_cancellation_reason": "too_expensive" }' ``` You can optionally record a **cancellation reason** and **comment** — useful for tracking churn. The supported reasons are `too_expensive`, `missing_features`, `switched_service`, `unused`, `customer_service`, `low_quality`, `too_complex`, and `other`. > [!NOTE] > > Only set the cancellation reason and comment when they actually come from > the customer (for example, from a cancellation survey or a support > conversation). Customers can see their `customer_cancellation_comment` in > their purchase history, so don't put internal notes there. ### Revoke immediately Revoking ends the subscription **right now**: status moves to `canceled`, `ended_at` is set to the current time, and all benefits are revoked. There's no refund — if you need to issue one, do it separately from [Refunds](/features/refunds). Use this when access needs to stop immediately — a terms-of-service violation, a chargeback, or an explicit customer request. ```bash cURL theme={null} curl --request DELETE \ --url https://api.crosspoint.finance/v1/subscriptions/{subscription_id} \ --header 'Authorization: Bearer ' ``` > [!WARNING] > > Revoking is irreversible. If you want the option to reverse the decision, > cancel at period end instead. ### Uncancel If a subscription is set to cancel at period end and hasn't ended yet, you (or the customer) can reverse the decision. The `cancel_at_period_end` flag is cleared, `ends_at` and `canceled_at` are unset, and the subscription goes back to renewing normally. ```bash cURL theme={null} curl --request PATCH \ --url https://api.crosspoint.finance/v1/subscriptions/{subscription_id} \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "cancel_at_period_end": false }' ``` Uncancelling is not possible once the subscription has actually ended. ## What customers can do The [Customer Portal](/features/customer-portal/introduction) exposes a subset of these actions to the customer, gated by your [portal settings](/features/customer-portal/settings): * **Cancel at period end** is always available — this is the self-service guarantee the portal provides. * **Update the default payment method** is always available — the primary way customers recover from a failed renewal. * **Change plan** is available when **Enable subscription plan changes** is on. * **Change seats** (for seat-based subscriptions) is available when **Enable subscription seat management** is on. * **Pause and resume** is available when **Enable subscription pause** is on. Everything else on this page is merchant-only: revoking a subscription, applying or changing a discount, extending or ending a trial, and rescheduling the renewal date are not exposed to customers. # Proration Source: https://staging-docs.crosspoint.finance/features/subscriptions/proration Control how the price difference is billed when a subscription is upgraded or downgraded. Whenever a subscription's price changes mid-cycle — typically because the customer switched to a different product or changed seats — there's an unused portion of the current billing period that has already been paid for. **Proration** is how Crosspoint reconciles that difference. You pick the proration behavior either at the organization level (as the default) or per API call. ## Proration behaviors Crosspoint supports three standard proration behaviors: ### Prorate and charge now (`invoice`) The subscription is updated **immediately** and Crosspoint invoices the prorated difference right away. If the update is an upgrade the customer is charged; if it's a downgrade they're credited on the new invoice. Use this when you want money to change hands at the same time as the change — for example, on upgrades where you want to collect the extra revenue now. ### Prorate on next invoice (`prorate`) The subscription is updated **immediately**, but the prorated difference is carried over and applied on the **next scheduled invoice** instead of triggering a charge now. The customer's billing cycle is unchanged. This is typically the smoothest experience for the customer: their plan changes instantly, and they see the adjustment on their regular renewal invoice. > [!NOTE] > > If the billing interval changes (for example, monthly to yearly), `prorate` is promoted to > `invoice` automatically — there's no "next invoice" on the old cycle to defer the difference to. > [!WARNING] > > For `invoice` and `prorate`, the subscription update is applied only if the immediate payment (if > any) succeeds. If the payment fails, the API returns an error and the subscription stays > unchanged. ### Schedule for next cycle (`next_period`) The change is **not applied immediately**. It's scheduled as a pending update and applied at the start of the next billing period. No proration charge or credit is issued — the new plan simply takes effect on renewal. While a `next_period` update is pending, the subscription's `pending_update` field describes the scheduled change. Submitting a new update always supersedes the pending one: if you scheduled a `next_period` change and then make another update with `invoice` or `prorate`, the pending update is discarded and the new change is applied right away. This behavior is the safer default for downgrades where you don't want to issue credits, and for any case where you want the current period's terms to stay intact. ### Charge full amount and reset cycle (`reset`) The subscription switches to the new plan **immediately**, Crosspoint invoices the **full price of the new plan** right away, and the **billing cycle is reset** to start now. No proration is applied: the customer isn't credited for the unused portion of the old period, and they aren't charged a prorated amount — they pay the full new price and begin a fresh billing period. Because the cycle restarts, the new period's anchor day becomes the day the change was made, and all future renewals follow the new schedule. Use this when a plan change should behave like a brand-new subscription — for example, when an upgrade should restart the commitment period rather than carry over the current one. > [!NOTE] > > The `reset` proration behavior is currently in preview. Email [support@crosspoint.finance](mailto:support@crosspoint.finance) to have it switched on for your store. ## Setting the default behavior Each organization has a default proration behavior that applies whenever you don't pass an explicit `proration_behavior` on an API call — including plan changes customers initiate from the [Customer Portal](/features/customer-portal/introduction). You can change it from **Settings → Subscriptions** in the dashboard, or via the [Update Organization](/api-reference/current/organizations/update-organization) API. ## Overriding per update Every subscription update that changes the price — a product change or a seat change — accepts an optional `proration_behavior` that overrides the organization default for that single call: ```bash cURL theme={null} curl --request PATCH \ --url https://api.crosspoint.finance/v1/subscriptions/{subscription_id} \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "product_id": "", "proration_behavior": "invoice" }' ``` ```py Python theme={null} from crosspoint.v2026_10 import Crosspoint with Crosspoint("") as crosspoint: res = crosspoint.subscriptions.update( "", product_id="", proration_behavior="invoice", # or "prorate" or "next_period" ) ``` Valid values are `invoice`, `prorate`, and `next_period`. If your organization has access to the preview `reset` behavior, you can pass `reset` here as well. ## How the prorated amount is calculated Crosspoint prorates on a **per-second** basis. If `S` seconds remain out of `T` total seconds in the current billing period: * Unused credit on the old plan: `old_plan_price * S / T` * Charge on the new plan for the remainder: `new_plan_price * S / T` * Prorated difference: the new-plan remainder minus the old-plan unused credit For an upgrade the difference is positive, so the customer is charged; for a downgrade it's negative, so the customer is credited. ### Upgrade A customer subscribed to a \$5/month plan on a 30-day month (2,592,000 seconds total). After 1 day (86,400 seconds elapsed, 2,505,600 seconds remaining) they upgrade to a \$20/month plan. * Unused credit on the $5 plan: `\$5 \* 2,505,600 / 2,592,000 = \$4.83\` * New charge for the remaining 2,505,600 seconds on the $20 plan: `\$20 \* 2,505,600 / 2,592,000 = \$19.33\` * **Prorated difference: \$14.50** With `invoice`, that \$14.50 is charged immediately. With `prorate`, it's added to the next monthly invoice (which is also the new \$20 charge for the next cycle). ### Downgrade A customer subscribed to a \$20/month plan on a 30-day month (2,592,000 seconds total). After 1 day (2,505,600 seconds remaining) they downgrade to a \$5/month plan. * Unused credit on the $20 plan: `\$20 \* 2,505,600 / 2,592,000 = \$19.33\` * New charge for the remaining 2,505,600 seconds on the $5 plan: `\$5 \* 2,505,600 / 2,592,000 = \$4.83\` * **Prorated difference: -\$14.50** (credit to the customer) With `invoice`, a credit invoice for \$14.50 is issued immediately. With `prorate`, the credit is applied on the next invoice. > [!NOTE] > > Because proration is computed from the exact number of seconds remaining, the change time-of-day > matters: upgrading at noon credits a different amount than upgrading at midnight. Crosspoint always > uses the real length of the current billing period (so 28-, 29-, 30-, and 31-day months are all > handled exactly). # Trials Source: https://staging-docs.crosspoint.finance/features/subscriptions/trials Offer free trials on your subscriptions Trials are a great way to let potential customers experience your product before committing to a subscription. With Crosspoint, you can easily set up free trials for your subscription products. ## Setting up a trial You can set up a trial period through the following means: * When creating or editing a [product](/features/products). * When creating or editing a [checkout link](/features/checkout/links). * When creating a Checkout Session through the [API](/api-reference/current/checkouts/create-checkout-session). If you set a trial period on the Checkout Link or Checkout Session, it will **override the trial period set on the product**. The trial period consists of two parameters: * **A unit**: day, week, month, or year. * **A duration**: a number representing how many units the trial will last. ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/features/trials/setup.light.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=049139d00313f1239bc491ddae11be72) ## Starting a trial When a customer checks out a subscription product with a trial period, they will not be charged immediately. Instead, they will have access to the product for the duration of the trial period. We'll still collect their payment information at checkout, but they won't be charged until the trial period ends. This means that if they decide to cancel before the trial ends, they won't be charged at all. Once the trial period ends, the customer will be automatically charged for the subscription, and their billing cycle will begin. ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/features/trials/checkout.light.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=12051ea1bb20edc973b4a70159530400) ## Adding, extending or canceling a trial For existing subscriptions, you can add, extend or cancel a customer's trial period at any time through the dashboard, from the subscription details page. Click on **Update Subscription**, then click on the **Trial** tab. To add or extend a trial, set a new trial end date in the future. If the subscription was active, its status will be changed to **trialing**, and the billing will be postponed until the end of the trial. To cancel a trial, click on the **End trial** button. Crosspoint will charge the customer immediately and change the subscription to **active** if payment succeeds. ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/features/trials/update.light.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=fab83038c7d06177d7cbda4210f09d4a) ## Trial conversion reminders Before a trial ends, Crosspoint emails the customer to remind them that they're about to be charged. This keeps conversions transparent and avoids surprise charges that often end in chargebacks. When the reminder goes out depends on the length of the trial: * **Trials of 3 days or more** — reminder sent **3 days before** the trial ends. * **Trials between 1 and 3 days** — reminder sent **1 day before** the trial ends. * **Trials shorter than 1 day** — no reminder is sent (there isn't enough runway). Reminders are skipped for fully free subscriptions and for trialing subscriptions already scheduled to cancel at period end. You can turn trial conversion reminders off under [**Settings → Customer notifications**](https://staging.crosspoint.finance/to/dashboard/settings) if you prefer to handle that communication yourself. ## Preventing trial abuse To protect your business from customers repeatedly signing up for trials, you can enable the **Prevent trial abuse** feature from your organization's subscription settings. ### How it works When this feature is enabled, Crosspoint tracks trial redemptions using: * **Email addresses**: We automatically detect and normalize email aliases (e.g., `user+alias@example.com` is treated as `user@example.com`) to prevent abuse through simple email variations. * **Payment method fingerprints**: We track the unique fingerprint of payment methods (credit cards) used during checkout to identify returning customers even if they use a different email address. A customer will be blocked from starting a new trial if they have previously redeemed a trial for any of your products and match either: * The same unaliased email address, OR * The same payment method fingerprint ### Enabling the feature 1. Open your organization's [**Settings**](https://staging.crosspoint.finance/to/dashboard/settings) page 2. Navigate to the **Subscription** section 3. Toggle on **Prevent trial abuse** Once enabled, the feature will apply to all future trial checkout attempts across all your products. ### Customer experience When a customer who has already used a trial attempts to check out again: 1. They will see an error message: "You have already used a trial for this product. Trials can only be used once per customer." 2. The checkout will automatically refresh without the trial period 3. The customer can still complete their purchase and subscribe at the regular price This approach ensures a smooth experience while protecting your business from trial abuse. # Tax Inclusive Pricing Source: https://staging-docs.crosspoint.finance/features/tax-inclusive-pricing Control whether your product prices include or exclude tax at checkout When displaying a price to a customer, there are two common conventions: either the displayed price **already includes tax** (inclusive), or tax is **calculated and added on top** at checkout (exclusive). Crosspoint gives you fine-grained control over this behavior at both the organization level and on individual product prices. ## Tax Behavior Options There are three options available: | Option | Description | | - | - | | **Location-based** | Crosspoint automatically picks the right behavior based on the customer's country. This is the default. | | **Inclusive** | The price shown to the customer already includes tax. Tax is extracted from the total at checkout. | | **Exclusive** | The price shown to the customer is before tax. Tax is calculated and added on top at checkout. | > [!NOTE] > > **Location-based** is the recommended option for most businesses. It follows > common regional conventions — customers in the United States, Canada, and > India see exclusive pricing, while customers in the rest of the world (e.g. > EU, UK) see inclusive pricing. ### How it affects checkout * **Inclusive**: If a product is priced at \$12.00 and the tax rate is 20%, the customer pays \$12.00 — \$2.00 of which is tax and \$10.00 is the net amount. * **Exclusive**: If a product is priced at \$10.00 and the tax rate is 20%, the customer pays \$12.00 — \$10.00 net plus \$2.00 tax added on top. In both cases the tax amount is fully visible and itemized on the checkout page and on receipts. ## Setting the Default Tax Behavior The default tax behavior applies to all products in your organization that don't have a price-level override. To change it: 1. Go to your organization's settings. 2. Find the **Default tax behavior** selector under the **Payments** section. 3. Choose **Location-based**, **Inclusive**, or **Exclusive**. 4. The setting is saved automatically. ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/features/tax-inclusive-pricing/settings.light.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=5fd31bc53a1f6b19ca587b8f2b0576be) ## Location-based Behavior in Detail When the tax behavior is set to **Location-based**, Crosspoint resolves the actual inclusive/exclusive behavior at checkout time based on the customer's billing address: | Countries | Behavior | | - | - | | United States, Canada, India | **Exclusive** — tax is added on top of the listed price | | All other countries | **Inclusive** — tax is already included in the listed price | This follows prevailing regional conventions: North American and Indian consumers generally expect prices before tax, while European and most other international customers expect VAT-inclusive pricing. ## What tax behavior changes Tax behavior only controls **how the price is presented** to your customer — it does not affect whether tax is charged or how much. > [!NOTE] > > Changing the tax behavior on a price does **not** change the effective tax > rate or your tax obligations. It only changes whether the stated price is > presented as tax-included or tax-excluded to the customer. # Team Management Source: https://staging-docs.crosspoint.finance/features/team-management Invite teammates to your organization, manage their roles, and control what they can access in the Crosspoint dashboard. Crosspoint organizations support multiple users. You can invite teammates to collaborate on products, orders, customers, and other parts of your business — each with a role that determines what they're allowed to do. Team management lives under [**Settings → Members**](https://staging.crosspoint.finance/to/dashboard/settings/members) in your dashboard. > [!NOTE] > > With SSO switched on for your store, you can connect your own identity provider instead of inviting people one by one. See [Single Sign-On](/features/sso). ## Roles Every member of an organization has one of six roles. Pick the one that matches the person's job, so money and secrets stay with the people who need them. | Role | What they can do | | - | - | | **Owner** | Everything. There is exactly one owner per organization. | | **Administrator** | Everything except transferring ownership: members, finances, settings, webhooks and API tokens. | | **Developer** | Products, custom fields, webhooks, API tokens and IP allowlists. Can view sales, customers, analytics and members. | | **Finance** | Balance and payouts, and sales (orders, subscriptions, refunds). Can view products and customers. | | **Sales / Support** | Customers and sales (orders, subscriptions, refunds), products and analytics. | | **Viewer** | Can view everything, change nothing. | You choose the role when you invite someone, and can change it later. ## Inviting a member 1. Go to [**Settings → Members**](https://staging.crosspoint.finance/to/dashboard/settings/members) in your organization's dashboard. 2. Click **Invite**. 3. Enter the email address of the person you want to add. 4. Click **Send Invite**. The invitee receives an email with a link to join your organization. If they don't already have a Crosspoint account, one is created for them when they accept the invitation. New members join with the **Member** role by default. To grant additional access, change their role after they've been added. > [!NOTE] > > **Already a member?** If you invite an email that's already part of your organization, no new invite is sent — the existing membership is shown instead. > [!WARNING] > > **Enforcing [SSO](/features/sso)?** Invitations stop being a way in. The invited person becomes a member, but reaching the organization requires signing in through your identity provider. Add them there instead. ## Changing a member's role Only **admins** and **owners** can change roles. 1. Go to [**Settings → Members**](https://staging.crosspoint.finance/to/dashboard/settings/members). 2. Click the **⋯** menu on the member's row. 3. Choose **Change role**. 4. Select **Admin** or **Member** and click **Save**. The role picker shows a side-by-side permissions matrix so you can confirm what each role grants before saving. > [!NOTE] > > **The owner's role can't be changed from this menu.** Every organization has exactly one owner at all times. Ownership can only be transferred to another member of the organization who has completed identity verification — please [contact support](/support) to request an ownership transfer. ## Removing a member Only **admins** and **owners** can remove other members. 1. Go to [**Settings → Members**](https://staging.crosspoint.finance/to/dashboard/settings/members). 2. Click the **⋯** menu on the member's row. 3. Choose **Remove from organization**. 4. Confirm the removal. The removed member immediately loses access to the organization. They keep their Crosspoint account and any other organizations they're a member of. > [!WARNING] > > **You can't remove the owner.** Every organization always has exactly one owner — to remove the current owner, ownership must first be transferred to another member who has completed identity verification. Please [contact support](/support) to request an ownership transfer. > [!WARNING] > > **Using [SSO](/features/sso)?** Removing a member here doesn't keep them out. Anyone who can still authenticate against your identity provider rejoins on their next sign-in. Revoke their access in your identity provider instead. ## Leaving an organization If you want to leave an organization yourself: 1. Go to [**Settings → Members**](https://staging.crosspoint.finance/to/dashboard/settings/members). 2. Click the **⋯** menu on your own row. 3. Choose **Leave organization** and confirm. You can't leave an organization if you are: * The **owner** — ownership must be transferred first. * The **only remaining member** of the organization. ## Frequently asked questions > **Do invited users need an existing Crosspoint account?** > > No. If the invited email doesn't have a Crosspoint account yet, one is created automatically. The user signs in to claim it when they accept the invitation. > **Can people join without an invitation?** > > Yes, if your organization uses [Single Sign-On](/features/sso). Anyone who authenticates through your identity provider becomes a member with the Member role — no invitation needed. Your identity provider controls who can do that. > **Can a user belong to more than one organization?** > > Yes. A user can be a member of any number of organizations and may have different roles in each one. You can switch between them from the organization picker in the dashboard. > **How do I transfer ownership of an organization?** > > Ownership can only be transferred to another member of the organization who has completed identity verification. Every organization always has exactly one owner — the previous owner becomes an admin once the transfer completes. > Please [contact support](/support) to request an ownership transfer. # Unit-Based Pricing Source: https://staging-docs.crosspoint.finance/features/unit-based-pricing Sell a quantity of anything, paid up front, with tiered rates Unit-based pricing lets a customer buy a quantity of something and pay for it up front. Devices, licenses, gigabytes, servers. You name the unit, set the rate, and the buyer picks how many they want at checkout. > [!NOTE] > > **Unit-based pricing is ideal for:** > > * Per-device or per-installation licensing > * Prepaid capacity, such as storage or bandwidth > * Any quantity that isn't a person and isn't measured after the fact > * Products where volume discounts should apply to the declared quantity ## Choosing between unit-based, seat-based, and metered Three pricing models charge by quantity. They differ in what the quantity counts and when the customer pays. | Model | The quantity counts | Customer pays | Benefits go to | | - | - | - | - | | **Unit-based** | Anything that isn't a person | Up front | The customer, once | | **Seat-based** | People | Up front | Each member who claims a seat | | **Metered** | Consumption you record | After the period | The customer, once | Pick seat-based when each unit maps to a person who needs their own access. Crosspoint then handles invitations, seat claims, and per-member benefits. Pick unit-based for everything else. Pick metered when Crosspoint counts the quantity for you, from usage events. With unit-based the buyer declares the number themselves, at checkout. ## How it works The buyer chooses a quantity at checkout and pays for it immediately. The price comes from the tiers you configure and the quantity they picked. On a subscription, the quantity carries across billing periods. You can change it at any time, and so can the customer if you let them. Crosspoint prorates the difference. On a one-time purchase, the quantity is fixed at the moment of sale. ## Creating a unit-based product **1. Create a new product** From your dashboard, [create a new product](https://staging.crosspoint.finance/to/dashboard/products/new). **2. Configure basic settings** Set your product name, description, and media as usual. **3. Select unit-based pricing** Under **Pricing**, select: * **Product type**: Subscription or One-time * **Billing cycle** (subscriptions only): Monthly or Yearly * **Pricing type**: Unit-based **4. Name the unit** Set the singular and plural nouns for what you're selling, such as `device` and `devices`. They appear at checkout, in the Customer Portal, and on invoices. Leave them empty and Crosspoint falls back to "unit" and "units". **5. Choose a tiering model** Under **Tiering model**, select how the quantity is priced: | Model | Description | | - | - | | **Fixed price per unit** | Every unit costs the same rate. | | **Volume** | The rate is set by the total quantity, and that rate applies to every unit. Crossing a threshold lowers the price on the whole order. | | **Graduated** | Each tier range is priced at its own rate. The total is the sum across ranges. | Define a tier by an upper bound and a rate. The bound is inclusive, and each tier starts where the previous one ended. Leave the last tier's bound empty for an open-ended top range. **Example** with tiers at up to 10: \$10 per unit, and beyond: \$8 per unit: * Volume: 14 units = 14 × \$8 = \$112. Every unit gets the lower rate. * Graduated: 14 units = 10 × \$10 + 4 × \$8 = \$132. Each range keeps its own rate. **6. Set a minimum and maximum** **Minimum units** is the smallest quantity a customer can buy. It defaults to 1. **Maximum units** caps the quantity a customer can buy. Leave it empty for unlimited, and customers can buy any quantity above the minimum. **7. Add benefits** Configure the benefits the customer receives on purchase. ### Combining with other prices Add a fixed price alongside the unit-based one for a base fee plus a per-unit rate. Metered prices can sit on top of both, so a subscription can charge a flat monthly amount, a rate per device, and usage on a meter. ## Benefits The customer receives each benefit once, whatever quantity they buy. Buying 40 devices grants one license key, not 40. If you need one grant per unit, use [seat-based pricing](/features/seat-based-pricing). ## Changing the quantity You can change the quantity on a subscription from your dashboard or through the API at any time. Customers can change it themselves from the Customer Portal once you allow it. Go to **Settings**, then **Customer portal**, and turn on **Enable subscription unit changes**. It is off by default. ### Proration When the quantity changes mid-period, Crosspoint prorates the difference using your organization's proration behavior: * **Increasing the quantity** charges for the added units, prorated over the remainder of the period. * **Reducing the quantity** credits the removed units for the unused part of the period. The full amount applies from the next billing period onward. ## Limitations * The quantity is a whole number. Fractional quantities need [metered billing](/features/usage-based-billing/introduction) instead. * Tier rates are whole cents. * A product carries at most one unit-based price. * Unit-based cannot be combined with seat-based or pay-what-you-want pricing. * One-time purchases cannot change quantity after the sale. The customer buys again instead. # Billing Source: https://staging-docs.crosspoint.finance/features/usage-based-billing/billing How billing works with Usage Based ## Metered Pricing Metered Pricing is a pricing model where you charge your customers based on the usage of your application. There are a few different pricing models unique to Usage Based Billing: * Unit Pricing * Volume Pricing * Graduated Pricing ### Unit Pricing Unit pricing is a simple pricing model where you charge a fixed amount for each unit of usage. For example: | Product Meter | Price per unit | | - | - | | `prompt-tokens` | \$0.10 | | `completion-tokens` | \$0.18 | This means that every unit of `prompt-tokens` consumed by a customer will be charged at \$0.10 and every unit of `completion-tokens` will be charged at \$0.18. It's a linear pricing model, where the price per unit is fixed. ### Volume Pricing Volume pricing sets the rate from the total usage over the period, and that rate applies to every unit. Crossing a threshold lowers the price on all of it. ### Graduated Pricing Graduated pricing charges each range at its own rate. The total is the sum across ranges. ### Defining tiers Volume and graduated take the same tiers. Define one by an upper bound and a rate. The bound is inclusive, and each tier starts where the previous one ended. The last tier is always unbounded, since usage has no ceiling. A [cap](#add-a-metered-price-to-your-product) still limits what the customer pays. **Example** with tiers at up to 1,000: \$0.10 per unit, and beyond: \$0.08 per unit: * Volume: 1,400 units = 1,400 × \$0.08 = \$112. Every unit gets the lower rate. * Graduated: 1,400 units = 1,000 × \$0.10 + 400 × \$0.08 = \$132. Each range keeps its own rate. ## Invoicing Customers for Usage Our Usage Based Billing infrastructure is built to work with Subscription products out of the box. ### Add a metered price to your product To charge your customers for usage, you need to add a metered price to your product. You'll need to select the **Meter** and the **Pricing model**. A flat price per unit takes a single **amount per unit**; volume and graduated take a list of tiers. > [!NOTE] > > The price is always entered as the cost per **single unit**. How it is > displayed to customers — for example as `$20 / 1M tokens` — is controlled by > the [Unit](/features/usage-based-billing/meters#unit) setting on the meter. ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/features/usage/cap.light.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=6da7b7745fc3810771369f572713546b) Optionally, you can set a **cap**. The customer will be charged the cap amount if they exceed it, regardless of the usage. ### Monthly Invoicing If a customer has a subscription with a monthly billing period, usage is aggregated monthly and invoiced at the end of the month with the rest of the subscription. ### Yearly Invoicing If a customer has a subscription with a yearly billing period, usage is aggregated yearly and invoiced at the end of the year with the rest of the subscription. ### Usage Charges and Subscription Cancellation When a subscription is canceled, it generally remains active until the end of the current billing period (known as the grace period). During this grace period, all accumulated usage-based charges continue to be tracked. A final invoice will be issued at the end of that period to cover the consumed usage, even if the subscription will not be renewed. This ensures no pending usage charges are lost. > [!WARNING] > > If a [discount](/features/discounts) is applied on the subscription, it'll be > applied on the **whole invoice**, including metered usage. ## Customer Portal Customers can view their estimated charges for each meter in the Customer Portal. ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/features/usage/portal.light.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=aacafd03cc502d9eacb160b94c0c7cb4) # Credits Source: https://staging-docs.crosspoint.finance/features/usage-based-billing/credits Crediting customers for Usage Based Billing Credits is the way to pre-pay for usage in Crosspoint. It allows you to give your customers the ability to pre-pay for usage instead of risk getting a hefty bill at the end of the month. ## How Credits Work When you ingest events into a Usage Meter, customers will be charged for the usage based on the product's pricing model. However, sometimes you may want to give your customers the ability to pre-pay for usage instead of risk getting a hefty bill at the end of the month. When you issue Credits to a customer, we first deduct the Credits from their Usage Meter balance. If the Usage Meter balance reaches 0, the customer will be charged for the overage. ### Credits-only spending To avoid any overage charges, don't create any Metered price on your product. This way, billing won't be triggered at all for the meter ## Issuing Credits with the Credits Benefit ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/features/benefits/credits/credits.light.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=26efa0555d7d1d797f68919f67f31a00) The Credits benefit will credit a customer's Usage Meter balance at different points in time depending on the type of product the benefit is attached to. ### 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. ## Tracking customer's balance In your application, you'll likely need to track the customer's balance for a given meter. The easiest way to do this is to use the [Customer State](/integrate/customer-state), which will give you the overview of the customer, including the balance for each of their active meters. You can also specifically query the meters balance using the [Customer Meters API](/api-reference/current/customer_meters/list-customer-meters). > [!WARNING] > > Crosspoint doesn't block usage if the customer exceeds their balance. You're > responsible for implementing the logic you need to prevent usage if they > exceed it. # Event Ingestion Source: https://staging-docs.crosspoint.finance/features/usage-based-billing/event-ingestion Ingest events from your application Events are the core of Usage Based Billing. They represent *some* usage done by a customer in your application. Typical examples of events are: * A customer consumed AI LLM tokens * A customer streamed minutes of video * A customer uploaded a file to your application Events are sent to Crosspoint using the [Events Ingestion API](/api-reference/current/events/ingest-events) and are stored in our database. An event consists of the following fields: * A `name`, which is a string that can be used to identify the type of event. For example, `ai_usage`, `video_streamed` or `file_uploaded`. * A `customer_id` or `external_customer_id`, which is Crosspoint's customer ID or your user's ID. This is used to identify the customer that triggered the event. * A `metadata` object, which is a JSON object that can contain any additional information about the event. This is useful for storing information that can be used to filter the events or compute the actual usage. For example, you can store the duration of the video streamed or the size of the file uploaded. Here is an example of an event: ```json theme={null} { "name": "ai_usage", "external_customer_id": "cus_123", "metadata": { "model": "gpt-4.1-nano", "requests": 1, "total_tokens": 77, "request_tokens": 58, "response_tokens": 19 } } ``` ## Ingest events using the Crosspoint SDK To ingest events, you can use the Crosspoint SDKs. ### TypeScript Example ```typescript theme={null} import { createCrosspoint } from "@crossdev/sdk/2026-10"; const crosspoint = createCrosspoint({ accessToken: process.env.CROSSPOINT_ACCESS_TOKEN!, }); await crosspoint.events.ingest({ events: [ { name: "", external_customer_id: "", metadata: { key: "value", }, }, ], }); ``` > [!NOTE] > > You are always responsible for checking the balance of your customers' Usage > Meter. As events always are ingested, we will never prohibit any customer's > action based on their Usage Meter balance. ## Ingestion Strategies To make it easier to ingest events, we have created a set of ingestion strategies for common event sources. Learn more about our [Ingestion Strategies](/features/usage-based-billing/ingestion-strategies). ## Good to know ### Events are immutable Once an event is ingested, it cannot be changed, nor can it be deleted. ### Backdated events You can set the `timestamp` field on an event to any point in the past — useful for batched ingestion or replaying from your own queue. The timestamp does not need to be part of the current billing cycle. Crosspoint attributes events to billing periods based on **when they were received by Crosspoint**, not the supplied `timestamp`. An event ingested today with a `timestamp` from a previous billing period is included on the **current cycle** — it is not added to the already-closed invoice for the period it logically belongs to. Crosspoint never issues retroactive invoices or credits for late events. The supplied `timestamp` is still used to: * Place events on the time series shown in the dashboard and the Customer Portal. * Set the date range displayed on the metered line item of the next invoice. # Introduction Source: https://staging-docs.crosspoint.finance/features/usage-based-billing/introduction Usage based billing using ingested events > [!NOTE] > > Usage Based Billing is a new feature. We have a lot in store and welcome > feedback! ## Overview Crosspoint has a powerful Usage Based Billing infrastructure that allows you to charge your customers based on the usage of your application. This is done by ingesting events from your application, creating Meters to represent that usage, and then adding metered prices to Products to charge for it. ## Concepts ### Events Events are the core of Usage Based Billing. They represent *some* usage done by a customer in your application. Typical examples of events are: * A customer consumed AI LLM tokens * A customer streamed minutes of video * A customer uploaded a file to your application Events are sent to Crosspoint using the [Events Ingestion API](/api-reference/current/events/ingest-events) and are stored in our database. An event consists of the following fields: * A `name`, which is a string that can be used to identify the type of event. For example, `ai_usage`, `video_streamed` or `file_uploaded`. * A `customer_id` or `external_customer_id`, which is Crosspoint's customer ID or your user's ID. This is used to identify the customer that triggered the event. * A `metadata` object, which is a JSON object that can contain any additional information about the event. This is useful for storing information that can be used to filter the events or compute the actual usage. For example, you can store the duration of the video streamed or the size of the file uploaded. Here is an example of an event: ```json theme={null} { "name": "ai_usage", "external_customer_id": "cus_123", "metadata": { "model": "gpt-4.1-nano", "requests": 1, "total_tokens": 77, "request_tokens": 58, "response_tokens": 19 } } ``` ### Meters Meters are there to filter and aggregate the events that are ingested. Said another way, this is how you define what usage you want to charge for, based on the events you send to Crosspoint. For example: * AI usage meter, which filters the events with the name `ai_usage` and sums the `total_tokens` field. * Video streaming meter, which filters the events with the name `video_streamed` and sums the `duration` field. * File upload meter, which filters the events with the name `file_uploaded` and sums the `size` field. You can create and manage your meters from the dashboard. Crosspoint is then able to compute the usage over time, both globally and per customer. ### Metered Price A metered price is a price that is based on the usage of a meter, which is computed by filtering aggregating the events that are ingested. This is how you charge your customers for the usage of your application. ### Meter Credits benefit You can give credits to your customers on a specific meter. This is done by creating a Meter Credits Benefit, which is a special type of benefit that allows you to give credits to your customers on a specific meter. On a recurring product, the customer will be credited the amount of units specified in the benefit at the beginning of every subscription cycle period — monthly or yearly. ## Quickstart Get up and running in 5 minutes **1. Create a Meter** Meters consist of filters and an aggregation function. The filter is used to filter the events that should be included in the meter and the aggregation function is used to compute the usage. ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/features/usage/create-meter.light.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=623ac4db2a6f2592342d343b1012f4e9) **2. Add metered price to a Product** To enable usage based billing for a Product, you need to add a metered price to the Product. Metered prices are only applicable to Subscription Products. ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/features/usage/product-meter.light.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=3fb1a6df49ca3ae33f0928d806511e06) **3. Ingest Events** Now you're ready to ingest events from your application. Sending events which match the meter's filter will increment the meter's usage for the customer. ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/features/usage/ingest.light.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=3cb6b09d707932beeea787db19e780b5) **4. Customer Usage** Customers can view their estimated charges for each meter in the Customer Portal. ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/features/usage/portal.light.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=aacafd03cc502d9eacb160b94c0c7cb4) # Meters Source: https://staging-docs.crosspoint.finance/features/usage-based-billing/meters Creating and managing meters for Usage Based Billing Meters are there to filter and aggregate the events that are ingested. Said another way, this is how you define what usage you want to charge for, based on the events you send to Crosspoint. For example: * AI usage meter, which filters the events with the name `ai_usage` and sums the `total_tokens` field. * Video streaming meter, which filters the events with the name `video_streamed` and sums the `duration` field. * File upload meter, which filters the events with the name `file_uploaded` and sums the `size` field. You can create and manage your meters from the dashboard. Crosspoint is then able to compute the usage over time, both globally and per customer. ## Creating a Meter To create a meter, [open the Meters page](https://staging.crosspoint.finance/to/dashboard/products/meters) and click the "Create Meter" button. ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/features/usage/create-meter.light.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=623ac4db2a6f2592342d343b1012f4e9) ## Filters A filter is a set of clauses that are combined using conjunctions. They're used to filter events that you've ingested into Crosspoint. ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/features/usage/filter.light.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=a429c57c3f06745ea71c48703622687b) ### Clauses A clause is a condition that an event must meet to be included in the meter. #### Property Properties are the properties of the event that you want to filter on. If you want to match on a metadata field, you can use the metadata key directly. No need to include a `metadata.` prefix. #### Operator Operators are the operators that you want to use to filter the events. * **Equals** * **Not equals** * **Greater Than** * **Greater Than or Equals** * **Less Than** * **Less Than or Equals** * **Contains** * **Does Not Contain** #### Value Values are automatically parsed in the filter builder. They're parsed in the following order: 1. Number — Tries to parse the value as number 2. Boolean — Checks if value is "true" or "false" 3. String — Treats value as string as fallback ### Conjunctions A conjunction is a logical operator that combines two or more clauses. * **and** — All clauses must be true for the event to be included. * **or** — At least one clause must be true for the event to be included. ## Aggregation The aggregation is the function that is used to aggregate the events that match the filter. For example, if you want to count the number of events that match the filter, you can use the **Count** aggregation. If you want to sum the value of a metadata field, you can use the **Sum** aggregation. * **Count** — Counts the number of events that match the filter. * **Sum** — Sums the value of a property. * **Average** — Computes the average value of a property. * **Minimum** — Computes the minimum value of a property. * **Maximum** — Computes the maximum value of a property. * **Unique** — Counts the number of unique values of a property. > **Example** > > Consider the following events: > > ```json theme={null} > [ > { > "name": "ai_usage", > "external_customer_id": "cus_123", > "metadata": { > "total_tokens": 10 > } > }, > { > "name": "ai_usage", > "external_customer_id": "cus_123", > "metadata": { > "total_tokens": 20 > } > }, > { > "name": "ai_usage", > "external_customer_id": "cus_123", > "metadata": { > "total_tokens": 30 > } > }, > { > "name": "ai_usage", > "external_customer_id": "cus_123", > "metadata": { > "total_tokens": 30 > } > } > ] > ``` > > Here is the result of each aggregation function, over the `total_tokens` metadata property: > > * **Count**: 4 units > * **Sum**: 90 units > * **Average**: 22.5 units > * **Minimum**: 10 units > * **Maximum**: 30 units > * **Unique**: 3 units If you want to use a metadata property in the aggregation, you can use the metadata property directly. No need to include a `metadata.` prefix. ## Unit The unit controls how prices for this meter are **formatted and displayed** to customers — on invoices, in the customer portal, and in your checkout. It does not affect billing calculation; it is purely presentational. | Unit | Display format | Best for | | - | - | - | | Scalar | \$0.05 / unit | Generic counts (API calls, events, seats) | | Token | \$20.00 / 1M tokens | LLM token consumption | | Custom | Configurable (see below) | Any unit not covered above | ### Custom unit Select **Custom** to define your own display format. Two additional fields appear: * **Unit label** — The singular name shown after the price, e.g. `gigabyte` displays as `$0.023 / gigabyte`. * **Unit multiplier** — Scales the displayed price so you can show a more readable denomination. For example, a multiplier of `1000` shows the price per 1 000 units rather than per single unit. > [!TIP] > > The unit multiplier only affects how the price is shown. The raw `unit_amount` > you set is still the price per single event unit — the multiplier scales the > display amount for readability. ## Example The following Meter Filter & Aggregation will match events that have the name `openai-usage` and sum units over metadata property `completionTokens`. ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/features/usage/meter.light.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=978c78fd95400b5780680bbd45cdecdd) > [!TIP] > > You can **Preview** the events matched by the meter while creating it. ## Good to know A few things to keep in mind when creating and managing meters: ### Updating a Meter You may update a meter's filters or aggregation function as long as the meter doesn't have any processed events or does not have any customer purchase associated with it. # Authentication Source: https://staging-docs.crosspoint.finance/integrate/authentication > [!NOTE] > > All bearer tokens should be kept private and never shared or exposed in client-side code. To authenticate requests, Crosspoint API has two mechanisms. 1. [Organization Access Tokens (OAT)](/integrate/oat) - Recommended 2. [OAuth 2.0 Provider](/integrate/oauth2/introduction) (Partner Integrations) ## Organization Access Tokens (OAT) They are tied to **one** of your organization. You can create them from your organization settings, and limit each one to your servers' IP addresses with an [IP allowlist](/africa/ip-allowlist). ## Security Limit organization access tokens to your servers' IP addresses with an [IP allowlist](/africa/ip-allowlist): a leaked token is then useless anywhere else. To protect your data, we revoke tokens that are reported to us as leaked publicly on the web. If you received an email about one of your token being leaked, it means that we were notified of such situation. The email contains the details about the nature of the token and the source of the leak. In the future, it's crucial that you remain extra cautious about not leaking your tokens publicly online. You can read more about the good practices to manage secrets in the [OWASP Secrets Management Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html). # Customer State Source: https://staging-docs.crosspoint.finance/integrate/customer-state The quickest way to integrate billing in your application Customer State is a concept allowing you to query for the current state of a customer, including their active subscriptions and granted [benefits](/features/benefits/introduction), in a single [API call](/api-reference/current/customers/get-customer-state-by-external-id) or single [webhook event](/api-reference/current/customer_state_changed). Combined with the [External ID](/features/customer-management#external-id) feature, you can get up-and-running in minutes. ## The customer state object The customer state object contains: * All the data about the customer. * The list of their **active** subscriptions. * The list of their **granted** benefits. * The list of their **active** meters, with their current balance. Thus, with that single object, you have all the required information to check if you should provision access to your service or not. > **[Get Customer State by External ID](/api-reference/current/customers/get-customer-state-by-external-id)** > > One endpoint to rule them all, using your own customer ID. > **[Get Customer State](/api-reference/current/customers/get-customer-state)** > > The same one, but with internal Crosspoint customer ID. ## The `customer.state_changed` webhook To be notified of the customer state changes, you can listen to the `customer.state_changed` webhook event. It's triggered when: * Customer is created, updated or deleted. * A subscription is created or updated. * A benefit is granted or revoked. By subscribing to this webhook event, you keep your system up-to-date and update your customer's access accordingly. > **[customer.state\_changed](/api-reference/current/customer_state_changed)** > > One webhook to rule them all. # OAuth 2.0 Connect Source: https://staging-docs.crosspoint.finance/integrate/oauth2/connect ## Authorize To start the authorization flow you need to redirect the user to the authorization URL. It looks like this: ``` https://staging.crosspoint.finance/oauth2/authorize? response_type=code &client_id=CLIENT_ID &redirect_uri=https%3A%2F%2Fexample.com%2Fcallback &scope=openid%20email ``` The parameters are the one described in the [OpenID Connect specification](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). The most important ones are: **response\_type=code** `string` *required* Indicates that you want to use the authorization code flow. Most common and the only one supported by Crosspoint. **client\_id** `string` *required* The Client ID you got when creating the OAuth 2.0 client. **redirect\_uri** `string` *required* The URL where the user will be redirected after granting access to their data. Make sure you declared it when creating the OAuth2 client. **scope** `string` *required* A space-separated list of scopes you want to ask for. Make sure they are part of the scopes you declared when creating the OAuth2 client. If you redirect the user to this URL, they'll see a page asking them to grant access to their data, corresponding to the scopes you asked for. ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/integrate/oauth2/connect.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=9749c470eb9312705e00446566ac8f53) Crosspoint issues **user-scoped** access tokens, tied to the user granting access. On the consent screen, the user can optionally **limit the token to specific organizations**: by default it can access all of their organizations (including ones they join later), but if they select specific ones, it's restricted to those. Access always intersects with the user's current membership, so it adjusts automatically if they leave an organization. You can check which organizations a token is limited to by [introspecting it](/api-reference/current/oauth2/introspect-token) — the response includes an `organizations` array (an empty array means all of them). If the user grants access, they'll be redirected to your `redirect_uri` with a `code` parameter in the query string. This code is a one-time code that you can exchange for an access token. #### Exchange code token Once you have the authorization code, you can exchange it for an access token. To do so, you'll need to make a `POST` request to the token endpoint. This call needs to be authenticated with the Client ID and Client Secret you got when creating the OAuth2 client. Here is an example with cURL: ```bash Terminal theme={null} curl -X POST https://api.crosspoint.finance/v1/oauth2/token \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'grant_type=authorization_code&code=AUTHORIZATION_CODE&client_id=CLIENT_ID&client_secret=CLIENT_SECRET&redirect_uri=https://example.com/callback' ``` You should get the following response: ```json theme={null} { "token_type": "Bearer", "access_token": "cxp_at_u_XXX", "expires_in": 864000, "refresh_token": "cxp_rt_u_XXX", "scope": "openid email", "id_token": "ID_TOKEN" } ``` The `access_token` will allow you to make authenticated API requests on behalf of the user. The `refresh_token` is a long-lived token that you can use to get new access tokens when the current one expires. The `id_token` is a signed JWT token containing information about the user, as per the [OpenID Connect specification](https://openid.net/specs/openid-connect-core-1_0.html#IDToken). #### Public Clients Public clients are clients where the Client Secret can't be kept safe, as it would be accessible by the final user. This is the case for SPA, mobile applications, or any client running on the user's device. In this case, **and only if the client is configured as a Public Client**, the request to the token endpoint won't require the `client_secret` parameter. However, the [PKCE](https://oauth.net/2/pkce/) method will be required to maximize security. ### Make authenticated requests Once you have an access token, either from a Personal Access Token or from the OpenID Connect flow, you can make authenticated requests to the API. Here is a simple example with cURL: ```bash Terminal theme={null} curl -X GET https://api.crosspoint.finance/v1/oauth2/userinfo \ -H 'Authorization: Bearer cxp_at_u_XXX' ``` # Introduction Source: https://staging-docs.crosspoint.finance/integrate/oauth2/introduction For partners building services and extensions for Crosspoint customers ### OpenID Connect (OAuth2) Only use our **OpenID Connect** in case you want to act on the behalf of other users via our API, e.g building an app/service for Crosspoint customers. Otherwise, always use an **Organization Access Token (OAT)** to integrate Crosspoint for your own service. Crosspoint implements the [OpenID Connect specification](https://openid.net/developers/how-connect-works/) to enable third-party authentication. It's a layer on top of the OAuth2 framework aiming at making integration more standard and predictable. In particular, it comes with a **discovery endpoint** allowing compatible clients to automatically work with the OpenID Connect server. Here is Crosspoint's one: [OpenID Configuration](https://api.crosspoint.finance/.well-known/openid-configuration) # Create an OAuth 2.0 Client Source: https://staging-docs.crosspoint.finance/integrate/oauth2/setup Before being able to make authentication requests, you'll need an **OAuth2 Client**. It's the entity that'll identify you, as a third-party developer, between Crosspoint and the final user. You can manage them from your [User Settings](https://staging.crosspoint.finance/settings#oauth) Here are the required fields: * *Application Name*: the name of the application that'll be shown to the final users. * *Client Type*: the type of client you are creating. [Read more](#public-clients) * *Redirect URIs*: for security reasons, you need to declare your application URL where the users will be redirected after granting access to their data. > [!NOTE] > > When configuring your OAuth client, you must use an `https://` URL for security reasons. We block `http://` URLs, except when the hostname is `localhost`. This exception allows you to use `http://localhost` for convenient testing in development mode. * *Scopes*: the list of scopes your app will be able to ask for. To improve privacy and security, select only the scopes you really need for your application. * *Homepage URL*: the URL of your application. It'll be shown to the final users on the authorization page. Optionally, you can also add a **terms of service** and **privacy policy** URL. They'll be shown to the final users on the authorization page. Once your client is created, you'll get a **Client ID** and a **Client Secret**. You'll need those values to make authentication requests. Those values are super sensitive and should be kept secret. They allow making authentication requests on Crosspoint! # Handle & monitor webhook deliveries Source: https://staging-docs.crosspoint.finance/integrate/webhooks/delivery How to parse, validate and handle webhooks and monitor their deliveries on Crosspoint ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/integrate/webhooks/delivery.light.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=f8551425cd798691021112636f778e24) Once a webhook endpoint is setup you will have access to the delivery overview page. Here you can: * See historic deliveries * Review payload sent * Trigger redelivery in case of failure Now, let's integrate our endpoint route to validate, parse & handle incoming webhooks. ## Validate & parse webhooks You now need to setup a route handler for the endpoint registered on Crosspoint to receive, validate and parse webhooks before handling them according to your needs. ### Using our SDKs Our TypeScript & Python SDKs come with a built-in helper function to easily validate and parse the webhook event - see full examples below. ```typescript icon="square-js" JS (Express) theme={null} import express, { NextFunction, Request, Response } from 'express' import { webhooks } from '@crossdev/sdk/2026-10' const app = express() app.post( '/webhook', express.raw({ type: 'application/json' }), async (req: Request, res: Response, next: NextFunction) => { try { const event = await webhooks.validateEvent( req.body, { 'webhook-id': req.header('webhook-id') ?? '', 'webhook-timestamp': req.header('webhook-timestamp') ?? '', 'webhook-signature': req.header('webhook-signature') ?? '', }, process.env['CROSSPOINT_WEBHOOK_SECRET'] ?? '', ) // Process the event res.status(202).send('') } catch (error) { if (error instanceof webhooks.WebhookVerificationError) { res.status(403).send('') return } if (error instanceof webhooks.WebhookUnknownTypeError) { // Event type added after this SDK version: acknowledge it so Crosspoint doesn't retry res.status(202).send('') return } next(error) } }, ) ``` ```python Python (Flask) theme={null} import os from flask import Flask, request from crosspoint.v2026_10.webhooks import ( WebhookUnknownTypeError, WebhookVerificationError, validate_event, ) app = Flask(__name__) @app.route('/webhook', methods=['POST']) def webhook(): try: event = validate_event( request.get_data(), dict(request.headers), os.getenv('CROSSPOINT_WEBHOOK_SECRET', ''), ) # Process the event return "", 202 except WebhookVerificationError: return "", 403 except WebhookUnknownTypeError: # Event type added after this SDK version: acknowledge it so Crosspoint doesn't retry return "", 202 ``` Both examples above expect an environment variable named `CROSSPOINT_WEBHOOK_SECRET` to be set to the secret you configured during the endpoint setup. ### Custom validation Use a [Standard Webhooks library](https://github.com/standard-webhooks/standard-webhooks/tree/main/libraries) or follow the [specification](https://github.com/standard-webhooks/standard-webhooks/blob/main/spec/standard-webhooks.md). Secrets generated on or after 8 September 2026, 00:00 UTC are Standard Webhooks. Older secrets are Crosspoint HMAC. > [!NOTE] > > **Signing schemes** > > Crosspoint sends one `webhook-signature`. The HMAC key is which secret you have. > > **Before 8 September 2026, 00:00 UTC:** Crosspoint HMAC. Key is the UTF-8 bytes of > the full `whsec_…` string. Base64-encode that string before you hand it to a > Standard Webhooks library. > > **On or after that instant:** [Standard Webhooks](https://github.com/standard-webhooks/standard-webhooks/blob/main/spec/standard-webhooks.md). > Pass the `whsec_…` secret to the library as-is. > > Crosspoint SDKs 1.0.0-alpha.19 and later try both keys. Pass the `whsec_…` secret > from the dashboard. ## IP Allowlist Crosspoint sends webhooks from fixed IP addresses. If your firewall or reverse proxy only accepts requests from known IPs, email [support@crosspoint.finance](mailto:support@crosspoint.finance) and we'll send you the current list for live and sandbox. ## Failure Handling ### Delivery Retries If we hit an error while trying to reach your endpoint, whether it is a temporary network error or a bug, we'll retry to send the event up to **10 times** with an exponential backoff. ### Delivery Timeouts We currently timeout our requests to your endpoint after **10 seconds**, triggering a retry attempt after a delay as explained above. However, we strongly recommend you optimize your endpoint route to respond within **2 seconds** to ensure reliable delivery. We may lower the timeout threshold in the future, so we advise implementing your webhook handler to queue a background worker task to handle the payload asynchronously. ### Endpoint Disabling Webhook endpoints are automatically disabled after **10 consecutive failed deliveries** (non-2xx responses). When this happens: * The endpoint is marked as disabled and will no longer receive new events. * All organization members will receive an email notification. To re-enable a disabled endpoint, open your organization's [webhook settings](https://staging.crosspoint.finance/to/dashboard/settings/webhooks) and manually enable it. Before re-enabling, ensure your endpoint is properly configured and reachable to avoid repeated disabling. ## Troubleshooting ### Not receiving webhooks Seeing deliveries on Crosspoint, but not receiving them on your end? Below are some common techniques to resolve the issue depending on the reported error status. **General** *Start ngrok or similar* Make sure you have started `ngrok` or whatever tunneling service you're using during local development. *Add excessive logging* E.g `console.log('webhook.handler_called')`, `console.log('webhook.validate_signature')`, `console.log('webhook.signature_validated')` etc. So you can easily confirm if the handler is called and how far it gets before any issues arise. `HTTP 404` * Try `curl -vvv -X POST ` in your terminal to confirm the route exists and see any issues along the way * Try adding trailing `/` to the URL on Crosspoint. Often `/foo` is resolved to `/foo/` by frameworks. `HTTP 3xx` Redirect responses (301, 302, 307, etc.) are treated as failures. Crosspoint does not follow redirects for webhook deliveries. Update your webhook URL to the final destination URL to avoid redirects. A common cause is hosting providers like Vercel that redirect between `www` and non-`www` domains. Make sure your configured URL matches your actual domain. `HTTP 403` * Using middleware for authorization? Make sure to exclude the webhook route from it since it needs to be publicly accessible * Using Cloudflare? * Check the firewall logs to verify if they are blocking our requests and setup a custom WAF rule to accept incoming requests from Crosspoint. * Webhook delivery failures with 403 errors can occur when Cloudflare's Bot Fight Mode is enabled. Bot Fight Mode automatically blocks requests it identifies as bots, including legitimate webhook requests from Crosspoint. Adding Crosspoint's IP addresses to your IP Allow List or creating custom WAF rules will not resolve this issue. To fix webhook delivery problems, disable Bot Fight Mode in your Cloudflare dashboard under Security > Bots. Alternatively, you can check your Cloudflare firewall logs to confirm if requests are being blocked and create appropriate firewall rules if needed. ### Invalid signature exceptions Rolling your own validation? Older secrets are Crosspoint HMAC: base64-encode the entire `whsec_…` string. Secrets generated on or after 8 September 2026, 00:00 UTC are Standard Webhooks: pass the secret as-is. Crosspoint SDKs 1.0.0-alpha.19 and later try both. # Setup Webhooks Source: https://staging-docs.crosspoint.finance/integrate/webhooks/endpoints Get notifications asynchronously when events occur instead of having to poll for updates Secrets generated on or after 8 September 2026, 00:00 UTC follow [Standard Webhooks](https://www.standardwebhooks.com/). Older secrets use Crosspoint HMAC. See [Handle incoming webhooks](/integrate/webhooks/delivery#custom-validation). Our SDKs offer: * Built-in webhook signature validation * Fully typed webhook payloads In addition, our webhooks offer built-in support for **Slack** & **Discord** formatting. Making it a breeze to setup in-chat notifications for your team. ## Get Started > [!NOTE] > > **Use our sandbox environment during development** > > So you can easily test purchases, subscriptions, cancellations and refunds to > automatically trigger webhook events without spending a dime. **1. Add new endpoint** Head over to your organization settings and click on the `Add Endpoint` button to create a new webhook. ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/integrate/webhooks/create.light.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=19dd7dc9aa7bfd6e5176320c7379c8b3) **2. Specify your endpoint URL** Enter the URL to which the webhook events should be sent. ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/integrate/webhooks/url.light.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=2ef4dc48b400a922bb5dc36fc613522a) **3. Choose a delivery format** For standard, custom integrations, leave this parameter on **Raw**. This will send a payload in JSON format. If you wish to send notifications to a Discord or Slack channel, you can select the corresponding format here. Crosspoint will then adapt the payload so properly formatted messages are sent to your channel. ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/integrate/webhooks/format.light.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=f061fc3e8663aee20cb9e2e3ea03bc1b) If you paste a Discord or Slack Webhook URL, the format will be automatically selected. **4. Copy the signing secret** Crosspoint generates a secret for every endpoint and signs each request with it, so you can check with our SDKs that a payload really comes from Crosspoint. After you create the endpoint, open it and click **Copy Secret**. **Reset Secret** gives you a new one. ![](https://mintcdn.com/crosspoint/UQs51vKfpD2N4tlw/assets/integrate/webhooks/secret.light.png?fit=max\&auto=format\&n=UQs51vKfpD2N4tlw\&q=85\&s=be144451f2df3aec2f2332f00790e4da) **5. Subscribe to events** Finally, select all the events you want to be notified about and you're done 🎉 [Now, it's time to integrate our endpoint to receive events →](/integrate/webhooks/delivery) # Webhook Events Source: https://staging-docs.crosspoint.finance/integrate/webhooks/events Our webhook events and in which context they are useful ## Billing Events ### Checkout > **[checkout.created](/api-reference/current/checkout_created)** > **[checkout.updated](/api-reference/current/checkout_updated)** > **[checkout.expired](/api-reference/current/checkout_expired)** > > Fired when a checkout link has expired without being completed. ### Customers > **[customer.created](/api-reference/current/customer_created)** > > Fired when a new customer has been created. > **[customer.updated](/api-reference/current/customer_updated)** > > Fired when a customer has been updated. > **[customer.deleted](/api-reference/current/customer_deleted)** > > Fired when a customer has been deleted. > **[customer.state\_changed](/api-reference/current/customer_state_changed)** > > Fired when a customer's state has changed. Includes active subscriptions and > granted benefits. ### Subscriptions In order to properly implement logic for handling subscriptions, you should look into the following events. > **[subscription.created](/api-reference/current/subscription_created)** > > Fired when a new subscription has been created. > **[subscription.active](/api-reference/current/subscription_active)** > **[subscription.uncanceled](/api-reference/current/subscription_uncanceled)** > **[subscription.cycled](/api-reference/current/subscription_cycled)** > > Fired when a subscription enters a new billing period, before the renewal order exists and whether or not the payment succeeds. > **[subscription.canceled](/api-reference/current/subscription_canceled)** > **[subscription.past\_due](/api-reference/current/subscription_past_due)** > > Fired when a subscription payment has failed. The customer can recover by updating their payment method. > **[subscription.updated](/api-reference/current/subscription_updated)** > > Use this event if you want to handle cancellations, un-cancellations, etc. The > updated event is a catch-all event for `subscription.active`, > `subscription.canceled`, `subscription.uncanceled`, `subscription.cycled`, > `subscription.past_due`, `subscription.revoked`, `subscription.paused`, > `subscription.resumed` and `subscription.migrated`. > **[order.created](/api-reference/current/order_created)** > > Carries a `billing_reason` field, which can be `purchase`, > `subscription_create`, `subscription_cycle` and `subscription_update`. To act > on a renewal, listen to `subscription.cycled` instead: it fires whether or not > the renewal payment succeeds. > **[subscription.revoked](/api-reference/current/subscription_revoked)** > **[subscription.paused](/api-reference/current/subscription_paused)** > > Fired when a scheduled pause takes effect at the end of the period. Billing stops and benefits are revoked until the subscription resumes. > **[subscription.resumed](/api-reference/current/subscription_resumed)** > > Fired when a paused subscription resumes. A new billing period starts and the customer is charged immediately. > **[subscription.migrated](/api-reference/current/subscription_migrated)** > > Fired when Crosspoint takes over billing of a subscription migrated from another provider. Includes `provider` and `provider_subscription_id` so you can correlate the two. Does not send `subscription.created` or `subscription.active`. #### Cancellation Sequences When a subscription is canceled, the events triggered depend on whether the cancellation is immediate or scheduled for the end of the billing period. **End-of-Period Cancellation (default)** When a subscription is **canceled** (by customer action from the portal or by the merchant from the dashboard/API), the following events are sent immediately: 1. `subscription.updated` 2. `subscription.canceled` Both events contain the same subscription data. The subscription will still have `active` status, but the `cancel_at_period_end` flag will be set to `true`. When the end of the current billing period arrives, the subscription is definitively revoked: billing cycles stop and benefits are revoked. The following events are then sent: 3. `subscription.updated` 4. `subscription.revoked` Both events contain the same subscription data. The subscription will have the `canceled` status. **Immediate Revocation** When a merchant cancels a subscription with **immediate revocation**, those events are sent at once: 1. `subscription.updated` 2. `subscription.canceled` 3. `subscription.revoked` All three events contain the same subscription data. The subscription will have the `canceled` status immediately. #### Renewal Sequences When a subscription is renewed for a new cycle, the webhook events are triggered in a specific sequence to help you track the renewal process and handle billing logic appropriately. **Initial Renewal Events** When a subscription reaches its renewal date, the following events are sent immediately (if enabled on the webhook): 1. `subscription.cycled` 2. `subscription.updated` 3. `order.created` `subscription.cycled` fires only on a new billing period, so you can act on a renewal without inspecting `billing_reason` on the order. It also fires when a trial converts to a paid subscription, since that starts a period too: read `status` to tell the two apart. The subscription data will reflect the new billing period through the `current_period_start` and `current_period_end` properties, showing the updated cycle dates. The order data represents the new invoice for the upcoming cycle, with a total representing what the customer will pay for this new period. If usage-based billing is involved, their consumption for the past period will be included in the total. The status of this order is `pending` at this stage. **Payment Processing Events** Shortly after the initial renewal events, the platform will trigger a payment for the new order. Once the payment is successfully processed, the following events are sent: 4. `order.updated` 5. `order.paid` Both events will contain the same order data, with the order status changed to `paid`. #### Pause Sequences Pausing is scheduled for the end of the current billing period, much like an end-of-period cancellation. When a subscription is **paused** (by customer action from the portal or by the merchant from the dashboard/API), only one event is sent immediately, because the subscription stays `active` until the period ends: 1. `subscription.updated` The `pause_at_period_end` flag is set to `true`, and `resumes_at` holds the scheduled resume date if you set one. When the end of the current billing period arrives, the pause takes effect: billing stops and benefits are revoked. The following events are then sent: 2. `subscription.updated` 3. `subscription.paused` Both events contain the same subscription data, now with the `paused` status. **Resume** When a paused subscription resumes, either immediately when you resume it or automatically on its `resumes_at` date, a new billing period starts and the customer is charged right away. The following events are sent: 1. `subscription.updated` 2. `subscription.resumed` 3. `order.created` The subscription data reflects the new period through `current_period_start` and `current_period_end`, and the order represents the immediate charge for the new cycle. #### Migration Sequences When Crosspoint takes over billing of a subscription imported from another provider, these events are sent at cutover: 1. `subscription.updated` 2. `subscription.migrated` `subscription.migrated` carries `provider` and `provider_subscription_id` so you can match the Crosspoint subscription to the source. Crosspoint does not send `subscription.created` or `subscription.active` for this path. ### Orders > **[order.created](/api-reference/current/order_created)** > **[order.paid](/api-reference/current/order_paid)** > **[order.updated](/api-reference/current/order_updated)** > **[order.refunded](/api-reference/current/order_refunded)** ### Refunds > **[refund.created](/api-reference/current/refund_created)** > **[refund.updated](/api-reference/current/refund_updated)** ### Benefit Grants > **[benefit\_grant.created](/api-reference/current/benefit_grant_created)** > **[benefit\_grant.updated](/api-reference/current/benefit_grant_updated)** > **[benefit\_grant.revoked](/api-reference/current/benefit_grant_revoked)** ## Organization Events ### Benefits > **[benefit.created](/api-reference/current/benefit_created)** > **[benefit.updated](/api-reference/current/benefit_updated)** ### Products > **[product.created](/api-reference/current/product_created)** > **[product.updated](/api-reference/current/product_updated)** ### Discounts > **[discount.created](/api-reference/current/discount_created)** > **[discount.updated](/api-reference/current/discount_updated)** > **[discount.deleted](/api-reference/current/discount_deleted)** ### Organization > **[organization.updated](/api-reference/current/organization_updated)** # Acceptable Use Source: https://staging-docs.crosspoint.finance/merchant-of-record/acceptable-use/introduction What you can sell with Crosspoint, and why we have a policy for it ### What is the Acceptable Use Policy? The Acceptable Use Policy (AUP) defines which products and business models can be sold through Crosspoint. Crosspoint is built for software companies, so the policy covers digital goods and services such as: * Software & SaaS * Digital products: templates, eBooks, code, icons, fonts, design assets, and similar * Premium content & access: courses, private GitHub repositories, Discord servers, and subscription content It also lists what isn't supported — for example physical goods, human services, marketplaces, and high-risk or regulated categories — along with businesses that require a closer review. ### Why do we have one? As your [Merchant of Record](/merchant-of-record/introduction), Crosspoint is the legal seller of your products. That means we're accountable to card networks and payment partners for every transaction on the platform. The AUP keeps the platform compliant, protects buyers from fraud and abuse, and keeps chargeback and risk levels low so we can keep offering great rates and fast payouts to everyone. Every organization is reviewed against the AUP before its first payout and monitored continuously afterwards. Learn more in [Account Reviews](/africa/account-reviews). > **[Read the full Acceptable Use Policy](https://staging.crosspoint.finance/legal/acceptable-use-policy)** > > The complete and binding list of acceptable, prohibited, and restricted products. # Create Checkout Link Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/checkout-links/create-checkout-link /openapi/2026-10.openapi.json post /v1/checkout-links/ Create a checkout link. **Scopes**: `checkout_links:write` > [!WARNING] > > Looking to create a single use checkout session? Checkout Links are probably **not** what you're looking for. > > Checkout Links are shareable links that generate checkout sessions when opened. They are very handy to start a purchase from your website or social media. > > However, if you want to start a checkout for one of your user **inside** your product, you should use the [Checkout Sessions API](/api-reference/checkouts/create-checkout-session). # Delete Checkout Link Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/checkout-links/delete-checkout-link /openapi/2026-10.openapi.json delete /v1/checkout-links/{id} Delete a checkout link. **Scopes**: `checkout_links:write` # Get Checkout Link Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/checkout-links/get-checkout-link /openapi/2026-10.openapi.json get /v1/checkout-links/{id} Get a checkout link by ID. **Scopes**: `checkout_links:read` `checkout_links:write` # List Checkout Links Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/checkout-links/list-checkout-links /openapi/2026-10.openapi.json get /v1/checkout-links/ List checkout links. **Scopes**: `checkout_links:read` `checkout_links:write` # Update Checkout Link Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/checkout-links/update-checkout-link /openapi/2026-10.openapi.json patch /v1/checkout-links/{id} Update a checkout link. **Scopes**: `checkout_links:write` # Confirm Checkout Session from Client Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/checkouts/confirm-checkout-session-from-client /openapi/2026-10.openapi.json post /v1/checkouts/client/{client_secret}/confirm Confirm a checkout session by client secret. Orders and subscriptions will be processed. # Create Checkout Session Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/checkouts/create-checkout-session /openapi/2026-10.openapi.json post /v1/checkouts/ Create a checkout session. **Scopes**: `checkouts:write` # Get Checkout Session Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/checkouts/get-checkout-session /openapi/2026-10.openapi.json get /v1/checkouts/{id} Get a checkout session by ID. **Scopes**: `checkouts:read` `checkouts:write` # Get Checkout Session from Client Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/checkouts/get-checkout-session-from-client /openapi/2026-10.openapi.json get /v1/checkouts/client/{client_secret} Get a checkout session by client secret. # List Checkout Sessions Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/checkouts/list-checkout-sessions /openapi/2026-10.openapi.json get /v1/checkouts/ List checkout sessions. **Scopes**: `checkouts:read` `checkouts:write` # Update Checkout Session Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/checkouts/update-checkout-session /openapi/2026-10.openapi.json patch /v1/checkouts/{id} Update a checkout session. **Scopes**: `checkouts:write` # Update Checkout Session from Client Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/checkouts/update-checkout-session-from-client /openapi/2026-10.openapi.json patch /v1/checkouts/client/{client_secret} Update a checkout session by client secret. # Create Custom Field Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/custom-fields/create-custom-field /openapi/2026-10.openapi.json post /v1/custom-fields/ Create a custom field. **Scopes**: `custom_fields:write` # Delete Custom Field Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/custom-fields/delete-custom-field /openapi/2026-10.openapi.json delete /v1/custom-fields/{id} Delete a custom field. **Scopes**: `custom_fields:write` # Get Custom Field Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/custom-fields/get-custom-field /openapi/2026-10.openapi.json get /v1/custom-fields/{id} Get a custom field by ID. **Scopes**: `custom_fields:read` `custom_fields:write` # List Custom Fields Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/custom-fields/list-custom-fields /openapi/2026-10.openapi.json get /v1/custom-fields/ List custom fields. **Scopes**: `custom_fields:read` `custom_fields:write` # Update Custom Field Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/custom-fields/update-custom-field /openapi/2026-10.openapi.json patch /v1/custom-fields/{id} Update a custom field. **Scopes**: `custom_fields:write` # Create Customer Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customers/create-customer /openapi/2026-10.openapi.json post /v1/customers/ Create a customer. Set `external_id` to the customer's ID in your system to look it up by that ID. **Scopes**: `customers:write` # Delete Customer Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customers/delete-customer /openapi/2026-10.openapi.json delete /v1/customers/{id} Delete a customer. This action cannot be undone and will immediately: - Cancel any active subscriptions for the customer - Revoke all their benefits - Clear any `external_id` Use it only in the context of deleting a user within your own service. Otherwise, use more granular API endpoints to cancel a specific subscription or revoke certain benefits. Note: The customers information will nonetheless be retained for historic orders and subscriptions. Set `anonymize=true` to also anonymize PII for GDPR compliance. **Scopes**: `customers:write` # Delete Customer by External ID Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customers/delete-customer-by-external-id /openapi/2026-10.openapi.json delete /v1/customers/external/{external_id} Delete a customer by external ID. Immediately cancels any active subscriptions and revokes any active benefits. Set `anonymize=true` to also anonymize PII for GDPR compliance. **Scopes**: `customers:write` # Get Customer Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customers/get-customer /openapi/2026-10.openapi.json get /v1/customers/{id} Get a customer by ID. **Scopes**: `customers:read` `customers:write` # Get Customer by External ID Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customers/get-customer-by-external-id /openapi/2026-10.openapi.json get /v1/customers/external/{external_id} Get a customer by external ID. **Scopes**: `customers:read` `customers:write` # Get Customer State Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customers/get-customer-state /openapi/2026-10.openapi.json get /v1/customers/{id}/state Get a customer state by ID. The customer state includes information about the customer's active subscriptions and benefits. It's the ideal endpoint to use when you need to get a full overview of a customer's status. **Scopes**: `customers:read` `customers:write` # Get Customer State by External ID Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customers/get-customer-state-by-external-id /openapi/2026-10.openapi.json get /v1/customers/external/{external_id}/state Get a customer state by external ID. The customer state includes information about the customer's active subscriptions and benefits. It's the ideal endpoint to use when you need to get a full overview of a customer's status. **Scopes**: `customers:read` `customers:write` # List Customer Payment Methods Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customers/list-customer-payment-methods /openapi/2026-10.openapi.json get /v1/customers/{id}/payment-methods Get saved payment methods of a customer. **Scopes**: `customers:read` `customers:write` # List Customer Payment Methods by External ID Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customers/list-customer-payment-methods-by-external-id /openapi/2026-10.openapi.json get /v1/customers/external/{external_id}/payment-methods Get saved payment methods of a customer by external ID. **Scopes**: `customers:read` `customers:write` # List Customers Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customers/list-customers /openapi/2026-10.openapi.json get /v1/customers/ List customers. **Scopes**: `customers:read` `customers:write` # Update Customer Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customers/update-customer /openapi/2026-10.openapi.json patch /v1/customers/{id} Update a customer. **Scopes**: `customers:write` # Update Customer by External ID Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customers/update-customer-by-external-id /openapi/2026-10.openapi.json patch /v1/customers/external/{external_id} Update a customer by external ID. **Scopes**: `customers:write` # Create Discount Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/discounts/create-discount /openapi/2026-10.openapi.json post /v1/discounts/ Create a discount. **Scopes**: `discounts:write` # Delete Discount Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/discounts/delete-discount /openapi/2026-10.openapi.json delete /v1/discounts/{id} Delete a discount. **Scopes**: `discounts:write` # Get Discount Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/discounts/get-discount /openapi/2026-10.openapi.json get /v1/discounts/{id} Get a discount by ID. **Scopes**: `discounts:read` `discounts:write` # List Discounts Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/discounts/list-discounts /openapi/2026-10.openapi.json get /v1/discounts/ List discounts. **Scopes**: `discounts:read` `discounts:write` # Update Discount Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/discounts/update-discount /openapi/2026-10.openapi.json patch /v1/discounts/{id} Update a discount. **Scopes**: `discounts:write` # Get Event Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/events/get-event /openapi/2026-10.openapi.json get /v1/events/{id} Get an event by ID. **Scopes**: `events:read` `events:write` # Ingest Events Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/events/ingest-events /openapi/2026-10.openapi.json post /v1/events/ingest Ingest batch of events. **Scopes**: `events:write` # List Events Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/events/list-events /openapi/2026-10.openapi.json get /v1/events/ List events. **Scopes**: `events:read` `events:write` # API Overview Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/introduction Official SDK quickstarts, base URLs, authentication, pagination, rate limits, and API concepts > **Production Base URL** > > `https://api.crosspoint.finance/v1` > **Sandbox Base URL** > > `https://sandbox-api.crosspoint.finance/v1` > **[Auth (Organization)](/integrate/oat)** > > Use an **Organization Access Token (OAT)** in the `Authorization: Bearer` > header > **[Auth (Customer Portal)](/api-reference/2026-10/customer-sessions/create-customer-session)** > > Use a **Customer Access Token** created via `/v1/customer-sessions/` ## Official SDKs Use our new, fully typed SDKs to integrate with the Crosspoint API from TypeScript or Python. Create an [organization access token](/integrate/oat), then install the SDK and make your first request: **TypeScript** ```bash npm theme={null} npm install @crossdev/sdk ``` ```typescript app.ts theme={null} import { createCrosspoint } from "@crossdev/sdk/2026-10"; const crosspoint = createCrosspoint({ accessToken: process.env.CROSSPOINT_ACCESS_TOKEN!, }); const customerState = await crosspoint.customers.getStateExternal("customer_external_id"); console.log(customerState); ``` **Python** ```bash uv theme={null} uv add crosspoint ``` ```bash pip theme={null} pip install crosspoint ``` ```python main.py theme={null} import os from crosspoint.v2026_10 import Crosspoint crosspoint = Crosspoint(os.environ["CROSSPOINT_ACCESS_TOKEN"]) customer_state = crosspoint.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](/integrate/sandbox). ## Base URLs | Environment | Base URL | Purpose | | - | - | - | | Production | `https://api.crosspoint.finance/v1` | Real customers & live payments | | Sandbox | `https://sandbox-api.crosspoint.finance/v1` | Safe testing & integration work | > [!NOTE] > > 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](/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 cxp_oat_xxxxxxxxxxxxxxxxx ``` > [!TIP] > > Create OATs in your organization settings. See: [Organization Access > Tokens](/integrate/oat) > [!WARNING] > > 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**](/api-reference/2026-10/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) | > [!NOTE] > > 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.crosspoint.finance/v1/products/ \ -H "Authorization: Bearer $CROSSPOINT_OAT" \ -H "Accept: application/json" ``` ```bash curl (Sandbox - Core API) theme={null} curl https://sandbox-api.crosspoint.finance/v1/products/ \ -H "Authorization: Bearer $CROSSPOINT_OAT_SANDBOX" \ -H "Accept: application/json" ``` ```bash curl (Customer Portal API) theme={null} curl https://api.crosspoint.finance/v1/customer-portal/orders/ \ -H "Authorization: Bearer $CROSSPOINT_CUSTOMER_TOKEN" \ -H "Accept: application/json" ``` ## Pagination List endpoints in the Crosspoint 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) | > [!NOTE] > > 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.crosspoint.finance/v1/products/?page=1&limit=100 \ -H "Authorization: Bearer $CROSSPOINT_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) > [!TIP] > > 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 Crosspoint 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](/api-reference/2026-10/customer_portal/validate-license-key), [activation](/api-reference/2026-10/customer_portal/activate-license-key), and [deactivation](/api-reference/2026-10/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. > [!NOTE] > > Organizations requiring higher rate limits for production workloads may > contact our support team to discuss elevated limits. # Create Meter Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/meters/create-meter /openapi/2026-10.openapi.json post /v1/meters/ Create a meter. **Scopes**: `meters:write` # Generate Order Invoice Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/orders/generate-order-invoice /openapi/2026-10.openapi.json post /v1/orders/{id}/invoice Trigger generation of an order's invoice. **Scopes**: `orders:read` > [!WARNING] > > Once the invoice is generated, it's permanent and cannot be modified. > > Make sure the billing details (name and address) are correct before generating the invoice. You can update them before generating the invoice by calling the [`PATCH /v1/orders/{id}`](/api-reference/orders/update-order) endpoint. > [!NOTE] > > After successfully calling this endpoint, you get a `202` response, meaning the generation of the invoice has been scheduled. It usually only takes a few seconds before you can retrieve the invoice using the [`GET /v1/orders/{id}/invoice`](/api-reference/orders/get-order-invoice) endpoint. > > If you want a reliable notification when the invoice is ready, you can listen to the [`order.updated`](/api-reference/orderupdated) webhook and check the [`is_invoice_generated` field](/api-reference/orderupdated#schema-data-is-invoice-generated). # Get Order Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/orders/get-order /openapi/2026-10.openapi.json get /v1/orders/{id} Get an order by ID. **Scopes**: `orders:read` # Get Order Invoice Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/orders/get-order-invoice /openapi/2026-10.openapi.json get /v1/orders/{id}/invoice Get an order's invoice data. **Scopes**: `orders:read` > [!NOTE] > > The invoice must be generated first before it can be retrieved. You should call the [`POST /v1/orders/{id}/invoice`](/api-reference/orders/generate-order-invoice) endpoint to generate the invoice. > > If the invoice is not generated, you will receive a `404` error. # Get Order Receipt Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/orders/get-order-receipt /openapi/2026-10.openapi.json get /v1/orders/{id}/receipt Get a presigned URL to download an order's receipt PDF. **Scopes**: `orders:read` # List Orders Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/orders/list-orders /openapi/2026-10.openapi.json get /v1/orders/ List orders. **Scopes**: `orders:read` # Update Order Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/orders/update-order /openapi/2026-10.openapi.json patch /v1/orders/{id} Update an order. **Scopes**: `orders:write` # Create Product Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/products/create-product /openapi/2026-10.openapi.json post /v1/products/ Create a product. **Scopes**: `products:write` # Delete Product Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/products/delete-product /openapi/2026-10.openapi.json delete /v1/products/{id} Delete a product. Only products without orders, subscriptions, trials or discounts can be deleted. Products that are in use can only be archived. **Scopes**: `products:write` # Get Product Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/products/get-product /openapi/2026-10.openapi.json get /v1/products/{id} Get a product by ID. **Scopes**: `products:read` `products:write` # List Products Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/products/list-products /openapi/2026-10.openapi.json get /v1/products/ List products. **Scopes**: `products:read` `products:write` # Update Product Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/products/update-product /openapi/2026-10.openapi.json patch /v1/products/{id} Update a product. **Scopes**: `products:write` # Update Product Benefits Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/products/update-product-benefits /openapi/2026-10.openapi.json post /v1/products/{id}/benefits Update benefits granted by a product. **Scopes**: `products:write` # Create Refund Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/refunds/create-refund /openapi/2026-10.openapi.json post /v1/refunds/ Create a refund. **Scopes**: `refunds:write` # List Refunds Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/refunds/list-refunds /openapi/2026-10.openapi.json get /v1/refunds/ List refunds. **Scopes**: `refunds:read` `refunds:write` # Create Subscription Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/subscriptions/create-subscription /openapi/2026-10.openapi.json post /v1/subscriptions/ Create a subscription programmatically. This endpoint only allows to create subscription on free products. For paid products, use the checkout flow. No initial order will be created and no confirmation email will be sent. **Scopes**: `subscriptions:write` # Get Subscription Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/subscriptions/get-subscription /openapi/2026-10.openapi.json get /v1/subscriptions/{id} Get a subscription by ID. **Scopes**: `subscriptions:read` `subscriptions:write` # List Subscriptions Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/subscriptions/list-subscriptions /openapi/2026-10.openapi.json get /v1/subscriptions/ List subscriptions. **Scopes**: `subscriptions:read` `subscriptions:write` # Revoke Subscription Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/subscriptions/revoke-subscription /openapi/2026-10.openapi.json delete /v1/subscriptions/{id} Revoke a subscription, i.e cancel immediately. **Scopes**: `subscriptions:write` # Update Subscription Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/subscriptions/update-subscription /openapi/2026-10.openapi.json patch /v1/subscriptions/{id} Update a subscription. **Scopes**: `subscriptions:write` # API Versioning Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/versioning Learn how Crosspoint API versions are selected, released, deprecated, and upgraded. Crosspoint uses date-based API versions to evolve the API without unexpectedly changing the contract your integration depends on. Versions use the `YYYY-MM` format, such as 2026-10, and apply to API requests, responses, webhook payloads, and SDK types. > [!WARNING] > > Pin a version in every production integration. If you don't, your requests use > the Current version, which changes at each quarterly release. ## Choosing a version Crosspoint maintains three API versions at a time: | Version | Stability | When to use it | | - | - | - | | **Current** | Stable and the default | New production integrations | | **Deprecated** | Stable until the next quarterly release, when it is removed | Existing integrations while you migrate to Current | | **Next** | In development and may introduce breaking changes at any time | Testing upcoming features before they become Current | Don't start a new integration on Deprecated. Use Next only if you need an upcoming feature and can adapt to changes during the development cycle. ## Version lifecycle Crosspoint releases a new API version during the first week of January, April, July, and October. At each release: 1. Deprecated is removed and no longer accepts requests. 2. Current becomes Deprecated. 3. Next becomes Current and its API contract is frozen. 4. A new Next version is created for ongoing development. Each version is supported for approximately nine months: three months as Next, three months as Current, and three months as Deprecated. ```mermaid theme={null} flowchart LR next["Next
Development"] --> current["Current
Stable and default"] current --> deprecated["Deprecated
Stable until next release"] deprecated -.-> removed(["Removed"]) classDef void fill:transparent,stroke:#9ca3af,stroke-dasharray:5 5,color:#6b7280 class removed void ``` Current and Deprecated are frozen. New endpoints and any changes to request or response schemas (including additive changes such as a new optional field) are introduced in Next. Bug fixes, performance improvements, and internal changes can be released to stable versions when they don't change the public API contract. The versioned contract includes endpoints, fields and types, validation rules, status and error responses, and authentication requirements. ## Pinning API requests Set the version with the `Polar-Version` request header. The base URL and endpoint paths remain the same: ```bash theme={null} curl -i https://api.crosspoint.finance/v1/products/ \ -H "Authorization: Bearer $CROSSPOINT_ACCESS_TOKEN" \ -H "Polar-Version: 2026-10" \ -H "Accept: application/json" ``` Responses include the version used to process the request: ```http theme={null} Polar-Version: 2026-10 ``` If the header is omitted, Crosspoint uses Current. A malformed, unknown, or removed version returns `404 Not Found`. ## Pinning webhooks Webhook endpoints are versioned independently from API requests so their payloads remain stable across release cycles. Set `api_version` when you create an endpoint: ```json theme={null} { "url": "https://example.com/webhooks", "format": "raw", "events": ["order.created"], "api_version": "2026-10" } ``` If you omit `api_version`, the endpoint uses Current. Only supported API versions are accepted. The selected version determines which fields are included in future raw event payloads. You can update an endpoint's `api_version`, but the change applies only to events created afterward. Existing events retain their original version, so a redelivery has the same payload contract as the first delivery. Every delivery includes the selected version in the `webhook-api-version` header. Raw JSON payloads also include it in the `api_version` field. Use the same version for API requests and webhooks unless you intentionally support different contracts. Updating the version used by your API client does not migrate existing webhook endpoints; upgrade them separately. ## Pinning SDKs The [Python SDK](/integrate/sdk/python) and [TypeScript SDK](/integrate/sdk/typescript) include typed clients and models for each supported API version. Importing a versioned client automatically sets the matching `Polar-Version` header: **Python** ```py theme={null} from crosspoint.v2026_10 import Crosspoint ``` **JavaScript** ```ts theme={null} import { createCrosspoint } from "@crossdev/sdk/2026-10"; ``` The SDK package version and API version are separate. Updating the package can deliver fixes without changing your API contract; changing the version in the import path upgrades the API contract. ## Upgrading versions Upgrade before your pinned version is removed: 1. Review the new version's API reference and release notes. 2. Change the `Polar-Version` header or versioned SDK import in your sandbox environment. 3. Run your integration tests and update code for changed endpoints, fields, or webhook payloads. 4. Upgrade webhook endpoints that should use the new contract. 5. Deploy the API client change and confirm the `Polar-Version` response header. # benefit.created Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/benefit_created /openapi/2026-10.openapi.json webhook benefit.created Sent when a new benefit is created. **Discord & Slack support:** Basic # benefit_grant.created Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/benefit_grant_created /openapi/2026-10.openapi.json webhook benefit_grant.created Sent when a new benefit grant is created. **Discord & Slack support:** Basic # benefit_grant.cycled Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/benefit_grant_cycled /openapi/2026-10.openapi.json webhook benefit_grant.cycled Sent when a benefit grant is cycled, meaning the related subscription has been renewed for another period. **Discord & Slack support:** Basic # benefit_grant.revoked Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/benefit_grant_revoked /openapi/2026-10.openapi.json webhook benefit_grant.revoked Sent when a benefit grant is revoked. **Discord & Slack support:** Basic # benefit_grant.updated Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/benefit_grant_updated /openapi/2026-10.openapi.json webhook benefit_grant.updated Sent when a benefit grant is updated. **Discord & Slack support:** Basic # benefit.updated Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/benefit_updated /openapi/2026-10.openapi.json webhook benefit.updated Sent when a benefit is updated. **Discord & Slack support:** Basic # Create Benefit Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/benefits/create-benefit /openapi/2026-10.openapi.json post /v1/benefits/ Create a benefit, for example license keys, file downloads or Discord access. **Scopes**: `benefits:write` # Delete Benefit Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/benefits/delete-benefit /openapi/2026-10.openapi.json delete /v1/benefits/{id} Delete a benefit. > [!WARNING] > Every grants associated with the benefit will be revoked. > Users will lose access to the benefit. **Scopes**: `benefits:write` # Get Benefit Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/benefits/get-benefit /openapi/2026-10.openapi.json get /v1/benefits/{id} Get a benefit by ID. **Scopes**: `benefits:read` `benefits:write` # List Benefit Grants Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/benefits/list-benefit-grants /openapi/2026-10.openapi.json get /v1/benefits/{id}/grants List the individual grants for a benefit. It's especially useful to check if a user has been granted a benefit. **Scopes**: `benefits:read` `benefits:write` # List Benefits Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/benefits/list-benefits /openapi/2026-10.openapi.json get /v1/benefits/ List benefits. **Scopes**: `benefits:read` `benefits:write` # Update Benefit Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/benefits/update-benefit /openapi/2026-10.openapi.json patch /v1/benefits/{id} Update a benefit. **Scopes**: `benefits:write` # checkout.created Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/checkout_created /openapi/2026-10.openapi.json webhook checkout.created Sent when a new checkout is created. **Discord & Slack support:** Basic # checkout.expired Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/checkout_expired /openapi/2026-10.openapi.json webhook checkout.expired Sent when a checkout expires. This event fires when a checkout reaches its expiration time without being completed. Developers can use this to send reminder emails or track checkout abandonment. **Discord & Slack support:** Basic # checkout.updated Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/checkout_updated /openapi/2026-10.openapi.json webhook checkout.updated Sent when a checkout is updated. **Discord & Slack support:** Basic # Assign Seat Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer-seats/assign-seat /openapi/2026-10.openapi.json post /v1/customer-seats Assign a seat for a subscription or order. **Scopes**: `customer_seats:write` # Claim Seat Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer-seats/claim-seat /openapi/2026-10.openapi.json post /v1/customer-seats/claim Claim a customer seat with an invitation token. # Get Claim Info Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer-seats/get-claim-info /openapi/2026-10.openapi.json get /v1/customer-seats/claim/{invitation_token} Get claim information for a customer seat invitation. # List Seats Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer-seats/list-seats /openapi/2026-10.openapi.json get /v1/customer-seats List seats for a subscription or order. **Scopes**: `customer_seats:read` # Resend Invitation Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer-seats/resend-invitation /openapi/2026-10.openapi.json post /v1/customer-seats/{seat_id}/resend Resend an invitation for a pending customer seat. **Scopes**: `customer_seats:write` # Revoke Seat Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer-seats/revoke-seat /openapi/2026-10.openapi.json delete /v1/customer-seats/{seat_id} Revoke a customer seat. **Scopes**: `customer_seats:write` # Create Customer Session Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer-sessions/create-customer-session /openapi/2026-10.openapi.json post /v1/customer-sessions/ Create a customer session. For organizations with `member_model_enabled`, this will automatically create a member session for the owner member of the customer. **Scopes**: `customer_sessions:write` # customer.created Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_created /openapi/2026-10.openapi.json webhook customer.created Sent when a new customer is created. A customer can be created: * After a successful checkout. * Programmatically via the API. **Discord & Slack support:** Basic # customer.deleted Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_deleted /openapi/2026-10.openapi.json webhook customer.deleted Sent when a customer is deleted. **Discord & Slack support:** Basic # Get Customer Meter Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_meters/get-customer-meter /openapi/2026-10.openapi.json get /v1/customer-meters/{id} Get a customer meter by ID. **Scopes**: `customer_meters:read` # List Customer Meters Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_meters/list-customer-meters /openapi/2026-10.openapi.json get /v1/customer-meters/ List customer meters. **Scopes**: `customer_meters:read` # Activate License Key Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_portal/activate-license-key /openapi/2026-10.openapi.json post /v1/customer-portal/license-keys/activate Activate a license key instance. > This endpoint doesn't require authentication and can be safely used on a public > client, like a desktop application or a mobile app. > If you plan to validate a license key on a server, use the `/v1/license-keys/activate` > endpoint instead. > [!TIP] > > You only need to use this endpoint if you have device **activations** enabled on the license key benefit. You then use this endpoint to reserve an allocation for a specific device. Store the unique activation ID from the response on the device and use it as extra validation in the [/validate](/api-reference/customer_portal/validate-license-key) endpoint. > > Not using **activations**? Just use the [/validate](/api-reference/customer_portal/validate-license-key) endpoint directly instead. # Assign Seat Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_portal/assign-seat /openapi/2026-10.openapi.json post /v1/customer-portal/seats # Cancel Subscription Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_portal/cancel-subscription /openapi/2026-10.openapi.json delete /v1/customer-portal/subscriptions/{id} Cancel a subscription of the authenticated customer. # Deactivate License Key Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_portal/deactivate-license-key /openapi/2026-10.openapi.json post /v1/customer-portal/license-keys/deactivate Deactivate a license key instance. > This endpoint doesn't require authentication and can be safely used on a public > client, like a desktop application or a mobile app. > If you plan to validate a license key on a server, use the `/v1/license-keys/deactivate` > endpoint instead. # Delete Customer Payment Method Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_portal/delete-customer-payment-method /openapi/2026-10.openapi.json delete /v1/customer-portal/customers/me/payment-methods/{id} Delete a payment method from the authenticated customer. # Generate Order Invoice Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_portal/generate-order-invoice /openapi/2026-10.openapi.json post /v1/customer-portal/orders/{id}/invoice Trigger generation of an order's invoice. > [!WARNING] > > Once the invoice is generated, it's permanent and cannot be modified. > > Make sure the billing details (name and address) are correct before generating the invoice. You can update them before generating the invoice by calling the [`PATCH /v1/customer-portal/orders/{id}`](/api-reference/customer_portal/update-order) endpoint. > [!NOTE] > > After successfully calling this endpoint, you get a `202` response, meaning the generation of the invoice has been scheduled. It usually only takes a few seconds before you can retrieve the invoice using the [`GET /v1/customer-portal/orders/{id}/invoice`](/api-reference/customer_portal/get-order-invoice) endpoint. > > If you want a reliable notification when the invoice is ready, you can listen to the [`order.updated`](/api-reference/orderupdated) webhook and check the [`is_invoice_generated` field](/api-reference/orderupdated#schema-data-is-invoice-generated). # Get Customer Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_portal/get-customer /openapi/2026-10.openapi.json get /v1/customer-portal/customers/me Get authenticated customer. **Scopes**: `customer_portal:read` `customer_portal:write` # Get License Key Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_portal/get-license-key /openapi/2026-10.openapi.json get /v1/customer-portal/license-keys/{id} Get a license key. **Scopes**: `customer_portal:read` `customer_portal:write` # Get Order Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_portal/get-order /openapi/2026-10.openapi.json get /v1/customer-portal/orders/{id} Get an order by ID for the authenticated customer. # Get Order Invoice Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_portal/get-order-invoice /openapi/2026-10.openapi.json get /v1/customer-portal/orders/{id}/invoice Get an order's invoice data. > [!NOTE] > > The invoice must be generated first before it can be retrieved. You should call the [`POST /v1/customer-portal/orders/{id}/invoice`](/api-reference/customer_portal/generate-order-invoice) endpoint to generate the invoice. > > If the invoice is not generated, you will receive a `404` error. # Get Order Receipt Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_portal/get-order-receipt /openapi/2026-10.openapi.json get /v1/customer-portal/orders/{id}/receipt Get a presigned URL to download an order's receipt PDF. # Get Organization Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_portal/get-organization /openapi/2026-10.openapi.json get /v1/customer-portal/organizations/{slug} Get a customer portal's organization by slug. # Get Subscription Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_portal/get-subscription /openapi/2026-10.openapi.json get /v1/customer-portal/subscriptions/{id} Get a subscription for the authenticated customer. **Scopes**: `customer_portal:read` `customer_portal:write` # List Claimed Subscriptions Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_portal/list-claimed-subscriptions /openapi/2026-10.openapi.json get /v1/customer-portal/seats/subscriptions List all subscriptions where the authenticated customer has claimed a seat. **Scopes**: `customer_portal:read` `customer_portal:write` # List Customer Payment Methods Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_portal/list-customer-payment-methods /openapi/2026-10.openapi.json get /v1/customer-portal/customers/me/payment-methods Get saved payment methods of the authenticated customer. > [!NOTE] > > To change the default payment method, call the [`PATCH /v1/customer-portal/customers/me`](/api-reference/customer_portal/update-customer) endpoint with the desired `default_payment_method_id`. # List Downloadables Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_portal/list-downloadables /openapi/2026-10.openapi.json get /v1/customer-portal/downloadables/ **Scopes**: `customer_portal:read` `customer_portal:write` # List License Keys Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_portal/list-license-keys /openapi/2026-10.openapi.json get /v1/customer-portal/license-keys/ **Scopes**: `customer_portal:read` `customer_portal:write` # List Orders Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_portal/list-orders /openapi/2026-10.openapi.json get /v1/customer-portal/orders/ List orders of the authenticated customer. # List Seats Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_portal/list-seats /openapi/2026-10.openapi.json get /v1/customer-portal/seats **Scopes**: `customer_portal:read` `customer_portal:write` # List Subscriptions Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_portal/list-subscriptions /openapi/2026-10.openapi.json get /v1/customer-portal/subscriptions/ List subscriptions of the authenticated customer. **Scopes**: `customer_portal:read` `customer_portal:write` # Resend Invitation Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_portal/resend-invitation /openapi/2026-10.openapi.json post /v1/customer-portal/seats/{seat_id}/resend # Revoke Seat Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_portal/revoke-seat /openapi/2026-10.openapi.json delete /v1/customer-portal/seats/{seat_id} # Update Customer Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_portal/update-customer /openapi/2026-10.openapi.json patch /v1/customer-portal/customers/me Update authenticated customer. # Update Order Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_portal/update-order /openapi/2026-10.openapi.json patch /v1/customer-portal/orders/{id} Update an order for the authenticated customer. # Update Subscription Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_portal/update-subscription /openapi/2026-10.openapi.json patch /v1/customer-portal/subscriptions/{id} Update a subscription of the authenticated customer. # Validate License Key Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_portal/validate-license-key /openapi/2026-10.openapi.json post /v1/customer-portal/license-keys/validate Validate a license key. > This endpoint doesn't require authentication and can be safely used on a public > client, like a desktop application or a mobile app. > If you plan to validate a license key on a server, use the `/v1/license-keys/validate` > endpoint instead. # customer_seat.assigned Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_seat_assigned /openapi/2026-10.openapi.json webhook customer_seat.assigned Sent when a new customer seat is assigned. This event is triggered when a seat is assigned to a customer by the organization. The customer will receive an invitation email to claim the seat. # customer_seat.claimed Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_seat_claimed /openapi/2026-10.openapi.json webhook customer_seat.claimed Sent when a customer seat is claimed. This event is triggered when a customer accepts the seat invitation and claims their access. # customer_seat.revoked Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_seat_revoked /openapi/2026-10.openapi.json webhook customer_seat.revoked Sent when a customer seat is revoked. This event is triggered when access to a seat is revoked, either manually by the organization or automatically when a subscription is canceled. # customer.state_changed Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_state_changed /openapi/2026-10.openapi.json webhook customer.state_changed Sent when a customer state has changed. It's triggered when: * Customer is created, updated or deleted. * A subscription is created or updated. * A benefit is granted or revoked. **Discord & Slack support:** Basic # customer.updated Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customer_updated /openapi/2026-10.openapi.json webhook customer.updated Sent when a customer is updated. This event is fired when the customer details are updated. If you want to be notified when a customer subscription or benefit state changes, you should listen to the `customer_state_changed` event. **Discord & Slack support:** Basic # List Members Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customers/list-members /openapi/2026-10.openapi.json get /v1/customers/{id}/members List the members of a customer. **Scopes**: `members:read` `members:write` # List Members by Customer External ID Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/customers/list-members-by-customer-external-id /openapi/2026-10.openapi.json get /v1/customers/external/{external_id}/members List the members of a customer identified by its external ID. **Scopes**: `members:read` `members:write` # Complete File Upload Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/files/complete-file-upload /openapi/2026-10.openapi.json post /v1/files/{id}/uploaded Complete a file upload. **Scopes**: `files:write` # Create File Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/files/create-file /openapi/2026-10.openapi.json post /v1/files/ Create a file. **Scopes**: `files:write` # Delete File Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/files/delete-file /openapi/2026-10.openapi.json delete /v1/files/{id} Delete a file. **Scopes**: `files:write` # List Files Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/files/list-files /openapi/2026-10.openapi.json get /v1/files/ List files. **Scopes**: `files:read` `files:write` # Update File Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/files/update-file /openapi/2026-10.openapi.json patch /v1/files/{id} Update a file. **Scopes**: `files:write` # Activate License Key Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/license_keys/activate-license-key /openapi/2026-10.openapi.json post /v1/license-keys/activate Activate a license key instance. **Scopes**: `license_keys:write` # Deactivate License Key Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/license_keys/deactivate-license-key /openapi/2026-10.openapi.json post /v1/license-keys/deactivate Deactivate a license key instance. **Scopes**: `license_keys:write` # Get Activation Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/license_keys/get-activation /openapi/2026-10.openapi.json get /v1/license-keys/{id}/activations/{activation_id} Get a license key activation. **Scopes**: `license_keys:read` `license_keys:write` # Get License Key Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/license_keys/get-license-key /openapi/2026-10.openapi.json get /v1/license-keys/{id} Get a license key. **Scopes**: `license_keys:read` `license_keys:write` # List License Keys Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/license_keys/list-license-keys /openapi/2026-10.openapi.json get /v1/license-keys/ Get license keys connected to the given organization & filters. **Scopes**: `license_keys:read` `license_keys:write` # Update License Key Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/license_keys/update-license-key /openapi/2026-10.openapi.json patch /v1/license-keys/{id} Update a license key. **Scopes**: `license_keys:write` # Validate License Key Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/license_keys/validate-license-key /openapi/2026-10.openapi.json post /v1/license-keys/validate Validate a license key. **Scopes**: `license_keys:write` # Get Meter Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/meters/get-meter /openapi/2026-10.openapi.json get /v1/meters/{id} Get a meter by ID. **Scopes**: `meters:read` `meters:write` # Get Meter Quantities Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/meters/get-meter-quantities /openapi/2026-10.openapi.json get /v1/meters/{id}/quantities Get quantities of a meter over a time period. **Scopes**: `meters:read` `meters:write` # List Meters Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/meters/list-meters /openapi/2026-10.openapi.json get /v1/meters/ List meters. **Scopes**: `meters:read` `meters:write` # Update Meter Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/meters/update-meter /openapi/2026-10.openapi.json patch /v1/meters/{id} Update a meter. **Scopes**: `meters:write` # Get Metrics Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/metrics/get-metrics /openapi/2026-10.openapi.json get /v1/metrics/ Get metrics about your orders and subscriptions. Currency values are output in cents. **Scopes**: `metrics:read` # Get Metrics Limits Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/metrics/get-metrics-limits /openapi/2026-10.openapi.json get /v1/metrics/limits Get the interval limits for the metrics endpoint. **Scopes**: `metrics:read` # order.created Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/order_created /openapi/2026-10.openapi.json webhook order.created Sent when a new order is created. A new order is created when: * A customer purchases a one-time product. In this case, `billing_reason` is set to `purchase`. * A customer starts a subscription. In this case, `billing_reason` is set to `subscription_create`. * A subscription is renewed. In this case, `billing_reason` is set to `subscription_cycle`. * A subscription is upgraded or downgraded with an immediate proration invoice. In this case, `billing_reason` is set to `subscription_update`. > [!WARNING] > The order might not be paid yet, so the `status` field might be `pending`. **Discord & Slack support:** Full # order.paid Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/order_paid /openapi/2026-10.openapi.json webhook order.paid Sent when an order is paid. When you receive this event, the order is fully processed and payment has been received. **Discord & Slack support:** Full # order.refunded Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/order_refunded /openapi/2026-10.openapi.json webhook order.refunded Sent when an order is fully or partially refunded. **Discord & Slack support:** Full # order.updated Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/order_updated /openapi/2026-10.openapi.json webhook order.updated Sent when an order is updated. An order is updated when: * Its status changes, e.g. from `pending` to `paid`. * It's refunded, partially or fully. **Discord & Slack support:** Full # Create Organization Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/organizations/create-organization /openapi/2026-10.openapi.json post /v1/organizations/ Create an organization. **Scopes**: `organizations:write` # Get Organization Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/organizations/get-organization /openapi/2026-10.openapi.json get /v1/organizations/{id} Get an organization by ID. **Scopes**: `organizations:read` `organizations:write` # List Organizations Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/organizations/list-organizations /openapi/2026-10.openapi.json get /v1/organizations/ List organizations. **Scopes**: `organizations:read` `organizations:write` # Update Organization Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/organizations/update-organization /openapi/2026-10.openapi.json patch /v1/organizations/{id} Update an organization. **Scopes**: `organizations:write` # product.created Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/product_created /openapi/2026-10.openapi.json webhook product.created Sent when a new product is created. **Discord & Slack support:** Basic # product.updated Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/product_updated /openapi/2026-10.openapi.json webhook product.updated Sent when a product is updated. **Discord & Slack support:** Basic # refund.created Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/refund_created /openapi/2026-10.openapi.json webhook refund.created Sent when a refund is created regardless of status. **Discord & Slack support:** Full # refund.updated Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/refund_updated /openapi/2026-10.openapi.json webhook refund.updated Sent when a refund is updated. **Discord & Slack support:** Full # subscription.active Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/subscription_active /openapi/2026-10.openapi.json webhook subscription.active Sent when a subscription becomes active, whether because it's a new paid subscription or because payment was recovered. **Discord & Slack support:** Full # subscription.canceled Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/subscription_canceled /openapi/2026-10.openapi.json webhook subscription.canceled Sent when a subscription is canceled. Customers might still have access until the end of the current period. **Discord & Slack support:** Full # subscription.created Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/subscription_created /openapi/2026-10.openapi.json webhook subscription.created Sent when a new subscription is created. When this event occurs, the subscription `status` might not be `active` yet, as we can still have to wait for the first payment to be processed. **Discord & Slack support:** Full # subscription.cycled Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/subscription_cycled /openapi/2026-10.openapi.json webhook subscription.cycled Sent when a subscription enters a new billing period. The payload carries the new `current_period_start` and `current_period_end`. It fires when the period rolls over, before the renewal order exists and regardless of whether the renewal payment succeeds — listen to `order.paid` if you need the payment. A trial converting to a paid subscription starts a new period, so it fires there too. Read `status` to tell the two apart. **Discord & Slack support:** Basic # subscription.migrated Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/subscription_migrated /openapi/2026-10.openapi.json webhook subscription.migrated Sent when Crosspoint takes over billing of a subscription migrated from another provider. This fires at cutover, once the subscription is live on Crosspoint. `provider` and `provider_subscription_id` identify the subscription on the billing provider so you can correlate the two. **Discord & Slack support:** Basic # subscription.past_due Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/subscription_past_due /openapi/2026-10.openapi.json webhook subscription.past_due Sent when a subscription payment fails and the subscription enters `past_due` status. This is a recoverable state - the customer can update their payment method to restore the subscription. Benefits may be revoked depending on the organization's grace period settings. If payment retries are exhausted, a `subscription.revoked` event will be sent. **Discord & Slack support:** Full # subscription.paused Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/subscription_paused /openapi/2026-10.openapi.json webhook subscription.paused Sent when a subscription is paused and the customer temporarily loses access. No order is created while paused. The subscription resumes either on its scheduled resume date or when resumed manually, starting a new billing period. **Discord & Slack support:** Full # subscription.resumed Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/subscription_resumed /openapi/2026-10.openapi.json webhook subscription.resumed Sent when a paused subscription resumes, restoring the customer's access. Resuming starts a new billing period and charges the customer immediately. **Discord & Slack support:** Full # subscription.revoked Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/subscription_revoked /openapi/2026-10.openapi.json webhook subscription.revoked Sent when a subscription is revoked and the user loses access immediately. Happens when the subscription is canceled or payment retries are exhausted (status becomes `unpaid`). For payment failures that can still be recovered, see `subscription.past_due`. **Discord & Slack support:** Full # subscription.uncanceled Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/subscription_uncanceled /openapi/2026-10.openapi.json webhook subscription.uncanceled Sent when a customer revokes a pending cancellation. When a customer cancels with "at period end", they retain access until the subscription would renew. During this time, they can change their mind and undo the cancellation. This event is triggered when they do so. **Discord & Slack support:** Full # subscription.updated Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/subscription_updated /openapi/2026-10.openapi.json webhook subscription.updated Sent when a subscription is updated. This event fires for all changes to the subscription, including renewals. If you want more specific events, you can listen to `subscription.active`, `subscription.canceled`, `subscription.past_due`, and `subscription.revoked`. To listen specifically for renewals, listen to `subscription.cycled`. **Discord & Slack support:** On cancellation, past due, and revocation. Renewals are skipped. # discount.created Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/discount_created /openapi/2026-10.openapi.json webhook discount.created Sent when a new discount is created. **Discord & Slack support:** Basic # discount.deleted Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/discount_deleted /openapi/2026-10.openapi.json webhook discount.deleted Sent when a discount is deleted. **Discord & Slack support:** Basic # discount.updated Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/discount_updated /openapi/2026-10.openapi.json webhook discount.updated Sent when a discount is updated. **Discord & Slack support:** Basic # Authorize Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/oauth2/authorize /openapi/2026-10.openapi.json get /v1/oauth2/authorize # Get User Info Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/oauth2/get-user-info /openapi/2026-10.openapi.json get /v1/oauth2/userinfo Get information about the authenticated user. # Introspect Token Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/oauth2/introspect-token /openapi/2026-10.openapi.json post /v1/oauth2/introspect Get information about an access token. # Request Token Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/oauth2/request-token /openapi/2026-10.openapi.json post /v1/oauth2/token Request an access token using a valid grant. # Revoke Token Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/oauth2/revoke-token /openapi/2026-10.openapi.json post /v1/oauth2/revoke Revoke an access token or a refresh token. # organization.updated Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/organization_updated /openapi/2026-10.openapi.json webhook organization.updated Sent when a organization is updated. **Discord & Slack support:** Basic # Create Webhook Endpoint Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/webhooks/create-webhook-endpoint /openapi/2026-10.openapi.json post /v1/webhooks/endpoints Create a webhook endpoint. **Scopes**: `webhooks:write` # Delete Webhook Endpoint Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/webhooks/delete-webhook-endpoint /openapi/2026-10.openapi.json delete /v1/webhooks/endpoints/{id} Delete a webhook endpoint. **Scopes**: `webhooks:write` # Get Webhook Endpoint Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/webhooks/get-webhook-endpoint /openapi/2026-10.openapi.json get /v1/webhooks/endpoints/{id} Get a webhook endpoint by ID. **Scopes**: `webhooks:read` `webhooks:write` # List Webhook Endpoints Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/webhooks/list-webhook-endpoints /openapi/2026-10.openapi.json get /v1/webhooks/endpoints List webhook endpoints. **Scopes**: `webhooks:read` `webhooks:write` # Update Webhook Endpoint Source: https://staging-docs.crosspoint.finance/api-reference/2026-10/webhooks/update-webhook-endpoint /openapi/2026-10.openapi.json patch /v1/webhooks/endpoints/{id} Update a webhook endpoint. **Scopes**: `webhooks:write` # Security and Vulnerability Reporting Source: https://staging-docs.crosspoint.finance/africa/security How to report a security issue in Crosspoint. Keeping your money and your customers' data safe comes first. If you've found a security issue in Crosspoint, please tell us. ## Making a report Email [support@crosspoint.finance](mailto:support@crosspoint.finance) with **Security** in the subject. Please include: * A clear summary of the issue and what someone could do with it. * Detailed steps to reproduce it. * Relevant details about your setup (browser, OS, version numbers). * Any proof-of-concept code, if you have it. Please don't share the issue publicly until we've fixed it. We'll keep you updated and may ask for more details. ## Timeline We'll get back to you within a few days to acknowledge your report. ## What we're most interested in * Getting into an account or a store without permission, or getting more access than your role allows. * Exposure of personal data. * Access to data that isn't meant to be public without signing in. * Anything that moves money where it shouldn't go. ## In scope * The Crosspoint dashboard and checkout ([app.crosspoint.finance](https://app.crosspoint.finance)) * The Crosspoint API ([api.crosspoint.finance](https://api.crosspoint.finance)) * The sandbox ([sandbox.crosspoint.finance](https://sandbox.crosspoint.finance) and its API) ## Out of scope * Automated scanning. * Social engineering. * Denial of service attacks. * Attacks that need physical access to someone's device. * Theoretical attacks you can't actually exploit. * Man-in-the-middle attacks. * Clickjacking or UI redress attacks. * CSV injection. * HTML injection. * Missing security headers, weak TLS cipher suites or DNS setup issues. ## Please be considerate while investigating * Only test with your own account (or get the account owner's permission first). The [sandbox](/africa/sandbox) is the best place to test. * Don't change, delete or keep private data that isn't yours. * Avoid anything that could break or slow down our services. * If you get access to our systems, don't try to go further. # API Changelog Source: https://staging-docs.crosspoint.finance/changelog/api Stay up to date with the latest changes introduced in our API Crosspoint uses date-based API versions to evolve the API without unexpectedly changing the contract your integration depends on. Versions use the `YYYY-MM` format, such as `2026-10`, and apply to API requests, responses, webhook payloads, and SDK types. [Read more](/api-reference/current/versioning) **2026-10** **This will become the Current version on October 1st.** * Removed the `secret` parameter when [creating](/api-reference/2026-10/webhooks/create-webhook-endpoint) or [updating](/api-reference/2026-10/webhooks/update-webhook-endpoint) webhook endpoints. New endpoint secrets are generated by Crosspoint. * Added `member_id` and `member` to [license key responses](/api-reference/2026-10/license_keys/get-license-key), identifying the seat member holding the key. Both are `null` for keys granted directly to a customer. **2026-04** ### Standard Webhooks signing New webhook endpoints with secrets generated by Crosspoint on or after **September 8, 2026 at 00:00 UTC** use signing compliant with the Standard Webhooks specification. Endpoints with secrets generated before this cutoff continue to use Crosspoint's previous signing approach, preserving compatibility with existing integrations. Endpoints with user-provided secrets also retain the previous approach. We will continue to support this approach for as long as necessary; there is no required migration deadline. To adopt Standard Webhooks signing for an existing endpoint, **regenerate its secret** and update your webhook handler to use the new secret. When using a Standard Webhooks library directly, pass the new secret as-is, without Base64-encoding it first. See [webhook signature validation](/integrate/webhooks/delivery#custom-validation) for details on both signing approaches. # Support Source: https://staging-docs.crosspoint.finance/support ## Documentation Our documentation covers common questions, setup guides, and product details. We recommend using it as a first point of reference whenever you need guidance or support. If question isn't addressed in the documentation, feel free to reach out to us over email. ## Dashboard Feature requests & bugs can be reported through the "Feedback" button found in the sidebar, bottom left of your Crosspoint dashboard. ## Email You can reach us at [support@crosspoint.finance](mailto:support@crosspoint.finance). We typically respond within **24–48 hours on weekdays** for customers and active merchants, and prioritize questions related to ongoing billing, payouts or payments. Please note that during periods of higher request volume, response times may occasionally be longer. ## Merchant Account Reviews We review every seller by hand, usually within one business day. More information on our account reviewal process can be found in our [Account Reviews documentation](/africa/account-reviews)