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
43 changes: 26 additions & 17 deletions apps/sim/blocks/blocks/resend.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ResendIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import { AuthMode } from '@/blocks/types'

export const ResendBlock: BlockConfig = {
type: 'resend',
Expand All @@ -11,23 +12,21 @@ export const ResendBlock: BlockConfig = {
category: 'tools',
bgColor: '#181C1E',
icon: ResendIcon,
authMode: AuthMode.ApiKey,

subBlocks: [
{
id: 'operation',
title: 'Operation',
type: 'dropdown',
options: [
// Email Operations
{ label: 'Send Email', id: 'send_email' },
{ label: 'Get Email', id: 'get_email' },
// Contact Operations
{ label: 'Create Contact', id: 'create_contact' },
{ label: 'List Contacts', id: 'list_contacts' },
{ label: 'Get Contact', id: 'get_contact' },
{ label: 'Update Contact', id: 'update_contact' },
{ label: 'Delete Contact', id: 'delete_contact' },
// Domain Operations
{ label: 'List Domains', id: 'list_domains' },
],
value: () => 'send_email',
Expand All @@ -41,7 +40,6 @@ export const ResendBlock: BlockConfig = {
password: true,
},

// Send Email fields
{
id: 'fromAddress',
title: 'From Address',
Expand Down Expand Up @@ -80,7 +78,7 @@ export const ResendBlock: BlockConfig = {
"Order confirmation" -> "Your Order #12345 is Confirmed"
"Newsletter about new features" -> "New Features You'll Love"

Return ONLY the subject line - no explanations.`,
Return ONLY the subject line - no explanations, no extra text.`,
placeholder: 'Describe the email topic...',
},
},
Expand All @@ -100,7 +98,7 @@ Return ONLY the subject line - no explanations.`,
- Keep paragraphs short
- Include appropriate greeting and sign-off

Return ONLY the email body - no explanations.`,
Return ONLY the email body - no explanations, no extra text.`,
placeholder: 'Describe the email content...',
},
},
Expand All @@ -114,44 +112,62 @@ Return ONLY the email body - no explanations.`,
],
value: () => 'text',
condition: { field: 'operation', value: 'send_email' },
mode: 'advanced',
},
{
id: 'cc',
title: 'CC',
type: 'short-input',
placeholder: 'cc@example.com',
condition: { field: 'operation', value: 'send_email' },
mode: 'advanced',
},
{
id: 'bcc',
title: 'BCC',
type: 'short-input',
placeholder: 'bcc@example.com',
condition: { field: 'operation', value: 'send_email' },
mode: 'advanced',
},
{
id: 'replyTo',
title: 'Reply To',
type: 'short-input',
placeholder: 'reply@example.com',
condition: { field: 'operation', value: 'send_email' },
mode: 'advanced',
},
{
id: 'scheduledAt',
title: 'Schedule At',
type: 'short-input',
placeholder: '2024-08-05T11:52:01.858Z',
condition: { field: 'operation', value: 'send_email' },
mode: 'advanced',
wandConfig: {
enabled: true,
generationType: 'timestamp',
prompt:
'Generate an ISO 8601 timestamp for scheduling email delivery. Return ONLY the timestamp - no explanations, no extra text.',
placeholder: 'Describe when to send (e.g., "tomorrow at 9am")...',
},
},
{
id: 'tags',
title: 'Tags',
type: 'short-input',
placeholder: 'category:welcome,type:onboarding',
condition: { field: 'operation', value: 'send_email' },
mode: 'advanced',
wandConfig: {
enabled: true,
prompt:
'Generate comma-separated key:value pairs for email tags based on the description. Example format: "category:welcome,type:onboarding". Return ONLY the tag pairs - no explanations, no extra text.',
placeholder: 'Describe the email tags...',
},
},

// Get Email fields
{
id: 'emailId',
title: 'Email ID',
Expand All @@ -161,7 +177,6 @@ Return ONLY the email body - no explanations.`,
required: true,
},

// Create Contact fields
{
id: 'email',
title: 'Email',
Expand Down Expand Up @@ -196,7 +211,6 @@ Return ONLY the email body - no explanations.`,
condition: { field: 'operation', value: ['create_contact', 'update_contact'] },
},

// Get/Update/Delete Contact fields
{
id: 'contactId',
title: 'Contact ID or Email',
Expand Down Expand Up @@ -239,7 +253,6 @@ Return ONLY the email body - no explanations.`,
inputs: {
operation: { type: 'string', description: 'Operation to perform' },
resendApiKey: { type: 'string', description: 'Resend API key' },
// Send email inputs
fromAddress: { type: 'string', description: 'Email address to send from' },
to: { type: 'string', description: 'Recipient email address' },
subject: { type: 'string', description: 'Email subject' },
Expand All @@ -250,9 +263,7 @@ Return ONLY the email body - no explanations.`,
replyTo: { type: 'string', description: 'Reply-to email address' },
scheduledAt: { type: 'string', description: 'Scheduled send time in ISO 8601 format' },
tags: { type: 'string', description: 'Email tags as key:value pairs' },
// Get email inputs
emailId: { type: 'string', description: 'Email ID to retrieve' },
// Contact inputs
email: { type: 'string', description: 'Contact email address' },
firstName: { type: 'string', description: 'Contact first name' },
lastName: { type: 'string', description: 'Contact last name' },
Expand All @@ -262,24 +273,22 @@ Return ONLY the email body - no explanations.`,

outputs: {
success: { type: 'boolean', description: 'Operation success status' },
// Send email outputs
id: { type: 'string', description: 'Email or contact ID' },
to: { type: 'string', description: 'Recipient email address' },
subject: { type: 'string', description: 'Email subject' },
body: { type: 'string', description: 'Email body content' },
// Get email outputs
id: { type: 'string', description: 'Email or contact ID' },
from: { type: 'string', description: 'Sender email address' },
html: { type: 'string', description: 'HTML email content' },
text: { type: 'string', description: 'Plain text email content' },
lastEvent: { type: 'string', description: 'Last event status' },
createdAt: { type: 'string', description: 'Creation timestamp' },
scheduledAt: { type: 'string', description: 'Scheduled send timestamp' },
tags: { type: 'json', description: 'Email tags as name-value pairs' },
// Contact outputs
email: { type: 'string', description: 'Contact email address' },
firstName: { type: 'string', description: 'Contact first name' },
lastName: { type: 'string', description: 'Contact last name' },
unsubscribed: { type: 'boolean', description: 'Whether the contact is unsubscribed' },
contacts: { type: 'json', description: 'Array of contacts' },
// Domain outputs
domains: { type: 'json', description: 'Array of domains' },
hasMore: { type: 'boolean', description: 'Whether more results are available' },
deleted: { type: 'boolean', description: 'Whether the resource was deleted' },
Expand Down
4 changes: 2 additions & 2 deletions apps/sim/tools/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1599,13 +1599,13 @@ import {
} from '@/tools/redis'
import { reductoParserTool, reductoParserV2Tool } from '@/tools/reducto'
import {
mailSendTool,
resendCreateContactTool,
resendDeleteContactTool,
resendGetContactTool,
resendGetEmailTool,
resendListContactsTool,
resendListDomainsTool,
resendSendTool,
resendUpdateContactTool,
} from '@/tools/resend'
import {
Expand Down Expand Up @@ -2390,7 +2390,7 @@ export const tools: Record<string, ToolConfig> = {
luma_update_event: lumaUpdateEventTool,
linkedin_share_post: linkedInSharePostTool,
linkedin_get_profile: linkedInGetProfileTool,
resend_send: mailSendTool,
resend_send: resendSendTool,
resend_get_email: resendGetEmailTool,
resend_create_contact: resendCreateContactTool,
resend_list_contacts: resendListContactsTool,
Expand Down
14 changes: 14 additions & 0 deletions apps/sim/tools/resend/create_contact.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { createLogger } from '@sim/logger'
import type { CreateContactParams, CreateContactResult } from '@/tools/resend/types'
import type { ToolConfig } from '@/tools/types'

const logger = createLogger('ResendCreateContactTool')

export const resendCreateContactTool: ToolConfig<CreateContactParams, CreateContactResult> = {
id: 'resend_create_contact',
name: 'Create Contact',
Expand Down Expand Up @@ -58,6 +61,17 @@ export const resendCreateContactTool: ToolConfig<CreateContactParams, CreateCont
transformResponse: async (response: Response): Promise<CreateContactResult> => {
const data = await response.json()

if (!data.id) {
logger.error('Resend Create Contact API error:', JSON.stringify(data, null, 2))
return {
success: false,
error: data.message || 'Failed to create contact',
output: {
id: '',
},
}
}

return {
success: true,
output: {
Expand Down
19 changes: 17 additions & 2 deletions apps/sim/tools/resend/delete_contact.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { createLogger } from '@sim/logger'
import type { DeleteContactParams, DeleteContactResult } from '@/tools/resend/types'
import type { ToolConfig } from '@/tools/types'

const logger = createLogger('ResendDeleteContactTool')

export const resendDeleteContactTool: ToolConfig<DeleteContactParams, DeleteContactResult> = {
id: 'resend_delete_contact',
name: 'Delete Contact',
Expand All @@ -24,7 +27,7 @@ export const resendDeleteContactTool: ToolConfig<DeleteContactParams, DeleteCont

request: {
url: (params: DeleteContactParams) =>
`https://api.resend.com/contacts/${encodeURIComponent(params.contactId)}`,
`https://api.resend.com/contacts/${encodeURIComponent(params.contactId.trim())}`,
method: 'DELETE',
headers: (params: DeleteContactParams) => ({
Authorization: `Bearer ${params.resendApiKey}`,
Expand All @@ -35,10 +38,22 @@ export const resendDeleteContactTool: ToolConfig<DeleteContactParams, DeleteCont
transformResponse: async (response: Response): Promise<DeleteContactResult> => {
const data = await response.json()

if (data.message && !data.deleted) {
logger.error('Resend Delete Contact API error:', JSON.stringify(data, null, 2))
return {
success: false,
error: data.message || 'Failed to delete contact',
output: {
id: '',
deleted: false,
},
}
}

return {
success: true,
output: {
id: data.contact || data.id || '',
id: data.contact ?? data.id ?? '',
deleted: data.deleted ?? true,
},
}
Expand Down
31 changes: 25 additions & 6 deletions apps/sim/tools/resend/get_contact.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { createLogger } from '@sim/logger'
import type { GetContactParams, GetContactResult } from '@/tools/resend/types'
import type { ToolConfig } from '@/tools/types'

const logger = createLogger('ResendGetContactTool')

export const resendGetContactTool: ToolConfig<GetContactParams, GetContactResult> = {
id: 'resend_get_contact',
name: 'Get Contact',
Expand All @@ -24,7 +27,7 @@ export const resendGetContactTool: ToolConfig<GetContactParams, GetContactResult

request: {
url: (params: GetContactParams) =>
`https://api.resend.com/contacts/${encodeURIComponent(params.contactId)}`,
`https://api.resend.com/contacts/${encodeURIComponent(params.contactId.trim())}`,
method: 'GET',
headers: (params: GetContactParams) => ({
Authorization: `Bearer ${params.resendApiKey}`,
Expand All @@ -35,15 +38,31 @@ export const resendGetContactTool: ToolConfig<GetContactParams, GetContactResult
transformResponse: async (response: Response): Promise<GetContactResult> => {
const data = await response.json()

if (!data.id) {
logger.error('Resend Get Contact API error:', JSON.stringify(data, null, 2))
return {
success: false,
error: data.message || 'Failed to retrieve contact',
output: {
id: '',
email: '',
firstName: '',
lastName: '',
createdAt: '',
unsubscribed: false,
},
}
}

return {
success: true,
output: {
id: data.id,
email: data.email,
firstName: data.first_name || '',
lastName: data.last_name || '',
createdAt: data.created_at || '',
unsubscribed: data.unsubscribed || false,
email: data.email ?? '',
firstName: data.first_name ?? '',
lastName: data.last_name ?? '',
createdAt: data.created_at ?? '',
unsubscribed: data.unsubscribed ?? false,
},
}
},
Expand Down
Loading