chore: import upstream snapshot with attribution
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
import { LoopsIcon } from '@/components/icons'
|
||||
import { buildTriggerSubBlocks } from '@/triggers'
|
||||
import {
|
||||
buildLoopsExtraFields,
|
||||
buildLoopsSentOutputs,
|
||||
loopsSetupInstructions,
|
||||
loopsTriggerOptions,
|
||||
} from '@/triggers/loops/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
/**
|
||||
* Loops Campaign Email Sent Trigger.
|
||||
* Triggers when a campaign email is sent to a contact.
|
||||
*/
|
||||
export const loopsCampaignEmailSentTrigger: TriggerConfig = {
|
||||
id: 'loops_campaign_email_sent',
|
||||
name: 'Loops Campaign Email Sent',
|
||||
provider: 'loops',
|
||||
description: 'Trigger workflow when a Loops campaign email is sent',
|
||||
version: '1.0.0',
|
||||
icon: LoopsIcon,
|
||||
|
||||
subBlocks: buildTriggerSubBlocks({
|
||||
triggerId: 'loops_campaign_email_sent',
|
||||
triggerOptions: loopsTriggerOptions,
|
||||
setupInstructions: loopsSetupInstructions('campaign.email.sent'),
|
||||
extraFields: buildLoopsExtraFields('loops_campaign_email_sent'),
|
||||
}),
|
||||
|
||||
outputs: buildLoopsSentOutputs(),
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { LoopsIcon } from '@/components/icons'
|
||||
import { buildTriggerSubBlocks } from '@/triggers'
|
||||
import {
|
||||
buildLoopsExtraFields,
|
||||
buildLoopsOutputs,
|
||||
loopsSetupInstructions,
|
||||
loopsTriggerOptions,
|
||||
} from '@/triggers/loops/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
/**
|
||||
* Loops Email Clicked Trigger.
|
||||
* Triggers when a recipient clicks a link in an email (campaigns and loops only).
|
||||
*/
|
||||
export const loopsEmailClickedTrigger: TriggerConfig = {
|
||||
id: 'loops_email_clicked',
|
||||
name: 'Loops Email Clicked',
|
||||
provider: 'loops',
|
||||
description: 'Trigger workflow when a link in a Loops email is clicked',
|
||||
version: '1.0.0',
|
||||
icon: LoopsIcon,
|
||||
|
||||
subBlocks: buildTriggerSubBlocks({
|
||||
triggerId: 'loops_email_clicked',
|
||||
triggerOptions: loopsTriggerOptions,
|
||||
setupInstructions: loopsSetupInstructions('email.clicked'),
|
||||
extraFields: buildLoopsExtraFields('loops_email_clicked'),
|
||||
}),
|
||||
|
||||
outputs: buildLoopsOutputs(),
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { LoopsIcon } from '@/components/icons'
|
||||
import { buildTriggerSubBlocks } from '@/triggers'
|
||||
import {
|
||||
buildLoopsExtraFields,
|
||||
buildLoopsOutputs,
|
||||
loopsSetupInstructions,
|
||||
loopsTriggerOptions,
|
||||
} from '@/triggers/loops/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
/**
|
||||
* Loops Email Delivered Trigger.
|
||||
* Triggers when an email is delivered to the recipient.
|
||||
*
|
||||
* This is the PRIMARY trigger — it includes the dropdown for selecting trigger type.
|
||||
*/
|
||||
export const loopsEmailDeliveredTrigger: TriggerConfig = {
|
||||
id: 'loops_email_delivered',
|
||||
name: 'Loops Email Delivered',
|
||||
provider: 'loops',
|
||||
description: 'Trigger workflow when a Loops email is delivered',
|
||||
version: '1.0.0',
|
||||
icon: LoopsIcon,
|
||||
|
||||
subBlocks: buildTriggerSubBlocks({
|
||||
triggerId: 'loops_email_delivered',
|
||||
triggerOptions: loopsTriggerOptions,
|
||||
includeDropdown: true,
|
||||
setupInstructions: loopsSetupInstructions('email.delivered'),
|
||||
extraFields: buildLoopsExtraFields('loops_email_delivered'),
|
||||
}),
|
||||
|
||||
outputs: buildLoopsOutputs(),
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { LoopsIcon } from '@/components/icons'
|
||||
import { buildTriggerSubBlocks } from '@/triggers'
|
||||
import {
|
||||
buildLoopsExtraFields,
|
||||
buildLoopsOutputs,
|
||||
loopsSetupInstructions,
|
||||
loopsTriggerOptions,
|
||||
} from '@/triggers/loops/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
/**
|
||||
* Loops Email Hard Bounced Trigger.
|
||||
* Triggers on a permanent email delivery failure.
|
||||
*/
|
||||
export const loopsEmailHardBouncedTrigger: TriggerConfig = {
|
||||
id: 'loops_email_hard_bounced',
|
||||
name: 'Loops Email Hard Bounced',
|
||||
provider: 'loops',
|
||||
description: 'Trigger workflow when a Loops email hard bounces',
|
||||
version: '1.0.0',
|
||||
icon: LoopsIcon,
|
||||
|
||||
subBlocks: buildTriggerSubBlocks({
|
||||
triggerId: 'loops_email_hard_bounced',
|
||||
triggerOptions: loopsTriggerOptions,
|
||||
setupInstructions: loopsSetupInstructions('email.hardBounced'),
|
||||
extraFields: buildLoopsExtraFields('loops_email_hard_bounced'),
|
||||
}),
|
||||
|
||||
outputs: buildLoopsOutputs(),
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { LoopsIcon } from '@/components/icons'
|
||||
import { buildTriggerSubBlocks } from '@/triggers'
|
||||
import {
|
||||
buildLoopsExtraFields,
|
||||
buildLoopsOutputs,
|
||||
loopsSetupInstructions,
|
||||
loopsTriggerOptions,
|
||||
} from '@/triggers/loops/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
/**
|
||||
* Loops Email Opened Trigger.
|
||||
* Triggers when a recipient opens an email (campaigns and loops only).
|
||||
*/
|
||||
export const loopsEmailOpenedTrigger: TriggerConfig = {
|
||||
id: 'loops_email_opened',
|
||||
name: 'Loops Email Opened',
|
||||
provider: 'loops',
|
||||
description: 'Trigger workflow when a Loops email is opened',
|
||||
version: '1.0.0',
|
||||
icon: LoopsIcon,
|
||||
|
||||
subBlocks: buildTriggerSubBlocks({
|
||||
triggerId: 'loops_email_opened',
|
||||
triggerOptions: loopsTriggerOptions,
|
||||
setupInstructions: loopsSetupInstructions('email.opened'),
|
||||
extraFields: buildLoopsExtraFields('loops_email_opened'),
|
||||
}),
|
||||
|
||||
outputs: buildLoopsOutputs(),
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { LoopsIcon } from '@/components/icons'
|
||||
import { buildTriggerSubBlocks } from '@/triggers'
|
||||
import {
|
||||
buildLoopsExtraFields,
|
||||
buildLoopsOutputs,
|
||||
loopsSetupInstructions,
|
||||
loopsTriggerOptions,
|
||||
} from '@/triggers/loops/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
/**
|
||||
* Loops Email Soft Bounced Trigger.
|
||||
* Triggers on a temporary email delivery failure.
|
||||
*/
|
||||
export const loopsEmailSoftBouncedTrigger: TriggerConfig = {
|
||||
id: 'loops_email_soft_bounced',
|
||||
name: 'Loops Email Soft Bounced',
|
||||
provider: 'loops',
|
||||
description: 'Trigger workflow when a Loops email soft bounces',
|
||||
version: '1.0.0',
|
||||
icon: LoopsIcon,
|
||||
|
||||
subBlocks: buildTriggerSubBlocks({
|
||||
triggerId: 'loops_email_soft_bounced',
|
||||
triggerOptions: loopsTriggerOptions,
|
||||
setupInstructions: loopsSetupInstructions('email.softBounced'),
|
||||
extraFields: buildLoopsExtraFields('loops_email_soft_bounced'),
|
||||
}),
|
||||
|
||||
outputs: buildLoopsOutputs(),
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export { loopsCampaignEmailSentTrigger } from './campaign_email_sent'
|
||||
export { loopsEmailClickedTrigger } from './email_clicked'
|
||||
export { loopsEmailDeliveredTrigger } from './email_delivered'
|
||||
export { loopsEmailHardBouncedTrigger } from './email_hard_bounced'
|
||||
export { loopsEmailOpenedTrigger } from './email_opened'
|
||||
export { loopsEmailSoftBouncedTrigger } from './email_soft_bounced'
|
||||
export { loopsLoopEmailSentTrigger } from './loop_email_sent'
|
||||
export { loopsTransactionalEmailSentTrigger } from './transactional_email_sent'
|
||||
@@ -0,0 +1,38 @@
|
||||
import { LoopsIcon } from '@/components/icons'
|
||||
import { buildTriggerSubBlocks } from '@/triggers'
|
||||
import {
|
||||
buildLoopsExtraFields,
|
||||
buildLoopsSentOutputs,
|
||||
loopsSetupInstructions,
|
||||
loopsTriggerOptions,
|
||||
} from '@/triggers/loops/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
/**
|
||||
* Loops Loop Email Sent Trigger.
|
||||
* Triggers when a loop (workflow) email is sent to a contact.
|
||||
*/
|
||||
export const loopsLoopEmailSentTrigger: TriggerConfig = {
|
||||
id: 'loops_loop_email_sent',
|
||||
name: 'Loops Loop Email Sent',
|
||||
provider: 'loops',
|
||||
description: 'Trigger workflow when a Loops loop email is sent',
|
||||
version: '1.0.0',
|
||||
icon: LoopsIcon,
|
||||
|
||||
subBlocks: buildTriggerSubBlocks({
|
||||
triggerId: 'loops_loop_email_sent',
|
||||
triggerOptions: loopsTriggerOptions,
|
||||
setupInstructions: loopsSetupInstructions('loop.email.sent'),
|
||||
extraFields: buildLoopsExtraFields('loops_loop_email_sent'),
|
||||
}),
|
||||
|
||||
outputs: buildLoopsSentOutputs(),
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { LoopsIcon } from '@/components/icons'
|
||||
import { buildTriggerSubBlocks } from '@/triggers'
|
||||
import {
|
||||
buildLoopsExtraFields,
|
||||
buildLoopsSentOutputs,
|
||||
loopsSetupInstructions,
|
||||
loopsTriggerOptions,
|
||||
} from '@/triggers/loops/utils'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
/**
|
||||
* Loops Transactional Email Sent Trigger.
|
||||
* Triggers when a transactional email is sent to a contact.
|
||||
*/
|
||||
export const loopsTransactionalEmailSentTrigger: TriggerConfig = {
|
||||
id: 'loops_transactional_email_sent',
|
||||
name: 'Loops Transactional Email Sent',
|
||||
provider: 'loops',
|
||||
description: 'Trigger workflow when a Loops transactional email is sent',
|
||||
version: '1.0.0',
|
||||
icon: LoopsIcon,
|
||||
|
||||
subBlocks: buildTriggerSubBlocks({
|
||||
triggerId: 'loops_transactional_email_sent',
|
||||
triggerOptions: loopsTriggerOptions,
|
||||
setupInstructions: loopsSetupInstructions('transactional.email.sent'),
|
||||
extraFields: buildLoopsExtraFields('loops_transactional_email_sent'),
|
||||
}),
|
||||
|
||||
outputs: buildLoopsSentOutputs(),
|
||||
|
||||
webhook: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
import type { SubBlockConfig } from '@/blocks/types'
|
||||
import type { TriggerOutput } from '@/triggers/types'
|
||||
|
||||
/**
|
||||
* Maps Sim Loops trigger IDs to the Loops webhook `eventName` value.
|
||||
* Kept in sync with `matchEvent` in the Loops webhook provider handler.
|
||||
* @see https://loops.so/docs/webhooks
|
||||
*/
|
||||
export const LOOPS_TRIGGER_TO_EVENT_TYPE: Record<string, string> = {
|
||||
loops_email_delivered: 'email.delivered',
|
||||
loops_email_opened: 'email.opened',
|
||||
loops_email_clicked: 'email.clicked',
|
||||
loops_email_hard_bounced: 'email.hardBounced',
|
||||
loops_email_soft_bounced: 'email.softBounced',
|
||||
loops_campaign_email_sent: 'campaign.email.sent',
|
||||
loops_loop_email_sent: 'loop.email.sent',
|
||||
loops_transactional_email_sent: 'transactional.email.sent',
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared trigger dropdown options for all Loops triggers.
|
||||
*/
|
||||
export const loopsTriggerOptions = [
|
||||
{ label: 'Email Delivered', id: 'loops_email_delivered' },
|
||||
{ label: 'Email Opened', id: 'loops_email_opened' },
|
||||
{ label: 'Email Clicked', id: 'loops_email_clicked' },
|
||||
{ label: 'Email Hard Bounced', id: 'loops_email_hard_bounced' },
|
||||
{ label: 'Email Soft Bounced', id: 'loops_email_soft_bounced' },
|
||||
{ label: 'Campaign Email Sent', id: 'loops_campaign_email_sent' },
|
||||
{ label: 'Loop Email Sent', id: 'loops_loop_email_sent' },
|
||||
{ label: 'Transactional Email Sent', id: 'loops_transactional_email_sent' },
|
||||
]
|
||||
|
||||
/**
|
||||
* Returns true if the incoming Loops webhook body matches the configured trigger.
|
||||
* Matches on the payload `eventName` field.
|
||||
*/
|
||||
export function isLoopsEventMatch(triggerId: string, body: Record<string, unknown>): boolean {
|
||||
const expected = LOOPS_TRIGGER_TO_EVENT_TYPE[triggerId]
|
||||
if (!expected) return false
|
||||
return (body?.eventName as string | undefined) === expected
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates setup instructions for manual Loops webhook configuration.
|
||||
* Loops webhooks are created in the Loops dashboard; the signing secret must be
|
||||
* pasted into the trigger configuration so Sim can verify the signature.
|
||||
*/
|
||||
export function loopsSetupInstructions(eventType: string): string {
|
||||
const instructions = [
|
||||
'Copy the <strong>Webhook URL</strong> above.',
|
||||
'In Loops, go to <a href="https://app.loops.so/settings?page=webhooks" target="_blank" rel="noopener noreferrer">Settings > Webhooks</a> and click <strong>"Add endpoint"</strong>.',
|
||||
'Paste the <strong>Webhook URL</strong> into the endpoint URL field.',
|
||||
`Subscribe the endpoint to the <strong>${eventType}</strong> event.`,
|
||||
'Copy the endpoint <strong>Signing Secret</strong> from Loops and paste it into the <strong>Signing Secret</strong> field above.',
|
||||
'Click <strong>"Save"</strong> above to activate your trigger.',
|
||||
]
|
||||
|
||||
return instructions
|
||||
.map(
|
||||
(instruction, index) =>
|
||||
`<div class="mb-3"><strong>${index + 1}.</strong> ${instruction}</div>`
|
||||
)
|
||||
.join('')
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds Loops-specific extra fields. Includes the signing secret used to verify
|
||||
* the webhook signature (manual setup — pasted from the Loops dashboard).
|
||||
*/
|
||||
export function buildLoopsExtraFields(triggerId: string): SubBlockConfig[] {
|
||||
return [
|
||||
{
|
||||
id: 'signingSecret',
|
||||
title: 'Signing Secret',
|
||||
type: 'short-input',
|
||||
placeholder: 'Paste the Loops endpoint signing secret',
|
||||
description: 'Required to verify the webhook signature from Loops.',
|
||||
password: true,
|
||||
paramVisibility: 'user-only',
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
condition: { field: 'selectedTriggerId', value: triggerId },
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs for Loops email tracking events (delivered, opened, clicked, hardBounced,
|
||||
* softBounced). All five engagement events share this payload shape.
|
||||
* @see https://loops.so/docs/webhooks
|
||||
*/
|
||||
export function buildLoopsOutputs(): Record<string, TriggerOutput> {
|
||||
return {
|
||||
eventName: {
|
||||
type: 'string',
|
||||
description: 'Event type (e.g., email.delivered, email.opened, email.clicked)',
|
||||
},
|
||||
eventTime: {
|
||||
type: 'number',
|
||||
description: 'Unix timestamp (seconds) when the event occurred',
|
||||
},
|
||||
webhookSchemaVersion: {
|
||||
type: 'string',
|
||||
description: 'Webhook schema version (e.g., "1.0.0")',
|
||||
},
|
||||
sourceType: {
|
||||
type: 'string',
|
||||
description: 'Source of the email: "campaign", "loop", or "transactional"',
|
||||
},
|
||||
campaignId: {
|
||||
type: 'string',
|
||||
description: 'Campaign ID, present when sourceType is "campaign"',
|
||||
},
|
||||
loopId: {
|
||||
type: 'string',
|
||||
description: 'Loop (workflow) ID, present when sourceType is "loop"',
|
||||
},
|
||||
transactionalId: {
|
||||
type: 'string',
|
||||
description: 'Transactional email ID, present when sourceType is "transactional"',
|
||||
},
|
||||
email: {
|
||||
type: 'json',
|
||||
description: 'Email object from the payload (id, emailMessageId, subject)',
|
||||
},
|
||||
emailId: {
|
||||
type: 'string',
|
||||
description: 'Unique email ID (payload `email.id`)',
|
||||
},
|
||||
emailMessageId: {
|
||||
type: 'string',
|
||||
description: 'Sent email message ID (payload `email.emailMessageId`)',
|
||||
},
|
||||
subject: {
|
||||
type: 'string',
|
||||
description: 'Email subject line (payload `email.subject`)',
|
||||
},
|
||||
contactIdentity: {
|
||||
type: 'json',
|
||||
description: 'Contact identity object from the payload (id, email, userId)',
|
||||
},
|
||||
contactId: {
|
||||
type: 'string',
|
||||
description: 'Contact ID (payload `contactIdentity.id`)',
|
||||
},
|
||||
contactEmail: {
|
||||
type: 'string',
|
||||
description: 'Contact email address (payload `contactIdentity.email`)',
|
||||
},
|
||||
userId: {
|
||||
type: 'string',
|
||||
description: 'Contact user ID, when set (payload `contactIdentity.userId`)',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs for Loops "sent" events (campaign.email.sent, loop.email.sent,
|
||||
* transactional.email.sent). These payloads omit `sourceType` and instead carry the
|
||||
* source name (`campaignName`/`loopName`) and, for campaign/loop sends, the resolved
|
||||
* `mailingLists` array.
|
||||
* @see https://loops.so/docs/webhooks
|
||||
*/
|
||||
export function buildLoopsSentOutputs(): Record<string, TriggerOutput> {
|
||||
return {
|
||||
eventName: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Event type (e.g., campaign.email.sent, loop.email.sent, transactional.email.sent)',
|
||||
},
|
||||
eventTime: {
|
||||
type: 'number',
|
||||
description: 'Unix timestamp (seconds) when the event occurred',
|
||||
},
|
||||
webhookSchemaVersion: {
|
||||
type: 'string',
|
||||
description: 'Webhook schema version (e.g., "1.0.0")',
|
||||
},
|
||||
campaignId: {
|
||||
type: 'string',
|
||||
description: 'Campaign ID, present on campaign.email.sent',
|
||||
},
|
||||
campaignName: {
|
||||
type: 'string',
|
||||
description: 'Campaign name, present on campaign.email.sent',
|
||||
},
|
||||
loopId: {
|
||||
type: 'string',
|
||||
description: 'Loop (workflow) ID, present on loop.email.sent',
|
||||
},
|
||||
loopName: {
|
||||
type: 'string',
|
||||
description: 'Loop (workflow) name, present on loop.email.sent',
|
||||
},
|
||||
transactionalId: {
|
||||
type: 'string',
|
||||
description: 'Transactional email ID, present on transactional.email.sent',
|
||||
},
|
||||
email: {
|
||||
type: 'json',
|
||||
description: 'Email object from the payload (id, emailMessageId, subject)',
|
||||
},
|
||||
emailId: {
|
||||
type: 'string',
|
||||
description: 'Unique email ID (payload `email.id`)',
|
||||
},
|
||||
emailMessageId: {
|
||||
type: 'string',
|
||||
description: 'Sent email message ID (payload `email.emailMessageId`)',
|
||||
},
|
||||
subject: {
|
||||
type: 'string',
|
||||
description: 'Email subject line (payload `email.subject`)',
|
||||
},
|
||||
contactIdentity: {
|
||||
type: 'json',
|
||||
description: 'Contact identity object from the payload (id, email, userId)',
|
||||
},
|
||||
contactId: {
|
||||
type: 'string',
|
||||
description: 'Contact ID (payload `contactIdentity.id`)',
|
||||
},
|
||||
contactEmail: {
|
||||
type: 'string',
|
||||
description: 'Contact email address (payload `contactIdentity.email`)',
|
||||
},
|
||||
userId: {
|
||||
type: 'string',
|
||||
description: 'Contact user ID, when set (payload `contactIdentity.userId`)',
|
||||
},
|
||||
mailingLists: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Mailing lists the send targeted (id, name, description, isPublic); present on campaign and loop sends',
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user