Skip to content

Commit fd422b5

Browse files
authored
feat(sms): add generic sms sending block/tool (#1349)
* update infra and remove railway * feat(sms): add generic sms sending block/tool * added docs * Revert "update infra and remove railway" This reverts commit abfa2f8. * updated colors * remove fallbacks, stronger typing
1 parent 17cf728 commit fd422b5

File tree

17 files changed

+566
-13
lines changed

17 files changed

+566
-13
lines changed

apps/docs/content/docs/en/tools/mail.mdx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
77

88
<BlockInfoCard
99
type="mail"
10-
color="#FF6B35"
10+
color="#181C1E"
1111
icon={true}
1212
iconSvg={`<svg className="block-icon"
1313
@@ -27,6 +27,12 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
2727
</svg>`}
2828
/>
2929

30+
{/* MANUAL-CONTENT-START:intro */}
31+
The Mail block allows you to send emails directly from your workflows using Sim's own mail sending infrastructure powered by [Resend](https://resend.com/). This integration enables you to programmatically deliver notifications, alerts, and other important information to users' email addresses without requiring any external configuration or OAuth.
32+
33+
Our internal mail service is designed for reliability and ease of use, making it ideal for automating communications and ensuring your messages reach recipients efficiently.
34+
{/* MANUAL-CONTENT-END */}
35+
3036
## Usage Instructions
3137

3238
Send emails directly using the internal mail service. Uses MAIL_BLOCK_FROM_ADDRESS if configured, otherwise falls back to FROM_EMAIL_ADDRESS. No external configuration or OAuth required. Perfect for sending notifications, alerts, or general purpose emails from your workflows. Supports HTML formatting.

apps/docs/content/docs/en/tools/meta.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
"serper",
5353
"sharepoint",
5454
"slack",
55+
"sms",
5556
"stagehand",
5657
"stagehand_agent",
5758
"supabase",
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
---
2+
title: SMS
3+
description: Send SMS messages using the internal SMS service
4+
---
5+
6+
import { BlockInfoCard } from "@/components/ui/block-info-card"
7+
8+
<BlockInfoCard
9+
type="sms"
10+
color="#E0E0E0"
11+
icon={true}
12+
iconSvg={`<svg className="block-icon" fill="#000000" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><path d="M 2 5 L 2 25 L 7 25 L 7 30.09375 L 8.625 28.78125 L 13.34375 25 L 30 25 L 30 5 Z M 4 7 L 28 7 L 28 23 L 12.65625 23 L 12.375 23.21875 L 9 25.90625 L 9 23 L 4 23 Z M 8 12 L 8 14 L 24 14 L 24 12 Z M 8 16 L 8 18 L 20 18 L 20 16 Z"/></svg>`}
13+
/>
14+
15+
{/* MANUAL-CONTENT-START:intro */}
16+
The SMS block allows you to send text messages directly from your workflows using Sim's own SMS sending infrastructure powered by Twilio. This integration enables you to programmatically deliver notifications, alerts, and other important information to users' mobile devices without requiring any external configuration or OAuth.
17+
18+
Our internal SMS service is designed for reliability and ease of use, making it ideal for automating communications and ensuring your messages reach recipients efficiently.
19+
{/* MANUAL-CONTENT-END */}
20+
21+
## Usage Instructions
22+
23+
Send SMS messages directly using the internal SMS service powered by Twilio. No external configuration or OAuth required. Perfect for sending notifications, alerts, or general purpose text messages from your workflows. Requires valid phone numbers with country codes.
24+
25+
26+
27+
## Tools
28+
29+
### `sms_send`
30+
31+
Send an SMS message using the internal SMS service powered by Twilio
32+
33+
#### Input
34+
35+
| Parameter | Type | Required | Description |
36+
| --------- | ---- | -------- | ----------- |
37+
| `to` | string | Yes | Recipient phone number \(include country code, e.g., +1234567890\) |
38+
| `body` | string | Yes | SMS message content |
39+
40+
#### Output
41+
42+
| Parameter | Type | Description |
43+
| --------- | ---- | ----------- |
44+
| `success` | boolean | Whether the SMS was sent successfully |
45+
| `to` | string | Recipient phone number |
46+
| `body` | string | SMS message content |
47+
48+
49+
50+
## Notes
51+
52+
- Category: `tools`
53+
- Type: `sms`

apps/sim/app/api/tools/mail/send/route.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,18 @@ export async function POST(request: NextRequest) {
4040
const body = await request.json()
4141
const validatedData = MailSendSchema.parse(body)
4242

43-
const fromAddress = env.MAIL_BLOCK_FROM_ADDRESS || env.FROM_EMAIL_ADDRESS || 'system default'
43+
const fromAddress = env.MAIL_BLOCK_FROM_ADDRESS || env.FROM_EMAIL_ADDRESS
44+
45+
if (!fromAddress) {
46+
logger.error(`[${requestId}] Email sending failed: No from address configured`)
47+
return NextResponse.json(
48+
{
49+
success: false,
50+
message: 'Email sending failed: No from address configured.',
51+
},
52+
{ status: 500 }
53+
)
54+
}
4455

4556
logger.info(`[${requestId}] Sending email via internal mail API`, {
4657
to: validatedData.to,
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { type NextRequest, NextResponse } from 'next/server'
2+
import { z } from 'zod'
3+
import { checkHybridAuth } from '@/lib/auth/hybrid'
4+
import { env } from '@/lib/env'
5+
import { createLogger } from '@/lib/logs/console/logger'
6+
import { type SMSOptions, sendSMS } from '@/lib/sms/service'
7+
import { generateRequestId } from '@/lib/utils'
8+
9+
export const dynamic = 'force-dynamic'
10+
11+
const logger = createLogger('SMSSendAPI')
12+
13+
const SMSSendSchema = z.object({
14+
to: z.string().min(1, 'To phone number is required'),
15+
body: z.string().min(1, 'SMS body is required'),
16+
})
17+
18+
export async function POST(request: NextRequest) {
19+
const requestId = generateRequestId()
20+
21+
try {
22+
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
23+
24+
if (!authResult.success) {
25+
logger.warn(`[${requestId}] Unauthorized SMS send attempt: ${authResult.error}`)
26+
return NextResponse.json(
27+
{
28+
success: false,
29+
message: authResult.error || 'Authentication required',
30+
},
31+
{ status: 401 }
32+
)
33+
}
34+
35+
logger.info(`[${requestId}] Authenticated SMS request via ${authResult.authType}`, {
36+
userId: authResult.userId,
37+
})
38+
39+
const body = await request.json()
40+
const validatedData = SMSSendSchema.parse(body)
41+
42+
const fromNumber = env.TWILIO_PHONE_NUMBER
43+
44+
if (!fromNumber) {
45+
logger.error(`[${requestId}] SMS sending failed: No phone number configured`)
46+
return NextResponse.json(
47+
{
48+
success: false,
49+
message: 'SMS sending failed: No phone number configured.',
50+
},
51+
{ status: 500 }
52+
)
53+
}
54+
55+
logger.info(`[${requestId}] Sending SMS via internal SMS API`, {
56+
to: validatedData.to,
57+
bodyLength: validatedData.body.length,
58+
from: fromNumber,
59+
})
60+
61+
const smsOptions: SMSOptions = {
62+
to: validatedData.to,
63+
body: validatedData.body,
64+
from: fromNumber,
65+
}
66+
67+
const result = await sendSMS(smsOptions)
68+
69+
logger.info(`[${requestId}] SMS send result`, {
70+
success: result.success,
71+
message: result.message,
72+
})
73+
74+
return NextResponse.json(result)
75+
} catch (error) {
76+
if (error instanceof z.ZodError) {
77+
logger.warn(`[${requestId}] Invalid request data`, { errors: error.errors })
78+
return NextResponse.json(
79+
{
80+
success: false,
81+
message: 'Invalid request data',
82+
errors: error.errors,
83+
},
84+
{ status: 400 }
85+
)
86+
}
87+
88+
logger.error(`[${requestId}] Error sending SMS via API:`, error)
89+
90+
return NextResponse.json(
91+
{
92+
success: false,
93+
message: 'Internal server error while sending SMS',
94+
data: {},
95+
},
96+
{ status: 500 }
97+
)
98+
}
99+
}

apps/sim/blocks/blocks/mail.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export const MailBlock: BlockConfig<MailSendResult> = {
99
longDescription:
1010
'Send emails directly using the internal mail service. Uses MAIL_BLOCK_FROM_ADDRESS if configured, otherwise falls back to FROM_EMAIL_ADDRESS. No external configuration or OAuth required. Perfect for sending notifications, alerts, or general purpose emails from your workflows. Supports HTML formatting.',
1111
category: 'tools',
12-
bgColor: '#FF6B35',
12+
bgColor: '#E0E0E0',
1313
icon: MailIcon,
1414

1515
subBlocks: [

apps/sim/blocks/blocks/sms.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { SMSIcon } from '@/components/icons'
2+
import type { BlockConfig } from '@/blocks/types'
3+
import type { SMSSendResult } from '@/tools/sms/types'
4+
5+
export const SMSBlock: BlockConfig<SMSSendResult> = {
6+
type: 'sms',
7+
name: 'SMS',
8+
description: 'Send SMS messages using the internal SMS service',
9+
longDescription:
10+
'Send SMS messages directly using the internal SMS service powered by Twilio. No external configuration or OAuth required. Perfect for sending notifications, alerts, or general purpose text messages from your workflows. Requires valid phone numbers with country codes.',
11+
category: 'tools',
12+
bgColor: '#E0E0E0',
13+
icon: SMSIcon,
14+
15+
subBlocks: [
16+
{
17+
id: 'to',
18+
title: 'To',
19+
type: 'short-input',
20+
layout: 'full',
21+
placeholder: '+1234567890',
22+
required: true,
23+
},
24+
{
25+
id: 'body',
26+
title: 'Message',
27+
type: 'long-input',
28+
layout: 'full',
29+
placeholder: 'Your SMS message content...',
30+
required: true,
31+
},
32+
],
33+
34+
tools: {
35+
access: ['sms_send'],
36+
config: {
37+
tool: () => 'sms_send',
38+
params: (params) => ({
39+
to: params.to,
40+
body: params.body,
41+
}),
42+
},
43+
},
44+
45+
inputs: {
46+
to: { type: 'string', description: 'Recipient phone number (include country code)' },
47+
body: { type: 'string', description: 'SMS message content' },
48+
},
49+
50+
outputs: {
51+
success: { type: 'boolean', description: 'Whether the SMS was sent successfully' },
52+
to: { type: 'string', description: 'Recipient phone number' },
53+
body: { type: 'string', description: 'SMS message content' },
54+
},
55+
}

apps/sim/blocks/registry.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ export const registry: Record<string, BlockConfig> = {
140140
s3: S3Block,
141141
serper: SerperBlock,
142142
sharepoint: SharepointBlock,
143+
// sms: SMSBlock,
143144
stagehand: StagehandBlock,
144145
stagehand_agent: StagehandAgentBlock,
145146
slack: SlackBlock,

apps/sim/components/icons.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3650,3 +3650,16 @@ export const HIPAABadgeIcon = (props: SVGProps<SVGSVGElement>) => (
36503650
</defs>
36513651
</svg>
36523652
)
3653+
3654+
export const SMSIcon = (props: SVGProps<SVGSVGElement>) => (
3655+
<svg
3656+
{...props}
3657+
fill='#000000'
3658+
width='800px'
3659+
height='800px'
3660+
viewBox='0 0 32 32'
3661+
xmlns='http://www.w3.org/2000/svg'
3662+
>
3663+
<path d='M 2 5 L 2 25 L 7 25 L 7 30.09375 L 8.625 28.78125 L 13.34375 25 L 30 25 L 30 5 Z M 4 7 L 28 7 L 28 23 L 12.65625 23 L 12.375 23.21875 L 9 25.90625 L 9 23 L 4 23 Z M 8 12 L 8 14 L 24 14 L 24 12 Z M 8 16 L 8 18 L 20 18 L 20 16 Z' />
3664+
</svg>
3665+
)

apps/sim/lib/env.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ export const env = createEnv({
5757
MAIL_BLOCK_FROM_ADDRESS: z.string().min(1).optional(), // Custom from address for mail block tool (e.g., "Bot <[email protected]>")
5858
AZURE_ACS_CONNECTION_STRING: z.string().optional(), // Azure Communication Services connection string
5959

60+
// SMS & Messaging
61+
TWILIO_ACCOUNT_SID: z.string().min(1).optional(), // Twilio Account SID for SMS sending
62+
TWILIO_AUTH_TOKEN: z.string().min(1).optional(), // Twilio Auth Token for API authentication
63+
TWILIO_PHONE_NUMBER: z.string().min(1).optional(), // Twilio phone number for sending SMS
64+
6065
// AI/LLM Provider API Keys
6166
OPENAI_API_KEY: z.string().min(1).optional(), // Primary OpenAI API key
6267
OPENAI_API_KEY_1: z.string().min(1).optional(), // Additional OpenAI API key for load balancing

0 commit comments

Comments
 (0)