Quickstart
Overview Bulk send Status writeback Scheduled sends Functions Limits Troubleshooting Support Quickstart
Documentation

Documentation

The reference for Bulk SMS Sender, the add-in that sends and schedules SMS from Microsoft Excel through your own Twilio account: the exact range contract, what the delivery-status column means, how scheduled sends fire from the cloud, the two custom functions, and every limit in one place.

Overview

Bulk SMS Sender is an add-in for Microsoft Excel that turns a selected range into text messages. The first column holds phone numbers, the second holds message text, and a per-row delivery status is written back into the column immediately right of your selection. It can also schedule a range to go out later from the cloud, and it exposes two custom functions, =SMS.SEND and =SMS.STATUS, for use directly in formulas.

The defining design decision is bring-your-own-Twilio. You connect your own Twilio account once, and every message is sent on that account, from your own sender number, at Twilio's own rates.

📡 We never relay, resell or mark up messages

The backend calls the Twilio API with your credentials on your behalf. There is no pooled number, no gateway of ours in the middle, and no per-message margin. Twilio bills you directly for traffic, your messages appear in your own Twilio console, and your sender reputation, A2P 10DLC registration and carrier relationships all stay yours. What you pay us is a flat subscription, or nothing at all on the free tier.

🏪 Not on Microsoft AppSource yet

The store listing is prepared but still pending, so the add-in cannot be installed from AppSource today. Until it is published, installation is by sideloading the manifest, and the quickstart has the steps for Windows, Mac and the web. Email support@innovaapps.ai for the manifest file.

Requirements

Component Requirement
Excel Excel on Windows or Mac (Microsoft 365), or Excel on the web. Not supported on Excel for iOS or Android.
Twilio account Your own, with an SMS-capable number you own or a Messaging Service (MG…)
Twilio credentials Account SID (AC…) and Auth Token, both from the Twilio Console dashboard
Add-in account A free Bulk SMS Sender account: email and password, used for sign-in and plan status
Permissions ReadWriteDocument: the add-in reads the range you select and writes the status column beside it

There is no API key to mint, no webhook to configure and no public URL to expose. Sign-in is email and password only: popup-based OAuth is unreliable inside the Office webviews the add-in runs in, so there is deliberately no single sign-on.

Getting started

The short version, in six steps:

  1. Install the add-in and open it from the Home tab of the ribbon.
  2. Create an account in the task pane with an email address and a password.
  3. Paste your Twilio Account SID, Auth Token and a From number (or MG… Messaging Service SID) into 1 · Twilio setup and save.
  4. Run Test my Twilio setup and receive a real test text on your own phone.
  5. Select a range (phones in the first column, message text in the second), then preview and send from 2 · Send to selected range.
  6. Read the statuses written into the column right of your selection.

The quickstart walks through the same six steps in full, including the sideload instructions per platform and an example of what a good sheet looks like.

Bulk send from a range

Select a range in the sheet, choose Preview selected range, check what the preview reports, then send. Messages go out one row at a time, with a running counter, and the pane finishes with Done: N sent. Statuses written next to your selection.

The column contract

Only two columns are read, and their meaning is fixed:

Position Holds Notes
First column of the selection The recipient's phone number Must resolve to E.164 after normalisation.
Second column of the selection The message text for that recipient 1 to 1,600 characters. A numeric cell is converted to text.
Any further columns Ignored by the parser Keep names, order numbers or notes here freely.

One row is one recipient, and each row carries its own message text, so every message can be different. Selecting fewer than two columns is refused outright with "Select at least two columns: phone numbers in the first, message text in the second."

Phone-number normalisation

Before a number is validated, spaces, hyphens, brackets and dots are stripped, and a leading 00 is converted to +. What must remain is E.164: a +, a non-zero first digit, and 7 to 15 digits in total.

Normalisation is cosmetic only. A country code is never guessed.
You type Result
+45 20 30 40 50 Accepted as +4520304050
0045-2030-4051 Accepted as +4520304051
+1 (500) 555-0006 Accepted as +15005550006
20304050 Rejected: no country code, and the add-in will not invent one.

Header-row detection

The first row of the selection is skipped as a header only when all of the following hold: the selection has more than one row, the first cell is not a valid phone number, it is not empty, and it contains at least one letter. The pane then says "First row looks like a header and was skipped."

ℹ️ Why the "contains a letter" rule matters

A heading such as Phone or Number has letters. A badly formatted number such as 20304050 does not, so it is treated as data and surfaces as a row error you can see and fix, rather than quietly disappearing as a "header" and never being sent.

What makes a row invalid

Bad rows are flagged, never silently dropped. Each one gets its own reason, shown in the preview and written into the status column:

Condition Reason shown
Both cells blank Row is empty. Excluded from the preview list and from writeback, so selecting whole columns is safe.
Phone blank, message present Phone number is missing.
Starts with + but is not valid E.164 "…" is not a valid phone number.
No + after normalisation "…" is missing its country code. Use E.164 like +4520304050.
Valid phone, blank message Message text is empty.

The preview reports Selection <address>: N message(s) ready., then the first five problem rows, then …and X more rows with problems. If nothing is sendable you get "Nothing sendable in this selection."

⚠️ Row numbers are counted from the top of the selection

"Row 3" in the preview means the third row of your selection, not row 3 of the sheet. If the selection starts at row 10, that is sheet row 12.

Delivery-status writeback

Statuses are written into the column immediately right of the entire selection, on the same sheet, aligned row for row. Select A1:B50 and they land in C; select A1:D50 and they land in E. A skipped header row and completely blank rows get no cell written at all.

Every value the status column can contain after a bulk send.
Cell text Meaning
queued · SM… Twilio accepted the message and returned this SID. The word is Twilio's own status at creation time, normally queued or accepted.
duplicate of SM… An identical recipient and body was sent within the last 60 seconds. No second message went out; this is the original SID.
skipped: <reason> The row failed parsing. The reason is one of those in What makes a row invalid.
error: <message> Twilio or the backend refused this row, carrying Twilio's own code and message where there is one, for example Twilio error 21610: The recipient has unsubscribed.
skipped: free limit reached An earlier row exhausted the monthly free allowance and the rest of the batch was stopped. The same abort happens as skipped: Twilio not connected and skipped: Twilio credentials rejected.

⚠️ The written status is a snapshot, not a live delivery receipt

What lands in the column is Twilio's status at the moment it accepted the message, which is why you normally see queued rather than delivered. Nothing polls afterwards, so those cells never change on their own. To find out where a message actually ended up, refresh it with =SMS.STATUS(sid), which performs a live lookup against your Twilio account.

If the send succeeded but the sheet could not be written, the pane says Done: N sent, but writing statuses to the sheet failed. The messages were still sent; only the writeback failed.

Scheduled sends

Scheduling is a paid feature. It takes the same selected range and a date and time, stores it, and sends it from the cloud when the moment arrives, with Excel closed and your computer off. On a free plan the attempt is refused with "Scheduled sends are a paid feature. Upgrade to schedule messages."

Creating a schedule

Select the range, pick a date and time under 3 · Schedule, and choose Schedule selected range. The same parser runs, so invalid rows are reported and left out, and the confirmation tells you how many were scheduled and how many were skipped.

Rule Detail
Maximum size 500 messages per schedule. Beyond that: "A schedule can hold at most 500 messages."
Time window Must be in the future and within one year: "Send time must be in the future." / "Send time must be within one year."
Empty selection "Nothing to schedule: the selected range has no rows."
Twilio must be connected Creation fails immediately if no credentials are saved, rather than creating a schedule that would die on firing.

How a schedule fires

A worker runs every minute in the cloud and claims schedules that are due, one at a time and transactionally, so overlapping runs can never double-send. Each run has a self-imposed sending budget; if a very large batch reaches the end of it, the remaining rows are closed out honestly with Not sent: the sending window closed before this row was reached. Re-send these rows from the sheet. rather than being left in limbo.

🔁 The 60-second duplicate guard does not apply here

Scheduled batches send directly, so an intentional repeat of the same message to the same recipient goes out as expected. The duplicate guard exists to protect against Excel recalculating =SMS.SEND cells, which schedules do not do.

Cancelling

Up to 50 schedules are listed, ordered by send time with the latest first. A Cancel button appears while a schedule is still pending. Once the worker has claimed it the cancel is refused, with "Schedule is already processing and can no longer be canceled." That is deliberate: by then the messages are already going out, and reporting a successful cancel would be a lie.

Writing statuses to the sheet afterwards

A finished schedule shows a Write statuses to sheet button when it still knows which sheet and range it came from. It writes <status> · <sid>, or error: <message> for rows that failed, into the same column right of the original range.

⚠️ That button replays stored results, it does not re-query Twilio

It writes the status each message had when the schedule ran. For the current delivery state of a message, recalculate =SMS.STATUS(sid) against its SID.

🔒 Scheduled message text is deleted once the schedule runs or is cancelled

Message bodies have to be stored for a scheduled send, because the cloud has to know what to send. They are held only until the schedule runs or is cancelled, and are dropped at that point either way. What remains afterwards is the recipient number, the Twilio message SID and the status, which is what the writeback needs. Cancelling a schedule clears its stored text exactly as a completed run does, so a schedule that never fires leaves no message bodies behind. See the privacy policy.

Custom functions

Two custom functions are available in the SMS namespace. They run in the same shared runtime as the task pane and reuse its sign-in, so the task pane must have been opened and signed in for them to work. Otherwise the cell reads "Sign in first: open the Bulk SMS Sender task pane (Home ribbon) and log in."

🚫 =SMS.SEND sends a real message when the cell calculates

This is a function with a side effect, which is unusual in a spreadsheet and worth internalising. Every time Excel calculates that cell (on entry, on a workbook event, on a full recalculation), it attempts to send a text. The only guard is the 60-second duplicate window, which returns the original SID for an identical recipient and body inside that period. It is a mitigation for recalculation storms, not a guarantee. For anything more than a handful of rows, use the task pane's bulk send instead.

SMS.SEND: send one message

=SMS.SEND(to, body)

Sends an SMS through your own Twilio account and returns the Twilio message SID.

Parameters

Parameter Required What to pass
to Yes Recipient phone number in E.164 format, for example +4520304050. The same normalisation as the range parser is applied, so spacing and hyphens are tolerated.
body Yes Message text, 1 to 1,600 characters.

Returns

The Twilio message SID, a 34-character string beginning SM. Feed it to =SMS.STATUS to follow the message.

Example

=SMS.SEND(A2, B2)

With a number in A2 and text in B2, this sends one message and leaves its SID in the cell.

Errors

Failures surface as a cell error carrying the reason, truncated to fit: an invalid recipient, an empty or over-long body, Monthly free limit of 25 messages reached. Upgrade to send unlimited messages., or Twilio's own code and message.

SMS.STATUS: look up a delivery status

=SMS.STATUS(sid)

Returns the current delivery status of a message you have already sent, read live from your own Twilio account each time the cell calculates. Unlike =SMS.SEND, this only reads; it never sends anything.

Parameters

Parameter Required What to pass
sid Yes The Twilio message SID returned by =SMS.SEND, or the one written into the status column by a bulk send. Must look like SM… or MM….

Returns

Whatever Twilio reports: commonly queued, sending, sent, delivered, undelivered or failed. When Twilio attaches an error code it is appended, as in undelivered (Twilio error 30003).

Example

=SMS.STATUS(C2)

If C2 holds a SID, this returns that message's current state. This is the way to turn a column of queued statuses into real delivery outcomes.

Errors

ℹ️ If a cell shows #NAME?

The add-in has not loaded yet, rather than the formula being wrong. Open the task pane once, then recalculate with Ctrl+Alt+F9 (Cmd+Alt+F9 on Mac).

Test my Twilio setup

The validator in 1 · Twilio setup runs four checks in order and stops at the first failure, so you get one specific thing to fix instead of a wall of errors. It needs your own mobile number in E.164 format, because the last check sends a real message to it.

# Check What it proves
1 Credentials Twilio accepts your Account SID and Auth Token. An HTTP 401 is called out specifically, with the reminder that the token sits behind a "show" toggle in the Console.
2 Account active The Twilio account status is active. Any other value is reported verbatim so you can go and look at billing or suspension.
3 From number owned The number in your From field is genuinely owned by that Twilio account, and on failure up to three numbers the account does own are listed. Passes automatically when From is a Messaging Service SID (MG…), reporting that Twilio will pick the sender number.
4 Real test send An actual SMS reaches your phone, proving credentials, account state, sender and delivery path end to end. Failures carry Twilio's own error code.

Credential format is checked earlier still, at save time and before Twilio is contacted at all, so a mistyped SID is caught immediately: the Account SID must be AC plus 32 hex characters, the Auth Token must be at least 32 characters, and From must be E.164 or an MG… Messaging Service SID.

✅ Test sends are not metered

The test message does not count against your monthly free allowance, because it is onboarding, not campaign traffic. Twilio still charges its normal rate for it, as for every message.

⚠️ Twilio trial accounts and test credentials

A Twilio trial account can only send to numbers verified in the Twilio Console; an unverified recipient fails check 4 with Twilio error 21608. Twilio's test credentials and its magic number +15005550006 exercise the API without sending real messages, but they cannot pass checks 3 and 4, which need an owned number and a real delivery.

Plans & the free cap

Free Pro: $19 per month
Messages 25 per calendar month, no card required Unlimited, subject to fair use
Bulk send from a range Included Included
Status writeback Included Included
=SMS.SEND / =SMS.STATUS Included Included
Scheduled sends Not available Included
Twilio message fees Separate, and billed to you directly by Twilio at its own per-message rates

How the cap behaves

💳 Paid upgrades are still being switched on

Pro is billed through Stripe on Innova Apps' own Stripe account, not through Microsoft, and card details never reach our servers. Checkout is not live on this deployment yet, so a free account currently sees "Paid upgrades are not enabled on this deployment yet." in 4 · Account. Everything on the free tier works today. Email support@innovaapps.ai if you need scheduling before then.

Limits & what it does not do

Hard limits

Limit Value
Message body length 1 to 1,600 characters, which is Twilio's concatenated-SMS ceiling. Longer bodies are refused with Message body exceeds 1600 characters.
Messages per schedule 500
How far ahead you can schedule One year
Schedules listed in the pane 50, ordered by send time with the latest first
Duplicate-suppression window 60 seconds for an identical recipient and body
Free tier 25 messages per UTC calendar month
Immediate bulk send No fixed row cap, but rows are sent one at a time, so a long list takes a while and the task pane must stay open until it finishes.

Deliberately out of scope

Bulk SMS Sender is send-only by design. That shapes several things it does not do:

⚠️ You are the sender of record

Messages go out on your Twilio account, from your number, under your Twilio agreement. Obtaining recipient consent and honouring opt-outs where the law requires it (TCPA, GDPR and their equivalents) is your responsibility, as is any A2P 10DLC or similar registration your route needs. See the terms of use.

Troubleshooting

Most problems are Twilio configuration rather than the add-in. Run Test my Twilio setup first: it stops at the first failure and names the fix. The messages below are the ones the add-in actually shows.

"Select at least two columns: phone numbers in the first, message text in the second."
Your selection is one column wide. Select the phone column and the message column together, for example A1:B50.
"… is missing its country code. Use E.164 like +4520304050."
The number has no country code. This usually happens when Excel has stored the number as a plain number and dropped the leading +. Format the column as Text and re-enter it, or write the number with a leading + or 00.
"Could not read the selection. Click a range in the sheet first."
Nothing is selected in the workbook, or focus never left the task pane. Click a range in the grid and preview again.
"Connect your Twilio account first…"
No credentials are saved for this account. Fill in Account SID, Auth Token and From in 1 · Twilio setup and save. During a bulk send this also stops the rest of the batch, since nothing else would succeed either.
"Twilio rejected the Account SID or Auth Token (HTTP 401)."
Copy both values again from the Account Info panel of the Twilio Console dashboard. The Auth Token is hidden behind a "show" toggle, and a partial copy is the usual cause. The saved-credentials version of the same problem reads "Your saved Twilio credentials were rejected (HTTP 401). Re-enter your Account SID and Auth Token in Settings."
"This Twilio account does not own +1555…"
The From value must be a number you bought or verified in that same Twilio account (Phone Numbers → Manage → Active numbers), or a Messaging Service SID starting with MG. The error lists up to three numbers the account does own.
Your Twilio account status is not "active"
Twilio has suspended or closed the account, usually for billing. Resolve it in the Twilio Console; nothing can be sent until the status is active.
Twilio error 21608
Your Twilio account is on trial and may only text verified numbers. Verify the recipient in the Twilio Console, or upgrade the Twilio account. This is Twilio's restriction, not ours.
Messages stay on "queued"
That is the creation-time status and it never updates by itself. Recalculate =SMS.STATUS(sid) against the SID for the live state.
If Twilio itself keeps the message queued or reports it as filtered, the cause is usually on the route: US long-code traffic without A2P 10DLC registration is commonly filtered by carriers. Register your brand and campaign in the Twilio Console.
A row says duplicate of SM…
That exact recipient and body already went out within the previous 60 seconds, so it was not sent a second time and your quota was not charged. If the repeat was intentional, wait a minute or change the text.
"Monthly free limit of 25 messages reached."
The free allowance for this UTC calendar month is used up. It resets at the start of the next month, and upgrading removes the cap and unlocks scheduled sends.
"Schedule is already processing and can no longer be canceled."
The per-minute worker has already claimed that schedule and its messages are going out. Cancelling is only possible while a schedule is still pending.
A cell says "Sign in first: open the Bulk SMS Sender task pane (Home ribbon) and log in."
The custom functions reuse the task pane's session. Open the pane from the Home tab, sign in, then recalculate.
#NAME? in a cell
The add-in is not loaded, so Excel does not recognise the function. Open the task pane once, then recalculate with Ctrl+Alt+F9 (Cmd+Alt+F9 on Mac).
The task pane opens but stays blank
Close and reopen it first. If it persists, write in and say which Excel you are on: Excel on the web loads the add-in from a regional Office origin, and that has been the source of this before.
Sign-in problems
The pane reports these plainly: "Wrong email or password.", "An account with that email already exists. Sign in instead.", "Password must be at least 6 characters.", "Too many attempts. Wait a minute and try again." and "Network error. Check your connection." Use Forgot password? to get a reset email.

FAQ

Why do I have to bring my own Twilio account?

Because it is better for you on every axis that matters. You pay Twilio's own rates with no margin added by us. The sender number is yours, so the reputation built on it is yours. Your A2P 10DLC or equivalent registration stays under your control, which matters because carriers increasingly filter traffic that is not registered. And there is no gateway lock-in: if you stop using the add-in tomorrow, your number, your history and your Twilio account are untouched.

Do you see or store my messages?

Message bodies are not retained after a send completes. For a scheduled send the text has to be stored, because the cloud sends it while Excel is closed, and it is kept only until the schedule runs or is cancelled. It is deleted at that point either way. What we keep per message is the recipient number, the Twilio message SID and the delivery status, which is exactly what the sheet needs to show results. Your Twilio credentials are encrypted at rest with AES-256-GCM under a key unique to your account, and nothing is read from your spreadsheet except the range you explicitly select. The privacy policy has the full list.

Can people reply to my messages?

They can, and those replies go to your Twilio number, where you can see them in your Twilio console. Bulk SMS Sender itself is send-only and never reads inbound messages. STOP and opt-out handling likewise stays on your Twilio number, which is where carriers require it to live.

What does sending actually cost?

Two separate things. Our subscription is flat: free for 25 messages a month, or $19 per month for unlimited sending and scheduling. Twilio bills your own Twilio account separately for the messages themselves, at its standard per-message rate for the destination country, typically a fraction of a cent to a few cents. We never add a per-message fee.

Which platforms are supported?

Excel on Windows and Mac with Microsoft 365, and Excel on the web. Excel for iOS and Android are not supported.

What happens when I hit the free cap mid-batch?

The batch stops rather than half-sending in silence. The row that hits the cap gets an error: cell carrying Monthly free limit of 25 messages reached. Upgrade to send unlimited messages., and every row after it is written as skipped: free limit reached without any further calls to Twilio. You can see precisely which rows went out and which did not, and re-send just the remainder next month or after upgrading.

Is it safe to leave =SMS.SEND in a workbook?

Treat it with care. The cell attempts a send every time it calculates, so reopening or recalculating a workbook full of =SMS.SEND formulas can text people again. The 60-second duplicate window catches the immediate case, but it is a safety net rather than a guarantee. Once a batch has gone out, the safer pattern is to convert those cells to their values, and to use the task pane's bulk send for anything sizeable.

Do I need Excel open for a scheduled send?

No. That is the point of the feature. Once a schedule is created it lives in the cloud and a worker sends it at the appointed time whether Excel is open, closed or uninstalled. You can write the results back into the sheet later from the task pane.

Can I try it with Twilio test credentials?

Partly. Twilio's test credentials and the magic number +15005550006 exercise the API without sending real messages, which is useful for seeing the flow. They cannot pass the setup validator, though: check 3 needs a number the account actually owns, and check 4 needs a real delivery.

Where do I install the add-in?

It is not on Microsoft AppSource yet; the listing is prepared but pending. Until then it is installed by sideloading the manifest, which the quickstart covers for Windows, Mac and the web. Email support@innovaapps.ai for the file.

How do I delete my account and my data?

Email support@innovaapps.ai from the address the account uses. We delete the account, the encrypted Twilio credentials and the stored metadata. You can also revoke access from your side at any time by rotating your Twilio Auth Token in the Twilio Console, which immediately stops the stored credentials from working.

Support

📧 Contact us

For technical questions, bug reports or feature requests: support@innovaapps.ai. The support page carries the same troubleshooting steps in a shorter form.

When you write in, it helps to include:

Please never send us your Twilio Auth Token, and do not paste it into an email or a screenshot. We do not need it and cannot use it.

See also the privacy policy for exactly what is stored and where, and the terms of use for acceptable use and billing.

📖 Read the quickstart 🛟 Go to support