code examples

Sent logo
Sent TeamMay 3, 2025 / code examples / Article

MessageBird Bulk SMS Broadcasting with Vue 3, Vite, and Node.js: Complete Tutorial

Build a production-ready bulk SMS and WhatsApp broadcast system with MessageBird API, Vue 3, Vite, and Node.js. Includes error handling, retry logic, and database schema examples.

How to Build Bulk SMS and WhatsApp Messaging with MessageBird, Vue, and Node.js

This comprehensive guide shows you how to build a production-ready web application for sending bulk SMS and WhatsApp messages using the MessageBird API. You'll create a modern Vue 3 frontend with Vite for optimal performance and a secure Node.js/Express backend that handles the MessageBird integration.

This application solves the common need to send announcements, notifications, or marketing messages to multiple recipients efficiently. By the end, you'll have a functional system with recipient management, message tracking, error handling, and the foundation for scaling to thousands of messages.

What You'll Build:

  • Vue 3 Frontend: Modern reactive UI with form validation and real-time feedback
  • Node.js Backend API: Secure endpoint with input validation and MessageBird integration
  • Bulk Messaging Logic: Efficient batch processing with individual recipient status tracking
  • Error Handling: Robust retry mechanisms and detailed logging for production reliability

Technologies Used:

  • Vite: A modern frontend build tool providing a fast development server and optimized builds.
  • Vue 3: A progressive JavaScript framework for building user interfaces (Composition API).
  • Node.js: A JavaScript runtime for building the backend API.
  • Express: A minimal and flexible Node.js web application framework for the API.
  • MessageBird: The communication platform API used for sending SMS/WhatsApp messages.
  • dotenv: A module to load environment variables from a .env file.

System Architecture:

+-----------------+ +--------------------+ +------------------+ | Vue Frontend |----->| Node.js Backend API|----->| MessageBird API | | (Vite/Vue) | | (Express) | | (SMS/WhatsApp) | | - Broadcast Form| | - Validate Request | +------------------+ | - API Call | | - Use API Key | +-----------------+ | - Loop & Send | | - Handle Responses | +--------------------+ | V +------------------+ | Database (Optional)| | - Store Recipients | | - Log Messages | +------------------+

Prerequisites:

  • Node.js 20.19+ and npm (or yarn) installed. Vite 7 requires Node.js 20.19+, 22.12+. Download Node.js
  • A MessageBird account. Sign up for MessageBird
  • Your MessageBird Live API Key (found in Dashboard > Developers > API access).
  • Basic understanding of Vue.js, Node.js, and REST APIs.
  • Note: MessageBird SDK v4.0.1 (last updated 2021–2022) – you may encounter limited updates or community support.

1. How to Set Up Your Vue and Node.js Project for Bulk Messaging

Set up both the frontend (Vue/Vite) and the backend (Node.js/Express) projects.

Frontend Setup (Vite + Vue)

  1. Create Vite Project: Open your terminal and run the Vite scaffolding command. Choose vue and vue-ts or vue (JavaScript) as you prefer. This guide uses JavaScript.

    bash
    npm create vite@latest sent-messagebird-broadcast --template vue
  2. Navigate and Install Dependencies:

    bash
    cd sent-messagebird-broadcast
    npm install
  3. Project Structure (Frontend): Vite creates a standard structure. You'll primarily work within the src directory:

    sent-messagebird-broadcast/ ├── public/ ├── src/ │ ├── assets/ │ ├── components/ # Add your form component here │ ├── App.vue # Main App component │ └── main.js # App entry point ├── index.html ├── package.json └── vite.config.js
  4. Run Development Server:

    bash
    npm run dev

    This starts the Vite development server, typically at http://localhost:5173. You should see the default Vue welcome page.

Backend Setup (Node.js + Express)

  1. Create Backend Directory: Inside your main project folder (sent-messagebird-broadcast), create a directory for the backend API.

    bash
    mkdir backend
    cd backend
  2. Initialize Node.js Project:

    bash
    npm init -y

    This creates a package.json file.

  3. Install Backend Dependencies: Install Express for the server, dotenv for environment variables, the MessageBird SDK, and cors for handling cross-origin requests from the frontend.

    bash
    npm install express dotenv @messagebird/api cors
  4. Project Structure (Backend):

    sent-messagebird-broadcast/ ├── backend/ │ ├── node_modules/ │ ├── .env # Store API keys here (DO NOT COMMIT) │ ├── server.js # Your main API server file │ └── package.json ├── src/ # Frontend code └── ... (other frontend files)
  5. Create .env File: Create a file named .env in the backend directory. Add your MessageBird Live API Key. Important: Add .env to your .gitignore file to avoid committing secrets.

    plaintext
    # Replace with your actual MessageBird Live API key obtained from the dashboard
    MESSAGEBIRD_API_KEY=YOUR_LIVE_API_KEY_HERE
    
    # Optional: Replace with your registered phone number (E.164 format) or Alphanumeric Sender ID
    MESSAGEBIRD_ORIGINATOR=YOUR_SENDER_ID_OR_NUMBER
  6. Create Basic Server (server.js):

    javascript
    require('dotenv').config();
    const express = require('express');
    const cors = require('cors');
    const messagebird = require('@messagebird/api')(process.env.MESSAGEBIRD_API_KEY);
    
    const app = express();
    const port = process.env.PORT || 3001; // Use environment port or default
    
    // Middleware
    app.use(cors()); // Enable CORS for requests from frontend
    app.use(express.json()); // Parse JSON request bodies
    
    // Basic health check route
    app.get('/health', (req, res) => {
      res.status(200).send('OK');
    });
    
    // Placeholder for our broadcast route
    app.post('/api/broadcast', (req, res) => {
      // Logic will go here in Section 3 & 4
      console.log('Received broadcast request:', req.body);
      res.status(200).json({ message: 'Broadcast request received (placeholder)' });
    });
    
    // Start the server
    app.listen(port, () => {
      console.log(`Backend server listening on port ${port}`);
      if (!process.env.MESSAGEBIRD_API_KEY) {
        console.warn('WARNING: MESSAGEBIRD_API_KEY environment variable not set.');
      }
       if (!process.env.MESSAGEBIRD_ORIGINATOR) {
        console.warn('WARNING: MESSAGEBIRD_ORIGINATOR environment variable not set. Using default.');
      }
    });
  7. Run Backend Server: Open a new terminal window, navigate to the backend directory, and run:

    bash
    node server.js

    You should see Backend server listening on port 3001.

Now you have both frontend and backend development servers running. Learn more about SMS delivery status tracking with Node.js and Express or explore building two-way SMS messaging systems.

2. How to Create a Vue Form for Bulk Message Broadcasting

Create the Vue component for users to enter recipients and their message.

  1. Create Component File: In the frontend project, create src/components/BroadcastForm.vue.

  2. Implement the Form: Add the following code to BroadcastForm.vue. This uses Vue 3's Composition API (setup script).

    vue
    <template>
      <div class="broadcast-form">
        <h2>Send Bulk Message</h2>
    
        <form @submit.prevent="handleBroadcastSubmit">
          <div class="form-group">
            <label for="recipients">Recipients (Phone numbers, one per line):</label>
            <textarea
              id="recipients"
              v-model="recipientsInput"
              rows="10"
              placeholder="e.g., +14155552671&#10;+442071838750"
              required
            ></textarea>
            <small>Enter numbers in E.164 format (e.g., +14155552671).</small>
          </div>
    
          <div class="form-group">
            <label for="message">Message:</label>
            <textarea
              id="message"
              v-model="messageBody"
              rows="5"
              placeholder="Enter your broadcast message here..."
              required
              maxlength="1600"
            ></textarea>
             <small>{{ messageBody.length }} / 1600 characters</small>
          </div>
    
          <button type="submit" :disabled="isLoading">
            {{ isLoading ? 'Sending...' : 'Send Broadcast' }}
          </button>
        </form>
    
        <div v-if="feedbackMessage" :class="['feedback', feedbackStatus]">
          {{ feedbackMessage }}
        </div>
      </div>
    </template>
    
    <script setup>
    import { ref } from 'vue';
    
    const recipientsInput = ref('');
    const messageBody = ref('');
    const isLoading = ref(false);
    const feedbackMessage = ref('');
    const feedbackStatus = ref(''); // 'success' or 'error'
    
    // Function to parse recipients from textarea
    const parseRecipients = (input) => {
      return input
        .split('\n') // Split by newline
        .map(line => line.trim()) // Trim whitespace
        .filter(line => line.length > 0); // Remove empty lines
    };
    
    const handleBroadcastSubmit = async () => {
      isLoading.value = true;
      feedbackMessage.value = '';
      feedbackStatus.value = '';
    
      const recipients = parseRecipients(recipientsInput.value);
    
      if (recipients.length === 0) {
        feedbackMessage.value = 'Please enter at least one recipient.';
        feedbackStatus.value = 'error';
        isLoading.value = false;
        return;
      }
    
      if (!messageBody.value.trim()) {
          feedbackMessage.value = 'Message body cannot be empty.';
          feedbackStatus.value = 'error';
          isLoading.value = false;
          return;
      }
    
      try {
        // API Endpoint URL - Ensure your backend runs on port 3001
        const apiUrl = 'http://localhost:3001/api/broadcast';
    
        const response = await fetch(apiUrl, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            recipients: recipients,
            message: messageBody.value.trim(),
          }),
        });
    
        const result = await response.json();
    
        if (!response.ok) {
          // Handle HTTP errors (e.g., 4xx, 5xx)
           throw new Error(result.error || `Server responded with ${response.status}`);
        }
    
        // Assuming backend returns { success: true, message: '...', details: [...] } on success
        // And { success: false, error: '...' } on failure
        if (result.success) {
            feedbackMessage.value = `Broadcast submitted successfully. Status: ${result.message || 'Processing initiated.'}`;
            feedbackStatus.value = 'success';
            // Optionally clear the form on success
            // recipientsInput.value = '';
            // messageBody.value = '';
        } else {
             throw new Error(result.error || 'An unknown error occurred on the server.');
        }
    
      } catch (error) {
        console.error('Error sending broadcast:', error);
        feedbackMessage.value = `Failed to send broadcast: ${error.message}`;
        feedbackStatus.value = 'error';
      } finally {
        isLoading.value = false;
      }
    };
    </script>
    
    <style scoped>
    .broadcast-form {
      max-width: 600px;
      margin: 2rem auto;
      padding: 2rem;
      border: 1px solid #ccc;
      border-radius: 8px;
      background-color: #f9f9f9;
    }
    
    .form-group {
      margin-bottom: 1.5rem;
    }
    
    label {
      display: block;
      margin-bottom: 0.5rem;
      font-weight: bold;
    }
    
    textarea {
      width: 100%;
      padding: 0.75rem;
      border: 1px solid #ccc;
      border-radius: 4px;
      font-size: 1rem;
      box-sizing: border-box; /* Include padding and border in element's total width */
    }
    
    textarea:focus {
        border-color: #007bff;
        outline: none;
    }
    
    small {
        display: block;
        margin-top: 0.25rem;
        font-size: 0.8rem;
        color: #666;
    }
    
    button {
      padding: 0.75rem 1.5rem;
      background-color: #007bff;
      color: white;
      border: none;
      border-radius: 4px;
      font-size: 1rem;
      cursor: pointer;
      transition: background-color 0.2s ease;
    }
    
    button:hover {
      background-color: #0056b3;
    }
    
    button:disabled {
      background-color: #ccc;
      cursor: not-allowed;
    }
    
    .feedback {
      margin-top: 1.5rem;
      padding: 1rem;
      border-radius: 4px;
      text-align: center;
    }
    
    .feedback.success {
      background-color: #d4edda;
      color: #155724;
      border: 1px solid #c3e6cb;
    }
    
    .feedback.error {
      background-color: #f8d7da;
      color: #721c24;
      border: 1px solid #f5c6cb;
    }
    </style>
    • Why: This code uses v-model for two-way data binding between the textareas and reactive refs (recipientsInput, messageBody). The handleBroadcastSubmit function prevents default form submission, parses recipients, performs basic validation, and uses fetch to send data to your backend API. It handles loading states and displays feedback messages based on the API response.
  3. Use the Component in App.vue: Replace the content of src/App.vue to include your new form.

    vue
    <template>
      <div id="app">
        <header>
          <h1>MessageBird Bulk Broadcaster</h1>
        </header>
        <main>
          <BroadcastForm />
        </main>
      </div>
    </template>
    
    <script setup>
    import BroadcastForm from './components/BroadcastForm.vue';
    </script>
    
    <style>
    /* Add some basic global styles if needed */
    #app {
      font-family: Avenir, Helvetica, Arial, sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      color: #2c3e50;
      margin-top: 60px;
      display: flex;
      flex-direction: column;
      align-items: center;
    }
    header {
        margin-bottom: 2rem;
    }
    main {
        width: 100%;
        max-width: 700px; /* Limit form width */
        padding: 0 1rem;
    }
    </style>

If you check your browser (where the frontend npm run dev is running), you should now see the broadcast form. Submitting it will currently log a message in the backend console and show a placeholder success message in the frontend, as the backend logic isn't fully implemented yet.

3. How to Build the MessageBird API Integration Layer

Enhance the backend POST /api/broadcast endpoint to handle the request properly, validate input, and prepare for MessageBird integration.

  1. Update server.js: Modify the /api/broadcast route handler in backend/server.js.

    javascript
    // ... (keep existing require statements and middleware setup) ...
    
    // Updated broadcast route
    app.post('/api/broadcast', async (req, res) => { // Mark as async
      const { recipients, message } = req.body;
    
      // --- 1. Input Validation ---
      if (!recipients || !Array.isArray(recipients) || recipients.length === 0) {
        return res.status(400).json({ success: false, error: 'Recipients array is missing or empty.' });
      }
      if (!message || typeof message !== 'string' || message.trim().length === 0) {
        return res.status(400).json({ success: false, error: 'Message is missing or empty.' });
      }
      if (message.length > 1600) { // Example length limit
           return res.status(400).json({ success: false, error: 'Message exceeds maximum length of 1600 characters.' });
      }
    
       // Basic E.164 format check (can be improved with regex)
      const invalidRecipients = recipients.filter(r => typeof r !== 'string' || !r.startsWith('+') || r.length < 10);
      if (invalidRecipients.length > 0) {
           return res.status(400).json({
               success: false,
               error: `Invalid recipient format found. Ensure all numbers start with '+' and are in E.164 format. Problematic entries: ${invalidRecipients.slice(0, 5).join(', ')}...`
           });
      }
    
    
      // --- 2. Authentication/Authorization (Placeholder) ---
      // In a real app, you'd verify user identity/permissions here.
      // For now, we assume the request is authorized if it reaches here.
      console.log('User authorized (placeholder). Proceeding with broadcast.');
    
      // --- 3. MessageBird Interaction (Implemented in Section 4) ---
      try {
          console.log(`Attempting to send message "${message}" to ${recipients.length} recipients.`);
          // Add MessageBird sending logic here in the next step
    
          // For now, simulate success
          const results = recipients.map(r => ({ recipient: r, status: 'submitted', id: `fake_msg_id_${Math.random().toString(16).slice(2)}` }));
    
          console.log('Placeholder: MessageBird processing would happen here.');
    
          // --- 4. Respond to Frontend ---
           res.status(200).json({
               success: true,
               message: `Broadcast request submitted for ${recipients.length} recipients.`,
               details: results // Provide some detail back
           });
    
      } catch (error) {
          console.error('Error during broadcast processing:', error);
          // This catch block will handle errors from the MessageBird sending logic later
          res.status(500).json({ success: false, error: 'An internal server error occurred while processing the broadcast.' });
      }
    });
    
    // ... (keep existing health check route and app.listen) ...
    • Why: We added robust input validation checks for the recipients array and the message string, including format checks (basic E.164 check) and length limits. We added a placeholder comment for where authentication/authorization logic would go in a real-world application. The main logic block is wrapped in a try...catch to handle potential errors during processing (especially when we add MessageBird calls). We simulate a successful response structure that the frontend expects.
  2. Testing the Endpoint: You can test this endpoint using curl or a tool like Postman.

    Curl Example: (Run from your terminal)

    bash
    curl -X POST http://localhost:3001/api/broadcast \
    -H "Content-Type: application/json" \
    -d '{
      "recipients": ["+14155550100", "+14155550101"],
      "message": "Hello from the API test!"
    }'

    Expected Response (JSON):

    json
    {
      "success": true,
      "message": "Broadcast request submitted for 2 recipients.",
      "details": [
        { "recipient": "+14155550100", "status": "submitted", "id": "fake_msg_id_..." },
        { "recipient": "+14155550101", "status": "submitted", "id": "fake_msg_id_..." }
      ]
    }

    Test edge cases like sending empty recipients, an empty message, or malformed numbers to ensure the validation works.

4. How to Send Bulk SMS and WhatsApp Messages with MessageBird

Now, integrate the MessageBird SDK into your backend API to actually send the messages.

  1. Get MessageBird Credentials:

    • Log in to your MessageBird Dashboard.
    • Navigate to Developers > API access (or similar path).
    • Find your Live API key. Copy this key.
    • Ensure this key is securely stored in your backend/.env file as MESSAGEBIRD_API_KEY.
    • Decide on your Originator (Sender ID). This can be:
      • A phone number you own (purchased through MessageBird or verified).
      • An Alphanumeric Sender ID (e.g., "MyCompany") – note that replies are not possible with Alphanumeric IDs, and they require pre-registration in some countries.
    • Add your chosen Originator to the backend/.env file as MESSAGEBIRD_ORIGINATOR.
  2. Implement Sending Logic in server.js: Replace the placeholder comment in the try block of the /api/broadcast route with the MessageBird sending logic.

    javascript
    // ... (require statements, middleware, validation) ...
    
    app.post('/api/broadcast', async (req, res) => {
      const { recipients, message } = req.body;
    
      // --- Input Validation (Keep as before) ---
      // ...
    
      // --- Authentication/Authorization (Keep placeholder) ---
      // ...
    
      const originator = process.env.MESSAGEBIRD_ORIGINATOR;
      if (!originator) {
          console.error('MessageBird Originator (sender ID/number) is not configured in .env');
          return res.status(500).json({ success: false, error: 'Server configuration error: Missing sender originator.' });
      }
    
      try {
        console.log(`Sending message via MessageBird from ${originator} to ${recipients.length} recipients.`);
    
        const messagePromises = recipients.map(recipient => {
          return new Promise((resolve, reject) => {
            const params = {
              originator: originator,
              recipients: [recipient], // Send one API call per recipient for individual status tracking
              body: message,
              // Optional: Add type for WhatsApp, reportUrl for status updates etc.
              // type: 'sms', // Default is sms
              // reportUrl: 'YOUR_WEBHOOK_URL' // See Section 10
            };
    
            messagebird.messages.create(params, (err, response) => {
              if (err) {
                console.error(`Failed to send message to ${recipient}:`, err);
                // Resolve even on error to not fail the entire batch, but include error status
                resolve({ recipient: recipient, status: 'failed', error: err.errors || err.message || 'Unknown error' });
              } else {
                // Log successful submission
                console.log(`Message submitted for ${recipient}, ID: ${response.id}`);
                resolve({ recipient: recipient, status: 'submitted', id: response.id, details: response });
              }
            });
          });
        });
    
        // Wait for all message submissions to complete (or fail individually)
        const results = await Promise.all(messagePromises);
    
        const successfulSubmissions = results.filter(r => r.status === 'submitted').length;
        const failedSubmissions = results.filter(r => r.status === 'failed').length;
    
        console.log(`Broadcast attempt complete. Success: ${successfulSubmissions}, Failed: ${failedSubmissions}`);
    
        // Respond with overall status and detailed results
        res.status(200).json({
          success: true, // Indicate the API call succeeded, even if some messages failed submission
          message: `Broadcast submitted. ${successfulSubmissions} successful, ${failedSubmissions} failed submissions.`,
          details: results
        });
    
      } catch (error) { // Catch errors during the Promise.all or other logic
        console.error('Critical error during broadcast processing:', error);
        res.status(500).json({ success: false, error: 'An internal server error occurred while processing the broadcast.' });
      }
    });
    
    // ... (health check, app.listen) ...
    • Why:
      • We retrieve the MESSAGEBIRD_ORIGINATOR from environment variables.
      • We iterate through the validated recipients array.
      • For each recipient, we create parameters (params) including the originator, the single recipient, and the message body. Sending one API call per recipient allows for individual status tracking and error handling, which is generally better for bulk sends than putting all recipients in one API call (though MessageBird supports that too).
      • We use messagebird.messages.create(params, callback) to initiate the sending.
      • Crucially, we wrap each messagebird.messages.create call in a Promise. This allows us to manage the asynchronous nature of these calls. The promise resolves even if the MessageBird API returns an error for a specific number, capturing the failure details. This prevents one failed number from stopping the entire batch.
      • Promise.all(messagePromises) waits for all the individual send attempts to complete (either successfully submitted or failed).
      • We collect the results array, containing the status for each recipient.
      • Finally, we send a 200 OK response back to the frontend, including a summary message and the detailed results array. The frontend can then interpret this data.
  3. Restart Backend: Stop (Ctrl+C) and restart your backend server (node server.js) to apply the changes.

  4. Test: Use the frontend application to send a message to one or two real phone numbers (including your own) that you can verify. Check your phone and the MessageBird Dashboard logs (Logging > Messages) to confirm messages are sent. Observe the backend console logs and the feedback message in the frontend.

For more advanced implementations, check out our guide on implementing SMS OTP and 2FA with MessageBird or learn about handling MessageBird delivery status webhooks.

5. How to Handle Errors and Implement Retry Logic for Bulk Messaging

Production systems need robust error handling and logging.

Backend Enhancements:

  1. Improved Logging: Replace console.log / console.error with a more structured logger like winston or pino for production. For this guide, we'll stick to console, but highlight its limitations.

    • Why: Structured logging (e.g., JSON format) makes logs easier to parse, filter, and analyze with log management tools (like Datadog, Splunk, ELK stack). It allows adding context (request IDs, user IDs) to log entries.
  2. Specific Error Handling: The current try...catch around Promise.all handles errors during the mapping or Promise.all itself. The individual promise wrappers handle errors from the MessageBird API for each message.

    • Distinguish between:
      • Submission Errors: MessageBird API rejects the request immediately (invalid number, API key error, insufficient balance). Our current code logs these per recipient.
      • Delivery Errors: MessageBird accepts the message, but it later fails to deliver (handset off, number blocked). These require Webhooks for reliable tracking (See Section 10).
      • Network/Server Errors: Problems connecting to MessageBird or internal server issues.
  3. Retry Mechanisms (Conceptual): For transient errors (e.g., temporary network issues connecting to MessageBird), implement a retry strategy.

    • Strategy: Use libraries like async-retry or implement manually with exponential backoff (wait longer between retries).
    • Implementation Sketch (Conceptual - not added to main code for brevity):
    javascript
    // Conceptual retry logic for a single message send
    const { retry } = require('async-retry'); // npm install async-retry
    
    async function sendMessageWithRetry(params) {
      await retry(async bail => {
        // bail is a function to call if the error is not retryable
        return new Promise((resolve, reject) => {
          messagebird.messages.create(params, (err, response) => {
            if (err) {
              // Decide if the error is retryable (e.g., network error, rate limit)
              // or permanent (e.g., invalid number, auth error)
              if (isRetryableError(err)) {
                 console.warn(`Retrying message to ${params.recipients[0]} due to: ${err.message}`);
                 return reject(err); // Reject to trigger retry
              } else {
                 console.error(`Non-retryable error for ${params.recipients[0]}:`, err);
                 // Bail out - don't retry, but resolve the outer promise with failure
                 return bail(new Error(`Permanent failure for ${params.recipients[0]}: ${err.message}`));
              }
            }
            resolve({ /* success details */ });
          });
        });
      }, {
        retries: 3, // Number of retries
        factor: 2, // Exponential backoff factor
        minTimeout: 1000, // Initial delay ms
        onRetry: (error, attempt) => {
            console.log(`Attempt ${attempt} failed for ${params.recipients[0]}. Retrying...`);
        }
      }).catch(finalError => {
         // This catch handles the case where all retries failed, or bail() was called
         console.error(`All retries failed for ${params.recipients[0]}:`, finalError);
         // Resolve the outer promise with failure status
         return Promise.resolve({ recipient: params.recipients[0], status: 'failed', error: finalError.message });
      });
    }
    
    // In the main loop, call sendMessageWithRetry instead of the direct Promise wrapper
    // const messagePromises = recipients.map(recipient => sendMessageWithRetry({ /* params */ }));
    • Why: Retries improve resilience against temporary glitches without manual intervention. Exponential backoff prevents overwhelming the service during widespread issues.

Frontend Enhancements:

  1. Clearer Feedback: The current feedback is okay, but could be more detailed based on the details array returned from the backend.

    vue
     <div v-if="feedbackMessage" :class="['feedback', feedbackStatus]">
        <p>{{ feedbackMessage }}</p>
        <ul v-if="detailedResults.length > 0" class="detailed-results">
            <li v-for="result in detailedResults" :key="result.recipient || result.id">
                {{ result.recipient }}:
                <strong :class="result.status === 'submitted' ? 'status-success' : 'status-error'">
                     {{ result.status }}
                 </strong>
                 <small v-if="result.status === 'failed'"> - {{ result.error?.message || result.error || 'Failed' }}</small>
                 <small v-if="result.status === 'submitted'"> - ID: {{ result.id }}</small>
            </li>
        </ul>
     </div>
    
    import { ref, computed } from 'vue'; // Import computed
    // ... existing refs ...
    const detailedResults = ref([]); // Add ref for detailed results
    
    // Inside handleBroadcastSubmit, in the success block:
    if (result.success) {
        feedbackMessage.value = result.message || `Broadcast submitted successfully.`;
        feedbackStatus.value = 'success';
        detailedResults.value = result.details || []; // Store detailed results
    } else {
         throw new Error(result.error || 'An unknown error occurred on the server.');
    }
    // Inside handleBroadcastSubmit, in the catch block:
    feedbackMessage.value = `Failed to send broadcast: ${error.message}`;
    feedbackStatus.value = 'error';
    detailedResults.value = []; // Clear details on error
    
     // Inside handleBroadcastSubmit, at the beginning:
     detailedResults.value = []; // Clear details before new submission

    Add corresponding styles for .detailed-results, .status-success, .status-error.

    • Why: Shows per-recipient status, giving the user more insight into partial failures or providing message IDs for successful submissions.

6. Should You Use a Database for Bulk SMS Broadcasting?

While not strictly required for basic sending, storing data enhances the application:

  • Recipient Management: Store lists of recipients for reuse.
  • Message History: Log sent messages, their content, recipients, and status.
  • Audit Trails: Track who sent what and when.
  • Status Tracking: Update message status via webhooks.

Conceptual Schema (using Prisma as an example ORM):

prisma
// prisma/schema.prisma

datasource db {
  provider = "postgresql" // or mysql, sqlite
  url      = env("DATABASE_URL")
}

generator client {
  provider = "prisma-client-js"
}

model User {
  id           Int          @id @default(autoincrement())
  email        String       @unique
  name         String?
  passwordHash String // Store hashed passwords only!
  createdAt    DateTime     @default(now())
  broadcasts   Broadcast[]
}

model RecipientList {
  id          Int         @id @default(autoincrement())
  name        String
  recipients  Recipient[]
  createdAt   DateTime    @default(now())
  // Optional: Link to a User if lists are user-specific
  // userId   Int?
  // user     User?    @relation(fields: [userId], references: [id])
}

model Recipient {
  id              Int            @id @default(autoincrement())
  phoneNumber     String         @unique // E.164 format
  firstName       String?
  lastName        String?
  createdAt       DateTime       @default(now())
  lists           RecipientList[] // Many-to-many relationship
  broadcastMessages BroadcastMessageLog[]
}

model Broadcast {
  id        Int          @id @default(autoincrement())
  userId    Int
  user      User         @relation(fields: [userId], references: [id])
  message   String
  status    String       // 'pending', 'inprogress', 'completed', 'failed'
  sentAt    DateTime     @default(now())
  createdAt DateTime     @default(now())
  messages  BroadcastMessageLog[]
}

model BroadcastMessageLog {
  id              Int       @id @default(autoincrement())
  broadcastId     Int
  broadcast       Broadcast @relation(fields: [broadcastId], references: [id])
  recipientId     Int
  recipient       Recipient @relation(fields: [recipientId], references: [id])
  messagebirdId   String?   // ID from MessageBird API
  status          String    // 'sent', 'delivered', 'failed'
  error           String?
  sentAt          DateTime  @default(now())
  deliveredAt     DateTime?
}

Frequently Asked Questions (FAQ)

How do I send bulk SMS messages with MessageBird and Vue.js?

To send bulk SMS with MessageBird and Vue.js, create a Vue 3 frontend form to collect recipients and message content, then connect it to a Node.js/Express backend that uses the MessageBird SDK to send messages. This tutorial provides complete implementation details including error handling and status tracking.

What is the MessageBird API rate limit for bulk sending?

MessageBird's SMS API rate limits vary by account type and region. Generally, you can send multiple messages per second, but it's recommended to implement batching with delays between batches (starting with 50 messages per batch with 1-second delays) to avoid throttling. Check your MessageBird Dashboard for specific limits.

How do I format phone numbers for MessageBird SMS API?

Phone numbers must be in E.164 format, which includes the country code preceded by a plus sign (e.g., +14155552671 for US numbers, +442071838750 for UK numbers). The format is: +[country code][area code][phone number] with no spaces, dashes, or parentheses.

Can I use MessageBird to send WhatsApp messages in bulk?

Yes, MessageBird supports WhatsApp Business API for bulk messaging. You need to set up a WhatsApp Business Account, register your sender, and modify the message parameters to include type: 'whatsapp'. WhatsApp has additional requirements including message templates and opt-in consent.

What's the difference between MessageBird Live and Test API keys?

Test API keys allow you to test your integration without sending real messages or incurring charges. Live API keys are required to actually send SMS/WhatsApp messages to recipients. Test keys return mock responses, while Live keys interact with the actual MessageBird messaging infrastructure.

How do I handle failed message deliveries with MessageBird?

Implement webhook endpoints to receive delivery status callbacks from MessageBird. Set the reportUrl parameter when creating messages to receive real-time updates about message delivery status, including failures. Store these statuses in a database for tracking and potential retry logic.

Is the MessageBird Node.js SDK production-ready?

The MessageBird SDK (@messagebird/api v4.0.1) hasn't been updated since 2021-2022, which means limited ongoing support. However, it remains functional for production use. Consider implementing comprehensive error handling, testing, and monitoring to ensure reliability in your specific use case.

How much does it cost to send bulk SMS with MessageBird?

MessageBird SMS pricing varies by destination country and volume. Typical rates range from $0.04-$0.15 per SMS in North America and Europe, with different rates for international destinations. Check the MessageBird pricing page for current rates specific to your target countries.

Can I use an alphanumeric sender ID with MessageBird?

Yes, MessageBird supports alphanumeric sender IDs (up to 11 characters) like "MyCompany" for branding purposes. However, availability varies by country—the United States doesn't support alphanumeric IDs, while many European and Asian countries do. Register your sender ID in the MessageBird Dashboard before use.

How do I implement retry logic for failed MessageBird API calls?

Use libraries like async-retry with exponential backoff to retry transient errors (network timeouts, rate limits) while avoiding retries for permanent failures (invalid recipients, authentication errors). Implement logic to categorize errors and only retry those that are likely to succeed on subsequent attempts.

Frequently Asked Questions

How to run the development servers for frontend and backend?

Run 'npm run dev' in the frontend project's root directory. In a separate terminal, navigate to the 'backend' directory and run 'node server.js'.

How to send bulk SMS messages using MessageBird?

Use the provided Vue frontend application to enter recipient phone numbers and your message. The app interacts with a Node.js backend that uses the MessageBird API to send the messages. Each recipient receives an individual SMS, allowing for better status tracking.

What is Vite and why is it used in this project?

Vite is a modern frontend build tool that offers a fast development server and optimized builds. It's used in this MessageBird integration project to streamline the Vue.js frontend development process.

Why is a Node.js backend used with a Vue frontend?

The Node.js backend handles secure interaction with the MessageBird API, including API key management and sending logic. It acts as an intermediary between the Vue frontend and MessageBird to enhance security and manage the complexities of bulk messaging.

When should I use an Alphanumeric Sender ID with MessageBird?

Use an Alphanumeric Sender ID when you want to brand your messages with your company name. Keep in mind, replies are not possible with these IDs, and they might require pre-registration depending on the country.

Can I store recipient lists and message history?

The tutorial suggests a database schema for storing recipients, message logs, and user data. Although not implemented in the example, this is highly recommended for production to enable features like recipient management and tracking message status.

How to install required dependencies for this project?

For the frontend, navigate to the 'sent-messagebird-broadcast' directory and run 'npm install'. For the backend, navigate to the 'backend' directory and run 'npm install express dotenv @messagebird/api cors'.

What is the purpose of the .env file?

The '.env' file in the backend directory stores sensitive information like your MessageBird API key and originator. It's crucial to add this file to your '.gitignore' to prevent committing these secrets to version control.

How to handle errors when sending bulk messages?

The backend code includes error handling at both individual message and batch levels. It distinguishes between submission, delivery, and network errors, though delivery errors require webhooks for full tracking.

What is the character limit for messages sent via MessageBird?

While the API might support longer messages, the example application sets a limit of 1600 characters per message. This limit is implemented in both the frontend and backend for consistency.

What is the maximum number of recipients for a single broadcast?

The article does not specify a limit. MessageBird's API documentation should detail limits on recipients per API call. This application sends one message per recipient, not a single message to all.

How to integrate retry mechanisms for failed messages?

The article provides a conceptual example using the 'async-retry' library for exponential backoff. This is recommended for handling transient errors and increasing the reliability of message delivery.

How to implement recipient list management in the application?

Although the frontend only accepts direct input, the article suggests a database schema with RecipientList and Recipient models, indicating how list management could be implemented.

Why does the application use the E.164 number format?

The E.164 format (+14155552671) ensures consistent and internationally recognized phone number formatting, which is essential for successful message delivery via the MessageBird API.