Add iMessage to Your React Native App
Want your React Native app to send iMessages to users? Whether you are building a CRM, a sales tool, or a customer engagement platform, adding iMessage lets you reach users on their most trusted messaging channel. This guide covers the architecture, backend setup, React Native client code, and security best practices.
Why Add iMessage to Your App?
iMessage has a 98% open rate and 3-5x higher response rates than SMS. For mobile apps, this opens up powerful use cases:
- In-app messaging: Let sales reps send iMessages to leads directly from your CRM app
- Appointment reminders: Send blue bubble reminders that users actually read
- Customer engagement: Reach users on their preferred channel, not just push notifications
- Order updates: Send shipping notifications as iMessages for higher visibility
The key advantage over push notifications: iMessages persist in the Messages app, feel personal, and don't require the user to have your app open or notifications enabled.
Architecture: Never Call the API from the Client
This is the most important design decision: never put your Sendblue API keys in your React Native app. Mobile apps can be decompiled, and any hardcoded secrets will be compromised.
Instead, use this three-tier architecture:
- React Native app calls your backend API (authenticated with user tokens)
- Your backend validates the request and calls Sendblue's API (with API keys stored server-side)
- Sendblue sends the iMessage
For incoming messages, Sendblue sends webhooks to your backend, which can then push notifications to the React Native app via Firebase Cloud Messaging (FCM) or Apple Push Notification service (APNs).
Backend Setup (Express.js)
Create a simple Express.js API that your React Native app will call. This server holds the Sendblue credentials securely:
// server.mjs — Your backend API
import express from 'express';
import Sendblue from 'sendblue';
const app = express();
app.use(express.json());
const sendblue = new Sendblue(
process.env.SENDBLUE_API_KEY,
process.env.SENDBLUE_API_SECRET
);
// Middleware: verify user auth token
function authenticate(req, res, next) {
const token = req.headers.authorization?.split(' ')[1];
// Validate token (JWT, session, etc.)
if (!token || !isValidToken(token)) {
return res.status(401).json({ error: 'Unauthorized' });
}
req.userId = getUserIdFromToken(token);
next();
}
// Send an iMessage (called by React Native app)
app.post('/api/send-message', authenticate, async (req, res) => {
const { number, content, mediaUrl } = req.body;
// Validate input
if (!number || !content) {
return res.status(400).json({ error: 'number and content required' });
}
try {
const result = await sendblue.sendMessage({
number,
content,
mediaUrl,
});
res.json(result);
} catch (err) {
console.error('Sendblue error:', err);
res.status(500).json({ error: 'Failed to send message' });
}
});
// Receive iMessage replies (webhook from Sendblue)
app.post('/webhooks/receive', async (req, res) => {
const { from_number, content, media_url } = req.body;
// Save to database
await saveMessage({ from: from_number, content, mediaUrl: media_url });
// Send push notification to the React Native app
await sendPushNotification(from_number, content);
res.json({ status: 'ok' });
});
app.listen(3000);React Native Client Code
In your React Native app, call your backend API — never Sendblue directly:
// services/messaging.ts
const API_BASE = 'https://your-backend.com';
export async function sendIMessage(
number: string,
content: string,
mediaUrl?: string
) {
const token = await getAuthToken(); // your auth implementation
const response = await fetch(`${API_BASE}/api/send-message`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
},
body: JSON.stringify({ number, content, mediaUrl }),
});
if (!response.ok) {
throw new Error(`Send failed: ${response.status}`);
}
return response.json();
}// components/SendMessageScreen.tsx
import React, { useState } from 'react';
import { View, TextInput, Button, Alert } from 'react-native';
import { sendIMessage } from '../services/messaging';
export function SendMessageScreen() {
const [number, setNumber] = useState('');
const [content, setContent] = useState('');
const [sending, setSending] = useState(false);
const handleSend = async () => {
setSending(true);
try {
await sendIMessage(number, content);
Alert.alert('Sent!', 'iMessage delivered successfully.');
setContent('');
} catch (err) {
Alert.alert('Error', 'Failed to send message.');
} finally {
setSending(false);
}
};
return (
<View style={{ padding: 20 }}>
<TextInput
placeholder="Phone number (+15551234567)"
value={number}
onChangeText={setNumber}
keyboardType="phone-pad"
style={{ borderBottomWidth: 1, marginBottom: 16, padding: 8 }}
/>
<TextInput
placeholder="Message"
value={content}
onChangeText={setContent}
multiline
style={{ borderBottomWidth: 1, marginBottom: 16, padding: 8 }}
/>
<Button
title={sending ? 'Sending...' : 'Send iMessage'}
onPress={handleSend}
disabled={sending || !number || !content}
/>
</View>
);
}Push Notification Flow
When someone replies to your iMessage, you want the React Native app user to know immediately. Here's the flow:
- User replies to the iMessage on their iPhone
- Sendblue webhook sends the reply to your backend
- Your backend saves the message and sends a push notification via FCM/APNs
- React Native app receives the push and displays the notification
// On your backend — send push after receiving webhook
import admin from 'firebase-admin';
async function sendPushNotification(fromNumber, content) {
// Look up which app user is associated with this conversation
const user = await getUserByConversation(fromNumber);
if (!user?.fcmToken) return;
await admin.messaging().send({
token: user.fcmToken,
notification: {
title: `Reply from ${fromNumber}`,
body: content,
},
data: {
type: 'imessage_reply',
fromNumber,
},
});
}This creates a seamless experience: the app user sends an iMessage from the app, and when the recipient replies, the app user gets a push notification instantly.
Security Best Practices
When integrating iMessage into a mobile app, security is critical:
- API keys on server only: Never include Sendblue credentials in your React Native app bundle. They belong on your backend only.
- Authenticate every request: Use JWT tokens or session cookies to verify that the React Native app is making authorized requests to your backend.
- Rate limit: Prevent abuse by limiting how many messages a user can send per minute/hour. Use middleware like
express-rate-limit. - Input validation: Validate phone numbers (E.164 format: +1XXXXXXXXXX) and message content on your backend before forwarding to Sendblue.
- Audit logging: Log all sent messages with the sending user's ID for compliance and debugging.
Send Media from Camera Roll
Let users send photos from their camera roll as iMessage attachments. First, upload the image to your server or a cloud storage service, then pass the URL:
import { launchImageLibrary } from 'react-native-image-picker';
async function sendPhoto(number: string) {
const result = await launchImageLibrary({ mediaType: 'photo' });
if (!result.assets?.[0]) return;
// Upload to your server first
const formData = new FormData();
formData.append('file', {
uri: result.assets[0].uri,
type: result.assets[0].type,
name: result.assets[0].fileName,
});
const uploadRes = await fetch('https://your-backend.com/api/upload', {
method: 'POST',
headers: { Authorization: `Bearer ${await getAuthToken()}` },
body: formData,
});
const { url } = await uploadRes.json();
// Send iMessage with the uploaded image URL
await sendIMessage(number, 'Check out this photo!', url);
}The uploaded image URL is passed as mediaUrl to Sendblue, which delivers it as a native iMessage attachment.
Next Steps
You now have a complete architecture for sending iMessages from a React Native app. Continue with:
- Node.js backend tutorial — Full guide for building the Express.js backend
- Webhook integration — Handle delivery receipts, typing indicators, and all webhook types
- iMessage for sales — See how sales teams use iMessage in their mobile CRM apps
- Full API documentation — Group messaging, reactions, and more
Get your free API keys and add iMessage to your React Native app today.
Ready to send your first iMessage?
Get API access in minutes. Free sandbox, no credit card required.