feat(webapp): hipaa baa add-on on paid pricing tiers (#3904)
## Summary HIPAA BAA is offered as a paid add-on on every paid plan. Each paid tier on the in-app pricing card now has a "HIPAA BAA add-on" row with a "Request a BAA" link that opens the existing contact dialog pre-filled with a new `hipaa` inquiry type, prompting the user for their company name and a brief description of the PHI workload. The contact form's `feedbackTypes` are restructured to match the marketing /contact form: every inquiry type carries a Plain label ID and a "Contact form: ..." thread title, so threads land in Plain identically whether they come from the dashboard or the marketing site. The included-compute line on each tier also picks up the credits wording from the marketing pricing page, and the Enterprise tier lifts its title above the features row.
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
area: webapp
|
||||
type: feature
|
||||
---
|
||||
|
||||
Request a HIPAA BAA add-on directly from any paid pricing tier in the dashboard.
|
||||
@@ -6,14 +6,16 @@ export function DefinitionTip({
|
||||
content,
|
||||
children,
|
||||
title,
|
||||
disableHoverableContent = true,
|
||||
}: {
|
||||
content: React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
title: React.ReactNode;
|
||||
disableHoverableContent?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<Tooltip disableHoverableContent>
|
||||
<Tooltip disableHoverableContent={disableHoverableContent}>
|
||||
<TooltipTrigger className="text-left">
|
||||
<span className="cursor-default underline decoration-charcoal-500 decoration-dashed underline-offset-4 transition hover:decoration-charcoal-400">
|
||||
{children}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { conform, useForm } from "@conform-to/react";
|
||||
import { parse } from "@conform-to/zod";
|
||||
import { InformationCircleIcon, ArrowUpCircleIcon } from "@heroicons/react/20/solid";
|
||||
import { EnvelopeIcon } from "@heroicons/react/24/solid";
|
||||
import { EnvelopeIcon, ShieldCheckIcon } from "@heroicons/react/24/solid";
|
||||
import { Form, useActionData, useLocation, useNavigation, useSearchParams } from "@remix-run/react";
|
||||
import { type ReactNode, useEffect, useState } from "react";
|
||||
import { type FeedbackType, feedbackTypeLabel, schema } from "~/routes/resources.feedback";
|
||||
import { type FeedbackType, feedbackTypes, schema } from "~/routes/resources.feedback";
|
||||
import { Button } from "./primitives/Buttons";
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTrigger } from "./primitives/Dialog";
|
||||
import { Fieldset } from "./primitives/Fieldset";
|
||||
@@ -84,9 +84,12 @@ export function Feedback({ button, defaultValue = "bug", onOpenChange }: Feedbac
|
||||
How can we help? We read every message and will respond as quickly as we can.
|
||||
</Paragraph>
|
||||
</div>
|
||||
{!(type === "feature" || type === "help" || type === "concurrency") && (
|
||||
<hr className="border-grid-dimmed" />
|
||||
)}
|
||||
{!(
|
||||
type === "feature" ||
|
||||
type === "help" ||
|
||||
type === "concurrency" ||
|
||||
type === "hipaa"
|
||||
) && <hr className="border-grid-dimmed" />}
|
||||
<Form method="post" action="/resources/feedback" {...form.props} className="w-full">
|
||||
<Fieldset className="max-w-full gap-y-3">
|
||||
<input value={location.pathname} {...conform.input(path, { type: "hidden" })} />
|
||||
@@ -132,6 +135,19 @@ export function Feedback({ button, defaultValue = "bug", onOpenChange }: Feedbac
|
||||
</Paragraph>
|
||||
</InfoPanel>
|
||||
)}
|
||||
{type === "hipaa" && (
|
||||
<InfoPanel
|
||||
icon={ShieldCheckIcon}
|
||||
iconClassName="text-green-500"
|
||||
panelClassName="w-full mb-2"
|
||||
>
|
||||
<Paragraph variant="small">
|
||||
We offer a signed Business Associate Agreement (BAA) as a paid add-on on any
|
||||
paid plan. To help us get back to you quickly, please include your company
|
||||
name, and a brief description of the PHI workload you plan to run.
|
||||
</Paragraph>
|
||||
</InfoPanel>
|
||||
)}
|
||||
<Select
|
||||
{...conform.select(feedbackType)}
|
||||
variant="tertiary/medium"
|
||||
@@ -139,12 +155,12 @@ export function Feedback({ button, defaultValue = "bug", onOpenChange }: Feedbac
|
||||
defaultValue={type}
|
||||
setValue={(v) => setType(v as FeedbackType)}
|
||||
placeholder="Select type"
|
||||
text={(value) => feedbackTypeLabel[value as FeedbackType]}
|
||||
text={(value) => feedbackTypes[value as FeedbackType].label}
|
||||
dropdownIcon
|
||||
>
|
||||
{Object.entries(feedbackTypeLabel).map(([name, title]) => (
|
||||
{Object.entries(feedbackTypes).map(([name, { label }]) => (
|
||||
<SelectItem key={name} value={name}>
|
||||
{title}
|
||||
{label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
|
||||
@@ -8,19 +8,55 @@ import { sendToPlain } from "~/utils/plain.server";
|
||||
|
||||
let client: PlainClient | undefined;
|
||||
|
||||
export const feedbackTypeLabel = {
|
||||
bug: "Bug report",
|
||||
feature: "Feature request",
|
||||
help: "Help me out",
|
||||
enterprise: "Enterprise enquiry",
|
||||
feedback: "General feedback",
|
||||
concurrency: "Increase my concurrency",
|
||||
region: "Suggest a new region",
|
||||
};
|
||||
export const feedbackTypes = {
|
||||
bug: {
|
||||
label: "Bug report",
|
||||
labelTypeId: "lt_01HB920BTPFS36KH1JT9C36YVY",
|
||||
threadTitle: "Contact form: Bug report",
|
||||
},
|
||||
feature: {
|
||||
label: "Feature request",
|
||||
labelTypeId: "lt_01HB920BV8CJGYXVE15WWN6P07",
|
||||
threadTitle: "Contact form: Feature request",
|
||||
},
|
||||
help: {
|
||||
label: "Help me out",
|
||||
labelTypeId: "lt_01KTVCAPZY5ZJ0SS4ACMXWYYT3",
|
||||
threadTitle: "Contact form: Help me out",
|
||||
},
|
||||
enterprise: {
|
||||
label: "Enterprise enquiry",
|
||||
labelTypeId: "lt_01K7PF5EV2877EH4SZYB667FW4",
|
||||
threadTitle: "Contact form: Enterprise enquiry",
|
||||
},
|
||||
feedback: {
|
||||
label: "General feedback",
|
||||
labelTypeId: "lt_01HB920BSRZ3RA1ETHBVEB5ST2",
|
||||
threadTitle: "Contact form: General feedback",
|
||||
},
|
||||
concurrency: {
|
||||
label: "Increase my concurrency",
|
||||
labelTypeId: "lt_01KTVCCY2PDE5V6WV2PQ8N85K2",
|
||||
threadTitle: "Contact form: Increase my concurrency",
|
||||
},
|
||||
region: {
|
||||
label: "Suggest a new region",
|
||||
labelTypeId: "lt_01KTVCDPYYBW6KS9H5V8MTQ0GG",
|
||||
threadTitle: "Contact form: Suggest a new region",
|
||||
},
|
||||
hipaa: {
|
||||
label: "HIPAA BAA request",
|
||||
labelTypeId: "lt_01KS54WBRYKE6DY369KPK2SS4W",
|
||||
threadTitle: "Contact form: HIPAA BAA request",
|
||||
},
|
||||
} as const satisfies Record<
|
||||
string,
|
||||
{ label: string; labelTypeId?: string; threadTitle: string }
|
||||
>;
|
||||
|
||||
export type FeedbackType = keyof typeof feedbackTypeLabel;
|
||||
export type FeedbackType = keyof typeof feedbackTypes;
|
||||
|
||||
const feedbackTypeLiterals = Object.keys(feedbackTypeLabel).map((key) => z.literal(key));
|
||||
const feedbackTypeLiterals = Object.keys(feedbackTypes).map((key) => z.literal(key));
|
||||
|
||||
const feedbackType = z.union(
|
||||
[feedbackTypeLiterals[0], feedbackTypeLiterals[1], ...feedbackTypeLiterals.slice(2)],
|
||||
@@ -46,16 +82,17 @@ export async function action({ request }: ActionFunctionArgs) {
|
||||
return json(submission);
|
||||
}
|
||||
|
||||
const title = feedbackTypeLabel[submission.value.feedbackType as FeedbackType];
|
||||
const inquiry = feedbackTypes[submission.value.feedbackType as FeedbackType];
|
||||
try {
|
||||
await sendToPlain({
|
||||
userId: user.id,
|
||||
email: user.email,
|
||||
name: user.name ?? user.displayName ?? user.email,
|
||||
title,
|
||||
title: inquiry.threadTitle,
|
||||
labelTypeIds: inquiry.labelTypeId ? [inquiry.labelTypeId] : undefined,
|
||||
components: [
|
||||
uiComponent.text({
|
||||
text: `New ${title} reported by ${user.name} (${user.email})`,
|
||||
text: `New ${inquiry.label} reported by ${user.name} (${user.email})`,
|
||||
}),
|
||||
uiComponent.divider({ spacingSize: "M" }),
|
||||
uiComponent.text({
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {
|
||||
ArrowUpRightIcon,
|
||||
CheckIcon,
|
||||
ExclamationTriangleIcon,
|
||||
ShieldCheckIcon,
|
||||
@@ -37,6 +36,7 @@ import { Header2 } from "~/components/primitives/Headers";
|
||||
import { Paragraph } from "~/components/primitives/Paragraph";
|
||||
import { Spinner } from "~/components/primitives/Spinner";
|
||||
import { TextArea } from "~/components/primitives/TextArea";
|
||||
import { TextLink } from "~/components/primitives/TextLink";
|
||||
import { SimpleTooltip } from "~/components/primitives/Tooltip";
|
||||
import { prisma } from "~/db.server";
|
||||
import { redirectWithErrorMessage } from "~/models/message.server";
|
||||
@@ -237,6 +237,11 @@ const pricingDefinitions = {
|
||||
title: "Query period",
|
||||
content: "The maximum number of days a query can look back when analyzing your task data.",
|
||||
},
|
||||
hipaaBaa: {
|
||||
title: "HIPAA BAA",
|
||||
content:
|
||||
"A signed Business Associate Agreement (BAA) is required to run tasks that process Protected Health Information (PHI) on Trigger.dev Cloud.",
|
||||
},
|
||||
};
|
||||
|
||||
type PricingPlansProps = {
|
||||
@@ -317,9 +322,6 @@ export function TierFree({
|
||||
<TierContainer>
|
||||
<div className="relative">
|
||||
<PricingHeader title={plan.title} cost={0} />
|
||||
<TierLimit href="https://trigger.dev/pricing#computePricing">
|
||||
${plan.limits.includedUsage / 100} free monthly usage
|
||||
</TierLimit>
|
||||
{showGithubVerificationBadge && status === "approved" && (
|
||||
<SimpleTooltip
|
||||
buttonClassName="absolute right-1 top-1"
|
||||
@@ -351,7 +353,6 @@ export function TierFree({
|
||||
</div>
|
||||
{status === "rejected" ? (
|
||||
<div>
|
||||
<hr className="my-6 border-grid-bright" />
|
||||
<div className="flex flex-col gap-2 rounded-sm border border-warning p-4">
|
||||
<ExclamationTriangleIcon className="size-6 text-warning" />
|
||||
<Paragraph variant="small/bright">
|
||||
@@ -533,6 +534,16 @@ export function TierFree({
|
||||
</Form>
|
||||
)}
|
||||
<ul className="flex flex-col gap-2.5">
|
||||
<FeatureItem checked>
|
||||
<DefinitionTip
|
||||
title="Free credits"
|
||||
content={`You get $${
|
||||
plan.limits.includedUsage / 100
|
||||
} of compute each month for free. Requires a verified GitHub account.`}
|
||||
>
|
||||
${plan.limits.includedUsage / 100} / month free credits
|
||||
</DefinitionTip>
|
||||
</FeatureItem>
|
||||
<ConcurrentRuns limits={plan.limits} />
|
||||
<FeatureItem checked>
|
||||
Unlimited{" "}
|
||||
@@ -584,9 +595,6 @@ export function TierHobby({
|
||||
return (
|
||||
<TierContainer isHighlighted={isHighlighted}>
|
||||
<PricingHeader title={plan.title} isHighlighted={isHighlighted} cost={plan.tierPrice} />
|
||||
<TierLimit href="https://trigger.dev/pricing#computePricing">
|
||||
${plan.limits.includedUsage / 100} usage included
|
||||
</TierLimit>
|
||||
<Form action={formAction} method="post" id="subscribe-hobby" className="py-6">
|
||||
<input type="hidden" name="type" value="paid" />
|
||||
<input type="hidden" name="planCode" value={plan.code} />
|
||||
@@ -652,6 +660,24 @@ export function TierHobby({
|
||||
)}
|
||||
</Form>
|
||||
<ul className="flex flex-col gap-2.5">
|
||||
<FeatureItem checked>
|
||||
<DefinitionTip
|
||||
disableHoverableContent={false}
|
||||
title="Credits included"
|
||||
content={
|
||||
<Paragraph variant="small/dimmed" spacing>
|
||||
This plan includes ${plan.limits.includedUsage / 100} of compute each month. After
|
||||
that's used, tasks keep running and you're billed per our{" "}
|
||||
<TextLink target="_blank" to="https://trigger.dev/pricing#computePricing">
|
||||
compute usage rates
|
||||
</TextLink>
|
||||
.
|
||||
</Paragraph>
|
||||
}
|
||||
>
|
||||
${plan.limits.includedUsage / 100} / month credits included
|
||||
</DefinitionTip>
|
||||
</FeatureItem>
|
||||
<ConcurrentRuns limits={plan.limits} />
|
||||
<FeatureItem checked>
|
||||
Unlimited{" "}
|
||||
@@ -672,6 +698,16 @@ export function TierHobby({
|
||||
<SupportLevel limits={plan.limits} />
|
||||
<Alerts limits={plan.limits} />
|
||||
<RealtimeConcurrency limits={plan.limits} />
|
||||
<HIPAAAddOn>
|
||||
<Feedback
|
||||
defaultValue="hipaa"
|
||||
button={
|
||||
<span className="cursor-pointer underline decoration-charcoal-500 underline-offset-4 transition hover:decoration-text-bright">
|
||||
Request a BAA
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
</HIPAAAddOn>
|
||||
</ul>
|
||||
</TierContainer>
|
||||
);
|
||||
@@ -701,9 +737,6 @@ export function TierPro({
|
||||
return (
|
||||
<TierContainer>
|
||||
<PricingHeader title={plan.title} cost={plan.tierPrice} />
|
||||
<TierLimit href="https://trigger.dev/pricing#computePricing">
|
||||
${plan.limits.includedUsage / 100} usage included
|
||||
</TierLimit>
|
||||
<Form action={formAction} method="post" id="subscribe-pro">
|
||||
<div className="py-6">
|
||||
<input type="hidden" name="type" value="paid" />
|
||||
@@ -773,6 +806,24 @@ export function TierPro({
|
||||
</div>
|
||||
</Form>
|
||||
<ul className="flex flex-col gap-2.5">
|
||||
<FeatureItem checked>
|
||||
<DefinitionTip
|
||||
disableHoverableContent={false}
|
||||
title="Credits included"
|
||||
content={
|
||||
<Paragraph variant="small/dimmed" spacing>
|
||||
This plan includes ${plan.limits.includedUsage / 100} of compute each month. After
|
||||
that's used, tasks keep running and you're billed per our{" "}
|
||||
<TextLink target="_blank" to="https://trigger.dev/pricing#computePricing">
|
||||
compute usage rates
|
||||
</TextLink>
|
||||
.
|
||||
</Paragraph>
|
||||
}
|
||||
>
|
||||
${plan.limits.includedUsage / 100} / month credits included
|
||||
</DefinitionTip>
|
||||
</FeatureItem>
|
||||
<ConcurrentRuns limits={plan.limits}>
|
||||
{`Then ${formatCurrency(concurrencyAddOnPricing.centsPerStep / 100, true)}/month per ${
|
||||
concurrencyAddOnPricing.stepSize
|
||||
@@ -801,6 +852,16 @@ export function TierPro({
|
||||
<RealtimeConcurrency limits={plan.limits}>
|
||||
{pricingDefinitions.additionalRealtimeConnections.content}
|
||||
</RealtimeConcurrency>
|
||||
<HIPAAAddOn>
|
||||
<Feedback
|
||||
defaultValue="hipaa"
|
||||
button={
|
||||
<span className="cursor-pointer underline decoration-charcoal-500 underline-offset-4 transition hover:decoration-text-bright">
|
||||
Request a BAA
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
</HIPAAAddOn>
|
||||
</ul>
|
||||
</TierContainer>
|
||||
);
|
||||
@@ -809,41 +870,12 @@ export function TierPro({
|
||||
export function TierEnterprise() {
|
||||
return (
|
||||
<TierContainer>
|
||||
<div className="flex w-full flex-col items-center justify-between gap-4 lg:flex-row">
|
||||
<div className="flex w-full flex-wrap items-center justify-between gap-2 lg:flex-nowrap">
|
||||
<div className="-mt-1 mb-2 flex w-full flex-col gap-2 lg:mb-0 lg:gap-0.5">
|
||||
<h2 className="text-xl font-medium text-text-dimmed">Enterprise</h2>
|
||||
<hr className="my-2 block border-grid-dimmed lg:hidden" />
|
||||
<p className="whitespace-nowrap font-sans text-lg font-normal text-text-bright lg:text-sm">
|
||||
Tailor a custom plan
|
||||
</p>
|
||||
</div>
|
||||
<ul className="flex w-full flex-col gap-y-3 lg:gap-y-1">
|
||||
<FeatureItem checked checkedColor="bright">
|
||||
All Pro plan features +
|
||||
</FeatureItem>
|
||||
<FeatureItem checked checkedColor="bright">
|
||||
Custom log retention
|
||||
</FeatureItem>
|
||||
</ul>
|
||||
<ul className="flex w-full flex-col gap-y-3 lg:gap-y-1">
|
||||
<FeatureItem checked checkedColor="bright">
|
||||
Priority support
|
||||
</FeatureItem>
|
||||
<FeatureItem checked checkedColor="bright">
|
||||
Role-based access control
|
||||
</FeatureItem>
|
||||
</ul>
|
||||
<ul className="flex w-full flex-col gap-y-3 lg:gap-y-1">
|
||||
<FeatureItem checked checkedColor="bright">
|
||||
SOC 2 report
|
||||
</FeatureItem>
|
||||
<FeatureItem checked checkedColor="bright">
|
||||
SSO
|
||||
</FeatureItem>
|
||||
</ul>
|
||||
<div className="mb-4 flex w-full flex-col items-start justify-between gap-4 lg:flex-row lg:items-center">
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<h2 className="text-xl font-medium text-text-dimmed">Enterprise</h2>
|
||||
<p className="font-sans text-lg font-normal text-text-bright">Tailor a custom plan</p>
|
||||
</div>
|
||||
<div className="w-full lg:max-w-[16rem]">
|
||||
<div className="w-full lg:w-auto lg:max-w-[16rem]">
|
||||
<Feedback
|
||||
defaultValue="enterprise"
|
||||
button={
|
||||
@@ -854,6 +886,44 @@ export function TierEnterprise() {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex w-full flex-wrap items-start justify-between gap-2 lg:flex-nowrap">
|
||||
<ul className="flex w-full flex-col gap-y-3 lg:gap-y-1">
|
||||
<FeatureItem checked checkedColor="bright">
|
||||
All Pro plan features +
|
||||
</FeatureItem>
|
||||
<FeatureItem checked checkedColor="bright">
|
||||
Custom log retention
|
||||
</FeatureItem>
|
||||
</ul>
|
||||
<ul className="flex w-full flex-col gap-y-3 lg:gap-y-1">
|
||||
<FeatureItem checked checkedColor="bright">
|
||||
Priority support
|
||||
</FeatureItem>
|
||||
<FeatureItem checked checkedColor="bright">
|
||||
Role-based access control
|
||||
</FeatureItem>
|
||||
</ul>
|
||||
<ul className="flex w-full flex-col gap-y-3 lg:gap-y-1">
|
||||
<FeatureItem checked checkedColor="bright">
|
||||
SOC 2 report
|
||||
</FeatureItem>
|
||||
<FeatureItem checked checkedColor="bright">
|
||||
SSO
|
||||
</FeatureItem>
|
||||
</ul>
|
||||
<ul className="flex w-full flex-col gap-y-3 lg:gap-y-1">
|
||||
<HIPAAAddOn checkedColor="bright">
|
||||
<Feedback
|
||||
defaultValue="hipaa"
|
||||
button={
|
||||
<span className="cursor-pointer underline decoration-charcoal-500 underline-offset-4 transition hover:decoration-text-bright">
|
||||
Request a BAA
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
</HIPAAAddOn>
|
||||
</ul>
|
||||
</div>
|
||||
</TierContainer>
|
||||
);
|
||||
}
|
||||
@@ -921,36 +991,6 @@ function PricingHeader({
|
||||
);
|
||||
}
|
||||
|
||||
function TierLimit({ children, href }: { children: React.ReactNode; href?: string }) {
|
||||
return (
|
||||
<>
|
||||
<hr className="my-6 border-grid-bright" />
|
||||
{href ? (
|
||||
<SimpleTooltip
|
||||
buttonClassName="text-left w-fit"
|
||||
disableHoverableContent
|
||||
button={
|
||||
<a
|
||||
href={href}
|
||||
className="text-left font-sans text-lg font-normal text-text-bright underline decoration-charcoal-500 underline-offset-4 transition hover:decoration-text-bright"
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
}
|
||||
content={
|
||||
<div className="flex items-center gap-1">
|
||||
<Paragraph variant="small">View compute pricing information</Paragraph>
|
||||
<ArrowUpRightIcon className="size-4 text-text-dimmed" />
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<div className="font-sans text-lg font-normal text-text-bright">{children}</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function FeatureItem({
|
||||
checked,
|
||||
checkedColor = "primary",
|
||||
@@ -1206,6 +1246,31 @@ function QueryPeriod({ limits }: { limits: Limits }) {
|
||||
);
|
||||
}
|
||||
|
||||
function HIPAAAddOn({
|
||||
children,
|
||||
checkedColor = "primary",
|
||||
}: {
|
||||
children?: React.ReactNode;
|
||||
checkedColor?: "primary" | "bright";
|
||||
}) {
|
||||
return (
|
||||
<FeatureItem checked checkedColor={checkedColor}>
|
||||
<div className="flex flex-col gap-y-0.5">
|
||||
<div>
|
||||
<DefinitionTip
|
||||
title={pricingDefinitions.hipaaBaa.title}
|
||||
content={pricingDefinitions.hipaaBaa.content}
|
||||
>
|
||||
HIPAA BAA
|
||||
</DefinitionTip>{" "}
|
||||
add-on
|
||||
</div>
|
||||
{children && <span className="text-xs text-text-dimmed">{children}</span>}
|
||||
</div>
|
||||
</FeatureItem>
|
||||
);
|
||||
}
|
||||
|
||||
function Branches({ limits, children }: { limits: Limits; children?: React.ReactNode }) {
|
||||
return (
|
||||
<FeatureItem checked={limits.branches.number > 0}>
|
||||
|
||||
@@ -7,9 +7,17 @@ type Input = {
|
||||
name: string;
|
||||
title: string;
|
||||
components: ReturnType<typeof uiComponent.text>[];
|
||||
labelTypeIds?: string[];
|
||||
};
|
||||
|
||||
export async function sendToPlain({ userId, email, name, title, components }: Input) {
|
||||
export async function sendToPlain({
|
||||
userId,
|
||||
email,
|
||||
name,
|
||||
title,
|
||||
components,
|
||||
labelTypeIds,
|
||||
}: Input) {
|
||||
if (!env.PLAIN_API_KEY) {
|
||||
return;
|
||||
}
|
||||
@@ -51,6 +59,7 @@ export async function sendToPlain({ userId, email, name, title, components }: In
|
||||
},
|
||||
title: title,
|
||||
components: components,
|
||||
labelTypeIds,
|
||||
});
|
||||
|
||||
if (createThreadRes.error) {
|
||||
|
||||
Reference in New Issue
Block a user