Add install your first apps onboarding V2 step (#22347)

https://github.com/user-attachments/assets/5326d48f-1842-4db1-bc7c-94852145c035


<img width="838" height="754" alt="CleanShot 2026-06-30 at 16 25 05@2x"
src="https://github.com/user-attachments/assets/5c7d53d7-4d65-4e35-aed1-edf0c104e140"
/>


Adds an "Install your first apps" step to the V2 onboarding, shown right
after import-contacts. It lets users opt into installing marketplace
apps (Call recorder and People Data Labs for now) during onboarding.

- New backend `OnboardingStatus.APPS_INSTALLATION` (between SYNC_EMAIL
and PROFILE_CREATION); V1 auto-skips it.
- The primary button sends the selected app ids to the server via
`triggerInstallAppsOnboardingStep`, which enqueues a dedicated job that
installs them asynchronously so onboarding isn't blocked. Skip continues
without installing.
- The workspace is credited per app on successful installation. Credits
are env-driven via `ONBOARDING_INSTALL_APPS_CREDITS_REWARD_PER_APP`,
shown as "Earn +N free credits (1 per tool)".

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22347?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
This commit is contained in:
Raphaël Bosi
2026-07-01 14:25:16 +02:00
committed by GitHub
parent 93b8f66794
commit 2e6077383b
45 changed files with 1336 additions and 16 deletions
@@ -1022,6 +1022,7 @@ enum OnboardingStatus {
WORKSPACE_ACTIVATION
PROFILE_CREATION
SYNC_EMAIL
APPS_INSTALLATION
INVITE_TEAM
BOOK_ONBOARDING
COMPLETED
@@ -3314,6 +3315,7 @@ type Mutation {
assignRoleToApiKey(apiKeyId: UUID!, roleId: UUID!): Boolean!
skipSyncEmailOnboardingStep: OnboardingStepSuccess!
skipBookOnboardingStep: OnboardingStepSuccess!
triggerInstallAppsOnboardingStep(universalIdentifiers: [String!]!): OnboardingStepSuccess!
updateOneApplicationVariable(key: String!, value: String!, applicationId: UUID!): Boolean!
checkoutSession(recurringInterval: SubscriptionInterval!, plan: BillingPlanKey! = PRO, requirePaymentMethod: Boolean! = true, successUrlPath: String): BillingSession!
createSubscriptionPaymentIntent(recurringInterval: SubscriptionInterval!, plan: BillingPlanKey! = PRO, requirePaymentMethod: Boolean! = true, successUrlPath: String, idempotencyKey: String!): BillingPaymentIntent!
@@ -731,7 +731,7 @@ export interface User {
/** Onboarding status */
export type OnboardingStatus = 'PLAN_REQUIRED' | 'WORKSPACE_ACTIVATION' | 'PROFILE_CREATION' | 'SYNC_EMAIL' | 'INVITE_TEAM' | 'BOOK_ONBOARDING' | 'COMPLETED'
export type OnboardingStatus = 'PLAN_REQUIRED' | 'WORKSPACE_ACTIVATION' | 'PROFILE_CREATION' | 'SYNC_EMAIL' | 'APPS_INSTALLATION' | 'INVITE_TEAM' | 'BOOK_ONBOARDING' | 'COMPLETED'
export interface RatioAggregateConfig {
fieldMetadataId: Scalars['UUID']
@@ -2835,6 +2835,7 @@ export interface Mutation {
assignRoleToApiKey: Scalars['Boolean']
skipSyncEmailOnboardingStep: OnboardingStepSuccess
skipBookOnboardingStep: OnboardingStepSuccess
triggerInstallAppsOnboardingStep: OnboardingStepSuccess
updateOneApplicationVariable: Scalars['Boolean']
checkoutSession: BillingSession
createSubscriptionPaymentIntent: BillingPaymentIntent
@@ -6047,6 +6048,7 @@ export interface MutationGenqlSelection{
assignRoleToApiKey?: { __args: {apiKeyId: Scalars['UUID'], roleId: Scalars['UUID']} }
skipSyncEmailOnboardingStep?: OnboardingStepSuccessGenqlSelection
skipBookOnboardingStep?: OnboardingStepSuccessGenqlSelection
triggerInstallAppsOnboardingStep?: (OnboardingStepSuccessGenqlSelection & { __args: {universalIdentifiers: Scalars['String'][]} })
updateOneApplicationVariable?: { __args: {key: Scalars['String'], value: Scalars['String'], applicationId: Scalars['UUID']} }
checkoutSession?: (BillingSessionGenqlSelection & { __args: {recurringInterval: SubscriptionInterval, plan: BillingPlanKey, requirePaymentMethod: Scalars['Boolean'], successUrlPath?: (Scalars['String'] | null)} })
createSubscriptionPaymentIntent?: (BillingPaymentIntentGenqlSelection & { __args: {recurringInterval: SubscriptionInterval, plan: BillingPlanKey, requirePaymentMethod: Scalars['Boolean'], successUrlPath?: (Scalars['String'] | null), idempotencyKey: Scalars['String']} })
@@ -8966,6 +8968,7 @@ export const enumOnboardingStatus = {
WORKSPACE_ACTIVATION: 'WORKSPACE_ACTIVATION' as const,
PROFILE_CREATION: 'PROFILE_CREATION' as const,
SYNC_EMAIL: 'SYNC_EMAIL' as const,
APPS_INSTALLATION: 'APPS_INSTALLATION' as const,
INVITE_TEAM: 'INVITE_TEAM' as const,
BOOK_ONBOARDING: 'BOOK_ONBOARDING' as const,
COMPLETED: 'COMPLETED' as const
@@ -7416,6 +7416,15 @@ export default {
"skipBookOnboardingStep": [
156
],
"triggerInstallAppsOnboardingStep": [
156,
{
"universalIdentifiers": [
1,
"[String!]!"
]
}
],
"updateOneApplicationVariable": [
6,
{