# Welcome from Kieran

Welcome friend,

Even after 30+ builds, making a SaaS in Bubble still takes me a long time.

So I created Bullet Launch to speed up my process, and yours too.

I've spent months thinking about and researching the best practices to make it flexible, scalable, and robust.

Bullet Launch isn't just a template, it's a **building system** aimed at creating high quality apps.

If you use it correctly I honestly believe you will be able to build full products in hours.

However, that **doesn't mean you don't need to put in the work**.

As much as I tried to simplify, at the end of the day you're still building a software product.

The best products require complexity behind-the-scenes to create an illusion of simplicity for the end user.

Bullet Launch has all the common functionality you find in software products: user authentication, onboarding, billing, team management, dashboards, and so on.

It also has a process for creating UIs quickly and with minimal pain.

**But the main functionality of your product, the part that makes it a unique piece of software people will pay you to use, still needs to be built by you.**

So I recommend you **watch all the videos** to properly understand how the template works.&#x20;

You should also **join the Bullet Launch** [**Slack workspace**](https://join.slack.com/t/slack-vhj7817/shared_invite/zt-2e5pzc48x-MOnZRD2E5Chi6ge0z_Vp4g) where you can ask questions, provide feedback, and chat to other builders.

You can also [book time with me ](https://nocodelife.lemonsqueezy.com/checkout/buy/667ac248-82c0-433c-8513-7d96390b05fc)to discuss your project or get help on anything.

Happy building!

Kieran


# READ ME FIRST

This page is for important things you need to know before you start using the template. I will keep adding to this as I discover new things!

## 1. If your app is on the free plan, some things will not work

{% hint style="info" %}
I would love to have made the app fully functional on the free plan but this would have meant building things in a sub-optimal way and ignoring best practices. In the end I decided to build as if it's on a paid plan because anyone who actually wants to launch their SaaS will have to be on a paid plan anyway.\
\
I recommend you use the 2 week free trial Bubble offers to properly try out the template and all its functionality.
{% endhint %}

#### Things that won't work properly

* Some of the **styling** may look slightly off because there is a small amount of custom CSS used in the app, but on the free plan custom header code does not get applied.
* Anything using a backend workflow will not work on the free plan:
  * Emails
  * Receiving subscription updates via webhook from Stripe or Lemon Squeezy
  * Activity logs (as this runs off a database trigger)
  * Adding a user to an entity if they joined by invite link
  * AI generation

## 2. 🚨 Disabling the app's demo features

The Bubble template marketplace requires a preview version of the app.

To make this possible I have a demo dashboard which allows people to try out the interface without needing to be logged in.

I recommend you follow these 4 steps every time you start with a new version of the template (should take about 2 minutes total):

**a) Re-enable the login/signup button and workflows**

The login button has been disabled to prevent people creating random accounts.

In the dropdown list of pages go to the reusable element called **⚙️Ftn: Group - Signup/Login**.

Find the **Button Log In** and uncheck the "This element isn't clickable" box to re-enable it.

<figure><img src="/files/ImAMCHjQh7Rk7eONafVN" alt=""><figcaption><p>The main login/signup button on the Auth page has been disabled to stop people creating random accounts. You can just uncheck the "This element isn't clickable" box to re-enable it.</p></figcaption></figure>

Then switch to the workflows tab (or click Edit workflows) and re-enable both the log in and sign up workflows - i.e. uncheck the Disable workflow box for both the blue and orange workflows shown below.

<figure><img src="/files/lDKmAK1yIXQObtzDturu" alt=""><figcaption></figcaption></figure>

**b) Repurpose the demo login buttons/workflow on the landing page**

Delete or repurpose the **Demo login** button on the landing page (there is also one near the bottom).

<figure><img src="/files/4qdOBq1x0X64ig7Vr9JE" alt=""><figcaption><p>Delete or repurpose this button</p></figcaption></figure>

**c) Delete the Example dashboard page**

The example dashboard is a duplicate of the main dashboard, so feel free to delete it unless you want to refer to it for UI ideas.

<figure><img src="/files/EXvHkuhLtgZSAGMXbocp" alt=""><figcaption></figcaption></figure>

**d) Optional: Delete the Example data items and data type**

**My advice is to leave this step until your app is ready to be put live as by that point you will have a good understanding of how all the UIs work and how to solve issues in the issue checker.**\
\
The database has data type called **Example data** which contains AI generated content used to display in UI elements as examples.

To enable demo users to view this data it is set to public with no privacy rules protecting it.

You may wish to delete the data in this table by going to the App data tab in the database view, selecting all and clicking **Delete**.

You can also choose to delete the entire data type. **Please be aware, this will create some issues in your app's issue checker** as certain parts of the app will be trying to reference the non-existent data type.

<figure><img src="/files/wuU4jEeqWoDRkd9aJQrH" alt=""><figcaption><p>How to delete example data items</p></figcaption></figure>

## 3. App settings have been moved to an option set

In some of the tutorial videos I mention the App Settings data type which is used to store things like the name of the app, logo image, choice of email platform etc.

I decided to move this to an option set to reduce workload unit usage.&#x20;

<figure><img src="/files/p43sHEaw4p7i3S3N0oP0" alt=""><figcaption></figcaption></figure>

I will update the videos to reflect this in due course.

## 4. Please join the Slack workspace

Please join the [Bullet Launch Slack](https://join.slack.com/t/bulletlaunch/shared_invite/zt-2e5pzc48x-MOnZRD2E5Chi6ge0z_Vp4g) to make it easy to report any bugs, provide feedback, and meet other builders!


# Changelog

Reminder: Changes here will affect only new apps created with the template. If you have already started your app using the template, you will not receive these updates in your existing apps.

{% hint style="info" %}
If you have already started your app using the template, you will not automatically receive these updates into your existing app. You will need to manually bring them over into your existing apps if you deem them necessary. I have tried to provide enough explanation below to make this simple but please let me know if you need help.
{% endhint %}

### 29th September 2025

* Improvements to onboarding flow
* Improvements to signup process and entity creation

### 14th March 2025

* **Added a custom dropdown with add item functionality**\
  Details on how to set this up are here: [UI components](/template-contents/ui-components#custom-dropdown)\ <br>

  <figure><img src="/files/F8ll6qI1IiGWSTISvPEE" alt=""><figcaption></figcaption></figure>

### 5th Feb 2025

* **Added an AI Chatbot reusable**\
  \
  AI Chatbot interfaces are so common these days I figured Bullet Launch needed one! You can use this to create custom chatbots with hidden system prompts. See the docs [here](/getting-started/using-the-chatbot-ui).

  <figure><img src="/files/0QTPnFxXMBb1NnorUz6t" alt=""><figcaption><p>The chatbot works in light and dark mode</p></figcaption></figure>
* **Made improvements to the signup/login reusable**\
  \
  Added "Only when element is visible" to all workflows on the signup/login element so that if it's on the page but hidden, none of its workflows will run on page load. This makes the app more efficient.

### 9th December 2024

* **Added Stripe customer portal link to App Settings**\
  \
  There are two buttons on the billing section of the dashboard which let the user cancel their plan or update payment details. If you're using Stripe these buttons just send the user to the Stripe customer portal, but the link was hard coded in the workflow.\
  \
  I changed it so you now add your customer portal link in the App Settings option set and the buttons will direct the users based off that.<br>

  <figure><img src="/files/sJwmRPT1ucpOeCBsZwTw" alt=""><figcaption></figcaption></figure>

### 9th December 2024

* **Fixes to responsiveness and width limits & change to how demo mode works**\
  \
  **Problem:** A user pointed out that large amounts of text in some text fields were not truncated correctly in certain table UIs. Also search boxes and titles of UI elements did not have a min width which made them look bad on certain screen sizes.\
  \
  **Solution:** I added some min and max widths and heights on certain elements in all of the table and Kanban UIs to improve these things.\
  \
  **Problem:** Demo mode with a test user was overcomplicated to remove.\
  \
  **Solution**: I made the Example Dashboard work without needing to login, so now demo users are just visiting the page rather than being logged in with a test account. See the READ ME FIRST page for new instructions on how to remove the demo features.<br>

### 13th November 2024

* **Added a new Multi-Step Form** **UI**\
  \
  Please see the [video here](https://youtu.be/QAVP8JEWoWg) to understand how it works.<br>

  <figure><img src="/files/vXCI6gddH8IaRJRfDGdf" alt=""><figcaption></figcaption></figure>

### 21st October 2024

* **Bugfix - Lemon Squeezy webhook**\
  \
  **Problem** - I noticed blank subscriptions were being created in the database every time a webhook came from Lemon Squeezy.\
  \
  **Solution** - In **Step 3** of the **lsupdate** API workflow (in backend workflows) the conditional search which prevents a new subscription being created if it already exists was searching for the wrong thing (it was constraining by `first_subscription_item's ID` but should have been constraining by `first_subscription_item's subscription ID`).\
  \
  For older versions of the template you will need click into the search itself and update that constraint.<br>

  <figure><img src="/files/xgGjcetG9OzhygPx8LyX" alt=""><figcaption><p>1) Find this workflow</p></figcaption></figure>

  <figure><img src="/files/6Nan8CVDLJiIZRU7LXsl" alt=""><figcaption><p>2) Change the constraint on this search (step 3) as shown above</p></figcaption></figure>

### 14th October 2024

* **Fixed potential security issue in privacy rules**\
  **The problem:** Having a rule that says `This thing's entity is current user's entity` does not guard against a situation where the entity field is empty for both the thing and the user, in which case this privacy rule would pass and data could be leaked.\
  **The solution:** Update the rule to say `This thing's entity is current user's entity AND current user's entity is not empty`.

Please watch this [video](https://youtu.be/bx8lZ2wAymw).

{% embed url="<https://youtu.be/bx8lZ2wAymw>" %}

### 4th August 2024

* **Fixed Kanban 2D UI bug**\
  I noticed that once a column is empty, you could not drag an item onto that column, so I added a dropzone which allows you to drag into an empty column.<br>
* **Disabled activity logs and deleted the existing logs**\
  Someone mentioned that the template was coming with over 1500 activity log entries in the database, which is not what I intended! I think every time I tested features it was creating the logs.\
  \
  I have deleted them all AND disabled the activity log feature, which was run off a database trigger. Any user who wishes to use the activity logs feature can simply remove this permanently false conditional in the backend workflows.\
  \ <br>

  <figure><img src="/files/zFcT0Rf48IQL6Gwrv8vx" alt=""><figcaption></figcaption></figure>
* **Fixed a login bug**\
  A user pointed out that if you try to login with an incorrect email, the login fails but the button remains in its "loading" state. I made a small change to fix this. If you want to implement the fix in your own app, you just need to go to the **⚙️ Ftn: Group - Signup/Login** reusable element, select the main login/signup button, and add the text shown in red to the conditional tab.\
  \
  The text is: `[hi]no-icon[/hi]`<br>

  <figure><img src="/files/fyHYXcS3P1iJwttH6V6n" alt=""><figcaption></figcaption></figure>

### 3rd August 2024

* **Added two drag-and-drop Kanban board UIs**\
  \
  If you want to create a user experience similar to a Trello board, a Kanban board is what you need.<br>

  <figure><img src="/files/Ouxnod42UdjHsQS3jL2g" alt=""><figcaption><p>This is the Kanban two-dimensional UI</p></figcaption></figure>

  \
  I created two different Kanban UIs, one-dimensional (1D) and two-dimensional (2D).\
  \
  The one-dimensional version is simpler and just allows the moving of items **between** columns but not up and down within the column. The columns will always be sorted in the order you specify, for example if it's sorted by name and you drop an item with a name starting with Z, the item will automatically show at the bottom no matter where you drop it.\
  \
  The two-dimensional version is slightly more complicated because it allows the reordering of items **within** each column as well as moving **between** the columns. This means whatever data type you are displaying must have a numeric field storing the order in which items should be displayed in the list.\
  \
  For example, if your Kanban is showing tasks in different statuses, the tasks should all have an **order** field of type **number** and at the point of task creation you will have to assign a unique number to each task.\
  \
  Any questions - let me know in the Slack! Thanks.

### 4th July 2024

* **Added Replicate API call for generating music**\
  \
  [Replicate](https://replicate.com) is an excellent service that lets you run all kinds of open source AI models, including your own, via an API. I added in a simple API call to one of the open source models which generates music from a prompt.\
  \
  You will need to add your own Replicate private key in the appropriate place. There are two API calls, one to **create a prediction** and one to **get a prediction**. Creating a prediction starts the model running based on the prompt you give it. You then need to check back periodically to see if your prediction has finished running, this is when you use **get a prediction**.\
  \
  You can replace the model ID with any of the open source Replicate models, but you will also need to update the JSON body so you are sending through the right parameters e.g. if you're creating an image, it won't need a duration.<br>

  <figure><img src="/files/O6qWncw4cA7zQuZo38wB" alt=""><figcaption></figcaption></figure>

  In the example I've created, I first run a backend workflow **replicate - generate music** which runs the **Create a prediction** API call. \ <br>

  <figure><img src="/files/JmWUJeBawkwKI6t0JO0B" alt=""><figcaption></figcaption></figure>

  The workflow then schedules another backend workflow **replicate - check prediction** to run in 10 seconds time. This workflow uses the **Get a prediction** API call to check if the AI generation is complete. If not, it schedules itself to run again in 10 seconds time. This will continue until it returns a response from Replicate.\ <br>

  <figure><img src="/files/JmG8ddkBBQKZTc3SijRh" alt=""><figcaption></figcaption></figure>

### 7th June 2024

* **Fixed signup issue where the "require email confirmation" functionality was not working correctly.**\
  \
  I was made aware that switching on "require email confirmation" on specific roles in the OS - User Roles option set was not working correctly. Users who signed up were receiving the confirmation email but somehow their email address was being automatically confirmed without them doing anything. \
  \
  After investigating I discovered this was likely a timing issue with the way the initial signup workflow was running, and I eventually ended up refactoring the entire signup workflow to make it more robust.\
  \
  If you are running into this issue [please watch this video](https://www.loom.com/share/da192c7f04df44668bb85b005dfc50f2?sid=b4efe767-0f95-4c76-acee-7eddc6ed6da0) to see how to update your signup/login workflows. <br>
* **Simplified the menu option sets by consolidating the OS admin dashboard tabs into the main OS dashboard tabs option set.**\
  \
  This should make it simpler to create multiple dashboards. All the sidebar menus now run off one option set and subtabs run off a second option set. There is a new option set called OS - App Locations which is a list of the menu locations within the app, this is used to decide which tabs to show where.\
  \
  For existing apps I wouldn't recommend trying to bring this change into your app, it's not necessary.

### 3rd May 2024

* **Fixed missing plugin actions (Toast plugin) on accounts + settings reusable**\
  \
  Weirdly a few actions related to Toast (confirmation notifications plugin) had gone missing in the workflows so they were showing this...\
  ![](/files/rR5KlR6imDcZicynchyo)\
  I have deleted and replaced these actions with the correct Toast action. If you need to fix an older version, simply delete any missing actions and add in a Toast action (or copy and paste from elsewhere), similar to below.\
  ![](/files/F1S0iAOPR6b9wGE0MlYl)

### 26th April 2024

* **Fixed a bug on the email confirmation page**\
  \
  The **confirmation** page is where users are sent if their role requires email confirmation but they have not yet confirmed their email by clicking the link in the automated email.\
  \
  I realised there was an error in one of the workflows on the page which was supposed to trigger when they had confirmed their email and either send the user to onboarding or to their homepage.\
  \
  The conditional below should say **is empty** rather than **isn't empty** (this is checking if the user has completed all their onboarding steps).\
  \
  ![](/files/pE9781cX6zqAwG0wKgIi)\
  \
  I also added another conditional to the trigger for this entire workflow to say that if the current user's role doesn't require email confirmation, do the same page redirect. This is just in case a user accidentally lands on this page when they shouldn't be there.\
  \
  ![](/files/liJBIm4bQeB7hcRtOsbe)<br>

### 11th April 2024

* **Made sure you cannot invite a user without filling out the email and role fields**\
  \
  In the **UI: Section - Account + Billing,** Donnie spotted a bug that allowed you to click the invite button even if you hadn't filled in the email field and role dropdown. I added a conditional on the button to make it not clickable if either were empty.\
  \
  ![](/files/SLgGwqYhPDfDM2B6JIpS)
* **Fixed a bug which was redirecting logged out users to the email confirmation page**\
  \
  There was a workflow in the 🖼️ **Layout: Dashboard header** reusable which would redirect users to the **confirmation** page if their email address had not been confirmed (i.e. they had not clicked the link in the confirmation email). This was incorrectly redirecting logged out users to the confirmation page. There was also no workflow generally redirecting logged out users away from the dashboard.\
  \
  To fix this I updated the redirect workflow to only occur when the user is logged in, and added another workflow to redirect the user to **auth** if they are logged out.\
  \
  **For older versions of the boilerplate you will need to do the same:**\
  \
  1\) In the Layout: Dashboard Header reusable on you will need to add "and the current user is logged in" to this workflow trigger's conditional. This will prevent it redirecting to that page. \
  \
  2\) You should also add another workflow "When user is logged out" with an action "Go to page... auth", which will then correctly redirect logged out users to the auth page.<br>

  <figure><img src="/files/JgV6xOEKWbegyJXGnErT" alt="" width="375"><figcaption><p>Step 1: Update this workflow</p></figcaption></figure>

  <figure><img src="/files/lzOlNfDxo889kvuxFqiI" alt=""><figcaption><p>Step 2: Add this workflow</p></figcaption></figure>

### 2nd April 2024

* **Simplified the demo mode to make it less work to remove!**

  \
  Unfortunately to provide a Bubble template it must offer a demo preview mode. Obviously I don't want random people creating things in the database or making changes to test credentials.\
  \
  So there is now one user in the database - <test@test.com> - and a "demo login" button on the landing page which logs the tester in as <test@test.com>.\
  \
  There are a load of conditionals on various buttons which make them not clickable if the current user's email is <test@test.com>.\
  \
  You can remove these conditionals if you want **but the point is you don't need to if you don't want to**, it shouldn't affect your app (unless you have a user with the email <test@test.com>).\
  \
  See the updated READ ME FIRST for more information.

### 23rd March 2024

* **Moved the right sidebar into the table reusable elements rather than a shared one on the dashboard**\
  \
  In the **Table** reusable elements (Classic rows, Image grid, Coloured grid, Calendar) I have moved the right **sidebar** to be within the reusable element itself. This makes it much less fiddly to customise the sidebar to show the information you need for each table.\
  \
  Previously the right sidebar was hidden on the **dashboard** and triggered with a URL parameter. This added extra complexity but at the time I thought it was the only way to do a full height sidebar. \
  \
  Thanks to some custom CSS it is now within the reusable itself.


# Quick start guide

If you're an experienced Bubbler who has used Airdev's Canvas template extensively then this might be all you need to get started! For everyone else, I highly recommend you go through the rest of the documentation and watch the videos.

1. **Create a new Bubble app** and select the Bullet Launch template during the creation process. Select Bubble's 2 week free trial to enable backend workflows and other paid features to work.
2. **Remove the demo features** (see the READ ME FIRST) section for instructions.
3. **Update the app settings option set** to reflect your own app information, such as app name, support email, legal entity name, payment provider choice.<br>

   <figure><img src="/files/iykGoC2PwF8KHsd0kCyk" alt=""><figcaption><p>You can find the app settings option set in the Option Sets section of the Data tab in your app editor</p></figcaption></figure>
4. **Change the default styles, colours, and fonts** to suit your app.
5. **Set up your data types** and fields in Bubble database tab and **set up privacy rules** to protect your user's data.
6. **Customise user roles** in the User Roles option set.
7. Delete the example UIs from the **dashboard** page.
8. **For each data type** you want users to be able to view/edit/delete:
   1. Create a new reusable based on an existing UI component you wish to copy
   2. Update the repeating group data source
   3. Update the fields you want to display within each cell (and the titles row if there is one)
   4. Update the filters to whatever fields you want to be able to filter by
   5. Update the create/edit popup with inputs for each field you want the user to be able to create/edit
   6. Update the data type for the custom state "selected item" on the reusable itself
   7. Update the data type and fields in the create/edit workflow
   8. Add the menu item to the Dashboard Tabs option set
   9. Drop the new UI into the dashboard and add a condition to show it when the dashboard tab is the one you just created
   10. Optional - Create a view UI for this data type in the right sidebar reusable and update the workflow to show it when an item is clicked
9. Create any **custom UI or functionality** your app needs (this is the tricky part).
10. Customise the **onboarding** page and **tour** popup in the dashboard header.
11. Customise the **pricing plans** option set and enter the appropriate IDs from your chosen payment platform.
12. **Add your own API keys** to the API Connector plugin for any third-party services you want to use e.g. OpenAI, Postmark, Loops, Stripe (also add your API keys to the Stripe plugin), LemonSqueezy.
13. **Sign up as a user** then switch your user role to **App admin** in the database. Go to the Admin dashboard and add your legal policies (or add these direct to the database, it's up to you).
14. Update the **app favicon and SEO metadata** in the settings tab in the Bubble editor.
15. Update the **landing page** or create a landing page on another platform.
16. Deploy your app to live.
17. Test test test.
18. Get users.


# Bullet Launch walkthrough

{% embed url="<https://youtu.be/EfPS2N6chTU>" %}

There should be a video displayed above. Not showing? [Click here to watch it](https://youtu.be/EfPS2N6chTU).&#x20;

Please note: The **App Settings** data type has been moved to Option Sets in order to improve the efficiency of the app.


# Updating the landing page

{% embed url="<https://youtu.be/NZtXNXO1dMY>" %}

There should be a video displayed above. Not showing? [Click here to watch it](https://youtu.be/NZtXNXO1dMY).&#x20;


# User roles

{% embed url="<https://youtu.be/RTLSq6Qha6E>" %}

There should be a video displayed above. Not showing? [Click here to watch it](https://youtu.be/RTLSq6Qha6E).&#x20;


# Designing your database

Designing your database is a fundamental step for all Bubble apps, and as BL already has most of the design aspects done for you, this is the best place to start.

Furthermore your database design will likely guide the required UI of your app. For example, if you're making a project management tool which has **teams, projects** and **tasks**, then it's likely your dashboard will need a separate view for each of those three data types.

Database design is one of those things that some people find easy but most find hard.

Below is a video I made on database design. It's worth watching!

{% embed url="<https://youtu.be/hofWkAFqCwU?si=huEeIpcKz7bnUBWU>" %}

There should be a video displayed above. Not showing?[ Click here to watch it. ](https://youtu.be/hofWkAFqCwU)

## Asking ChatGPT to design your database

If you're stuck on your database design, ChatGPT does a half-decent job of designing a database structure for you.

Here is a prompt I compiled which gives fairly good results. It contains some information and examples to help ChatGPT with its task.

To use this prompt you also need to add a description of your app to the end of the prompt (see below).

```
You're a software engineer and your task is to design a database structure for a Bubble app including privacy rules.
You should specify all the data types and option sets required for the app, and list all the fields within each. 
The database structure should be designed to enable effective privacy rules to be put in place.

The app will be built on a template which already has the datatypes User and Entity. Each user is tied to an Entity. It is not necessary or advisable to add a list of users to the Entity data type.

Key things to know about Bubble:

To create a link between two data types, you do not need to reference an ID. You just need to reference the data type. For example, a User can have a field Entity (type: Entity).
Two-way data linking is almost always not advisable as it is more work to maintain. Usually you should have a link on the child data type to the parent item. Lists should only be used where the list is likely to be less than 50 items in length. Otherwise it is better to just have a link on the child data type. If it is a many-to-many relationship, a linking data type may be necessary.  
Do not create two-way links unless absolutely necessary.
Do not create lists of child items on a parent unless the list is guaranteed to stay below 50 items in length, and even then only when absolutely necessary.


Privacy rules:  With privacy rules, you don't prohibit access, you grant it.
Bubble features a privacy rule editor that lets you control the privacy settings on all your data types in one central place. You access this by going to the Data tab and then clicking Privacy.
Privacy rules protect your data types in the following ways: You can stop specific fields from being viewed You can stop the data type from being found with Do a search for You can stop users from viewing uploaded files You can stop users from making changes with auto-binding  Example 1:

Data type: Product
Title: Logged in
Rule: When Current user is logged in
Users who match this rule can: View all fields, Find this in searches, View attached files

The rule above says "When the current user is logged in, users who match this rule can view all fields". Since we are working on the data type Product (visible in the header of the form), we could extend that sentence to "When the current user is logged in, users who match this rule can view all fields on the product".

Example 2: Shopping cart
We have a custom data type called Shopping Cart. This type should only be found in searches and viewable by the person who created it. But wait: if no one else can see the order, how are we to deliver the product?
Someone else needs to be able to see it, but it must be restricted. We'll set up a custom field on each user called Admin (yes/no), which grants access to see anyone's cart. On the cart, we can use the already existing and automatically populated Created by field.
This setup requires that we set up three privacy rules:
1. The first gives access to search for and view fields on the cart if you are the one who created it
2. The second gives access to search for and view fields on the cart if your admin field is set to yes
3. The third is the automatically generated Everyone else rule: this states that everyone else should have no access 
Data type: Shopping cart

Title: Own cart
Rule: This cart’s creator is current user
Users who match this rule can: View all fields, Find this in searches, View attached files

Title: Admin user
Rule: Current user’s admin is “yes”
Users who match this rule can: View all fields, Find this in searches, View attached files

Title: Everyone else (default permissions)
Users who match this rule cannot: View any fields, Find this in searches, View attached files

Example 2: Sharing tasks
In this example, we'll look into how properties on the data type to which the privacy rules are applied can further grant access under certain circumstances.
Let's imagine we have a task management app, where access to tasks follow two simple rules:
1.The creator of the task has full access to their tasks 2.The creator can invite other users into a specific task, granting them access  
In that case, we need some way to determine whether a user has been invited to a specific task. Keep in mind, we want to add that permission only to certain tasks, not all of them.
We can solve this by adding a new custom field on the Task data type that contains a list of all Users who have been invited to it. Whenever we want to give someone access to the Task, we add their name to the list using a workflow.

Since the Invited users field is set to be a list, Bubble lets us use the add function that will add that single user to the list of Invited Users (if we wanted to add a list of users we would use the add list function).
Now, to set up our privacy rule, we will again need three rules:
1.The first states that if the current user is the Creator of the task, you should have access 2.The second states that if the current user is in the list of invited users, you should have access 3.The third (everyone else) states that no one else should have access 
In the privacy rule editor it will look like this:

Data type: Task

Title: Creator
Rule: This task’s creator is current user
Users who match this rule can: View all fields, Find this in searches, View attached files

Title: Invited
Rule: This task’s invited users contains Current User
Users who match this rule can: View all fields, Find this in searches, View attached files

Title: Everyone else (default permissions)
Users who match this rule cannot: View any fields, Find this in searches, View attached files

Again we can see that the two rules grant access under special circumstances, while the bottom does not grant the permission to everyone else.
```

Below this prompt you should add a description of your app.

Below is a basic example description. Try to be as detailed as possible in your own description. Describe all the different types of users, and all the different functionalities of your app.

```
My app is a multi-tenanted SaaS CRM for veterinarians. 
Vets can set it up for their own practice and invite other staff to join their practice.
Multiple users within the practice can log in to that vet's account.
They can add patients (pets) to the system.
They can add customers (pet owners) and record their personal details.
They can record treatments
They can record surgeries
They can record insurance plans.
They can create invoices and bill customers.
```

Once you get your output from ChatGPT you'll need to set up the database and option sets in your Bubble app.

{% hint style="info" %}
Don't stress too much about nailing your database structure from the outset, Bubble makes it quite easy to make changes later on!
{% endhint %}


# Privacy rules (don't skip this)

{% embed url="<https://youtu.be/-fqZ7c35UNw>" %}

There should be a video displayed above. Not showing? [Click here to watch it. ](https://youtu.be/-fqZ7c35UNw)\
\
**IMPORTANT UPDATE OCT 2024:** Please watch the video below after watching the one above. Not showing? Click [here](https://youtu.be/bx8lZ2wAymw) to watch it.

{% embed url="<https://youtu.be/bx8lZ2wAymw>" %}


# The onboarding page

{% embed url="<https://youtu.be/-IoW-PECrh4>" %}

There should be a video displayed above. Not showing? [Click here to watch it](https://youtu.be/-IoW-PECrh4).&#x20;


# How the dashboard nav menu works

{% hint style="info" %}
In the 10th June 2024 release some of option sets relating to menu tabs were removed in order to simplify the setup. Now all sidebar menu tabs come from the OS - 🍔 All Menu Tabs option set and use the new option set OS - App location to determine where they are shown within the app. Likewise, all subtabs are now in a single OS - 🍔 All Menu Subtabs option set.
{% endhint %}

{% embed url="<https://youtu.be/Kkaqh55jEao>" %}

There should be a video displayed above. Not showing? [Click here to watch it](https://youtu.be/Kkaqh55jEao).&#x20;


# How the main header menu works

{% embed url="<https://youtu.be/rjsbbVYgLhc>" %}

There should be a video displayed above. Not showing? [Click here to watch it](https://youtu.be/rjsbbVYgLhc).&#x20;


# Create a new data type with UI

Updated video for anyone using the template on a new app after 10th June 2024. This version has simplifications to the process of creating a new data type UI and to the right sidebar process.

{% embed url="<https://youtu.be/r70lQGeiqK4>" %}

If the video isn't showing click [here](https://youtu.be/r70lQGeiqK4) to watch it.

### Older versions

If you have an older version of the template you may need to watch the version below.

{% embed url="<https://youtu.be/IEECgCKSDhE>" %}

There should be a video displayed above. Not showing? [Click here to watch it](https://youtu.be/IEECgCKSDhE).&#x20;


# Using the Kanban UIs

{% embed url="<https://youtu.be/S2aHS9WfEa0>" %}

If the video above isn't visible click here: <https://youtu.be/S2aHS9WfEa0>


# Using the Chatbot UI

The Chatbot UI lets you create custom chatbots with a similar styling to Chat GPT. You can feed them custom system prompts which are hidden from users.

{% embed url="<https://youtu.be/pSNbrxsnd9c>" %}

If the video isn't showing [click here to view it.](https://youtu.be/pSNbrxsnd9c)


# Using the Multi-Step Form

This is a multi-step form UI which can be used for any creation process that is better with multiple steps rather than one long form.

{% embed url="<https://youtu.be/QAVP8JEWoWg>" %}

If you can't see the video above you can [watch it here](https://youtu.be/QAVP8JEWoWg).


# Setting up Postmark (emails)

{% embed url="<https://youtu.be/bRPE--TsRl8>" %}

There should be a video displayed above. Not showing? [Click here to watch it](https://youtu.be/bRPE--TsRl8).&#x20;


# Setting up Loops.so (emails)

{% embed url="<https://youtu.be/BLCbjg7V1pg>" %}

There should be a video displayed above. Not showing? [Click here to watch it](https://youtu.be/BLCbjg7V1pg).&#x20;


# Setting up Stripe (payments)

{% embed url="<https://youtu.be/-a39XiaPzDk>" %}

There should be a video displayed above. Not showing? [Click here to watch it](https://youtu.be/-a39XiaPzDk).&#x20;


# Setting up Lemon Squeezy (payments)

{% embed url="<https://youtu.be/pOcWXmHahQg>" %}

There should be a video displayed above. Not showing? [Click here to watch it](https://youtu.be/pOcWXmHahQg).&#x20;


# Example build: Content Magic

For this example build I'm going to make a simple SaaS for content teams to generate text content with AI and manage their content.


# 1. Create a new app

{% embed url="<https://youtu.be/yjKmTf-AApM>" %}

There should be a video displayed above. Not showing? [Click here to watch it](https://youtu.be/yjKmTf-AApM).&#x20;


# 2. Set up the database structure

{% embed url="<https://youtu.be/IAS9nAs8ndQ>" %}

There should be a video displayed above. Not showing? [Click here to watch it](https://youtu.be/IAS9nAs8ndQ).&#x20;


# 3. Create a UI for prompts

{% embed url="<https://youtu.be/n_4LnD3ZogY>" %}

There should be a video displayed above. Not showing? [Click here to watch it](https://youtu.be/n_4LnD3ZogY).&#x20;


# 4. Editing the dashboard and adding the first UI

{% embed url="<https://youtu.be/MitknNnjEe0>" %}

There should be a video displayed above. Not showing? [Click here to watch it](https://youtu.be/MitknNnjEe0).&#x20;


# 5. Create a UI for inspirations

{% embed url="<https://youtu.be/7HIA4JM-mLY>" %}

There should be a video displayed above. Not showing? [Click here to watch it](https://youtu.be/7HIA4JM-mLY).&#x20;


# 6. Create a UI for content creation

{% embed url="<https://youtu.be/VT-PP42yWIA>" %}

There should be a video displayed above. Not showing? [Click here to watch it](https://youtu.be/VT-PP42yWIA).&#x20;


# 7. Create a calendar UI for content pipeline

{% embed url="<https://youtu.be/wWejnZzoFLM>" %}

There should be a video displayed above. Not showing? [Click here to watch it](https://youtu.be/wWejnZzoFLM).&#x20;


# Live build of a CRM tool

This is a 2 hour unedited video of me building an internal CRM tool for Ken Savage. Hopefully it will be interesting to Bullet Launch builders as we start from scratch and talk through each aspect.

{% embed url="<https://youtu.be/W8b21KyF3FI?si=pSzZJEFS1_QfsXT4>" %}

Covered in the video:

* Creating the database structure
* Customizing the dashboard layout for new data types (client and task management)
* Adapting the pre-built user management system for client data
* Modifying existing tables and forms to fit specific data requirements
* Implementing custom filters for client and task views
* Creating a Kanban board view from the existing UI components
* Adapting the calendar view for task management
* Setting up a public client view using existing components
* Customizing privacy settings for different user types
* Using dynamic colors in the UI based on task statuses
* Implementing a copy-to-clipboard feature for sharing client-specific URLs
* Tweaking the responsive design for mobile optimization
* Utilizing custom states for enhanced user interactions (e.g., expanding task descriptions)
* Demonstrating how to detach and modify reusable elements for specific pages

If you can't see the video above go here: <https://youtu.be/W8b21KyF3FI?si=pSzZJEFS1_QfsXT4>


# Data types

Bullet Launch comes with a number of data types and option sets already added.

## Activity

This is a data type for logging selected user activities across the app and displaying them to the users in their activity feed. The activity feed is in the **UI Dashboard Header** by clicking on the bell icon.

Activities are created by database trigger events in the backend workflows section. This enables them to react to changes in specified data without needing workflow triggers on individual pages and elements.

The activities you choose to log are up to you. By default users will be shown all activities logged across their **entity**. You can customise this further if needed.&#x20;

* **Action** (text) - plain text explaining what occurred.
* **By user** (User) - the user who performed the activity.
* **Entity** (Entity) - the entity of the user who perfomed the activity.

## Comment

This is an optional data type for allowing users to comment or add updates on specific items, such as a project, a listing, or a post.

The comments can be linked to any data type and displayed using the **UI: Comments Section** reusable.

* **Content** (text) - the content of the comment.
* **Entity** (Entity) - the entity of the user who commented (for privacy rules).
* **Example data** (Example data) - the **example data** the comment is linked to. You will need to create a new field for the data type you wish to link your comments to.
* **Parent comment** (Comment) - in case you want to allow nested comments/replies.
* **Read by** (list of Users) - in case you want to highlight comments/updates the current user hasn't read yet.

## User

This is the default Bubble data type which holds all of the users of your app. You can't delete it, but you can add new fields to it.

In general try to keep the **user** data type as lightweight as possible because it's loaded whenever a user loads a page in the app.

{% hint style="info" %}
Storing the user's **avatar** on the **user** may sound like it's not keeping things lightweight, but it's really only storing the image URL, so the data size is small.
{% endhint %}

* **Access revoked** (yes/no) - if set to **yes** the user will get logged out every time they try to log in. You can give entity owners control of this flag if you want to.
* **Avatar** (image) - user profile picture, uploaded during onboarding or from their account settings.
* **Entity** (Entity) - this links the user to a **entity**. This field should not be empty for any user except an App Admin.
* **Dark mode** (yes/no) - specifies whether to show the dark mode version of the app.
* **First** (text) **- user**'s first name, collected during onboarding or from their account settings.
* **Last** (text) **- user's** last name, collected during onboarding or from their account settings.
* **Onboarding step** (number) - use this field to determine which stage a user has reached in your onboarding flow. Increment it by 1 at each stage, and use conditionals only show onboarding if this number is below a certain value.
* **Pending payment** (yes/no) - this field is used when a user is attempting to subscribe to a plan but is logged out, it ensures that once they are logged in the payment process will be triggered.
* **Pending payment for price plan** (OS - Pricing - Plans) -  this field is used when a user is attempting to subscribe to a plan but is logged out, it ensures that once they are logged in the payment process will be triggered. It stores the plan the user clicked on.
* **Role** (OS - User Roles) - this is the role the user holds within the app. By default there are three roles but you can add as many as you want. This is used for privacy rules and for hiding/showing features so do not delete it.
* **x Stripe customer ID** - this saves the user's Stripe customer ID into the database. It's useful to have as a backup in case the Stripe plugin is deleted, which means all the built-in Stripe fields are lost.

## 🏢 Entity

All **users** are linked to a **entity** in BL. You can change the name of this data type to company, group, organisation, team, or any other word to describe a collection of users.

The purpose of this data type is:

* A group of users can share the benefits of a single subscription e.g. a team leader can upgrade to a paid plan on behalf of everyone in the team.
* It separates the data between entities using privacy rules to ensure no member of one entity will see data from another entity.
* Conversely it groups the data of one entity so you *can* give all (or some) users from a entity access to all of that entity's data (if you wish).

In other words, rather than having a single system with individual users who just have their own data, it enables you to make a "multi-tenanted" SaaS product where multiple groups of users can share data securely within gated perimeters controlled by privacy rules.

* **Current subscription** (subscription) **-** this links to the latest **subscription** thing created for this entity. It enables checks of a entity's current subscription status and current plan and is used throughout the app for paywall purposes.
* **Logo** (image) - the entity's logo and can be updated in the dashboard settings.
* **Name** (text) - this is the name of the entity and can be updated in the dashboard settings.

## 💳 Subscription

This is for saving subscription billing information from Stripe or Lemon Squeezy. Each one is linked to a **entity** and owned by a **user** who is the person who set up the subscription.

To see details about a **entity's** current subscription you can look at the&#x20;

```
Entity's Current Subscription...
```

* **Cancel at period end?** (yes/no) - this is a Stripe flag which is "yes" if the subscription has been cancelled but will remain active until the end of the current subscription period. In this situation the status of the subscription will remain "active" until  It is used to acknowledge that the subscription has been cancelled but&#x20;
* **Entity** (entity) **-** this is a helper field linking the subscription to the **entity** that owns it for the purposes of privacy rules.
* **Current period end date** (date) - the date of the next payment.
* **Current period start date** (date) **-** the date of the last payment.
* **Frequency** (text) - this is a Stripe field indicating the frequency of the subscription.
* **OS Plan** (OS - Pricing - Plans) - this is the plan the entity is subscribed to.
* **Owner** (User) **-** the user who created the subscription.
* **Status** (text) - this is a Stripe field indicating the status of the subscription, which can be "active" or "canceled".
* **Stripe Plan ID** (text) - this is a Stripe field containing the Stripe Price ID of the plan.
* **Type** (OS - Subscription types) - this indicates whether the subscription is with Stripe or Lemon Squeezy.

## 🔐 App settings&#x20;

This saves app-wide information to make it easier for app admin (you) to update and prevent the need for hard-coding information in the Bubble editor. It's up to you whether you keep this or prefer to hard code the information. The latter will reduce workload unit usage.

All of these fields can be altered from the **Admin dashboard** in the settings tab, or directly in the database itself.

* **Legal entity name** (text) - the name of your company/legal entity. This is used in your privacy policy, terms and conditions, and in the copyright notice in the footer.
* **Postmark enabled** (yes/no) - if you have integrated Postmark for emails, switching this to "yes" will mean all emails are sent from Postmark instead of the default Bubble emails.
* **Site name** (text) - this is the name of your app and is used to generate your terms & conditions and privacy policy, as well as used in place of a logo.
* **Support email** (text) - this is used as the sender address for all outbound email, as well as in your  terms & conditions and privacy policy.

## 🔐 FAQ

This stores FAQs for your site which are displayed in the Support section of the dashboard.

* **Answer** (text) - Answer to the question
* **Question** (text) - The question

## 🔐 Terms & Privacy

This stores the terms & conditions and privacy policy for your app.

* **App description** (text) - This is a description of your app and the data it collects to enable OpenAI to generate custom policy wording for you.
* **Privacy policy** (text) - Your privacy policy, optionally generated by OpenAI. This is visible on the **legal** page of your app.
* **Terms and conditions** (text) - Your terms and conditions, optionally generated by OpenAI. This is visible on the **legal** page of your app.

## 🔐💬 CHAT -  Conversation

BL comes with user-to-user chat functionality. This data type stores a conversation between two or more users. It has messages linked to it.

* **Users** (Users - list) - a list of users who are involved in this conversation.
* **Notifications scheduled for** (Users - list) - when a new message is sent within a conversation, a notification email is scheduled to be sent in the future to notify the recipient that there are new messages. To avoid scheduling a notification every time a new message is sent, this remembers who is awaiting a notification and prevents duplicate notifications being scheduled.
* **Last message** (date) - stores the date of the last message on the conversation, so it can be displayed in the list of conversations without needing to do nested searches (this is for performance reasons).

## 🔐💬 CHAT -  Message

BL comes with user-to-user chat functionality. This data type stores a conversation between two or more users. It has messages linked to it.

* **Allowed Users** (Users - list) - a list of users who are allowed to view this message, for privacy rules use.
* **Body** (text) - this is the body of the message.
* **Conversation** (conversation) - the conversation this message is part of.
* **JSON** (text) - this stores the body of the message in JSON format and is used if the message is being sent to or from OpenAI, for example in case you want to have an AI Bot in the conversation.
* **Read by** (Users - list) - a list of users who have read this message. A user is added to this list if they sent the message or if they load the messages list. This prevents notifications about this message being sent to the user.
* **Sender** (User) - the message sender. This is used to determine whether the message was sent by a user or created by an API call to OpenAI.

## 🪄AI Generation

This is an optional data type which you can choose to keep or delete. It enables the **Create content** UI reusable element to function. It acts as a storage container for any AI generations done from this interface. It consists of a number of input fields of different types which have corresponding output fields of different types (e.g. text, file, image), together with input prompts.

You are free to rename, alter, or remove this data type if you don't want it.

## 🪄GPT Prompt

This stores the GPT prompts for generating **Terms and conditions** and **Privacy policy**. These could instead be hard-coded into the workflow actions but having them in the database makes them easier to view and edit.

You can add other GPT prompts here for other parts of your app.

You are free to rename, alter, or remove this data type if you don't want it.&#x20;

## 🤷 Example data

This is a generic data type holding example data for the purposes of showcasing the functionality of the app in the **example dashboard**.&#x20;

You are free to rename, alter, or remove this data type if you don't want it.&#x20;

## 🏢 Entity


# Option sets

## OS - App locations

A list of locations in the app where menus are shown. This is used with the OS - 🍔 All Menu Tabs to determine which tabs to show in which location throughout the app.

For example, if you want to add a new dashboard you would need to add as a location in this OS, and then in the menu repeating group you can update the filter to only show menu tabs relevant to that location in the app.&#x20;

## OS - App settings ⚙️&#x20;

This saves app-wide information to make it easier for app admin (you) to update and prevent the need for hard-coding information in the Bubble editor. It's up to you whether you keep this or prefer to hard code the information.

* **App description** (text) - this is used in page meta data for SEO.
* **App name** (text) - this is used in the logo reusable element and in emails and page meta data.
* **Default dashboard tab** (OS - Dashboard Tabs) - this is the default tab you want the dashboard to load on. Usually this is the Home tab.
* **Email platform** (OS - Email platform) - this is the email platform you have chosen to integrate. This is used in backend workflows to determine which API call to use when sending emails. If left blank, it will use the standard Bubble send email action.
* **Legal entity name** (text) - the name of your company/legal entity. This is used in your privacy policy, terms and conditions, and in the copyright notice in the footer.
* **Logo image** (image) - this is used in the logo reusable element and will replace any text/icons there.
* **Payment platform** (OS - Payment providers) - this is the payment platform you have chosen to integrate. It is used in the Pricing table reusable UI to determine which workflow to run when a user tries to subscribe.
* **Support email** (text) - this is used as the sender address for all outbound email, as well as in your  terms & conditions and privacy policy.
* **Support sender name** (text) - this is used as the sender name for any support emails. You can have it as a person's name or something generic like "Support".

## OS - Content Options

These are placeholder content options for the **create content** UI reusable element. You can rename, edit, or delete this option set.

## OS - Payment Providers

These are the payment providers integrated with BL. The choice of payment provider is set in the App Settings datatype and used in conditionals where payments are involved.

## OS - Multi Part Form

These are the names for the different stages of a multi-part form. They are used in the example onboarding page. You can rename, edit, or delete this option set.

## OS - Rainbow Colors

These are a set of HEX colour codes which are used in the **Coloured Grid** UI reusable element.&#x20;

## OS - Sort By

These are two example sorting options which are used in the **Marketplace** page to sort the repeating group.

## OS - Onboarding Steps

These are the names for the different stages of a multi-part onboarding form. They are used in the example onboarding page. You can rename, edit, or delete this option set.

## OS - Subscription Statuses

Stripe and Lemon Squeezy have some differences in the subscription statuses they send through from their APIs, including in the spelling of words (canceled vs cancelled). This OS converts the plain text status from either provider into an equivalent option, so your app can rely on the options rather than the complexities of the different plain text statuses.

* Active - subscription is paid and active.
* Active until period end - subscription has been cancelled by the user but remains active until the end of this period i.e. the date of their next payment. The user should still have access to premium features.
* Cancelled - subscription is completely cancelled. The user should no longer have access to premium features.
* Paused - the user has paused their subscription. It's up to you whether you allow them to access premium features.
* Past due - the user's subscription payment failed. Both payment providers will continue retrying for payment. It's up to you how you handle this within your app.
* Unpaid - the user's subscription payment failed repeatedly and payment providers have given up trying again. It's up to you how you handle this within your app.

## OS - Upgrade Reasons

These are call-to-action reasons to upgrade which are shown in the CTA box on the dashboard. You can rename, edit, or delete this option set.

## OS - Welcome Popup

These are the different swipeable slides a new user will see in their welcome/onboarding popup. You can rename, edit, or delete this option set.

## OS - ✨ AI Generator Types

These are the different types of AI generations built in to BL and used in the **Create Content** UI reusable. They are written in lowercase because this looks better in the URL, but they are capitalised in the UI itself. You can rename, edit, or delete this option set.

## OS - ✨ GPT Models

This is a list of different GPT models which can be selected when adding new OpenAI calls in your backend workflows. You could also make it possible for a user to choose from these if you wanted. You can rename, edit, or delete this option set.

## OS - 🍔 Menu - Admin Dash Subtabs (deprecated)

{% hint style="info" %}
In the 10th June 2024 release this option set was removed. Instead all sidebar menu tabs come from the OS - 🍔 All Menu Tabs option set and use the new option set OS - App location to determine where they are shown within the app.
{% endhint %}

These are all the subtabs for the Admin Dash page.

## OS - 🍔 Menu - Admin Dash Tabs (deprecated)

{% hint style="info" %}
In the 10th June 2024 release this option set was removed. Instead all sidebar menu tabs come from the OS - 🍔 All Menu Tabs option set and use the new option set OS - App location to determine where they are shown within the app.
{% endhint %}

These are the names of the sidebar menu options on the **Admin dashboard page**. They are written in lowercase because this looks better in the URL, but they are capitalised in the UI itself. You can rename or edit this option set. **90**

## OS - 🍔 All Menu Subtabs

These are the names of all the subtab options throughout the app. They are referenced from the **All subtabs** attribute in the OS - All Menu Tabs option set. They are written in lowercase because this looks better in the URL, but they are capitalised in the UI itself. You can rename or edit this option set.&#x20;

* Only visible to (list of OS - User Roles) - this is a list of user roles which are allowed to see this subtab. If this list is empty, the subtab will be visible to all users.
* Display - this is the text displayed in the menu.

## OS - 🍔 All Menu Tabs

These are the names of the sidebar menu options throughout the app. They are written in lowercase because this looks better in the URL, but they are capitalised in the UI itself. You can rename or edit this option set.&#x20;

* **All subtabs**: A list of all the subtabs associated with a particular menu tab, derived from "OS - 🍔 All Menu Subtabs".
* **App location**: Specifies the location within the app where the menu tab is displayed, based on the "OS - App Locations" option set.
* **Bottom menu? (deprecated)**: A yes/no attribute indicating whether the menu tab should appear in the bottom menu of the app. Since 10th of June 2024 this is now determined using the **App location** attribute.
* **Default subtab**: The default subtab that is selected when the menu tab is accessed, linked to "OS - 🍔 All Menu Subtabs".
* **Default tab?**: A yes/no attribute indicating whether this tab is the default tab when the app is first loaded.
* **Description**: A text field providing a description of the menu tab. This is optional.
* **Hide header?**: A yes/no attribute indicating whether the header should be hidden when this menu tab is active.
* **Icon**: This determines the icon that appears next to the menu item name in the sidebar. For a full list see [Hero Icons](https://heroicons.com/).
* **Only visible to**: A list specifying which user roles can see this menu tab, based on the "OS - 👫 User Roles" option set. If the list is empty, the menu item will be visible to all users.
* **Display**: This is the text displayed in the menu. It is lowercase because this looks better when used in URL parameters.

## OS - 🍔 Menu - Item Page Subtabs (deprecated)

{% hint style="info" %}
In the 10th June 2024 release this option set was removed. Instead all menu subtabs come from the OS - 🍔 All Menu Subtabs option set.
{% endhint %}

These are the names of the tabs on the **p** page. They are example tabs or sections you may wish to have. You can rename or edit this option set.&#x20;

## OS - 🍔 Header Menu

These are the names of the top menu options in the **Header - Marketing Site** reusable. They each contain the information to tell the app where to redirect the user if they're clicked. You can rename or edit this option set.&#x20;

* Accounts subtab - If the menu item is linking to somewhere within the **Accounts** section in the dashboard (e.g. billing, profile), this will be added to the URL to direct the user correctly.
* Dashboard tab - If the menu item is linking to a section of the dashboard e.g. **Accounts**, this will be added to the URL to direct the user correctly.
* Icon - this determines the icon that appears next to the menu item name in mobile view or dropdown menu only. For a full list see [Hero Icons](https://heroicons.com/).
* Must be logged in? - this determines whether this item should be visible only to logged in users e.g. Dashboard should only be visible to logged in users.
* Page name - this is the name of the page to direct the user to.

## OS - 👯 User Roles

These are the user roles within the app. The **User** data type has a User Role field which you can set when a user signs up for the app, or when they're invited to join a team. This is used to determine what functionality they are allowed to access. For example, you can prevent certain user roles from accessing pages, from seeing menu items, or from clicking buttons. You can (and must) also refer to user roles in your privacy rules to protect data from the wrong users. You can rename or edit this option set.

The three default user roles are:

* App admin - this is you or someone from your team. Users with this role can see most data, and access the **admin** dashboard.
* Entity admin - this role is for users who have created an entity within the system (e.g. a company or organisation) and are in charge of billing and team management for that entity.
* Entity user - this is a team member who has been invited to join an entity by the entity admin. They are not responsible for billing or team management.

You can add as many user roles as you need to build the functionality of your app.

## OS - 💳 Pricing - Billing Frequency

These are the two billing frequency options - monthly or annual.

## OS - 💳 Pricing - Plan Features

This is a list of features your app has and is used in the **Marketing - Pricing Table** and **Popup - Upgrade** reusables. All plans will show all features, but those plans that do not have a certain feature can be listed within the **Plans without this feature** field of each option, in which case they will show an X rather than a tick next to that feature.

## OS - 💳 Pricing - Plans

This is a list of your price plans and should match the ones you've set up in Stripe or Lemon Squeezy. The BL boilerplate is set up to use test credentials when you are on version-test, and live credentials when you are on version-live.&#x20;

* Description - this is a brief description of the plan which is shown in the price plan table.
* Feature tier - this is a numeric value which gives you a simple way to block off features in a cascading way. For example, instead of needing to list all the features each plan can access, or list all the plans with access to a feature, you simple assign a feature tier number to a plan. Then on the feature you wish to restrict you put a conditional which says something like "If this user's plan's feature tier is less than 2, do not run this workflow or do not show this page". This way more expensive plans can access the features of lower plans.
* Frequency - Monthly or Annual.
* Icon - The icon to show in the pricing table.
* LS slug LIVE - this is the slug from Lemon Squeezy for this plan in LIVE mode. Watch [this video ](https://www.loom.com/share/08959f00257346a7956c1cbe6a2cd7f8?sid=b5f77741-ad33-4cae-97d8-50375aa8a82f)to see where to get it.
* LS slug TEST - this is the slug from Lemon Squeezy for this plan in TEST mode. Watch [this video ](https://www.loom.com/share/08959f00257346a7956c1cbe6a2cd7f8?sid=b5f77741-ad33-4cae-97d8-50375aa8a82f)to see where to get it.
* LS Variant ID LIVE - this is the variant ID from Lemon Squeezy for this plan in LIVE mode. Watch [this video](https://www.loom.com/share/4014c57705d64b7f9ed76f743cdcb98b?sid=14c41a5f-fad1-47bd-8939-eeb515dc363e) to see where to get it.
* LS Variant ID TEST -  this is the variant ID from Lemon Squeezy for this plan in TEST mode. Watch [this video](https://www.loom.com/share/4014c57705d64b7f9ed76f743cdcb98b?sid=14c41a5f-fad1-47bd-8939-eeb515dc363e) to see where to get it.
* Max users - if your app plans are based on limiting the number of users on a team, use this numeric value to prevent the entity admin from inviting new users once they've reached their limit. Alternatively you could rename this and use it in a different way, for example max number of AI generations per plan.
* Plan Name - This is the plan name as it's shown on the pricing plan table.
* Price USD - The price of the plan in USD.
* Stripe Price ID LIVE - This ID is found on the Stripe dashboard in LIVE mode.
* Stripe Price ID TEST - This ID is found on the Stripe dashboard in TEST mode.
* Display - This is for you to identify the plans, it is not shown within the app UI itself.


# Privacy rules

Privacy rules are one of the most important parts of any Bubble app that holds sensitive data.

## **Overview**

Privacy rules protect private and sensitive data in your app. They are conditions set on each data type to control data access and editing.

### **Key Points**

1. **Importance**: Privacy rules are crucial for app security, ensuring users' data remains safe.
2. **Function**: Privacy rules tell the server when to send or write data based on specific conditions.
3. **Example**: Allow products to be viewable only by logged-in users. This means data is only sent if the user is authenticated.
4. **Server-Side Security**: Data protection happens on the server, keeping data encrypted and secure until it's legitimately accessed.

### **Client-Side Data**

* Once data reaches a user's device, it's no longer secure.
* Developers must ensure only necessary and authorized data is sent to the device.
* Example: In an eCommerce app, all products should be publicly viewable, while shopping carts should only be viewable by their creators.

## How privacy rules work in Bullet Launch

Please watch the [video on privacy rules.](/getting-started/privacy-rules-dont-skip-this)

### A standard data type

The privacy rules for a standard data type might look like this.

**Everyone else (default permissions)** are all unchecked. This means this data is private by default.

**Visible to creator** states that whoever created this *thing* in the database is able to find it and view it. This is the default rule created by Bubble and is effectively redundant as the Entity user rule will grant the same permissions.

**Entity user** states that anyone who is in the same entity as the entity set on the *thing*'s can find it and view it.

**App admin** states that anyone whose role is App Admin can find and view all data of this type.

<figure><img src="/files/ASpNpj6SEVCkrUqFTO6d" alt=""><figcaption></figcaption></figure>

### A data type with some additional restrictions

This is an example of a data type where you want more granular controls over who can see what data. In this case the data type contains data about the **Subscription** held by the entity.

**Everyone else (default permissions)** are all unchecked. This means this data is private by default.

**Entity user** states that anyone who is in the same entity as the entity set on the *thing*'s can find it and view *selected fields only*.&#x20;

**Entity admin** states that anyone who is in the same entity as the entity set on the *thing* who also has the role **Entity admin** can find it and view all fields.

**App admin** states that anyone whose role is App Admin can find and view all data of this type.

<figure><img src="/files/ofiBEdsgCyJqTpul79K4" alt=""><figcaption></figcaption></figure>

### A more restricted data type

This is an example of a data type which is restricted to only one or two users.

In this case it is a chat message between users. These messages usually would be private and not visible to other entity users, entity admin, or app admin.

**Everyone else (default permissions)** are all unchecked. This means this data is private by default.

**Visible to participants** states that anyone who is listed on the *thing's **allowed users*** can find it and view it.

**Visible to creator** states that whoever created this *thing* in the database is able to find it and view it. This is the default rule created by Bubble and is effectively redundant as the Entity user rule will grant the same permissions.

<figure><img src="/files/6IsGUiKxl5d3XtLB0BzO" alt=""><figcaption></figcaption></figure>

### Creating your own privacy rules

You can, of course, create your own privacy rules if you need more granular control over who sees what data.

For example, you may have project teams within an entity and want project details only to be visible to people in that team rather than all the users in your entity.

In that case you will need to add *helper fields* to all the data types you wish to restrict to enable you to create effective privacy rules.

{% hint style="info" %}
**Rules that use "This Thing's X's Y" can't grant search access right now**\
\
You might see this message in the privacy rule editor. Bubble has a limitation that means you cannot rely on a linked data type to create a search privacy rule restriction.\
\
For example, you couldn't say *If this task's project's entity is current user's entity, then allow them to find it in searches*. This wouldn't work in Bubble.\
\
Instead you would need to record the entity on the task itself. Then you could say *If this task's entity is current user's entity, allow them to find it in searches*.\
\
It sometimes feels like you're adding unnecessary fields to data types, but in order to have effective privacy rules these "helper" fields are needed.
{% endhint %}


# UI components

Bullet Launch comes with a large number of UI components ready to use, including workflows, states, custom events and other functionality.

{% hint style="info" %}
UI components can be used in several ways:

* For components which **will only ever need one version for the entire app** (e.g. the signup/login popup, or the logo component), you can keep the reusable as it is and just drop it wherever you need it in the app.&#x20;
* For components which you want to use as a **starting point for a new interface but may wish to use again**, you can create a new reusable which is a clone of the original. Then use the new reusable within the app, keeping a clean untouched copy of the original.
* For **small components which will form part of a larger UI and need to be editable**, you can drop them onto a page and detached from their parent reusable so they become a normal element on the page. Then you can edit them without affecting the original reusable.
  {% endhint %}

## ✅ Admin - Charts

Charts for the admin dashboard.

<figure><img src="/files/Zl5Hf6qjOtTWlDzI3Y0V" alt=""><figcaption></figcaption></figure>

## ✅ Admin: Section - App settings

A section dedicated to app settings within the admin panel, for configuring and managing app-wide settings.

<figure><img src="/files/zuo6sloSWjqVSi4kVHOb" alt=""><figcaption></figcaption></figure>

## ✅ Admin - Mini Menu - Users

A compact administrative menu specific to user management tasks like add, edit, or delete users.

<figure><img src="/files/7yyYwjzSosANYoQockpo" alt=""><figcaption></figcaption></figure>

## ✅ Admin: Table - Users

A detailed table layout within the admin section for displaying and managing user accounts.

<figure><img src="/files/xouyLiCth6DkwbuIIQUB" alt=""><figcaption></figcaption></figure>

## ✏️ Input: Dropdown

A dropdown input field for forms, allowing selection from a list of options.

<figure><img src="/files/YcMUMjcXdksrDZ0I1Vl9" alt=""><figcaption></figcaption></figure>

## ✏️ Input: Custom dropdown with add item <a href="#custom-dropdown" id="custom-dropdown"></a>

<figure><img src="/files/F8ll6qI1IiGWSTISvPEE" alt=""><figcaption></figcaption></figure>

Setup guide here: <https://www.loom.com/share/08edb0ad4eea4abf80f176061a021b23?sid=53a3500e-03b0-44c7-94f7-007ede3ec23b>

## ✏️ Input: Image uploader

A tool for users to upload images, commonly used in profile settings or content uploads.

<figure><img src="/files/gc2BFG0DJSublusIueaV" alt=""><figcaption></figcaption></figure>

## ✏️ Input: Multiline input

A text input field for longer, multiline text entries, such as descriptions or messages.

<figure><img src="/files/b8k2SWS2vPnYwjjjl7bR" alt=""><figcaption></figcaption></figure>

## ✏️ Input: Star rating

An interactive element for users to rate items or services using a star system.

<figure><img src="/files/eWnz5xPJpHv4AFgN2k65" alt=""><figcaption></figcaption></figure>

## ✏️ Input: File uploader

Allows users to upload files, typically for document storage, profile pictures, or attachment uploads.

<figure><img src="/files/2RsSfQ2lnlDIinuxvfyL" alt=""><figcaption></figcaption></figure>

## ✏️ Input: Multidropdown

A form element that lets users select multiple options from a dropdown list, useful for fields requiring multiple choices.

<figure><img src="/files/FKk8q1BHc8z8n9gH9zZ2" alt=""><figcaption></figcaption></figure>

## ✏️ Input: Search input

A search bar that allows users to input text to query and filter through data or content in the app.

<figure><img src="/files/L9Gdf7kZwgzF0WDG0Ic1" alt=""><figcaption></figcaption></figure>

## ✏️ Input: Text input

A basic text field for user input, applicable for forms, search bars, and data entry.

<figure><img src="/files/a9hBrf69I3ZgAvbxKOJz" alt=""><figcaption></figcaption></figure>

## 🎨 Styles: Inputs

Design styles that define the appearance of various input fields across the app.

<figure><img src="/files/BHYFbVYAr8xmyqGyUzBU" alt=""><figcaption></figcaption></figure>

## 🎨 Styles: Text

A set of predefined text styles to ensure consistent typography throughout the app.

<figure><img src="/files/kjMhj6tElHKAK2qpbbt5" alt=""><figcaption></figcaption></figure>

## 🖼️ Layout: Header - Marketing Site

The header component for marketing or landing pages, including navigation and branding elements.

<figure><img src="/files/yvCPZI3AoK403bc1xQVq" alt=""><figcaption></figcaption></figure>

## 🖼️ Layout: Footer

The footer component of the app, containing links, credits, and other info typically found at the bottom of a page.

<figure><img src="/files/iL8DdMELvrR4Yi7FxRGE" alt=""><figcaption></figcaption></figure>

## 🧩 Audio player

A reusable element for playing audio files within the app.

<figure><img src="/files/ria2XpWmAXd8kZ7XUNKq" alt=""><figcaption></figcaption></figure>

## 📢 UI: Callout - Upgrade

A special alert or banner used to promote upgrades or special offers to users.

<figure><img src="/files/9ih78wQi1UWLBTnA5iuO" alt=""><figcaption></figcaption></figure>

## 🧩 UI: Comments section

A section for users to read and post comments, often used in blogs, product pages, or social feeds.

<figure><img src="/files/azwZiWWOxd36o6J5tipF" alt=""><figcaption></figcaption></figure>

## 🧩📝 UI: Form - Multi-Step Form

An interactive form component that guides users through a multi-step data entry process.

<figure><img src="/files/PlpjwgXrDHJ4NoVzaMg7" alt=""><figcaption></figcaption></figure>

## 🧩 UI: Group - Signup/Login

A group element that contains the signup and login forms.

<figure><img src="/files/axCUhpN6M4nvKonXxXJJ" alt=""><figcaption></figcaption></figure>

## 🧩❌ UI: Icon - Close Popup

A clickable icon for closing popups or modal windows within the app.

<figure><img src="/files/BlgpAIbx9qHNyWJMZ6kg" alt=""><figcaption></figcaption></figure>

## 🧩 UI: Item + comments

A component displaying an item and its associated comments or discussions.

<figure><img src="/files/ToewoA0F1MmIDZNT6jgP" alt=""><figcaption></figcaption></figure>

## 🧩🌐 UI: Logo

The app's logo element, reusable across various parts of the app for consistent branding.

<figure><img src="/files/GYW14HZScSv79BrU1wcr" alt=""><figcaption></figcaption></figure>

## 🧩 UI: Marketing - Pricing Table

A table that clearly lays out the pricing tiers and features for marketing purposes.

<figure><img src="/files/ptjGJrAjiiNmncbMXMMS" alt=""><figcaption></figcaption></figure>

## 🧩📋 UI: Mini Menu - Generic

A general-purpose mini menu for common actions or navigation that can be used throughout the app.

<figure><img src="/files/UudXaBjU7vkZ62Wxhu9V" alt=""><figcaption></figcaption></figure>

## 🧩 UI: Mini Menu - Users

A compact menu for user-specific actions, like account settings or logout options.

<figure><img src="/files/BXrRcPvn48MugIIDMMks" alt=""><figcaption></figcaption></figure>

## 🧩🚀 UI: Onboarding form

A form designed to collect necessary information from users during the onboarding process.

#### ![](/files/4L0IRAzgu8gK4dznlPko)

## 🧩 UI: Section - Create Content

An interface for users to create and submit content within the app, such as posts or listings.

<figure><img src="/files/QRZiONQqjgHFYU4JpqWR" alt=""><figcaption></figcaption></figure>

## 🧩👤 UI: User Avatar

A placeholder or container for displaying a user's profile picture or avatar.

<figure><img src="/files/qElAOf7fUqTcIWFaot3v" alt=""><figcaption></figcaption></figure>

## 🧩 UI: Dashboard header

The top section of a dashboard, usually containing navigation, user info, and key data points.

<figure><img src="/files/nIJus1injgllGQjyAbeh" alt=""><figcaption></figcaption></figure>

## 🧩 UI: Empty state

A visual indication for empty content areas.

<figure><img src="/files/yQFBceMvG1QG3k5a2jfu" alt=""><figcaption></figcaption></figure>

## 🧩 UI: Icon - Coloured

Icons enhance the visual interface and user experience of the app. BL mainly uses Heroicons.

<figure><img src="/files/2QYDtm9vs7clzRnccvbv" alt=""><figcaption></figcaption></figure>

## 🧩⚙️ UI: Section - Account + Settings

A user interface section dedicated to account information and user-specific settings.

<figure><img src="/files/osfpXCnPfrpoJvDFg9ir" alt=""><figcaption></figcaption></figure>

## 🧩 UI: Upgrade CTA w Popup

A Call To Action (CTA) for upgrading the service, which triggers a popup with more information.

<figure><img src="/files/60bqHc6KrD0lz2vK6E9P" alt=""><figcaption></figcaption></figure>

## 🧩🍿 UI: Popup - Create

A popup window designed for creating new records or entries, like posts or contacts.

<figure><img src="/files/pvZtuO7HScNrNY69Aq1K" alt=""><figcaption></figcaption></figure>

## 🧩🍿 UI: Popup - Delete

A confirmation popup for deleting items, to prevent accidental deletions by users.

<figure><img src="/files/HkOO2xyM3II9fhicfh2u" alt=""><figcaption></figcaption></figure>

## 🧩🍿 UI: Popup - Signup/Login

A modal window that facilitates the signup or login process for users.

<figure><img src="/files/WIKXlvYwlG14sCCtVTRX" alt=""><figcaption></figcaption></figure>

## 🧩🍿 UI: Popup - Tour

An informative popup that gives a guided tour of the app’s features or new updates.

<figure><img src="/files/1dWUWj6lKomHLbHredma" alt=""><figcaption></figcaption></figure>

## 🧩🍿 UI: Popup - Upgrade

A popup used to encourage and facilitate users to upgrade their account or service level.

<figure><img src="/files/Q8UX2dkbB97oeY1KAGOt" alt=""><figcaption></figcaption></figure>

## 🧩🍿 UI: Popup - View Item

A popup for viewing detailed information about an item, often used in catalogs or inventories.

<figure><img src="/files/26TBZyFNoYaT0dOesuqK" alt=""><figcaption></figcaption></figure>

## 🧩🍿 UI: Popup - View w/ image

A popup element that allows users to view detailed content, often accompanied by an image.

## 🧩💬 UI: Chat - Inbox

The main inbox component for a chat feature, where users can see their message threads.

<figure><img src="/files/CU3ZDc524v6ZSFcyTzBV" alt=""><figcaption></figcaption></figure>

## 🧩💬 UI: Chat - Individual Chat

An interface for one-on-one chat conversations between users.

<figure><img src="/files/MFJC0j9Wqbz1XvujV2nN" alt=""><figcaption></figcaption></figure>

## 🧩📊 UI: Charts

Reusable charts for displaying data, accessible throughout various parts of the app.

<figure><img src="/files/rPfdINQ4ejCBJnav8m0h" alt=""><figcaption></figcaption></figure>

## 🧩📖 UI: Table - Calendar

A table format specifically designed to represent calendar views, for events or scheduling.

<figure><img src="/files/a7P41NQe58chdOIGJlVa" alt=""><figcaption></figcaption></figure>

## 🧩📖 UI: Table - Classic Rows

A traditional row layout for tables, suitable for displaying structured data like spreadsheets.

<figure><img src="/files/uFYDkpLOthPJhMHTjTbZ" alt=""><figcaption></figcaption></figure>

## 🧩📖 UI: Table - Coloured Grid

A visually distinct grid with color coding, useful for categorizing or highlighting information.

<figure><img src="/files/fP8ueWghv8fc7sKzV7iw" alt=""><figcaption></figcaption></figure>

## 🧩📖 UI: Table - Image grid

A grid layout specifically designed for displaying images, such as galleries or product photos.

<figure><img src="/files/h3L7erEvlLFiuZ7gVowX" alt=""><figcaption></figcaption></figure>

## 🧩📖 UI: Table - Narrow

A more compact table design for displaying data where less width is needed or for mobile responsiveness.

<figure><img src="/files/9ifErSR8FNaeuYL17ZnG" alt=""><figcaption></figcaption></figure>

## 🧩🛟 UI: Section - Support + FAQs

A dedicated section for support and frequently asked questions to help users navigate common issues or inquiries.

<figure><img src="/files/k77XzzSx3MLG4mz7uvcu" alt=""><figcaption></figcaption></figure>


# Pages

Bullet Launch contains a small number of pages which represent the main functionalities within the app. The majority of the functionality within the app will occur on the dashboard page which will function as a "single-page app" where reusable elements are shown and hidden depending on the tab the user has selected. Single-page apps are best way to provide a snappy, responsive user experience as the page does not need to change every time the user moves to another section.

#### index

The main landing page of the app, typically serving as the entry point for users. This contains a number of typical SaaS landing page elements.

#### auth

A dedicated authentication page where users can log in or sign up to access the app.

#### example\_dashboard (can be deleted)

A sample dashboard page to showcase how a dashboard can be structured or designed.

#### logout

The function of this page is to enable the app to log users out via the navigation panel. The page has a single workflow which logs the user out as they arrive on the page, and then redirects them to the auth page.

#### onboarding

The onboarding process for new users, which can include tutorials, user information setup, or initial configuration steps.

#### upgrade

A page showing the app's price plans and premium features, where the user can choose a plan, upgrade and be redirected to Lemon Squeezy or Stripe.

#### 404

A custom error page displayed when a user attempts to access a page that does not exist or is unavailable.

#### admin

A customisable dashboard page for the app admin to manage the app, including some app-wide settings and generating legal terms.

#### dashboard

The main dashboard for users, where they can view, manage, and interact with various aspects of the app relevant to them.

{% hint style="info" %}
For apps with multiple types of users who require significantly different functionality, for example a marketplace with buyers and sellers, the dashboard page can be duplicated so that each user type has its own dashboard.
{% endhint %}

#### legal

A page that includes legal information such as Terms of Service and the Privacy Policy.

#### marketplace

An optional page designed to look like a marketplace search page.

#### p

A page for viewing an individual item, such as a user profile, an individual project, or anything the app requires.

#### reset\_pw

A page that allows users to reset their password if they have forgotten it or wish to change it for security reasons.


# Emails

Bubble can send emails without needing to set anything up, but they will be sent from a Bubble email address containing the name of your app. It's functional but it doesn't look professional.

In order to send from your own custom domain, you need to use a third-party email service provider like Sendgrid, Postmark, or Loops.

I'm not a fan of Sendgrid for various reasons, so I always opt for Postmark. I've also integrated Loops in case you prefer to use that.&#x20;

## Postmark

Postmark is my favourite email service provider and the one I use for all my projects. It's easier to use and has reputedly better deliverability than Sendgrid.

To use Postmark, follow the steps below:

1. Create an account on [Postmarkapp.com](https://postmarkapp.com)
2. Click the link in the confirmation email.
3. At this point you will be in test mode and able to send emails from the email address you just confirmed. I would recommend following the further steps to authenticate your domain in Postmark, which will allow you to send emails from any address on your domain.

### Send a plain text email

With Postmark you can send a plain text transactional email without needing to set up a template in the Postmark app.

Send a templated email

### Send a templated email

In the Postmark dashboard you can create your own HTML email templates complete with your logo and branding, and use them to send your emails. You can use dynamic variables to send through data to populate the email.

For example, you could have one template for all your emails and just replace the text with whatever you want to say each time.

Or you could design various fancy emails with different layouts and send them in different situations.

Below is a very basic example of a templated email.

<figure><img src="/files/qCx0LaRXncXnuFxFsGUK" alt=""><figcaption><p>Example of a basic email sent using a template</p></figcaption></figure>

## Loops.so

Loops is a relatively new platform but comes well recommended.


# AI integrations

#### Chat completion

Generates a response to a given piece of text within a chat context, simulating a conversation.

#### Chat completion multi message

Handles multiple messages in a conversation, providing context-aware completions over a series of chat exchanges.

#### FINE TUNING - File upload

Allows for the upload of a file containing training data to fine-tune the model for specific tasks or domains.

#### FINE TUNING - Start fine tuning job

Initiates a fine-tuning process on the model using the previously uploaded training data to customize its responses.

#### FINE TUNING - Get fine tuning job result

Retrieves the results of a fine-tuning job, which may include the model's performance and any errors encountered.

#### FINE TUNING - Delete model

Deletes a fine-tuned model from the system, likely for cleanup or to manage storage and model versions.

#### Create image with Dall-E

Uses the Dall-E model to generate an image based on a textual description, showcasing the AI's creative capabilities.

#### GPT Vision - Analyse image

Employs an AI model to analyze an image and provide insights or descriptions of its contents.

#### Text to speech

Converts text into spoken words, generating an audio output from a given text input.

#### Whisper transcribe

Transcribes audio to text, likely using OpenAI's Whisper model for accurate speech-to-text conversion.

#### Chat Completion JSON Mode

Processes chat inputs and generates completions in JSON format, potentially for integrations that require structured data output.


# Payments

Stripe and Lemon Squeezy are the two payment processors I use most often, although I have also used Paddle in the past.

Stripe is super powerful and the industry leader for payments. However if you're based in the UK or EU and care about your EU VAT obligations, you may want to use Lemon Squeezy instead. Lemon Squeezy acts as the merchant of record, meaning they handle all of the VAT remittances for you.

The end user experience for both platforms is fairly similar. For the developer, in my opinion the Lemon Squeezy dashboard is much simpler than Stripe but also has less functionality.

The integrations I've built into Bullet Launch enable users to create and manage their subscriptions via the hosted portals provided by each processor. This is the simplest way to integrate with the payment processors.&#x20;

If you require more advanced functionality or marketplace features then you will need to add these in yourself (or I can help you).

## Stripe

Watch the tutorial video [here](/getting-started/setting-up-stripe-payments).

Setting up Stripe as your payment processor involves configuring API keys, pricing plans, and webhooks to ensure secure and efficient handling of transactions. This guide provides a concise overview of the necessary steps to integrate Stripe into your application.

**Overview of Steps:**

1. **Add Stripe Plugin and API Keys:**
   * Navigate to the Stripe plugin in the plugins tab.
   * Add your secret and publishable API keys for both test and live environments.
   * Obtain these keys from your Stripe dashboard.
2. **Configure API Connector Plugin:**
   * Expand the Stripe call in the API connector plugin.
   * Enter your secret key for both test and live environments.
   * This ensures security checks for webhooks from Stripe.
3. **Set Up Pricing Plans:**
   * Go to the data tab and open option sets.
   * Modify existing pricing plans or create new ones as needed.
   * Ensure free plans for new users and define monthly and yearly plans.
   * Match these plans with those in your Stripe dashboard, copying the price IDs from Stripe to your app.
4. **Integrate Stripe and Lemon Squeezy (Optional):**
   * If not using Lemon Squeezy, focus on Stripe fields.
   * Mirror pricing plans in Stripe for both test and live modes, copying respective price IDs.
5. **Configure Webhooks:**
   * In Stripe, search for webhooks and add a new endpoint with your app’s URL.
   * Specify the events to monitor, such as `customer.subscription.updated` and `customer.subscription.deleted`.
   * This ensures your app is notified of subscription updates.
6. **Payment Provider Settings:**
   * Ensure your app's payment provider is set to Stripe in the app settings.
   * This configuration is used to determine the correct workflow for handling payments.
7. **Testing and Validation:**
   * Perform tests in both test and live modes to ensure everything is set up correctly.
   * Use backend workflows to handle Stripe’s webhook events, verifying the authenticity and updating user subscriptions accordingly.

By following these steps, you can successfully integrate Stripe into your application, enabling secure and efficient payment processing.

## Lemon Squeezy

Watch the tutorial video [here](/getting-started/setting-up-lemon-squeezy-payments).

Setting up Lemon Squeezy as your payment provider involves configuring API keys, creating and managing pricing plans, and setting up webhooks to handle subscription updates. This guide outlines the steps to integrate Lemon Squeezy into your application effectively.

**Overview of Steps:**

1. **Add API Key in API Connector:**
   * Navigate to the API connector plugin.
   * Expand Lemon Squeezy and enter your API key for both test and live modes.
2. **Create Pricing Plans:**
   * Go to Lemon Squeezy and create an account.
   * Switch to test mode and create a new product with variants for different pricing plans (monthly and annual).
   * Mirror these plans within your app’s option sets, ensuring the pricing details match.
3. **Configure Option Sets in App:**
   * In the option sets, create entries for each pricing plan.
   * Include the display name, plan name, icon, description, frequency, and feature tier.
   * Ensure the price and any user restrictions are set correctly.
   * Add the variant ID and slug for each plan from Lemon Squeezy.
4. **Setup Payment Provider in App Settings:**
   * Go to app data and app settings.
   * Set the payment provider to Lemon Squeezy and save.
5. **Handle Subscriptions and Plan Upgrades:**
   * In the pricing table, set up workflows to handle subscriptions.
   * Check the payment provider and create appropriate custom events to handle new subscriptions or updates.
   * Ensure the correct URL and variant ID are used depending on whether you are in test or live mode.
6. **Configure Webhooks in Lemon Squeezy:**
   * Go to Lemon Squeezy settings and create a new webhook.
   * Set the webhook URL to your app’s backend workflow URL.
   * Subscribe to `subscription.created` and `subscription.updated` events.
   * Make sure to repeat this process for both test and live modes, adjusting the URL as needed.
7. **Sync Subscription Data:**
   * Use backend workflows to handle webhook notifications.
   * When a subscription is created or updated, trigger an API call to get the subscription details.
   * Update the user’s subscription status and related data in the app accordingly.

By following these steps, you can successfully integrate Lemon Squeezy into your application, ensuring smooth handling of subscriptions and secure payment processing.


# Plugins

Bullet Launch comes with a number of free plugins installed to enable various functionality which is not native to Bubble. I have tried to minimise the number of plugins as much as possible and am only using free plugins from reputable sources.

Below is a list of the plugins and what they do.

#### Air Chart Lite - Best Free Charts

Enables the creation and display of line charts as seen in the **UI - Charts** element.

#### Air Copy To Clipboard

Allows app users to copy text to their clipboard with a single click, enhancing user interaction and data handling within the app.

#### API Connector

This plugin lets the app connect to various external APIs for extended functionality, such as sending emails, integrating AI services, processing payments, and more. This plugin is built by the Bubble team.

#### Audio Player Full - BEP

Provides a full-featured audio player that can be embedded within the app, allowing users to play, control, and manage audio files. This is used for playing audio generated by OpenAI with its Text-To-Speech functionality.

#### Bubble Toast

Displays non-intrusive alert messages or 'toasts' on the app's interface, which are useful for showing notifications or confirmations.

#### Chart Element

This is the standard Bubble plugin for creating dynamic, customizable charts directly in the app. It includes bar, pie, and line charts, albeit with relatively basic styling. This plugin is built by the Bubble team.

#### Classify

This is a useful plugin which lets you apply custom CSS and Javascript to elements in your Bubble app. Read more about it [here](https://forum.bubble.io/t/classify-4-a-tiny-plugin-that-brings-css-classes-and/67741).

#### Full Calendar

This is the standard Bubble calendar plugin, and is used in the **UI: Calendar** element. This plugin is built by the Bubble team.

#### Google Material Icons

Provides a collection of visually consistent icons based on Google's Material Design guidelines, enhancing the app's user interface. This is a fallback for situations where Heroicons is lacking a specific icon.

#### Heroicons

A set of simple, elegant open-source icons that can be used to improve the design and user navigation within the app. These are used widely within the app.

#### Multiselect Dropdown

Allows users to select multiple options from a dropdown list, useful for filters, forms, and any feature requiring multi-choice input. This plugin is built by the Bubble team.

#### Progress Bar

Creates visual progress indicators which can be used to show users the status of a process, like uploads, downloads, task completion, etc.

#### Rich Text Editor

Incorporates a text editor with formatting capabilities into the app, enabling users to input and edit styled text, much like a word processor. This plugin is built by the Bubble team.

#### Stripe

Facilitates secure payment processing within the app by integrating with Stripe, allowing for credit card payments, subscription billing, and financial transactions. This plugin is built by the Bubble team.

#### Toolbox

A collection of utility elements that provide extended functionality to the app, including running JavaScript, server-side actions, and more.


# Styles

## Fonts

Bullet Launch uses **Manrope** throughout the template and **Poppins** for headings. These are clean, modern fonts.

You can change the heading and app fonts in the **Styles** panel in the **Style variables** tab.

<figure><img src="/files/vhTZU85r6VUw5cN1bKja" alt=""><figcaption></figcaption></figure>

## Colours

Bullet Launch uses a colour scheme defined in the **Styles** panel. The default Primary colour is blue.

You can customise the colours to suit your own brand. The main colours to change are:

* Primary - this is the primary colour of the brand and is the 500 hue.
* Primary 900 - a dark version of the primary colour.
* Primary 700 - a slightly dark version of the primary colour.
* Primary 300 - a slightly light version of the pimary colour.
* Primary 100 - a light version of the primary colour.
* Text - this is the colour of heading and body text throughout the app. It should generally be a dark colour.

To generate your own colour shades based on a single primary colour, I recommend [using this page](https://uicolors.app/create). You then just need to copy in the HEX codes to the appropriate colours in the styles panel.

The "random" colours are used in the app where rainbow colours are applied. You can update these if you wish to, or simply not use them.

<figure><img src="/files/iEg4MFInvSEbeFoxYMYL" alt=""><figcaption></figcaption></figure>

## Styles

Bullet Launch uses universal styles throughout the app which are defined in **Styles** panel under **Element styles**. This is best practice as it reduces the compute and load time of the app, while also letting you control styles app-wide from one place.

{% hint style="info" %}
Be careful when updating styles as they may be used by elements you're not aware of.

Before you update a style it's a good idea to use the app-wide search feature to find all instances where that style is used.
{% endhint %}

## How dark mode works

There is a field on the user data type called **Dark mode? (yes/no)** which specifies whether the user wants to view the app in dark mode or not.

Throughout the app, primarily in the defined styles, there are conditionals which specify that if the Current user's Dark mode is yes, one or more colours related to that element should change to the dark mode version.

![](/files/sYxvjmQx2Iqdu4nZo6fx)

{% hint style="info" %}
Switching between dark/light mode works smoothly on desktop, however on mobile it can result in a rather odd looking situation where the background colour of part of the page is still the opposite of what it should be. This is because the background colour of the page is set by CSS styling and this is not applied until the page is reloaded.

For this reason the dark/light mode switch is not visible on mobiles. You may wish to make it available to mobile users, in which case you may want to force a reload of the page every time the user changes the mode.
{% endhint %}

## Trick for light mode/dark mode repeating group separators

Unfortunately Bubble does not allow conditional settings for the colour of separators in repeating groups. This means it is not possible to change the separator colour when the user switches to dark mode.

To get around this, all repeating groups use a mid-tone grey colour with a low opacity for the separators. This looks relatively good in both light and dark mode without needing to change the color.

* Very light grey (e.g. Separators in repeating group tables): Grey 500 at 10% opacity
* Light grey (e.g. Tab group separators): Grey 500 at 30% opacity


# Get support

I want to make this template as good as it possibly can be so I need your feedback on how I can improve it.

Please join the Bullet Launch [Slack workspace](https://join.slack.com/t/slack-vhj7817/shared_invite/zt-2e5pzc48x-MOnZRD2E5Chi6ge0z_Vp4g) where you can ask questions, provide feedback, and chat to other builders.

You can also [book time with me ](https://nocodelife.lemonsqueezy.com/checkout/buy/667ac248-82c0-433c-8513-7d96390b05fc)to discuss your project or get help on anything.


