A Node.js webhook service that receives messages and sends them via iMessage on macOS.
- Webhook Endpoint: Accepts POST requests with message payloads
- iMessage Integration: Sends messages via macOS Messages app using AppleScript
- Group Messaging: Sends to multiple recipients simultaneously (individual messages)
- Health Monitoring: Built-in health check endpoint
- Graceful Shutdown: Proper signal handling
- Resource Management: Memory and CPU limits for stability
message-relay/
βββ src/
β βββ server.js # Express server entry point
β βββ services/
β βββ webhookHandler.js # Webhook request handler
β βββ imessageSender.js # iMessage sending service (AppleScript)
βββ package.json # Node.js dependencies
βββ run-macos.sh # Direct macOS execution script
βββ README.md # This file
- macOS with Messages app
- Node.js 20+
Note: The service uses AppleScript to interact with the macOS Messages app.
-
Clone the repository:
git clone <repository-url> cd message-relay
-
Create environment file (optional): Create a
.envfile with fallback phone numbers (optional):PHONE_NUMBERS=+1234567890,+1987654321 PORT=3000 NODE_ENV=production
Note: The
PHONE_NUMBERSenvironment variable is now optional. You can provide phone numbers directly in each webhook request instead.
Set up the service to start automatically when your Mac boots:
# One-time setup
./setup-autostart.sh
# Install helpful aliases
./install-aliases.sh
source ~/.zshrcNow you can use commands like:
relay-start # Start service
relay-stop # Stop service
relay-restart # Restart service
relay-logs # Watch logs
relay-status # Check status
relay-help # See all commandsSee AUTOSTART_GUIDE.md for complete documentation.
Run directly when needed:
chmod +x run-macos.sh
./run-macos.shcurl -X POST http://localhost:3000/webhook \
-H "Content-Type: application/json" \
-d '{"message": "Testing iMessage!", "phoneNumbers": ["+1234567890"]}'| Variable | Description | Example | Required |
|---|---|---|---|
PHONE_NUMBERS |
Comma-separated phone numbers (fallback) | +1234567890,+1987654321 |
No* |
PORT |
Server port | 3000 |
No |
NODE_ENV |
Environment mode | production |
No |
*PHONE_NUMBERS is only required if you don't provide phone numbers in the webhook request body.
POST /webhook
Send a message to all configured phone numbers (sends individual messages to each recipient).
Request Body:
{
"message": "Your message here",
"phoneNumbers": ["+1234567890", "+1987654321"],
"groupKeyword": "Weekend Crew"
}Fields:
message(required): The message to sendphoneNumbers(optional): Array of phone numbers. If not provided, usesPHONE_NUMBERSenvironment variablegroupKeyword(optional): Keyword to find existing group chat. If provided, sends to the group chat instead of individual recipients
Response:
{
"success": true,
"message": "Message sent to group chat: Weekend Crew",
"recipients": 2,
"phoneNumbers": ["+1234567890", "+1987654321"],
"groupChat": "Weekend Crew"
}Note:
- If
groupKeywordis provided, the message is sent to the existing group chat - If no
groupKeywordor group chat not found, messages are sent individually to each recipient - The
groupChatfield shows which group chat was used (if any)
GET /health
Check service health status.
Response:
{
"status": "healthy",
"timestamp": "2024-01-01T00:00:00.000Z"
}# Basic message (uses environment variable phone numbers)
curl -X POST http://localhost:3000/webhook \
-H "Content-Type: application/json" \
-d '{"message": "Hello World!"}'
# Message with custom phone numbers
curl -X POST http://localhost:3000/webhook \
-H "Content-Type: application/json" \
-d '{"message": "Custom recipients message!", "phoneNumbers": ["+1234567890", "+1987654321"]}'
# Message to existing group chat
curl -X POST http://localhost:3000/webhook \
-H "Content-Type: application/json" \
-d '{"message": "Group chat message!", "phoneNumbers": ["+1234567890", "+1987654321"], "groupKeyword": "Weekend Crew"}'
# Message with emojis
curl -X POST http://localhost:3000/webhook \
-H "Content-Type: application/json" \
-d '{"message": "π Service is working!"}'
# Test error handling (missing message)
curl -X POST http://localhost:3000/webhook \
-H "Content-Type: application/json" \
-d '{}'
# Health check
curl -X GET http://localhost:3000/health-
Permission denied errors:
chmod +x run-macos.sh chmod +x setup-autostart.sh
-
Messages not sending:
- Ensure Messages app is open on macOS
- Check phone numbers are in correct format (+1234567890)
- Verify iMessage is enabled for target numbers
- Grant Terminal or your shell app accessibility permissions in System Preferences:
- Go to System Preferences β Security & Privacy β Privacy β Automation
- Allow Terminal to control Messages
-
Port already in use:
# Find what's using port 3000 lsof -i :3000 # Change the port in .env file PORT=8080
-
Service won't start (autostart):
# Check service status relay-status # View logs relay-logs # Reinstall service ./setup-autostart.sh
-
Can't find relay commands:
# Install/reinstall aliases ./install-aliases.sh source ~/.zshrc
- Express Server: HTTP endpoint handling
- Webhook Handler: Request validation and processing
- iMessage Sender: AppleScript integration for Messages app with group messaging support
# Install dependencies
npm install
# Run in development mode
npm run dev
# Run tests
npm testnpm start # Start production server
npm run dev # Start development server with nodemon
npm test # Run tests- Fork the repository
- Create a feature branch
- Make your changes
- Test thoroughly
- Submit a pull request
This project is licensed under the MIT License. See the LICENSE file for details.
- Uses native AppleScript for reliable iMessage integration