Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions internal/resend/emails/api_v1_migration.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
"use client";
import { Button } from "@react-email/button";
import { Heading } from "@react-email/heading";
import { Hr } from "@react-email/hr";
import { Link } from "@react-email/link";
import { Section } from "@react-email/section";
import { Text } from "@react-email/text";
// biome-ignore lint/correctness/noUnusedImports: react-email needs this imported
import React from "react";
import { Layout } from "../src/components/layout";
import { Signature } from "../src/components/signature";

export type Props = {
username: string;
workspaceName: string;
deprecatedEndpoints: string[];
};

export function ApiV1Migration({ username, workspaceName, deprecatedEndpoints }: Props) {
return (
<Layout>
<Heading className="font-sans text-3xl text-semibold text-center">
Action Required: Migrate from API v1 to v2
</Heading>
<Text>Hey {username},</Text>
<Text>
This is a follow-up to our previous email in August about the deprecation of our API v1.
We're reaching out because your workspace <strong>{workspaceName}</strong> is still using
deprecated v1 endpoints.
</Text>

<Text className="font-semibold text-red-600">
Important: <span className="font-mono">api.unkey.dev/v1</span> will be discontinued on
January 1st, 2026.
</Text>

<Section>
<Text className="font-semibold">
Your workspace is currently using the following deprecated v1 endpoints:
</Text>
<ul className="pb-4">
{deprecatedEndpoints.map((endpoint) => (
<li key={endpoint} className="pb-2">
<code className="bg-gray-100 px-2 py-1 rounded text-sm">{endpoint}</code>
</li>
))}
</ul>
</Section>

<Section className="text-center py-3">
<Button
href="https://www.unkey.com/docs/api-reference/v1/migration"
className="bg-gray-900 text-gray-50 rounded-lg p-3 w-2/3"
>
View Migration Guide
</Button>
</Section>

<Text>
Our migration guide includes step-by-step instructions, code examples, and a complete
mapping of v1 to v2 endpoints.
</Text>

<Hr />

<Text>
Need help with your migration? Please reach out to{" "}
<Link href="mailto:support@unkey.dev">support@unkey.dev</Link> or just reply to this email.
Our team is ready to help with anything.
</Text>

<Signature signedBy="Andreas" />
</Layout>
);
}

ApiV1Migration.PreviewProps = {
username: "John Doe",
workspaceName: "Acme Corp",
deprecatedEndpoints: ["/v1/keys.create", "/v1/keys.verify", "/v1/apis.create"],
} satisfies Props;

// biome-ignore lint/style/noDefaultExport: Too scared to modify that one
export default ApiV1Migration;
32 changes: 32 additions & 0 deletions internal/resend/src/client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Resend as Client } from "resend";
import { render } from "@react-email/render";
// biome-ignore lint/correctness/noUnusedImports: React UMD bypass
import React from "react";
import { ApiV1Migration } from "../emails/api_v1_migration";
import { PaymentIssue } from "../emails/payment_issue";
import { SecretScanningKeyDetected } from "../emails/secret_scanning_key_detected";
import { TrialEnded } from "../emails/trial_ended";
Expand Down Expand Up @@ -108,4 +109,35 @@ export class Resend {
console.error(error);
}
}

public async sendApiV1MigrationEmail(req: {
email: string;
name: string;
workspace: string;
deprecatedEndpoints: string[];
}): Promise<void> {
const html = render(
<ApiV1Migration
username={req.name}
workspaceName={req.workspace}
deprecatedEndpoints={req.deprecatedEndpoints}
/>,
);
try {
const result = await this.client.emails.send({
to: req.email,
from: "Andreas from Unkey <andreas@updates.unkey.com>",
replyTo: this.replyTo,
subject: "Action Required: Migrate from API v1 to v2 - Deadline January 1st, 2026",
html,
});

if (!result.error) {
return;
}
throw result.error;
} catch (error) {
console.error("Error occurred sending API v1 migration email ", JSON.stringify(error));
}
}
}
Loading
Loading