diff --git a/.gitignore b/.gitignore index e253d5f..ee236df 100644 --- a/.gitignore +++ b/.gitignore @@ -202,4 +202,6 @@ android/libs/x86_64/ # Demo/Test files demo-devices.json -test-tokens.txt \ No newline at end of file +test-tokens.txt + +.playwright-mcp diff --git a/app/.graphql/default/sdk.ts b/app/.graphql/default/sdk.ts index 4ecc325..3608377 100644 --- a/app/.graphql/default/sdk.ts +++ b/app/.graphql/default/sdk.ts @@ -216,6 +216,167 @@ export const GenerateVapidKeysDocument = /*#__PURE__*/ ` } } `; +export const CreateChannelDocument = /*#__PURE__*/ ` + mutation createChannel($input: CreateChannelInput!) { + createChannel(input: $input) { + id + appId + name + type + isActive + createdAt + updatedAt + } +} + `; +export const UpdateChannelDocument = /*#__PURE__*/ ` + mutation updateChannel($id: ID!, $input: UpdateChannelInput!) { + updateChannel(id: $id, input: $input) { + id + name + type + isActive + updatedAt + } +} + `; +export const DeleteChannelDocument = /*#__PURE__*/ ` + mutation deleteChannel($id: ID!) { + deleteChannel(id: $id) +} + `; +export const ChannelsDocument = /*#__PURE__*/ ` + query channels($appId: ID!, $type: ChannelType) { + channels(appId: $appId, type: $type) { + id + appId + name + type + isActive + createdAt + updatedAt + } +} + `; +export const ChannelDocument = /*#__PURE__*/ ` + query channel($id: ID!) { + channel(id: $id) { + id + appId + name + type + isActive + createdAt + updatedAt + } +} + `; +export const CreateContactDocument = /*#__PURE__*/ ` + mutation createContact($input: CreateContactInput!) { + createContact(input: $input) { + id + appId + externalId + name + email + phone + locale + createdAt + updatedAt + } +} + `; +export const UpdateContactDocument = /*#__PURE__*/ ` + mutation updateContact($id: ID!, $input: UpdateContactInput!) { + updateContact(id: $id, input: $input) { + id + externalId + name + email + phone + locale + updatedAt + } +} + `; +export const DeleteContactDocument = /*#__PURE__*/ ` + mutation deleteContact($id: ID!) { + deleteContact(id: $id) +} + `; +export const UpsertContactDeviceDocument = /*#__PURE__*/ ` + mutation upsertContactDevice($input: UpsertContactDeviceInput!) { + upsertContactDevice(input: $input) +} + `; +export const UpdateContactPreferenceDocument = /*#__PURE__*/ ` + mutation updateContactPreference($input: UpdateContactPreferenceInput!) { + updateContactPreference(input: $input) { + id + subscriberId + category + channelType + enabled + updatedAt + } +} + `; +export const ContactsDocument = /*#__PURE__*/ ` + query contacts($appId: ID!, $limit: Int, $offset: Int) { + contacts(appId: $appId, limit: $limit, offset: $offset) { + id + appId + externalId + name + email + phone + locale + metadata + createdAt + updatedAt + } +} + `; +export const ContactDocument = /*#__PURE__*/ ` + query contact($id: ID!) { + contact(id: $id) { + id + appId + externalId + name + email + phone + locale + metadata + createdAt + updatedAt + preferences { + id + subscriberId + category + channelType + enabled + updatedAt + } + } +} + `; +export const ContactByExternalIdDocument = /*#__PURE__*/ ` + query contactByExternalId($appId: ID!, $externalId: String!) { + contactByExternalId(appId: $appId, externalId: $externalId) { + id + appId + externalId + name + email + phone + locale + metadata + createdAt + updatedAt + } +} + `; export const RegisterDeviceDocument = /*#__PURE__*/ ` mutation registerDevice($input: RegisterDeviceInput!) { registerDevice(input: $input) { @@ -303,6 +464,65 @@ export const DeviceByTokenDocument = /*#__PURE__*/ ` } } `; +export const CreateHookDocument = /*#__PURE__*/ ` + mutation createHook($input: CreateHookInput!) { + createHook(input: $input) { + id + appId + name + url + events + isActive + createdAt + updatedAt + } +} + `; +export const UpdateHookDocument = /*#__PURE__*/ ` + mutation updateHook($id: ID!, $input: UpdateHookInput!) { + updateHook(id: $id, input: $input) { + id + name + url + events + isActive + updatedAt + } +} + `; +export const DeleteHookDocument = /*#__PURE__*/ ` + mutation deleteHook($id: ID!) { + deleteHook(id: $id) +} + `; +export const HooksDocument = /*#__PURE__*/ ` + query hooks($appId: ID!) { + hooks(appId: $appId) { + id + appId + name + url + events + isActive + createdAt + updatedAt + } +} + `; +export const HookDocument = /*#__PURE__*/ ` + query hook($id: ID!) { + hook(id: $id) { + id + appId + name + url + events + isActive + createdAt + updatedAt + } +} + `; export const SendNotificationDocument = /*#__PURE__*/ ` mutation sendNotification($input: SendNotificationInput!) { sendNotification(input: $input) { @@ -377,6 +597,7 @@ export const NotificationDocument = /*#__PURE__*/ ` totalSent totalDelivered totalFailed + totalOpened totalClicked createdAt updatedAt @@ -384,10 +605,13 @@ export const NotificationDocument = /*#__PURE__*/ ` deliveryLogs { id deviceId + to status errorMessage - createdAt + sentAt + openedAt clickedAt + createdAt } } } @@ -398,8 +622,11 @@ export const DeliveryLogsDocument = /*#__PURE__*/ ` id notificationId deviceId + to status errorMessage + sentAt + openedAt clickedAt createdAt updatedAt @@ -416,6 +643,185 @@ export const DashboardStatsDocument = /*#__PURE__*/ ` } } `; +export const CreateTemplateDocument = /*#__PURE__*/ ` + mutation createTemplate($input: CreateTemplateInput!) { + createTemplate(input: $input) { + id + appId + channelId + name + channelType + subject + body + htmlBody + createdAt + updatedAt + } +} + `; +export const UpdateTemplateDocument = /*#__PURE__*/ ` + mutation updateTemplate($id: ID!, $input: UpdateTemplateInput!) { + updateTemplate(id: $id, input: $input) { + id + name + subject + body + htmlBody + updatedAt + } +} + `; +export const DeleteTemplateDocument = /*#__PURE__*/ ` + mutation deleteTemplate($id: ID!) { + deleteTemplate(id: $id) +} + `; +export const TemplatesDocument = /*#__PURE__*/ ` + query templates($appId: ID!, $channelType: ChannelType) { + templates(appId: $appId, channelType: $channelType) { + id + appId + channelId + name + channelType + subject + body + htmlBody + createdAt + updatedAt + } +} + `; +export const TemplateDocument = /*#__PURE__*/ ` + query template($id: ID!) { + template(id: $id) { + id + appId + channelId + name + channelType + subject + body + htmlBody + createdAt + updatedAt + } +} + `; +export const CreateWorkflowDocument = /*#__PURE__*/ ` + mutation createWorkflow($input: CreateWorkflowInput!) { + createWorkflow(input: $input) { + id + appId + name + triggerIdentifier + triggerType + status + flowLayout + createdAt + updatedAt + } +} + `; +export const UpdateWorkflowDocument = /*#__PURE__*/ ` + mutation updateWorkflow($id: ID!, $input: UpdateWorkflowInput!) { + updateWorkflow(id: $id, input: $input) { + id + name + triggerIdentifier + triggerType + status + flowLayout + updatedAt + steps { + id + nodeId + type + order + config + } + } +} + `; +export const DeleteWorkflowDocument = /*#__PURE__*/ ` + mutation deleteWorkflow($id: ID!) { + deleteWorkflow(id: $id) +} + `; +export const TriggerWorkflowDocument = /*#__PURE__*/ ` + mutation triggerWorkflow($input: TriggerWorkflowInput!) { + triggerWorkflow(input: $input) { + id + workflowId + status + triggerIdentifier + startedAt + createdAt + } +} + `; +export const WorkflowsDocument = /*#__PURE__*/ ` + query workflows($appId: ID!, $status: WorkflowStatus) { + workflows(appId: $appId, status: $status) { + id + appId + name + triggerIdentifier + triggerType + status + flowLayout + createdAt + updatedAt + steps { + id + nodeId + type + order + config + } + } +} + `; +export const WorkflowDocument = /*#__PURE__*/ ` + query workflow($id: ID!) { + workflow(id: $id) { + id + appId + name + triggerIdentifier + triggerType + status + flowLayout + createdAt + updatedAt + steps { + id + nodeId + type + order + config + } + } +} + `; +export const WorkflowExecutionsDocument = /*#__PURE__*/ ` + query workflowExecutions($workflowId: ID!, $limit: Int, $offset: Int) { + workflowExecutions(workflowId: $workflowId, limit: $limit, offset: $offset) { + id + workflowId + subscriberId + triggerIdentifier + payload + status + currentStepOrder + errorMessage + startedAt + completedAt + createdAt + updatedAt + } +} + `; export type Requester = (doc: string, vars?: V, options?: C) => Promise> | AsyncIterable> export function getSdk(requester: Requester) { return { @@ -461,6 +867,45 @@ export function getSdk(requester: Requester) { generateVapidKeys(variables?: Types.GenerateVapidKeysQueryVariables, options?: C): Promise> { return requester(GenerateVapidKeysDocument, variables, options) as Promise>; }, + createChannel(variables: Types.CreateChannelMutationVariables, options?: C): Promise> { + return requester(CreateChannelDocument, variables, options) as Promise>; + }, + updateChannel(variables: Types.UpdateChannelMutationVariables, options?: C): Promise> { + return requester(UpdateChannelDocument, variables, options) as Promise>; + }, + deleteChannel(variables: Types.DeleteChannelMutationVariables, options?: C): Promise> { + return requester(DeleteChannelDocument, variables, options) as Promise>; + }, + channels(variables: Types.ChannelsQueryVariables, options?: C): Promise> { + return requester(ChannelsDocument, variables, options) as Promise>; + }, + channel(variables: Types.ChannelQueryVariables, options?: C): Promise> { + return requester(ChannelDocument, variables, options) as Promise>; + }, + createContact(variables: Types.CreateContactMutationVariables, options?: C): Promise> { + return requester(CreateContactDocument, variables, options) as Promise>; + }, + updateContact(variables: Types.UpdateContactMutationVariables, options?: C): Promise> { + return requester(UpdateContactDocument, variables, options) as Promise>; + }, + deleteContact(variables: Types.DeleteContactMutationVariables, options?: C): Promise> { + return requester(DeleteContactDocument, variables, options) as Promise>; + }, + upsertContactDevice(variables: Types.UpsertContactDeviceMutationVariables, options?: C): Promise> { + return requester(UpsertContactDeviceDocument, variables, options) as Promise>; + }, + updateContactPreference(variables: Types.UpdateContactPreferenceMutationVariables, options?: C): Promise> { + return requester(UpdateContactPreferenceDocument, variables, options) as Promise>; + }, + contacts(variables: Types.ContactsQueryVariables, options?: C): Promise> { + return requester(ContactsDocument, variables, options) as Promise>; + }, + contact(variables: Types.ContactQueryVariables, options?: C): Promise> { + return requester(ContactDocument, variables, options) as Promise>; + }, + contactByExternalId(variables: Types.ContactByExternalIdQueryVariables, options?: C): Promise> { + return requester(ContactByExternalIdDocument, variables, options) as Promise>; + }, registerDevice(variables: Types.RegisterDeviceMutationVariables, options?: C): Promise> { return requester(RegisterDeviceDocument, variables, options) as Promise>; }, @@ -479,6 +924,21 @@ export function getSdk(requester: Requester) { deviceByToken(variables: Types.DeviceByTokenQueryVariables, options?: C): Promise> { return requester(DeviceByTokenDocument, variables, options) as Promise>; }, + createHook(variables: Types.CreateHookMutationVariables, options?: C): Promise> { + return requester(CreateHookDocument, variables, options) as Promise>; + }, + updateHook(variables: Types.UpdateHookMutationVariables, options?: C): Promise> { + return requester(UpdateHookDocument, variables, options) as Promise>; + }, + deleteHook(variables: Types.DeleteHookMutationVariables, options?: C): Promise> { + return requester(DeleteHookDocument, variables, options) as Promise>; + }, + hooks(variables: Types.HooksQueryVariables, options?: C): Promise> { + return requester(HooksDocument, variables, options) as Promise>; + }, + hook(variables: Types.HookQueryVariables, options?: C): Promise> { + return requester(HookDocument, variables, options) as Promise>; + }, sendNotification(variables: Types.SendNotificationMutationVariables, options?: C): Promise> { return requester(SendNotificationDocument, variables, options) as Promise>; }, @@ -493,6 +953,42 @@ export function getSdk(requester: Requester) { }, dashboardStats(variables?: Types.DashboardStatsQueryVariables, options?: C): Promise> { return requester(DashboardStatsDocument, variables, options) as Promise>; + }, + createTemplate(variables: Types.CreateTemplateMutationVariables, options?: C): Promise> { + return requester(CreateTemplateDocument, variables, options) as Promise>; + }, + updateTemplate(variables: Types.UpdateTemplateMutationVariables, options?: C): Promise> { + return requester(UpdateTemplateDocument, variables, options) as Promise>; + }, + deleteTemplate(variables: Types.DeleteTemplateMutationVariables, options?: C): Promise> { + return requester(DeleteTemplateDocument, variables, options) as Promise>; + }, + templates(variables: Types.TemplatesQueryVariables, options?: C): Promise> { + return requester(TemplatesDocument, variables, options) as Promise>; + }, + template(variables: Types.TemplateQueryVariables, options?: C): Promise> { + return requester(TemplateDocument, variables, options) as Promise>; + }, + createWorkflow(variables: Types.CreateWorkflowMutationVariables, options?: C): Promise> { + return requester(CreateWorkflowDocument, variables, options) as Promise>; + }, + updateWorkflow(variables: Types.UpdateWorkflowMutationVariables, options?: C): Promise> { + return requester(UpdateWorkflowDocument, variables, options) as Promise>; + }, + deleteWorkflow(variables: Types.DeleteWorkflowMutationVariables, options?: C): Promise> { + return requester(DeleteWorkflowDocument, variables, options) as Promise>; + }, + triggerWorkflow(variables: Types.TriggerWorkflowMutationVariables, options?: C): Promise> { + return requester(TriggerWorkflowDocument, variables, options) as Promise>; + }, + workflows(variables: Types.WorkflowsQueryVariables, options?: C): Promise> { + return requester(WorkflowsDocument, variables, options) as Promise>; + }, + workflow(variables: Types.WorkflowQueryVariables, options?: C): Promise> { + return requester(WorkflowDocument, variables, options) as Promise>; + }, + workflowExecutions(variables: Types.WorkflowExecutionsQueryVariables, options?: C): Promise> { + return requester(WorkflowExecutionsDocument, variables, options) as Promise>; } }; } diff --git a/app/.graphql/nitro-graphql-client.d.ts b/app/.graphql/nitro-graphql-client.d.ts index dd6211f..ba424df 100644 --- a/app/.graphql/nitro-graphql-client.d.ts +++ b/app/.graphql/nitro-graphql-client.d.ts @@ -80,6 +80,26 @@ export type AppStats = { apiCalls: Scalars['Int']['output']; }; +export type Channel = { + __typename?: 'Channel'; + id: Scalars['ID']['output']; + appId: Scalars['ID']['output']; + name: Scalars['String']['output']; + type: ChannelType; + config?: Maybe; + isActive: Scalars['Boolean']['output']; + createdAt: Scalars['Timestamp']['output']; + updatedAt: Scalars['Timestamp']['output']; +}; + +export type ChannelType = + | 'PUSH' + | 'EMAIL' + | 'SMS' + | 'IN_APP' + | 'DISCORD' + | 'TELEGRAM'; + export type ConfigureApNsInput = { keyId: Scalars['String']['input']; teamId: Scalars['String']['input']; @@ -99,12 +119,83 @@ export type ConfigureWebPushInput = { privateKey: Scalars['String']['input']; }; +export type Contact = { + __typename?: 'Contact'; + id: Scalars['ID']['output']; + appId: Scalars['ID']['output']; + externalId: Scalars['String']['output']; + name?: Maybe; + email?: Maybe; + phone?: Maybe; + locale?: Maybe; + metadata?: Maybe; + devices?: Maybe>; + preferences?: Maybe>; + createdAt: Scalars['Timestamp']['output']; + updatedAt: Scalars['Timestamp']['output']; +}; + +export type ContactPreference = { + __typename?: 'ContactPreference'; + id: Scalars['ID']['output']; + subscriberId: Scalars['ID']['output']; + category: Scalars['String']['output']; + channelType: ChannelType; + enabled: Scalars['Boolean']['output']; + createdAt: Scalars['Timestamp']['output']; + updatedAt: Scalars['Timestamp']['output']; +}; + export type CreateAppInput = { name: Scalars['String']['input']; slug: Scalars['String']['input']; description?: InputMaybe; }; +export type CreateChannelInput = { + appId: Scalars['ID']['input']; + name: Scalars['String']['input']; + type: ChannelType; + config?: InputMaybe; +}; + +export type CreateContactInput = { + appId: Scalars['ID']['input']; + externalId: Scalars['String']['input']; + name?: InputMaybe; + email?: InputMaybe; + phone?: InputMaybe; + locale?: InputMaybe; + metadata?: InputMaybe; +}; + +export type CreateHookInput = { + appId: Scalars['ID']['input']; + name: Scalars['String']['input']; + url: Scalars['String']['input']; + secret?: InputMaybe; + events?: InputMaybe; +}; + +export type CreateTemplateInput = { + appId: Scalars['ID']['input']; + channelId?: InputMaybe; + name: Scalars['String']['input']; + channelType: ChannelType; + subject?: InputMaybe; + body: Scalars['String']['input']; + htmlBody?: InputMaybe; +}; + +export type CreateWorkflowInput = { + appId: Scalars['ID']['input']; + name: Scalars['String']['input']; + triggerIdentifier: Scalars['String']['input']; + triggerType?: InputMaybe; + steps?: InputMaybe>; + flowLayout?: InputMaybe; +}; + export type DashboardStats = { __typename?: 'DashboardStats'; totalApps: Scalars['Int']['output']; @@ -118,10 +209,13 @@ export type DeliveryLog = { id: Scalars['ID']['output']; notificationId: Scalars['ID']['output']; notification?: Maybe; - deviceId: Scalars['ID']['output']; + deviceId?: Maybe; device?: Maybe; + to?: Maybe; status: DeliveryStatus; errorMessage?: Maybe; + sentAt?: Maybe; + openedAt?: Maybe; clickedAt?: Maybe; createdAt: Scalars['Timestamp']['output']; updatedAt: Scalars['Timestamp']['output']; @@ -183,6 +277,41 @@ export type EngagementMetrics = { platformBreakdown: Array; }; +export type Hook = { + __typename?: 'Hook'; + id: Scalars['ID']['output']; + appId: Scalars['ID']['output']; + name: Scalars['String']['output']; + url: Scalars['String']['output']; + secret?: Maybe; + events?: Maybe; + isActive: Scalars['Boolean']['output']; + createdAt: Scalars['Timestamp']['output']; + updatedAt: Scalars['Timestamp']['output']; +}; + +export type HookEvent = + | 'NOTIFICATION_SENT' + | 'NOTIFICATION_DELIVERED' + | 'NOTIFICATION_FAILED' + | 'NOTIFICATION_CLICKED' + | 'WORKFLOW_COMPLETED' + | 'WORKFLOW_FAILED'; + +export type InAppMessage = { + __typename?: 'InAppMessage'; + id: Scalars['ID']['output']; + appId: Scalars['ID']['output']; + contactId: Scalars['ID']['output']; + notificationId?: Maybe; + title: Scalars['String']['output']; + body: Scalars['String']['output']; + data?: Maybe; + isRead: Scalars['Boolean']['output']; + readAt?: Maybe; + createdAt: Scalars['Timestamp']['output']; +}; + export type Mutation = { __typename?: 'Mutation'; _empty?: Maybe; @@ -191,8 +320,19 @@ export type Mutation = { configureFCM: App; configureWebPush: App; createApp: App; + createChannel: Channel; + createContact: Contact; + createHook: Hook; + createTemplate: Template; + createWorkflow: Workflow; deleteApp: Scalars['Boolean']['output']; + deleteChannel: Scalars['Boolean']['output']; + deleteContact: Scalars['Boolean']['output']; deleteDevice: Scalars['Boolean']['output']; + deleteHook: Scalars['Boolean']['output']; + deleteTemplate: Scalars['Boolean']['output']; + deleteWorkflow: Scalars['Boolean']['output']; + markInAppMessageRead: Scalars['Boolean']['output']; regenerateApiKey: App; registerDevice: Device; scheduleNotification: Notification; @@ -200,8 +340,16 @@ export type Mutation = { trackNotificationClicked: TrackEventResponse; trackNotificationDelivered: TrackEventResponse; trackNotificationOpened: TrackEventResponse; + triggerWorkflow: WorkflowExecution; updateApp: App; + updateChannel: Channel; + updateContact: Contact; + updateContactPreference: ContactPreference; updateDevice: Device; + updateHook: Hook; + updateTemplate: Template; + updateWorkflow: Workflow; + upsertContactDevice: Scalars['Boolean']['output']; }; @@ -233,16 +381,71 @@ export type MutationCreateAppArgs = { }; +export type MutationCreateChannelArgs = { + input: CreateChannelInput; +}; + + +export type MutationCreateContactArgs = { + input: CreateContactInput; +}; + + +export type MutationCreateHookArgs = { + input: CreateHookInput; +}; + + +export type MutationCreateTemplateArgs = { + input: CreateTemplateInput; +}; + + +export type MutationCreateWorkflowArgs = { + input: CreateWorkflowInput; +}; + + export type MutationDeleteAppArgs = { id: Scalars['ID']['input']; }; +export type MutationDeleteChannelArgs = { + id: Scalars['ID']['input']; +}; + + +export type MutationDeleteContactArgs = { + id: Scalars['ID']['input']; +}; + + export type MutationDeleteDeviceArgs = { id: Scalars['ID']['input']; }; +export type MutationDeleteHookArgs = { + id: Scalars['ID']['input']; +}; + + +export type MutationDeleteTemplateArgs = { + id: Scalars['ID']['input']; +}; + + +export type MutationDeleteWorkflowArgs = { + id: Scalars['ID']['input']; +}; + + +export type MutationMarkInAppMessageReadArgs = { + id: Scalars['ID']['input']; +}; + + export type MutationRegenerateApiKeyArgs = { id: Scalars['ID']['input']; }; @@ -278,17 +481,62 @@ export type MutationTrackNotificationOpenedArgs = { }; +export type MutationTriggerWorkflowArgs = { + input: TriggerWorkflowInput; +}; + + export type MutationUpdateAppArgs = { id: Scalars['ID']['input']; input: UpdateAppInput; }; +export type MutationUpdateChannelArgs = { + id: Scalars['ID']['input']; + input: UpdateChannelInput; +}; + + +export type MutationUpdateContactArgs = { + id: Scalars['ID']['input']; + input: UpdateContactInput; +}; + + +export type MutationUpdateContactPreferenceArgs = { + input: UpdateContactPreferenceInput; +}; + + export type MutationUpdateDeviceArgs = { id: Scalars['ID']['input']; input: UpdateDeviceInput; }; + +export type MutationUpdateHookArgs = { + id: Scalars['ID']['input']; + input: UpdateHookInput; +}; + + +export type MutationUpdateTemplateArgs = { + id: Scalars['ID']['input']; + input: UpdateTemplateInput; +}; + + +export type MutationUpdateWorkflowArgs = { + id: Scalars['ID']['input']; + input: UpdateWorkflowInput; +}; + + +export type MutationUpsertContactDeviceArgs = { + input: UpsertContactDeviceInput; +}; + export type Notification = { __typename?: 'Notification'; id: Scalars['ID']['output']; @@ -309,6 +557,7 @@ export type Notification = { totalSent: Scalars['Int']['output']; totalDelivered: Scalars['Int']['output']; totalFailed: Scalars['Int']['output']; + totalOpened: Scalars['Int']['output']; totalClicked: Scalars['Int']['output']; deliveryLogs?: Maybe>; createdAt: Scalars['Timestamp']['output']; @@ -375,6 +624,11 @@ export type Query = { appExists: Scalars['Boolean']['output']; appStats?: Maybe; apps: Array; + channel?: Maybe; + channels: Array; + contact?: Maybe; + contactByExternalId?: Maybe; + contacts: Array; dashboardStats: DashboardStats; deliveryLogs: Array; device?: Maybe; @@ -383,9 +637,18 @@ export type Query = { generateVapidKeys: VapidKeys; getEngagementMetrics?: Maybe; getNotificationAnalytics?: Maybe; + hook?: Maybe; + hooks: Array; + inAppMessages: Array; notification?: Maybe; notifications: Array; platformStats: Array; + template?: Maybe