# Event Ingestion API Source: https://docs.runonatlas.com/advanced-atlas/event-ingestion-api Start adding event ingestion to your application ## Endpoint Details **URL**: [https://platform.runonatlas.com/external/event](https://platform.runonatlas.com/external/event) **Method**: POST **Authentication**: The API requires authentication using an API key. * **Header**: x-api-key (Required) * **Value**: Provided by Atlas OR one can be created in Settings > API Keys (Required) ## Request Parameters * **eventName** (string): Name of the event being tracked * **generatedAt** (string): ISO 8861 timestamp of event creation * **customerId** (string): Unique identifier for the customer ## Optional Parameters * **idempotencyKey** (string): Globally unique UUID to prevent duplicate event processing. Recommended for ensuring exactly-once event delivery. If not provided, Atlas will generate one based on event payload. * **userId** (string): Unique identifier for the end user (the customer's user) * **data** (object): Key-value pair for additional event metadata. Used to provide context for event identification and usage calculation. Can include any relevant information about the event. ## Example Request ```json theme={null} curl --location --globoff '' \\ --header 'x-api-key: {{API_KEY}}' \\ --data '{ "eventName": "aiFeatureUsed", "data": {"featureName": "dataExport"}, "generatedAt": "2024-10-25T00:00:00.000Z", "customerId": "1e74c86e-62ce-4896-b466-e1dbae768e0b", "userId": "7e63ad67-bae9-4c18-9ec7-33440f39afbf", "idempotencyKey": "test-key" }' ``` ## Best Practices * Always include an idempotencyKey * Use meaningful event names * Provide comprehensive data for accurate usage tracking # Introduction Source: https://docs.runonatlas.com/advanced-atlas/index Heads up — this is our advanced setup. Most teams won’t need this. But if you’re working with usage- or credit-based billing, this is where the magic happens. ## Overview Atlas combines billing, usage data, and provisioning to provide a modern cockpit for product monetization. Our documentation is intended to be approachable and comprehensive, if something is not clear, or documentation is missing, please contact us. Atlas pipeline Atlas accepts first and third party usage data to create a baseline of understanding of what users are doing in your application. Once eventing is configured, non technical resources can configure billable metrics entitlements and subscription plans for customers. Entitlements can act as feature flags or directly integrate with existing feature flagging systems to allow basic provisioning and access gating. Billable metrics can be turned into invoices based on configuration and delivered directly to your payment gateway for seamless collection. Finally, our configuration CRM gateway allows you to bi-drectionally sync data to and from systems of record to ensure all data is in sync. ## Key Terms Here are some important terms to know as you navigate Atlas. * **Merchant**: Atlas customer - the entity that's using Atlas to bill their customers * **Customer**: The customer of the merchant * **User**: The individual who works at the customer * **Atlas User**: The merchant user who is using the atlas app * **Plan**: A bucket containing entitlements and/or metrics - most represent what the user is buying * **Entitlement**: Static capability or feature, can be granular or high level. Can also include non software specific items that are on a contract, e.g. support hours. * **Metric**: A set of events, filtered, and counted, summed, or averaged, etc. to be billed * **Price**: Price set on an entitlement or billable metric * **Event**: An event defined by our merchants that can be tied to a metric or feature * **Price Type**: Defines price structure (one-time, tiered, capped, etc.) * **Credits**: Pre-paid units that can be used to pay down invoices or unlock functionality * **Subscription**: Ties a plan to a specific user/customer and invoice * **Invoice**: What is billed to the customer. Contains terms and detailed line items. * **Jobs Engine**: Nightly procedure of reconciling usage with subscriptions, visitors, and accounts. Generates invoices, retries, etc. * **SDK**: Both client and server-side packages for calling Atlas API endpoints ## Entitlements Atlas Entitlements are static capabilities or features that help define what the customer receives by purchasing a plan. Entitlements can also be used for non-software-specific contractual items, e.g. professional support hours or consulting. To create an entitlement, go to the entitlements page, and press the "Create entitlement" button. Hero Light Like billable metrics, entitlements can be tied to specific events. However, this is optional and is only used to represent overall usage. If you want to bill based on usage, use a billable metric. Hero Light If you choose to tie an entitlement to an event, you’ll be able to view low, medium, and high usage statistics. Hero Light Usage is helpful at determining which capabilities may be in the wrong plan, are under or over monetized, or should be deprecated. Every entitlement auto-generates an `entitlement_key` that is returned via API and can be used in provisioning or feature flagging. ## Billable Metrics Billable Metrics are designed for usage, action, and outcome-based pricing scenarios. To create a new billable metric, go to the billable metric page and 'Create metric'. Hero Light To create a billable metric, you must have matching events flowing into Atlas. In the "Create metric" modal, you’ll populate a Name and Description, choose if you want to show this metric on invoice, and build the query that matches this billable metric to a specific event type. This UI allows non-technical resources to build and change billable metrics without reliance on engineering resources. Hero Light You can either use the UI rule builder or, for power users, you can write direct SQL using the Advanced SQL editor. Hero Light In either case, you can use the Run button to quickly check if events exist for your given query. ## Plans Atlas Plans represent a collection of entitlements and billable metrics that a customer can purchase or subscribe to. Entitlements and Billable Metrics must be created before you can add them to a Plan. To create a Plan, go to plans page, and press the Create plan button. Add plann modal Give the plan a Name, Description, Currency and Payment Terms, then press Save. You now have an empty plan ready to be populated. The newly-created Plan will go into a 'Draft' state. This is to ensure changes can be made to the Plan without fear of the Plan being subscribed to by a customer. Your newly created plan can be populated with entitlements and metrics by clicking the Add button on either. Plan settings When adding an entitlement or billable metric you’ll be asked to populate Billing Cadence, Price Type, and Price. Add entitlement modal ## Customers There are three main ways to create customers in Atlas: 1. Upload a .csv file 2. Use our CRM integrations 3. Manually To manually add a new customer, go to the customer page and select Add customer. Only the Name and Email fields are required. Customer modal Once a customer is created they can be linked to any available plan manually or via our SDK. Clicking on a customer brings you to the customer detail page, which allows you to view and customize any active plan, and view and edit invoice details. Customer dashboard ## Price Details Atlas supports multiple price types in order to support complex billing scenarios. * **Unit Pricing** - Charges are based on a fixed price per unit (e.g., \$10,000 platform fee / yr). This model is straightforward and can be used for many common basic billing scenarios. * **Tiered Pricing** - Prices change based on quantity thresholds. For example, you might charge $.55 per user for the first 1000 AI tokens, and $.50 for the next 1000, and so on. This encourages higher volume usage with price breaks. * **Package Pricing** - Allows bundling of multiple units into a package at a single price. For example, you may offer user accounts in packs of 10. If a customer needs 11 users, they will need to purchase an additional pack of 10, as billing is always done in increments of 10. * **Bulk Pricing** - Also known as volume pricing, bulk pricing refers to a pricing strategy used to offer reduced prices or discounts to customers who purchase quantities in bulk. For example, you may charge 100$ for 50 units ($2/unit), but if you buy 200 units you charge $300 ($1.5/unit). * **Matrix Pricing** - Matrix pricing uses multiple variables to calculate prices in a grid-like structure, offering flexibility for complex, customized pricing. For example, if you’re charging for API usage, you can set different prices based on the type of API response, allowing for more nuanced pricing. Each of these models ensures you can align pricing with the value your customers receive, offering both flexibility and precision in billing. # Payment Gateway Integration Source: https://docs.runonatlas.com/advanced-atlas/payment-gateway-integration Integrate Atlas with your payment gateway Atlas Billing manages all plans, entitlements, billable metrics, and pricing, and is responsible for the automatic generation of invoices based on each customer’s current subscription. Once invoices are created, they must be sent for payment collection, which requires integration with a payment gateway. Atlas currently supports two payment gateways: Stripe and QuickBooks. You can connect to either by navigating to Settings > Integrations. The integration is bi-directional for payment statuses. Once a payment is received or if an invoice becomes overdue in your connected gateway, the corresponding status is automatically synced back to Atlas. This ensures that billing data and downstream workflows (for example, dunning or access controls) stay aligned with the actual payment status. While payment status is synchronized, edits made directly to invoice line items or structure inside the payment gateway are not reflected back in Atlas. Atlas remains the system of record for invoice generation logic. Settings * QuickBooks uses OAuth for authentication. You’ll need administrator access to QuickBooks to complete the integration. * Once connected, the integration will remain active unless manually disconnected. * As invoices are created in Atlas, you can manually sync and review them in QuickBooks before sending to your customer. * Atlas supports automatic email delivery of invoices if you enable the corresponding toggle in the integration settings screen. Connecting ## Stripe Integration Atlas integrates with Stripe to help you manage customer invoicing and payments. To connect your Stripe account to Atlas, you’ll need to provide an API key—either a secret key or a restricted key.We recommend using a restricted key whenever possible. This approach aligns with our commitment to the principle of least privilege—a best practice in security that ensures systems only have access to the resources they absolutely need. Stripe API keys can be found in your [Stripe API Dashboard](https://dashboard.stripe.com/apikeys). * Atlas only accepts test keys in sandbox mode and production keys in production mode. * As invoices are created in Atlas, you can manually sync and review them in Stripe before sending to your customer. * Atlas supports automatic email delivery of invoices if you enable the corresponding toggle in the integration settings screen. ### API Key Options 1. Restricted Key (Recommended) * Access: Only to specific resources you select. * Pros: More secure. Limits Atlas access to only the necessary parts of your Stripe account. * Cons: Requires a few more steps to configure. You may need to update permissions in the future as Atlas adds new features. 2. Secret Key (Full Access) * Access: Full access to your Stripe account. * Pros: Easiest to set up - no configuration required, and no updates required if Atlas needs access to a new resource type. * Cons: Gives Atlas access to all your Stripe resources, including ones we don’t use. Not ideal from a security standpoint. ### What Permissions Does Atlas Need? When using a restricted key, Atlas currently needs write access to the following Stripe resources: * Customers * Invoices * Webhook Endpoints ### How to Connect with a Restricted API Key in Stripe? 1. Go to your [Stripe API Keys](https://dashboard.stripe.com/apikeys) page. 2. Click **Create restricted key**. 3. Select **Providing this key to another website**. 4. Name the key (e.g., Atlas Integration). 5. Set the **URL** to [https://app.runonatlas.com/](https://app.runonatlas.com/) 6. Check the box for **Customize permissions for this key**. 7. In the permissions section, set the following to **Write** (All other permissions should be set to **None**): * Customers * Invoices * Webhook Endpoints 8. Save the key and copy it. 9. Navigate back to the [Atlas application](https://app.runonatlas.com). 10. Select **Settings**. 11. Select the tab for **Integrations**. 12. Click the button for connecting to Stripe paste your key into the modal that opens, and click **Activate**. Payment gateway setup Atlas will verify that the provided key has the correct permissions and save it securely for use when syncing invoice data to Stripe. Be sure to store your key securely. Treat it like a password. # Bulk upsert customers Source: https://docs.runonatlas.com/api-reference/customers/bulk-upsert-customers https://platform.runonatlas.com/external/schema post /external/customers/upsert/bulk Bulk upsert customers into Atlas by unique external ID (e.g. the customer ID in your system). Does not automatically subscribe the customers to the default plan if eligible. # Create customer with default plan subscription Source: https://docs.runonatlas.com/api-reference/customers/create-customer-with-default-plan-subscription https://platform.runonatlas.com/external/schema post /external/customers Create a new customer with a unique external ID (e.g. the customer ID in your system). Also automatically subscribes the customer to the default plan if eligible. # Create Stripe setup checkout session for payment method update Source: https://docs.runonatlas.com/api-reference/customers/create-stripe-setup-checkout-session-for-payment-method-update https://platform.runonatlas.com/external/schema post /external/customers/external-id/{externalId}/stripe/checkout-sessions/setup Creates a new Stripe setup checkout session to update the payment method for a customer by external ID (e.g. the customer ID in your system). You must have a valid Stripe account connected to your Atlas merchant to use this endpoint. Redirect the customer to the URL returned in the response to update their payment method. # Get custom pricing unit usage Source: https://docs.runonatlas.com/api-reference/customers/get-custom-pricing-unit-usage https://platform.runonatlas.com/external/schema get /external/customers/external-id/{externalId}/customPricingUnits/{customPricingUnitId}/usage Get custom pricing unit usage for a customer by external ID (e.g. the customer ID in your system). # Get customer by ID Source: https://docs.runonatlas.com/api-reference/customers/get-customer-by-id https://platform.runonatlas.com/external/schema get /external/customers/{id} Get a customer by ID. # Get customer transactions Source: https://docs.runonatlas.com/api-reference/customers/get-customer-transactions https://platform.runonatlas.com/external/schema get /external/customers/external-id/{externalId}/transactions Get a paginated list of transactions for a customer by external ID (e.g. the customer ID in your system). # Get customer with expanded data Source: https://docs.runonatlas.com/api-reference/customers/get-customer-with-expanded-data https://platform.runonatlas.com/external/schema get /external/customers/external-id/{externalId}/expanded Get a customer by external ID with their active and upcoming subscriptions, custom pricing unit balances, and private plans. # Get customers Source: https://docs.runonatlas.com/api-reference/customers/get-customers https://platform.runonatlas.com/external/schema get /external/customers Get a paginated list of customers. # Upsert customer Source: https://docs.runonatlas.com/api-reference/customers/upsert-customer https://platform.runonatlas.com/external/schema post /external/customers/upsert Upsert customer by unique external ID (e.g. the customer ID in your system). Does not automatically subscribe the customer to the default plan if eligible. # Ingest events Source: https://docs.runonatlas.com/api-reference/events/ingest-events https://platform.runonatlas.com/external/schema post /external/event/ Ingest events into Atlas. Events are processed asynchronously, so the response does not indicate when the events have been processed or that the data is correct. For example, if you send a customer ID that does not exist, this endpoint will accept it anyway. # Get invoice with line items Source: https://docs.runonatlas.com/api-reference/invoices/get-invoice-with-line-items https://platform.runonatlas.com/external/schema get /external/invoices/{invoiceId}/with-line-items Get an invoice with its line items attached, including expanded price and billable information. # Cancel subscription Source: https://docs.runonatlas.com/api-reference/subscriptions/cancel-subscription https://platform.runonatlas.com/external/schema post /external/subscriptions/{id}/cancel Cancel a subscription. For currently active subscriptions, subscription will be scheduled to end at end of current billing period. For upcoming subscriptions, subscription will be deleted. # Create multiple subscriptions Source: https://docs.runonatlas.com/api-reference/subscriptions/create-multiple-subscriptions https://platform.runonatlas.com/external/schema post /external/subscriptions/bulk Create multiple subscriptions. # Create subscription Source: https://docs.runonatlas.com/api-reference/subscriptions/create-subscription https://platform.runonatlas.com/external/schema post /external/subscriptions Create a subscription. # Delete subscription Source: https://docs.runonatlas.com/api-reference/subscriptions/delete-subscription https://platform.runonatlas.com/external/schema delete /external/subscriptions/{id} Delete a subscription. Currently active subscriptions cannot be deleted, they must be cancelled first. # Get subscription by ID Source: https://docs.runonatlas.com/api-reference/subscriptions/get-subscription-by-id https://platform.runonatlas.com/external/schema get /external/subscriptions/{id} Get a subscription by ID. # Get subscription by ID with expanded data Source: https://docs.runonatlas.com/api-reference/subscriptions/get-subscription-by-id-with-expanded-data https://platform.runonatlas.com/external/schema get /external/subscriptions/{id}/expanded Get a subscription by ID with expanded plan data including prices and billables. # Get subscriptions Source: https://docs.runonatlas.com/api-reference/subscriptions/get-subscriptions https://platform.runonatlas.com/external/schema get /external/subscriptions Get a paginated list of subscriptions. # Renew cancelled subscription Source: https://docs.runonatlas.com/api-reference/subscriptions/renew-cancelled-subscription https://platform.runonatlas.com/external/schema post /external/subscriptions/{id}/renew Renew a cancelled subscription by removing its end date. Requires externalCustomerId to verify the customer matches the subscription. # Create a Pricing Model Source: https://docs.runonatlas.com/create-a-pricing-model Creating a pricing model with Atlas is fast, flexible, and backed by real market data. ``` ### CustomerPortalComponent Widget The CustomerPortalComponent widget shows the current user what plan they are subscribed to, a history of their payments, and allows them to cancel or change their plan at any time. **IMPORTANT:** The `successUrl` must be absolute. For example, `/success` is not valid, but `https://your-app.com/success` is valid. ```tsx theme={null} "use client"; import { CustomerPortalComponent } from "@runonatlas/next/client"; /** * IMPORTANT: * Replace the `successUrl` prop with values specific to your application. */ export default function CustomerPortalPage() { return ( ); } ``` ## Restrict user access based on their subscriptions You can prevent users from accessing restricted parts of your application based on their subscription. To do so, it is important that you do it both in the UI and in the backend: Protecting the UI is the easiest way to prevent users from accessing restricted parts of your application. Protecting the UI is cool, but not enough! You need to protect the backend as well. This way, you can ensure that users cannot access restricted parts of your application even if they bypass the UI. ### UI Protection #### Using hooks You can use the `useFeaturesAllowed` hook to check if a user has access to a feature. ```tsx theme={null} import { useFeaturesAllowed } from "@runonatlas/next/client"; export default function Home() { const { isAllowed } = useFeaturesAllowed(["feature-1", "feature-2"]); return (
Our system has determined that... {isAllowed ? "You have access to these features!" : "You don't have access to these features. Please upgrade your subscription!"}
); } ``` Or you can use the `useCustomerFeatures` hook to get a users access status for all features in your pricing model: ```tsx theme={null} import { useFeaturesAllowed } from "@runonatlas/next/client"; ... const { features } = useCustomerFeatures(); console.log(features); // { "data-explorer": { allowed: true, included: true, limit: 10, currentUsage: 5 }, "ai-assistant": { allowed: false, included: false } } ``` #### Using components You can use the `FeatureProtect` component to check if a user has access to a feature. ```tsx theme={null} import { FeatureProtect } from "@runonatlas/next/client"; export default function Home() { return ( Oh, you don't have access to these features. Upgrade now! } features={["feature-1", "feature-2"]} >
Cool features
); } ``` ### Backend Protection Using Atlas in the backend is **very easy**. You just need to use the `atlasServerClient` that we created before to check if a user has access to a feature. For example, if you are using Clerk: ```typescript src/app/api/example/route.ts expandable theme={null} import { atlasServerClient } from "@/atlas/server"; import { auth } from "@clerk/nextjs/server"; export async function GET() { const { userId } = await auth(); if (!userId) { return new Response("Unauthorized", { status: 401 }); } const { ok } = await atlasServerClient.areFeaturesAllowed(userId, [ "data-explorer", ]); if (!ok) { return new Response("You don't have this feature enabled", { status: 401 }); } return new Response("Hello from the backend!!"); } ``` ## Limit-based features Sometimes, just having a feature as enabled or disabled is not enough, and our pricing models require limits to be set. For example, 5 users per account, or 20GB of storage. Setting this up with Atlas is very easy. And, if at some point the limits change, you won't need to change the code again! ### Configuring the backend The backend needs to understand what the limits are and how to check if the limit has been reached. To do so, you need to use the `atlasServerClient` and provide callbacks per each limit that you might want to use. For example, given a feature whose id is `data-explorer`, you can provide a callback to check if the limit has been reached: ```typescript src/atlas/server.ts expandable {11,12,13,14,15,16,17} theme={null} import { AtlasNextServerClient } from "@runonatlas/next/server"; import { auth } from "@clerk/nextjs/server"; import { prisma } from "./db"; export const atlasServerClient = new AtlasNextServerClient( async () => { const { userId } = await auth(); return userId; }, { limits: { "data-explorer": (userId: string) => prisma.explorations.count({ where: { userId, }, }), }, } ); ``` Now, every time you the application needs to check if the `data-explorer` feature is available, Atlas will use the callback to compute it. By default, if you haven't configured a limit callback, Atlas will deny access to the feature if it has a limit. You can easily override this behavior. For example: ```typescript src/atlas/server.ts expandable {9,10,11} theme={null} import { AtlasNextServerClient } from "@runonatlas/next/server"; import { auth } from "@clerk/nextjs/server"; export const atlasServerClient = new AtlasNextServerClient( async () => { const { userId } = await auth(); return userId; }, { baseClientOptions: { allowUnknownLimits: true, }, } ); ``` ### Explanation in the UI When using both the `` component and the `useFeaturesAllowed()` hook, it will automatically check if the user has access to the features you are protecting AND if the limit has not been reached. However, it is possible that you want to show your user why the access was denied! To to do this, we give you the reasons why the access was denied. For example, with the hook `useFeaturesAllowed`: ```tsx src/app/home/page.tsx expandable theme={null} import { useFeaturesAllowed } from "@runonatlas/next/client"; export default function Home() { const { isAllowed, reasons } = useFeaturesAllowed(["feature-1", "feature-2"]); if (!isAllowed) { return (
Our system has determined that... {reasons.map((reason) => { if (reason.reason === "notIncluded") { return (
You need to upgrade your plan to access {reason.slug}
); } if (reason.reason === "limitReached") { return (
You have reached the limit for {reason.slug}. You currently have{" "} {reason.currentUsage}/{reason.limit}.
); } })}
); } return
Cool feature
; } ``` In the `` component, instead of directly providing the FallbackComponent, you can actually provide a function that returns the component to be rendered when the access is denied. For example: ```tsx src/app/home/page.tsx expandable theme={null} import { FeatureProtect } from "@runonatlas/next/client"; export default function Home() { return ( { return (
Our system has determined that... {reasons.map((reason) => { if (reason.reason === "notIncluded") { return (
You need to upgrade your plan to access {reason.slug}
); } if (reason.reason === "limitReached") { return (
You have reached the limit for {reason.slug}. You currently have {reason.currentUsage}/{reason.limit}.
); } })}
); }} features={["feature-1", "feature-2"]} >
Cool features
); } ``` ## Usage-based and credit-based features When using Next.js in a serverless environment (e.g., Vercel, Netlify), it is crucial to properly manage event batching to prevent data loss. See the [Batching events](#batching-events) section for required configurations. Usage-based and credit-based features allow you to bill customers based on their consumption. These features track usage events and apply billing according to your pricing model configuration. ### Understanding billing types Atlas supports two types of billing for consumption-based features: **Usage-based billing**: Events are rolled up for the entire billing period, the price is applied, and the user is billed in arrears according to what they used. This is traditional usage-based billing where customers pay for what they consume after the fact. **Credit-based billing**: Users are allocated a specific amount of a custom pricing unit (e.g., credits) each month. When an event is received in Atlas that matches a credit-based price, their balance of custom pricing units is instantly deducted. This provides near real-time consumption tracking with immediate balance updates. Atlas offers the ability to block access to a usage-based and credit-based features via the **Max usage** and **Block overage** options, respectively. **Max usage**: (Number) The maximum amount of a usage-based feature a customer is allowed to consume within a single billing period. If specified, Atlas will block access after usage reaches the amount specified and then unblock access at the start of the next billing period. If not specified, Atlas will allow unlimited usage in any given billing period. **Block overage**: (Boolean) Whether or not to prevent a customer from drawing custom pricing unit balance negative. If enabled, Atlas will block access to a feature when a balance of 0 has been detected. Otherwise, Atlas will continue accepting events and further reduce the customers credit balance negative. The next allocation of custom pricing units will be applied as normal, increasing the customers balance from the current negative balance by the amount of the allocation. Because Atlas's event ingestion system is designed to be eventually consistent, it is possible for a small number of events to be processed before Atlas feature flagging has recognized that the customer has hit a balance of 0. This means that small overages are possible even if `blockOverage` is enabled. For more real time access control, please reach out to customer support at [support@runonatlas.com](mailto:support@runonatlas.com). ### Configuring the backend To bill your customers correctly, your application must send an event to Atlas each time a usage-based or credit-based feature is consumed. This is done using the `atlasServerClient.enqueueFeatureEvents` method, followed by `atlasServerClient.flushEvents` to ensure the event is sent immediately. In a serverless environment, this two-step process is essential. ```tsx src/app/api/example/route.ts expandable {18-27} theme={null} import { atlasServerClient } from "@/atlas/server"; import { auth } from "@clerk/nextjs/server"; export async function GET() { const { userId } = await auth(); if (!userId) { return new Response("Unauthorized", { status: 401 }); } const { ok } = await atlasServerClient.areFeaturesAllowed(userId, [ "usage-based-feature", ]); if (!ok) { return new Response("You don't have this feature enabled", { status: 403 }); } try { await atlasServerClient.enqueueFeatureEvents({ featureIds: ["usage-based-feature"], customerId: userId, }); await atlasServerClient.flushEvents(); // This will send the events immediately } catch (err) { console.error("Failed to enqueue feature events", err); } return new Response("Hello from the backend!!"); } ``` ### Batching events To optimize performance and avoid rate limits, the Atlas client automatically batches usage events before sending them. You can customize this batching behavior by configuring the `eventsFlushAt` (number of events) and `eventsFlushInterval` (time in milliseconds) options during client initialization. This will be very important if you are using a serverless backend, such as Vercel or Netlify, to ensure that events are sent before the serverless function is terminated. ```typescript src/atlas/server.ts [expandable] {9,10,11} theme={null} import { AtlasNextServerClient } from "@runonatlas/next/server"; import { auth } from "@clerk/nextjs/server"; export const atlasServerClient = new AtlasNextServerClient( async () => { const { userId } = await auth(); return userId; }, { eventsFlushAt: 0, eventsFlushInterval: 0, } ); ``` You can also trigger a manual flush of all enqueued events at any time using the `flushEvents` method. ```typescript {11} theme={null} import { atlasServerClient } from "@/atlas/server"; async function onProcessShutdown() { await atlasServerClient.flushEvents(); } ``` ## Organizations In many applications, it's common to allow multiple users to belong to the same organization and share a single subscription. For example: Alice creates an organization called **Standard Corp**, purchases a plan, and invites Bob and Carol to join. Since all three are members of the same organization, they should share access to the same plan and limits. To enable this, you should use the **organization ID** as the user identifier instead of the individual user ID. This tells Atlas that access and usage should be scoped to the organization — not the individual — ensuring that Bob and Carol both have access under Standard Corp’s plan. ```tsx src/atlas/client.tsx expandable {2,17,24,25} theme={null} "use client"; import { useAuth, useUser, useOrganization } from "@clerk/nextjs"; import { AtlasProvider } from "@runonatlas/next/client"; import { redirect } from "next/navigation"; const loginCallback = () => { redirect("/login"); }; export function AtlasClientProvider({ children, }: { children: React.ReactNode; }) { const { userId, getToken, isLoaded } = useAuth(); const { user } = useUser(); const { organization } = useOrganization(); return ( {children} ); } ``` ```typescript src/atlas/server.ts expandable {6,7} theme={null} import { AtlasNextServerClient } from "@runonatlas/next/server"; import { auth } from "@clerk/nextjs/server"; export const atlasServerClient = new AtlasNextServerClient(async () => { const { orgId } = await auth(); return orgId; }); ``` # React Source: https://docs.runonatlas.com/developer-portal/sdk/react `@runonatlas/react` is our React library for Atlas, which provides all the components and hooks you need. ## Installation ```bash Terminal theme={null} npm install @runonatlas/react ``` ```bash Terminal theme={null} pnpm add @runonatlas/react ``` ```bash Terminal theme={null} yarn add @runonatlas/react ``` ## Initialization This only covers the frontend initialization with React. You will also need to initialize your backend. For example, [Express](/developer-portal/sdk/express). To initialize your frontend application, you need to create a client provider component and wrap your application with it. Since Atlas needs to authenticate with your backend and verify that users are only accessing what they can, you will need to place it under your authentication provider. It's very important that you provide all the required props: * `getAuth`: A function that returns the authentication token. This is only necessary if the authentication with the backend is done with tokens and headers (for example, Clerk). * `loginCallback`: A function that is called whenever the user tries to do an action that requires authentication. * `userId`: The ID of the user. * `userEmail`: The email of the user. Optional. * `userName`: The name of the user. Optional. * `isUserLoading`: A boolean that indicates whether the user is still loading. *Optional.* Helps Atlas to avoid flickering when the user status changes. You then need to wrap your application with the `AtlasProvider`, wherever you have the rest of your providers and under your authentication provider. For example, with Clerk: ```tsx src/atlas/client.tsx expandable theme={null} import { useAuth, useClerk, useUser } from "@clerk/clerk-react"; import { AtlasProvider } from "@runonatlas/react"; import { useCallback } from "react"; export function AtlasClientProvider({ children, }: { children: React.ReactNode; }) { const { getToken, userId, isLoaded } = useAuth(); const { user } = useUser(); const { openSignIn } = useClerk(); const loginCallback = useCallback(() => { openSignIn(); }, [openSignIn]); return ( {children} ); } ``` ```tsx src/App.tsx expandable theme={null} import { ClerkProvider } from "@clerk/clerk-react"; export default function App() { return ( ); } ``` You can specify the host of your backend when instantiating the provider. ```tsx src/atlas/client.tsx expandable {22} theme={null} import { useAuth, useClerk, useUser } from "@clerk/clerk-react"; import { AtlasProvider } from "@runonatlas/react"; import { useCallback } from "react"; export function AtlasClientProvider({ children, }: { children: React.ReactNode; }) { const { getToken, userId } = useAuth(); const { user } = useUser(); const { openSignIn } = useClerk(); const loginCallback = useCallback(() => { openSignIn(); }, [openSignIn]); return ( {children} ); } ``` ## Use our Atlas widgets Remember that you need to have followed the [installation guide](#installation) before you can use any of these widgets. You can use any of our Atlas widgets in your React application by importing them from `@runonatlas/react`. ### PricingComponent Widget The PricingComponent widget shows the plans in your Atlas pricing model and allows users to purhcase a subscription to a plan via Atlas' in-line checkout. **IMPORTANT:** The `successUrl` must be absolute. For example, `/success` is not valid, but `https://your-app.com/success` is valid. ```tsx theme={null} import { PricingComponent } from "@runonatlas/react"; /** * IMPORTANT: * Replace the `successUrl` prop with values specific to your application. */ export default function PricingPage() { return ( ); } ``` If you want to display your pricing model on a **public facing marketing site**, you can embed a hosted pricing page via `iFrame`. When a visitor selects a plan, they'll be redirected to your core application where the SDK is installed to sign in and complete the purchase. To embed pricing on a website outside your app, use the `/pricing-embed` endpoint and pass the following URL query parameters: * `publishableKey` - public key with read access to your pricing model (accessible in [settings/api-keys](https://app.runonatlas.com/settings/api-keys)) * `redirectTo` - absolute URL to redirect users to your app when they select a plan * `successUrl` - absolute URL to return users to after they succesfully purchase a plan **IMPORTANT:** The `redirectTo` and`successUrl` must be absolute. For example, `/success` is not valid, but `https://your-app.com/success` is valid. ```html theme={null} ``` ### CustomerPortalComponent Widget The CustomerPortalComponent widget shows the current user what plan they are subscribed to, a history of their payments, and allows them to cancel or change their plan at any time. **IMPORTANT:** The `successUrl` must be absolute. For example, `/success` is not valid, but `https://your-app.com/success` is valid. ```tsx theme={null} import { CustomerPortalComponent } from "@runonatlas/react"; /** * IMPORTANT: * Replace the `successUrl` prop with values specific to your application. */ export default function CustomerPortalPage() { return ( ); } ``` ## Limit user access based on their subscriptions You can prevent users from accessing restricted parts of your application based on their subscription. To do so, you can use our UI protection features. ### UI Protection #### Using hooks You can use the `useFeaturesAllowed` hook to check if a user has access to a feature. ```tsx theme={null} import { useFeaturesAllowed } from "@runonatlas/react"; export default function Home() { const { isAllowed } = useFeaturesAllowed(["feature-1", "feature-2"]); return (
Our system has determined that... {isAllowed ? "You have access to this feature!" : "You don't have access to this feature. Please upgrade your subscription!"}
); } ``` Or you can use the `useCustomerFeatures` hook to get a users access status for all features in your pricing model: ```tsx theme={null} import { useFeaturesAllowed } from "@runonatlas/react"; ... const { features } = useCustomerFeatures(); console.log(features); // { "data-explorer": { allowed: true, included: true, limit: 10, currentUsage: 5 }, "ai-assistant": { allowed: false, included: false } } ``` #### Using components You can use the `FeatureProtect` component to check if a user has access to a feature. ```tsx theme={null} import { FeatureProtect } from "@runonatlas/react"; export default function Home() { return ( Oh, you don't have access to this cool feature. Upgrade now!} features={["feature-1", "feature-2"]} >
Cool feature
); } ``` ## Limit-based features Sometimes, just having a feature as enabled or disabled is not enough, and our pricing models require limits to be set. For example, 5 users per account, or 20GB of storage. Setting this up with Atlas is very easy. And, if at some point the limits change, you won't need to change the code again! Make sure to configure the limits also on your backend. For example, [Express](/sdk/express). ### Explanation in the UI When using both the `` component and the `useFeaturesAllowed()` hook, it will automatically check if the user has access to the features you are protecting AND if the limit has not been reached. However, it is possible that you want to show your user why the access was denied! To to do this, we give you the reasons why the access was denied. For example, with the hook `useFeaturesAllowed`: ```tsx src/routes/home/index.tsx expandable theme={null} import { useFeaturesAllowed } from "@runonatlas/react"; export default function Home() { const { isAllowed, reasons } = useFeaturesAllowed(["feature-1", "feature-2"]); if (!isAllowed) { return (
Our system has determined that... {reasons.map((reason) => { if (reason.reason === "notIncluded") { return (
You need to upgrade your plan to access {reason.slug}
); } if (reason.reason === "limitReached") { return (
You have reached the limit for {reason.slug}. You currently have {reason.currentUsage}/{reason.limit}.
); } })}
); } return
Cool feature
; } ``` In the `` component, instead of directly providing the FallbackComponent, you can actually provide a function that returns the component to be rendered when the access is denied. For example: ```tsx src/routes/home/index.tsx expandable theme={null} import { FeatureProtect } from "@runonatlas/react"; export default function Home() { return ( { return (
Our system has determined that... {reasons.map((reason) => { if (reason.reason === "notIncluded") { return (
You need to upgrade your plan to access {reason.slug}
); } if (reason.reason === "limitReached") { return (
You have reached the limit for {reason.slug}. You currently have {reason.currentUsage}/{reason.limit}.
); } })}
); }} features={["feature-1", "feature-2"]} >
Cool feature
); } ``` ## Organizations This only covers the frontend configuration. You will also need to configure your backend. For example, [Express](/developer-portal/sdk/express?#organizations). In many applications, it's common to allow multiple users to belong to the same organization and share a single subscription. For example: Alice creates an organization called **Standard Corp**, purchases a plan, and invites Bob and Carol to join. Since all three are members of the same organization, they should share access to the same plan and limits. To enable this, you should use the **organization ID** as the user identifier instead of the individual user ID. This tells Atlas that access and usage should be scoped to the organization — not the individual — ensuring that Bob and Carol both have access under Standard Corp’s plan. ```tsx src/atlas/client.tsx expandable {1,12,24,25} theme={null} import { useAuth, useClerk, useOrganization, useUser } from "@clerk/clerk-react"; import { AtlasProvider } from "@runonatlas/react"; import { useCallback } from "react"; export function AtlasClientProvider({ children, }: { children: React.ReactNode; }) { const { getToken } = useAuth(); const { user } = useUser(); const { organization } = useOrganization(); const { openSignIn } = useClerk(); const loginCallback = useCallback(() => { openSignIn(); }, [openSignIn]); return ( {children} ); } ``` # Stripe Integration Source: https://docs.runonatlas.com/developer-portal/stripe-integration Integrate Atlas with Stripe to receive payments ## Stripe Integration Atlas integrates with Stripe to help you manage customer purchases and payments. To connect your Stripe account to Atlas, you’ll need to provide an API key—either a secret key or a restricted key. We **strongly** recommend using a restricted key whenever possible. This approach aligns with our commitment to the principle of least privilege—a best practice in security that ensures systems only have access to the resources they absolutely need. Stripe API keys can be found in your [Stripe API Dashboard](https://dashboard.stripe.com/apikeys). Atlas only accepts test keys in `test` mode and production keys in `prod` mode. ### API Key Options 1. Restricted Key (Recommended) * Access: Only to specific resources you select. * Pros: More secure. Limits Atlas access to only the necessary parts of your Stripe account. * Cons: Requires a few more steps to configure. You may need to update permissions in the future as Atlas adds new features. 2. Secret Key (Full Access) * Access: Full access to your Stripe account. * Pros: Easiest to set up - no configuration required, and no updates required if Atlas needs access to a new resource type. * Cons: Gives Atlas access to all your Stripe resources, including ones we don’t use. Not ideal from a security standpoint. ### What Permissions Does Atlas Need? When using a restricted key, Atlas currently needs write access to the following Stripe resources: * Customers * Checkout Sessions * Coupons * Invoices * Webhook Endpoints ### How to Connect with a Restricted API Key in Stripe? 1. Go to your [Stripe API Keys](https://dashboard.stripe.com/apikeys) page. 2. Click **Create restricted key**. 3. Select **Providing this key to another website**. 4. Name the key (e.g., Atlas Integration). 5. Set the **URL** to [https://app.runonatlas.com/](https://app.runonatlas.com/) 6. Check the box for **Customize permissions for this key**. 7. In the permissions section, set the following to **Write** (All other permissions should be set to **None**): * Customers * Checkout Sessions * Coupons * Invoices * Webhook Endpoints 8. Save the key and copy it. 9. Navigate back to the [Atlas integrations page ](https://app.runonatlas.com/integrations). 10. Click the button for connecting to Stripe, paste your key into the modal that opens, and click **Activate**. Atlas will verify that the provided key has the correct permissions and save it securely for use when charging customers. Be sure to store your key securely. Treat it like a password. # Edit and Publish a Plan Source: https://docs.runonatlas.com/edit-and-publish-a-plan Here’s how to customize your pricing model and publish it with Atlas.