--- title: Billing Setup for Sub-Organizations using the API | PostGrid description: How to configure centralized or individualized billing for PostGrid sub-organizations, including Stripe Connect setup via the API. --- In the [Sub-Organizations](/print-and-mail/dashboard/partnership/sub-organizations-set-p/index.md) guide we discuss how the feature allows you to create sandboxed PostGrid accounts under your account. As the parent organization, you can either opt for a ‘Centralized’ or ‘Individualized’ billing setup. Under the ‘Centralized’ billing scheme, all charges for mailings sent by both the parent and its sub-organizations are billed through the parent account. As a result, only the parent organization needs to have a payment method on file for sub-organizations to access live mode and send mailings via PostGrid. By default, billing is set to the centralized setup. In contrast, the ‘Individualized’ billing setup requires each sub-organization to add its own payment method. Sub-organizations are billed directly for the mailings they send. This setup is especially useful for parent organizations that wish to resell or white-label PostGrid. It enables them to onboard users as sub-organizations, with each sub-organization handling its own mailing-related payments. The ‘Individualized’ setup is powered by [Stripe Connect](https://docs.stripe.com/connect/how-connect-works). When setting up ‘Individualized’ billing, as the parent organization, you need to create a Stripe Connect account. We support this through our API which will link your Stripe Connect account to your PostGrid account. We also support use cases where sub-organizations pay for their own mailings without requiring you to setup Stripe Connect. However, this option disables features like revenue sharing and custom branding (e.g., your logo on invoices or payment setup pages). To enable this configuration, please contact . --- ## How to get started? To get started, ensure you have the sub-organization feature enabled for your account, and send a POST request to the route `/organization/sub_orgs/billing/individualized_setup_links`. This will return a URL that allows you to go through Stripe’s onboarding process for setting up a Connected account. If at any point, you exit the process and want to resume this process later, you can generate another link to continue the onboard by sending a POST request to the same endpoint. You can also generate a link the same way if you wish to update your account after onboarding. ![API response showing the Stripe Connect onboarding URL returned after posting to the individualized setup links endpoint](/_astro/01-Screenshot_2025-06-03_at_9.22.42_PM.Zn_W4zF3_Z2OXmQ.webp) Once your Stripe Connect account is setup, you can modify the Stripe checkout branding that is presented to your sub-organizations. You can modify your icon on Stripe checkout pages, brand colour and more detailed in this [guide](https://docs.stripe.com/get-started/account/branding). Once you have been onboarded with ‘Stripe Connect’, and the individualized billing process has been completed, any sub-organizations that go through our payment method setup process on our dashboard, we will do so *on behalf of* your Stripe Connect account. If you have any branding settings, it will be shown on the payment setup page, and it will be associated with your Stripe account. --- ## Integrating this into your platform We’ve covered the flow for sub-organizations adding their payment method through our dashboard. However, if you’re building your own integration using our API, we also support collecting payment details directly within your app. You can host the payment setup session in your own interface, allowing us to securely collect the payment method and associate it with the relevant PostGrid sub-organization for future mailing charges. This is made possible through Stripe’s [Embedded Checkout](https://docs.stripe.com/payments/checkout/save-and-reuse?payment-ui=embedded-form) flow. This checkout flow requires [Stripe.js](https://docs.stripe.com/js), Stripe’s client library. This package can be used with your frontend JavaScript application by either including it in the ` ``` OR by installing it as an `npm` package: Terminal window ``` npm install @stripe/stripe-js ``` There is further information on these steps here on Stripe.js [documentation](https://docs.stripe.com/js/including) and their GitHub [repository](https://github.com/stripe/stripe-js). Once you have Stripe.js installed in your application, the next step involves retrieving the `stripeSetupSessionID`, the `stripeSetupSessionClientSecret` and `stripePublishableKey` from PostGrid’s `POST` endpoint - `/sub_organizations/:id/embedded_mailings_payment_method_setup_sessions` ``` { "stripeSetupSessionID": "cs_live_xyz123", "stripeSetupSessionClientSecret": "cs_live_xyz123_secret_abc456", "stripePublishableKey": "pk_live_123" } ``` The above are necessary for you to continue the session on your application and surface the Stripe payment UI to your user. Below is some sample code for retrieving them from the PostGrid endpoint. ``` export const loadPostGridStripe = async (subOrgID) => { const url = `https://api.postgrid.com/print mail/v1/sub_organizations/${subOrgID}/embedded_mailings_payment_method_setup_sessions`; try { const res = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json", /** If you’re calling this from frontend code, do not expose x-api-key directly in the browser. Instead, make this request via your backend to keep the API key secure. */ "x-api-key": "YOUR_API_KEY", }, }); if (!res.ok) { throw new Error(`HTTP error! Status: ${res.status}`); } const { stripeSetupSessionID, stripePublishableKey, stripeSetupSessionClientSecret, } = await res.json(); // Initialize the Stripe client and return it const pgStripe = Stripe(stripePublishableKey); return { pgStripe, stripeSetupSessionID, stripeSetupSessionClientSecret, }; } catch (error) { console.error("Error fetching PostGrid config:", error); throw error; } }; ``` ### Using Vanilla JS If you are using React, feel free to skip to the [‘Using React’](/print-and-mail/dashboard/partnership/collecting-payment-details-from-sub-orgs-using-the-api#using-react/index.md) section. First, initialize the Stripe client using the `stripePublishableKey`. Then use the `stripeSetupSessionClientSecret` to create and start an embedded checkout session. Be sure to store the `stripeSetupSessionID`, you’ll need it later to check the session’s status. ``` import { loadPostGridStripe } from "./services"; const subOrgID = getSubOrgID(...); const { stripeSetupSessionID, stripeSetupSessionClientSecret, pgStripe } = await loadPostgridStripe(subOrgID); ``` ``` const checkout = await pgStripe.initEmbeddedCheckout({ /** A callback function fetchClientSecret() => Promise that resolves with the client secret for the Checkout Session. The client secret is retrieved from the PostGrid API */ fetchClientSecret: async () => stripeSetupSessionClientSecret, onComplete: async () => { /** Passing an onComplete callback function allows you to run custom actions after the checkout session finishes. For example, you might fetch the session status from the PostGrid endpoint to decide whether to move forward to the next step or prompt the user to finish the session. **/ }, }); ``` The checkout will then need to be [mounted](https://docs.stripe.com/js/embedded_checkout/mount) to the DOM. ```
``` It can be unmounted from the DOM using `checkout.unmount();` and also destroyed using `checkout.destroy();`. Do note that once you destroy the check, it cannot be mounted again and will need to be initialized again as above. To check the session status, call this endpoint in your onComplete() callback (in `initEmbeddedCheckout` or wherever you handle the status): `/sub_organizations/${subOrgID}/embedded_mailings_payment_method_setup_sessions/${stripeSetupSessionID}`. The response includes a `session` object with a `status` field, which can be `open`, `complete` or `expired`. For card payments, (the primary option for PostGrid’s embedded checkout) , Checkout renders a default success state instead of redirecting. If you want a custom success state, use the onComplete callback to destroy the Checkout instance and render your own. ### Using React If you are using React in your frontend application, Stripe.js provides the convenient `` component. ``` import React, { useState, useEffect } from "react"; import { EmbeddedCheckoutProvider } from "@stripe/react-stripe-js"; import { loadPostGridStripe } from "./services"; function App() { const [stripeConfig, setStripeConfig] = useState(null); const [isComplete, setIsComplete] = useState(false); const handleComplete = () => setIsComplete(true); useEffect(() => { async function fetchStripeConfig() { const subOrgID = getSubOrgID(...); // Fetches { stripeSetupSessionID, stripeSetupSessionClientSecret, pgStripe } const config = await loadPostGridStripe(subOrgID); setStripeConfig(config); } fetchStripeConfig(); }, []); if (!stripeConfig) { return
Loading payment setup…
; } return isComplete ? ( /** This success page can be a custom component to show to users after their checkout session **/ ) : ( stripeConfig.stripeSetupSessionClientSecret, onComplete: handleComplete }} > ); } export default App; ``` --- Once the session on your application is completed. PostGrid will receive updates from Stripe that will allows us to attach the entered payment details to your PostGrid sub-organization, which we can then charge later for any mailings that are sent out. Here are some additional links for working with Stripe.js and the ‘Embedded Checkout’ workflow. [Handling completion of the setup](https://docs.stripe.com/payments/checkout/custom-success-page?payment-ui=embedded-form\&locale=en-GB\&client=react#disable-redirects) [Stripe.js Reference](https://docs.stripe.com/js) [Embed a payment form on your site](https://docs.stripe.com/checkout/embedded/quickstart?lang=node\&client=react)