import { RESEND_CONTACT_SCHEMA, RESEND_CONTACT_DELETE_RESPONSE_SCHEMA, RESEND_CONTACT_DELETE_WIRE_SCHEMA, RESEND_CONTACT_MUTATION_RESPONSE_SCHEMA, RESEND_CONTACT_SEGMENT_DELETE_SCHEMA, RESEND_CONTACT_SEGMENT_DELETE_WIRE_SCHEMA, RESEND_CONTACT_SEGMENT_MUTATION_SCHEMA, RESEND_CONTACT_SEGMENT_MUTATION_WIRE_SCHEMA, RESEND_CONTACT_SUMMARY_SCHEMA, RESEND_CONTACT_SUMMARY_WIRE_SCHEMA, RESEND_CONTACT_WIRE_SCHEMA, RESEND_SEGMENT_SUMMARY_SCHEMA, RESEND_SEGMENT_SUMMARY_WIRE_SCHEMA, resendListSchema, resendListWireSchema, } from "@automate.ax/integration-contracts/resend" import * as z from "zod" import { defineAction } from "../../automation/actions" import { getResendApi, RESEND_FULL_ACCESS_ACCOUNT_OPTIONS } from "./lib" const RESEND_ACCOUNT = "resend" const CONTACT_PROPERTY_VALUE_SCHEMA = z.union([ z.string(), z.number(), z.null(), ]) const CONTACT_CREATE_SCHEMA = z.object({ /** Contact email address. */ email: z.email(), /** Contact first name. */ firstName: z.string().optional(), /** Contact last name. */ lastName: z.string().optional(), /** Custom Resend contact-property values. */ properties: z .record(z.string().min(1), CONTACT_PROPERTY_VALUE_SCHEMA) .optional(), /** Existing segment IDs the new contact should join. */ segments: z .object({ id: z.string().min(1) }) .array() .optional(), /** Per-topic subscription choices. */ topics: z .object({ id: z.string().min(1), subscription: z.enum(["opt_in", "opt_out"]), }) .array() .optional(), /** Whether the contact is globally unsubscribed from broadcasts. */ unsubscribed: z.boolean().optional(), }) const CONTACT_PATCH_SCHEMA = z .object({ /** Replacement contact email address. */ email: z.email().optional(), /** Replacement contact first name. */ firstName: z.string().nullable().optional(), /** Replacement contact last name. */ lastName: z.string().nullable().optional(), /** Replacement custom contact-property values. */ properties: z .record(z.string().min(1), CONTACT_PROPERTY_VALUE_SCHEMA) .optional(), /** Replacement global unsubscribe state. */ unsubscribed: z.boolean().optional(), }) .refine((patch) => Object.keys(patch).length > 0, { message: "Provide at least one contact field to update.", }) const PAGE_FIELDS = { after: z.string().min(1).optional(), before: z.string().min(1).optional(), limit: z.number().int().min(1).max(100).prefault(20), } const CONTACT_LIST_SCHEMA = resendListSchema(RESEND_CONTACT_SUMMARY_SCHEMA) const CONTACT_LIST_WIRE_SCHEMA = resendListWireSchema( RESEND_CONTACT_SUMMARY_WIRE_SCHEMA, ) const SEGMENT_LIST_SCHEMA = resendListSchema(RESEND_SEGMENT_SUMMARY_SCHEMA) const SEGMENT_LIST_WIRE_SCHEMA = resendListWireSchema( RESEND_SEGMENT_SUMMARY_WIRE_SCHEMA, ) /** Creates a Resend contact with properties and subscription memberships. */ export const createResendContact = defineAction("Create Resend contact") .describe("Creates a contact with properties, segments, and topic choices.") .account(RESEND_ACCOUNT, RESEND_FULL_ACCESS_ACCOUNT_OPTIONS) .input(CONTACT_CREATE_SCHEMA) .output(RESEND_CONTACT_MUTATION_RESPONSE_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler( async ({ account, input }) => await getResendApi(account).call("/contacts", { body: toContactWire(input), httpMethod: "POST", responseSchema: RESEND_CONTACT_MUTATION_RESPONSE_SCHEMA, }), ) /** Retrieves a Resend contact by ID or email address. */ export const getResendContact = defineAction("Get Resend contact") .describe("Retrieves one Resend contact by ID or email address.") .account(RESEND_ACCOUNT, RESEND_FULL_ACCESS_ACCOUNT_OPTIONS) .input(z.object({ contactId: z.string().min(1) })) .output(RESEND_CONTACT_SCHEMA) .retry({ replaySafety: "safe" }) .handler( async ({ account, input }) => await getResendApi(account).call(`/contacts/${encode(input.contactId)}`, { responseSchema: RESEND_CONTACT_WIRE_SCHEMA, }), ) /** Lists Resend contacts globally or within one segment. */ export const listResendContacts = defineAction("List Resend contacts") .describe("Lists contacts globally or within one segment using cursors.") .account(RESEND_ACCOUNT, RESEND_FULL_ACCESS_ACCOUNT_OPTIONS) .input( z .object({ ...PAGE_FIELDS, /** Restrict contacts to this segment ID. */ segmentId: z.string().min(1).optional(), }) .refine(({ after, before }) => !(after && before), { message: "after and before cannot be used together.", }), ) .output(CONTACT_LIST_SCHEMA) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => { const { segmentId, ...query } = input return await getResendApi(account).call( segmentId ? `/segments/${encode(segmentId)}/contacts` : "/contacts", { query, responseSchema: CONTACT_LIST_WIRE_SCHEMA }, ) }) /** Updates selected fields of a Resend contact by ID or email address. */ export const updateResendContact = defineAction("Update Resend contact") .describe("Updates identity, properties, or unsubscribe state for a contact.") .account(RESEND_ACCOUNT, RESEND_FULL_ACCESS_ACCOUNT_OPTIONS) .input( z.object({ contactId: z.string().min(1), patch: CONTACT_PATCH_SCHEMA, }), ) .output(RESEND_CONTACT_MUTATION_RESPONSE_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler( async ({ account, input }) => await getResendApi(account).call(`/contacts/${encode(input.contactId)}`, { body: toContactWire(input.patch), httpMethod: "PATCH", responseSchema: RESEND_CONTACT_MUTATION_RESPONSE_SCHEMA, }), ) /** Permanently deletes a Resend contact by ID or email address. */ export const deleteResendContact = defineAction("Delete Resend contact") .describe("Permanently deletes a Resend contact by ID or email address.") .account(RESEND_ACCOUNT, RESEND_FULL_ACCESS_ACCOUNT_OPTIONS) .input(z.object({ contactId: z.string().min(1) })) .output(RESEND_CONTACT_DELETE_RESPONSE_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler( async ({ account, input }) => await getResendApi(account).call(`/contacts/${encode(input.contactId)}`, { httpMethod: "DELETE", responseSchema: RESEND_CONTACT_DELETE_WIRE_SCHEMA, }), ) /** Lists the Resend segments containing one contact. */ export const listResendContactSegments = defineAction( "List Resend contact segments", ) .describe("Lists every segment containing one contact using cursors.") .account(RESEND_ACCOUNT, RESEND_FULL_ACCESS_ACCOUNT_OPTIONS) .input( z .object({ ...PAGE_FIELDS, contactId: z.string().min(1) }) .refine(({ after, before }) => !(after && before), { message: "after and before cannot be used together.", }), ) .output(SEGMENT_LIST_SCHEMA) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => { const { contactId, ...query } = input return await getResendApi(account).call( `/contacts/${encode(contactId)}/segments`, { query, responseSchema: SEGMENT_LIST_WIRE_SCHEMA }, ) }) /** Adds an existing Resend contact to a segment. */ export const addResendContactToSegment = defineAction( "Add Resend contact to segment", ) .describe("Adds an existing contact to a Resend segment.") .account(RESEND_ACCOUNT, RESEND_FULL_ACCESS_ACCOUNT_OPTIONS) .input( z.object({ contactId: z.string().min(1), segmentId: z.string().min(1), }), ) .output(RESEND_CONTACT_SEGMENT_MUTATION_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { return await getResendApi(account).call( `/contacts/${encode(input.contactId)}/segments/${encode(input.segmentId)}`, { httpMethod: "POST", responseSchema: RESEND_CONTACT_SEGMENT_MUTATION_WIRE_SCHEMA, }, ) }) /** Removes an existing Resend contact from a segment. */ export const removeResendContactFromSegment = defineAction( "Remove Resend contact from segment", ) .describe("Removes an existing contact from a Resend segment.") .account(RESEND_ACCOUNT, RESEND_FULL_ACCESS_ACCOUNT_OPTIONS) .input( z.object({ contactId: z.string().min(1), segmentId: z.string().min(1), }), ) .output(RESEND_CONTACT_SEGMENT_DELETE_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { return await getResendApi(account).call( `/contacts/${encode(input.contactId)}/segments/${encode(input.segmentId)}`, { httpMethod: "DELETE", responseSchema: RESEND_CONTACT_SEGMENT_DELETE_WIRE_SCHEMA, }, ) }) /** * Converts code-first contact field names to Resend's REST wire format. * * @param contact - Validated contact create or update input. */ function toContactWire( contact: | z.output | z.output, ) { return { ...(contact.email !== undefined && { email: contact.email }), ...(contact.firstName !== undefined && { first_name: contact.firstName }), ...(contact.lastName !== undefined && { last_name: contact.lastName }), ...(contact.properties !== undefined && { properties: contact.properties }), ...("segments" in contact && contact.segments !== undefined && { segments: contact.segments }), ...("topics" in contact && contact.topics !== undefined && { topics: contact.topics }), ...(contact.unsubscribed !== undefined && { unsubscribed: contact.unsubscribed, }), } } /** * URL-encodes one Resend path identifier. * * @param value - Provider resource identifier. */ function encode(value: string) { return encodeURIComponent(value) }