# Documentation ## Documentation URL: /docs Concepts, walkthroughs, embeddable UI, and operational setup for Surge messaging and voice. Documentation Welcome to Surge — the SMS API that actually cares about developers. This section covers the platform end-to-end: - Get started — what Surge is and how it's organised, plus a five-minute quickstart on the demo number. - Register your business — the API and dashboard paths through carrier registration, plus the patterns that consistently get rejected. - Sending messages — single sends, blasts, audiences, conversations, delivery tracking, and failure handling. - Receiving messages & webhooks — endpoint setup, signature verification, the 13 event types, and two-way messaging patterns. - Phone numbers — purchasing, importing, international, and attaching numbers to campaigns. - Verifications — OTP-style code send-and-check. - Channels roadmap — RCS, WhatsApp, voice (in development). - Embeddable UI components — drop-in inbox, conversation, unread count, and dialpad. - Concepts — SMS segments, TCR, consent flow rules, deliverability. For the request and response schemas, see API Reference. For language-specific install and idioms, see SDKs. For HIPAA, changelog, and glossary, see Resources. --- ## Channels Roadmap URL: /docs/channels-roadmap Channels Roadmap RCS and WhatsApp are coming to Surge. How to build SMS workflows today so your code extends to new channels without rewrites. Voice calls, recording, voicemail, and AI voice agent integrations available now; full GA arrives after HIPAA compliance ships. --- ## RCS and WhatsApp URL: /docs/channels-roadmap/rcs-and-whatsapp RCS and WhatsApp are coming to Surge. How to build SMS workflows today so your code extends to new channels without rewrites. RCS and WhatsApp SMS is Surge's first channel. RCS and WhatsApp are next. RCS (Rich Communication Services) RCS is the successor to SMS. It supports images, carousels, read receipts, typing indicators, and branded sender profiles — without requiring a separate app install. It works over the carrier network like SMS but looks more like a messaging app. Surge's RCS support will use the same API surface as SMS: the same POST /accounts/{account_id}/messages endpoint, the same contact and conversation model, and the same webhook events. Messages that can't be delivered as RCS (because the recipient's device or carrier doesn't support it) will fall back to SMS automatically. You won't need to write any fallback logic. WhatsApp WhatsApp support will follow a similar pattern: same API surface, with WhatsApp-specific features (templates, rich media) exposed as additional parameters on the existing message create endpoint. What to do now If you're building messaging workflows today that you'll eventually want to extend to RCS or WhatsApp, the patterns to use are: - Contacts and conversations: the contact and conversation model will extend to cover all channels. Messages today in a conversation thread will be associated with the right contact when new channels come online. - Webhook events: message.received, message.sent, and message.delivered will work identically across channels. Your handler code won't need to change. - Phone numbers: the same Surge phone numbers used for SMS will be used for RCS when the recipient's carrier supports it. There's nothing to configure ahead of time. The channel extension will happen at the API layer. Staying informed Subscribe to the changelog at surge.app/changelog (or see Release Notes) for release announcements. Deprecation notices are tracked at Deprecation Notices. --- ## Voice URL: /docs/channels-roadmap/voice Voice calls, recording, voicemail, and AI voice agent integrations available now; full GA arrives after HIPAA compliance ships. Voice Surge supports voice calls using the same phone numbers and accounts used for SMS. Outbound calls, call recording, voicemail, and one-click AI voice agent integrations are available now. HIPAA-compliant voice and full general availability are on the horizon. Available now - Outbound calls via POST /accounts/{account_id}/calls - Call recording (GET /accounts/{account_id}/recordings) - Voicemail - One-click AI voice agent integrations (ElevenLabs, Retell, Vapi) - STIR/SHAKEN call integrity Flag-gated webhook events The following webhook events are currently behind a feature flag. Contact support to enable voice webhooks for your account: - call.ended - recording.completed - voicemail.received In development - HIPAA-compliant voice (tied to the Custom plan; in development). See HIPAA Compliance for current status. Voice GA Voice will move to general availability after HIPAA compliance ships. The webhook events listed above will become available to all customers without the feature flag at that point. --- ## Concepts URL: /docs/concepts Concepts Valid opt-in methods, what makes a consent flow carrier-compliant, and how to write the consent_flow description. T-Mobile volume caps, Verizon batching delays, Surge's content policy layer, and how to monitor message delivery health. The three required SMS disclosures, where to place them, and copy-ready opt-in templates for common use cases. How SMS segments and character encodings work, why emoji messages use more segments, and practical cost advice. What TCR is, how Surge submits your registrations as your CSP, and when becoming a CSP yourself makes sense. --- ## Consent Flows: How Users Opt In URL: /docs/concepts/consent-flows Valid opt-in methods, what makes a consent flow carrier-compliant, and how to write the consent_flow description. Consent Flows: How Users Opt In A consent flow is the mechanism by which someone explicitly agrees to receive text messages from you. Carriers and regulators require documented, explicit consent before you send marketing or promotional messages. Getting this right is the most common source of campaign rejection. Why consent matters Carriers treat undocumented consent as no consent. If a recipient flags your message as spam, or if a reviewer visits your website and can't find a clear opt-in mechanism, your campaign will be flagged. Beyond compliance, good consent practices reduce spam complaints, which directly affects your message deliverability. Types of opt-in | Method | Description | When to use | |---|---|---| | Web form | User enters phone number on a web page with a visible consent checkbox | Checkout, signup, contact forms | | Keyword | User texts a keyword (e.g., JOIN) to your number | Physical signage, social media CTA | | Paper form | User signs a physical form | In-person service businesses | | Verbal | User provides consent verbally (with your documentation of it) | Call center, in-person interactions | What makes a consent flow valid A valid web form consent flow has three components: 1. A phone number input: users enter the number they want messages sent to 2. An explicit, unchecked checkbox: labeled something like "I agree to receive text messages from Acme Corp" 3. Required disclosures shown near the checkbox (see Required Disclosures) SMS consent must be separate from general terms acceptance. You cannot use "I agree to the terms of service" as your consent mechanism for text messages. Carriers require an explicit, optional opt-in. Writing your consent_flow description The consent_flow field in your campaign registration is a plain-English description of your opt-in mechanism. It should be specific enough that a reviewer could find the opt-in on your website and verify it matches your description. What to include: - Where the opt-in happens (URL, form type) - The exact wording of the checkbox or prompt - Whether the checkbox is pre-checked or unchecked by default (must be unchecked) - What disclosures are shown and where A vague description like "users opt in on our website" will be rejected. A specific description like the example below passes review: "Users enter their phone number during the account signup process at acme.com/signup. An unchecked checkbox labeled 'I agree to receive text messages from Acme Corp about my account' appears below the phone number field. The following disclosures appear immediately beneath the checkbox: 'Message frequency varies. Msg&data rates apply. Reply STOP to opt out. View our Privacy Policy at acme.com/privacy.'" Keyword opt-in For keyword-based opt-ins, the consent flow description should explain where the keyword appears (physical sign, website, etc.) and what the keyword is. "Users text JOIN to +18015559876 after seeing our in-store signage. The sign reads: 'Text JOIN to +18015559876 to receive exclusive deals from Acme Corp. Message frequency varies. Msg&data rates apply. Reply STOP to opt out.'" Double opt-in Some high-stakes use cases (medical, financial) benefit from double opt-in, where after the user submits their number, they receive a confirmation text asking them to reply YES. This creates an additional record of consent. It's not required by TCR but can reduce disputes. What happens when someone opts out When a contact texts STOP, Surge records the opt-out and fires a contact.optedout webhook event. Sending to an opted-out contact returns an optedout error. You cannot legally re-subscribe someone who has opted out without their explicit new consent (typically by having them text START). The HELP and START keywords are handled automatically by Surge. When a contact texts HELP, they receive a help message configured on the phone number. When they text START, they're re-subscribed. --- ## Deliverability URL: /docs/concepts/deliverability T-Mobile volume caps, Verizon batching delays, Surge's content policy layer, and how to monitor message delivery health. Deliverability Deliverability is the percentage of your sent messages that actually reach the recipient's handset. This section covers the factors that affect it and what you can do about them. T-Mobile volume caps T-Mobile enforces daily volume caps per campaign based on the volume tier you registered: | Campaign volume | T-Mobile limit | |---|---| | low | 2,000 SMS segments per day | | high | Up to 200,000 SMS segments per day (subject to TCR trust score) | Messages that exceed the cap are queued or filtered, not delivered. If you need to send more than 2,000 segments per day to T-Mobile, register your campaign at high volume or request a volume increase after your low campaign is approved. "SMS segments" are the carrier billing unit, not messages. A single message with 200 characters costs 2 segments (161–306 characters in GSM-7). See SMS Segments & Encoding. Verizon delivery delays Verizon delivers messages in batches, which means recipients on Verizon sometimes see messages several minutes after the carrier accepted them — even when Surge receives a delivered status from Verizon. This is a carrier-side behavior, not a Surge bug. If you're sending time-sensitive messages (appointment reminders 30 minutes ahead, flash sale notifications), build in extra lead time for Verizon subscribers. Surge's content policy layer Surge runs a content-policy check on outbound messages before they reach the carrier. This catches links, patterns, and content that carriers commonly filter — before the message is sent and marked as failed. If your message is blocked by the content policy, you'll see a message_filtered error in the message.failed webhook event. Common reasons: - Unregistered link shorteners. Third-party link shorteners (bit.ly, tinyurl.com, etc.) are frequently flagged by carrier spam filters. Use Surge's built-in link shortening or your own branded domain. - Content not matching your campaign. If your campaign is registered for customer_care but your messages contain marketing language and promotional links, the content mismatch will be flagged. - Age-gated or lending content without the flag. If your messages contain age-gated content or direct lending references but you didn't include agegated or directlending in your campaign's includes array, those messages will be filtered. The includes flags The includes array in your campaign registration tells carriers what content types your messages contain. Being accurate here directly affects deliverability: | Flag | When required | |---|---| | links | Your messages contain any URLs | | phone_numbers | Your messages contain phone numbers | | age_gated | Your messages contain age-restricted content (alcohol, cannabis, etc.) | | direct_lending | Your messages relate to lending or financial products (payday loans, mortgage offers, etc.) | Missing a flag when the content is present is a deliverability problem, not just a registration problem. Carriers can filter messages that don't match the registered content profile. See Canonical Schema Reference for the full list of valid includes values and their descriptions. Carrier fees Carriers charge per-message fees that vary by destination network. These fees are passed through in your Surge billing and visible in your usage reports. Fees differ by message type (SMS vs MMS), recipient carrier, and sometimes message content. Monitoring deliverability Watch the message.delivered and message.failed webhook events. A sustained spike in message.failed events with message_filtered as the reason is a signal to review your content against your campaign registration. Blast-level delivery metrics (aggregate deliverability rate, opt-out rate per send) are available in the dashboard under Blasts. --- ## Required Disclosures and Opt-In Templates URL: /docs/concepts/required-disclosures The three required SMS disclosures, where to place them, and copy-ready opt-in templates for common use cases. Required Disclosures and Opt-In Templates Carriers require specific disclosures at the point of SMS opt-in. This page lists what's required, where to put it, and templates you can adapt for common use cases. The three required disclosures Every SMS opt-in point must display: 1. Message frequency: e.g., "Message frequency varies" or "You'll receive up to 4 messages per month" 2. Cost notice: exactly "Msg&data rates may apply" or "Msg&data rates apply" 3. Opt-out instructions: exactly "Reply STOP to opt out" (STOP is the required keyword) Some businesses add "Reply HELP for help" as a fourth disclosure, which is best practice but not strictly required. Where to put disclosures Disclosures must appear at the point of opt-in: next to or immediately below the phone number field and consent checkbox. They should be readable without scrolling. Burying them in a terms of service page or a footer is insufficient. The font size should be legible (not 8px gray text). Reviewers visit your actual website, not just screenshots. Canonical compliance message When you send a first message to a new subscriber, include the full opt-in confirmation: "You are now opted in to messages from Acme Corp. Frequency varies. Msg&data rates apply. Reply STOP to opt out." Replace "Acme Corp" with your actual brand name, not a placeholder. Templates by use case Marketing Opt-in checkbox label: "I agree to receive promotional text messages from Acme Corp" Disclosure text beneath the checkbox: "Message frequency varies. Msg&data rates apply. Reply STOP to opt out. View our Privacy Policy." Confirmation message: "Acme Corp: You're in! Expect exclusive deals and updates. Msg freq varies. Msg&data rates apply. Reply STOP to opt out." Transactional (account notifications, order updates) Opt-in checkbox label: "Send me text message updates about my orders and account" Disclosure: "You may receive messages about order status, shipping, and account activity. Message frequency varies. Msg&data rates apply. Reply STOP to opt out." Confirmation message: "Acme Corp: You'll now receive order and account updates by text. Reply STOP to opt out. Msg&data rates apply." Two-factor authentication Opt-in checkbox label: "Use my phone number to receive one-time verification codes" Disclosure: "We'll send a verification code each time you sign in. Message frequency: one per login. Msg&data rates apply. Reply STOP to opt out." Note: For security-only OTP (where the user only receives verification codes, not marketing), some of these disclosures are less critical, but including them is still best practice. Customer care Opt-in at ticket submission: "Text me updates about my support request" Disclosure: "You'll receive updates about your support case by text. Frequency depends on case activity. Msg&data rates apply. Reply STOP to opt out." Platform vs direct customer templates If you're a SaaS platform registering on behalf of clients, the brand name in every message should be the client's brand, not your platform's brand. The opt-in checkbox label, disclosure text, and confirmation message should all name the client. "I agree to receive text messages from Smith Realty" — not — "I agree to receive text messages from YourPlatform on behalf of Smith Realty" Reviewers flag brand-in-disclosure mismatches. The message recipient should see one consistent brand name throughout: on the website where they opted in, in the SMS messages they receive, and in the registration you submitted. Privacy policy requirements Your privacy policy must explicitly state that you collect phone numbers for SMS communication and describe how you use them. The first section should not imply that agreeing to the policy constitutes SMS consent — that's a common rejection cause. Minimum language to include in your privacy policy: "We collect your phone number to send you [describe use cases]. You may opt out at any time by replying STOP to any text message from us." --- ## SMS Segments & Encoding URL: /docs/concepts/sms-segments How SMS segments and character encodings work, why emoji messages use more segments, and practical cost advice. SMS Segments & Encoding When you send a text message, carriers count it in segments, not characters. Understanding segments matters because they determine both cost and deliverability, and because the number of characters per segment changes depending on what you put in the message. The 140-byte rule Every SMS segment is 140 bytes. How many characters fit into those 140 bytes depends on the character encoding: | Encoding | Characters per segment | Concatenated (multi-part) | |---|---|---| | GSM-7 | 160 per segment | 153 per segment | | UCS-2 | 70 per segment | 67 per segment | When a message spans multiple segments, carriers use a small portion of each segment for a header that lets the handset reassemble the parts in order. That's why concatenated messages have slightly fewer usable characters per segment (153 and 67 instead of 160 and 70). GSM-7 vs UCS-2 GSM-7 is the standard encoding for basic Latin characters. It covers the full English alphabet, digits, and common punctuation. If every character in your message is in the GSM-7 character set, the 160-character-per-segment limit applies. UCS-2 activates automatically if your message contains any character outside GSM-7, including emoji, accented characters (like é, ñ, ü), curly quotes (“ ”), ellipses (…), and many symbols. When UCS-2 activates, the limit drops to 70 characters per segment. This is the most common surprise for developers — pasting text from a word processor or rich text editor often introduces curly quotes or em dashes that silently switch the message to UCS-2 and cut the character limit by more than half. Characters that trigger UCS-2 Common culprits: - “ and ” (curly double quotes, vs standard ") - ‘ and ’ (curly single quotes, vs standard ') - … (ellipsis, vs three periods ...) - — (em dash, vs hyphen -) - Emoji of any kind: 😀 🎉 ✅ - Any letter with an accent: é à ü ñ ç If you're interpolating user-provided content into messages, normalise the input to GSM-7-safe characters before sending. Counting segments A standard English message under 160 characters is one segment. A 300-character English message is two segments. A 70-character message containing a single emoji is two segments (UCS-2 encoding, where one segment fits 67 usable characters in multi-part, so 70 characters spans two segments). To count segments before sending, use a client-side SMS segment calculator. This helps you estimate cost and flag unexpectedly long messages before they go out. Segments as the billing unit Surge bills per segment, not per message. A blast of 1,000 two-segment messages costs twice as much as 1,000 one-segment messages. Volume caps (2,000 SMS segments per day to T-Mobile on the low campaign volume setting) are also counted in segments. Keep this in mind when estimating costs and when setting campaign volume levels during registration. Segment count and automatic protocol selection Segment count affects more than billing. It can determine whether your message is sent as SMS or MMS: - More than 10 segments → Surge automatically sends as MMS, regardless of whether you included attachments. This avoids the fragmented experience of 11 concatenated SMS parts on a recipient's device. - 3 or more segments → If your platform has autommsenabled, Surge converts the message to a single MMS. This setting is off by default; contact support to enable it. In both cases, if the recipient's carrier doesn't support MMS, Surge falls back to SMS and delivers the full concatenated message. See Send to One Person for details. Practical advice - Keep routine messages under 160 characters to stay within a single segment - Use straight quotes (" and ') instead of curly quotes in templates - Use three periods (...) instead of the ellipsis character (…) - If you support emoji in user-facing message templates, budget for UCS-2 costs - Test any message template with a segment counter before sending at scale — the segments field is included in every message response --- ## The Campaign Registry (TCR) and Becoming a CSP URL: /docs/concepts/tcr What TCR is, how Surge submits your registrations as your CSP, and when becoming a CSP yourself makes sense. The Campaign Registry (TCR) and Becoming a CSP The Campaign Registry (TCR) is an industry body created by mobile carriers to combat spam. Before your messages can be delivered at scale via US long codes (10-digit local phone numbers), the brand sending them must be registered with TCR and the use case (campaign) must be approved. The registry gives carriers a way to verify: who is behind this phone number, and what are they sending? Messages from unregistered or suspicious sources are increasingly filtered or blocked. How Surge fits in Surge is registered with TCR as a Campaign Service Provider (CSP): an intermediary that helps its customers register brands and campaigns. When you submit a campaign through Surge, Surge submits it to TCR on your behalf. From a practical standpoint, this means: - You manage everything through the Surge API and dashboard - Surge handles the TCR submission, status tracking, and communication with carriers - Your billing for TCR registration fees goes through Surge alongside your messaging charges - If there are carrier questions or changes needed, Surge routes that feedback to you The alternative (registering as a CSP yourself) is described below. Surge generally recommends against it for most customers. Becoming a CSP yourself If you're building a platform where thousands of your customers each send messages under their own brand, you may consider registering as a CSP directly with TCR. What it means: You apply to TCR to become an independent CSP. After approval, you submit your customers' brand and campaign registrations directly to TCR rather than through Surge. You take on responsibility for compliance, TCR billing, and any issues with your customers' campaigns. When it might make sense: - You have hundreds or thousands of distinct registered brands - You want to consolidate TCR fees and have a direct billing relationship with TCR - Your legal or compliance team requires direct regulatory responsibility Why Surge generally recommends against it: Going through Surge as your CSP is simpler in almost every respect: - API and dashboard integration. When you register through Surge, registration is part of the same API flow as messaging. Going direct to TCR means a separate integration with the TCR API and managing two systems. - Consolidated billing. With Surge as your CSP, all your costs appear in one place. As your own CSP, you pay Surge for messaging and TCR separately for registration fees. - Support. Campaign review feedback, carrier escalations, and status updates all flow through Surge's support team when you go through Surge. As an independent CSP, you deal directly with TCR. If you're considering the CSP path, contact Surge support before you start. There are specific scenarios where it's the right choice, and support can help you evaluate whether yours is one of them. --- ## Embeddable UI Components URL: /docs/embeddable Add a messaging inbox, conversation view, and dialpad to your app using Surge's embeddable UI components and JWT auth. Embeddable UI Components Surge provides ready-to-use UI components you can embed directly into your web application. They connect to Surge's messaging backend and give your users an inbox, conversation view, and unread count — without building any of that yourself. Components are served from embed.surge.app. Available components - Inbox: a list of all conversations for the current user, sortable and filterable - Conversation: a single threaded conversation with send capability - Unread count: a badge showing how many unread conversations the user has - Phone (dialpad): a voice dialer for outbound calls (voice-enabled accounts) Authentication Each component mount requires a signed JWT. The JWT identifies which Surge account and user the component is showing data for. Publishable tokens are deprecated. If you're currently using a publishable token, migrate to JWT authentication. See Deprecation Notices. Two ways to get a JWT Option 1: use the token endpoint (simpler) Call the user token endpoint from your server, then pass the resulting token to the component: The token is short-lived. Generate a fresh one on each page load or use a refresh mechanism. Option 2: create and sign your own JWT If you need more control over JWT claims (expiration, scopes), generate a signing key in the dashboard (Settings → Signing Keys) and sign your own JWTs. The key is an Ed25519 key pair: your private key stays on your server, and Surge uses the public key to verify tokens. Mounting a component Pass the token to the component via JavaScript. The exact mounting API depends on your framework. Refer to the component-specific pages for details. Next steps How End Users are provisioned and attributed to messages. Mounting options and callbacks for the conversation list. Mounting a single thread inside your own page. Badge integration for navigation chrome. --- ## Conversation Component URL: /docs/embeddable/conversation Mount the Conversation component to display a single message thread with send capability for the current End User. Conversation Component The Conversation component shows a single message thread and a send field. Use it to embed a specific conversation in your application, useful when users navigate from your own inbox UI or when you want to open a specific thread. !Conversation component showing a single message thread with a composer at the bottom Mount the component Options | Option | Type | Required | Description | |---|---|---|---| | target | HTMLElement | Yes | The DOM element to mount the component into | | token | string | Yes | A signed JWT for the current End User | | conversationId | string | No | The ID of the conversation to display; omit to start a new conversation | | onMessageSent | function | No | Callback when the user sends a message; receives the message object | | theme | object | No | Custom color and font overrides | Opening a new conversation Omit conversationId to show an empty thread where the user can compose a new message. The component creates the conversation when the first message is sent. --- ## Working with End Users URL: /docs/embeddable/end-users Provision End Users via the API, generate short-lived JWTs for embedded components, and attribute messages to users. Working with End Users An End User is a person in your application who sends and receives messages through Surge's embeddable UI components. End Users are distinct from Dashboard Users (people who log in to hq.surge.app). If you're building a SaaS platform where your customers interact with Surge-powered messaging, your customers' customers are End Users. What an End User is End Users are provisioned via the Surge API and belong to a specific account. They're used in two ways: 1. Embedded UI: the inbox and conversation components authenticate as a specific End User, showing that user's conversations and sending messages on their behalf 2. Message attribution: messages sent or received on behalf of an End User include a user_id field, so you can trace activity back to the individual End Users do not have Surge passwords or sessions. Authentication for embedded components goes through a short-lived JWT that your server generates. Provision an End User Create an End User via the API before you mount any components for them: Store the returned id alongside the user's record in your database. You'll use it to generate component tokens and attribute messages. Generate a component token Your server generates a short-lived JWT for each user session. The JWT scopes the embedded component to that specific user's data. The simplest path is the token endpoint: Return this token to your frontend and use it to mount the component. Generate a new token on each page load, tokens are short-lived. Typical vertical SaaS flow 1. When a customer signs up for your SaaS product, create a Surge End User via POST /accounts/{account_id}/users. Store the id. 2. When that customer loads a page in your app that includes embedded messaging, your server calls POST /users/{user_id}/tokens to get a fresh JWT. 3. Your frontend receives the JWT and mounts the Surge Inbox or Conversation component with it. 4. The component shows only that user's conversations and sends messages attributed to them. List and manage End Users Deleting an End User removes their Surge record but does not delete message history. Next steps Mount a conversation list for the current End User. Mount a single threaded conversation view. Show a live unread badge in your navigation. --- ## Inbox Component URL: /docs/embeddable/inbox Mount the Inbox component to show a real-time, paginated conversation list scoped to the current End User. Inbox Component The Inbox component shows a list of all conversations for the current End User. It handles loading, pagination, and real-time updates automatically. !Inbox component rendering a list of conversations with unread indicators and the most recent message snippet Mount the component Generate the JWT server-side on each page load. See Embeddable UI Overview for authentication options. Options | Option | Type | Required | Description | |---|---|---|---| | target | HTMLElement | Yes | The DOM element to mount the component into | | token | string | Yes | A signed JWT for the current End User | | onConversationSelect | function | No | Callback when the user taps a conversation; receives the conversation object | | theme | object | No | Custom color and font overrides | Handling conversation selection If you want to navigate to a conversation view when the user taps a thread, provide an onConversationSelect callback: Unmounting To clean up the component (e.g., when navigating away): --- ## Phone (dialpad) URL: /docs/embeddable/phone Embed a working dialpad so end users can place outbound voice calls directly from inside your application. Phone (dialpad) The Phone component embeds a complete dialpad in your application, letting end users place outbound voice calls directly from your UI. It pairs with Surge's voice API and one-click integrations to AI voice agents. When to use it - You're building a contact-center or SaaS dialer and you want a working dialpad on day one - You don't want to build call setup, mute, hold, and DTMF UI yourself - Your end users expect to make calls from inside your product without leaving the page If you only need to place programmatic calls (no UI), use the Calls API directly (POST /accounts/{account_id}/calls). Embed snippet Props | Prop | Type | Required | Description | |---|---|---|---| | target | string (CSS selector) | Yes | Where to mount the dialpad | | token | string (JWT) | Yes | Signed JWT scoping the user. See Authentication. | | accountId | string | Yes | Account the call is placed under. Determines billing and which campaign rules apply. | | fromNumber | string (E.164) | Yes | The Surge phone number that will appear as the caller ID. Must be attached to the account. | | onCallEnded | (call) => void | No | Fires when the call wraps up. Useful for logging or unlocking your UI. | | onError | (error) => void | No | Fires for setup errors (token invalid, number not provisioned, microphone denied). | What the user sees A standard 3×4 dialpad, a number-entry display, a mute toggle, and a green/red call/hangup button. During an active call, the timer counts up. After hangup, the component returns to the idle dialpad state. Microphone permissions The dialpad asks for microphone access on first call. If the browser denies it, onError fires with error.code: "microphone_denied" and you should surface a clear message — the embed cannot recover that permission on its own. Phone calls vs voice agents Outbound calls placed from this component go to the dialled number directly. If you want the call to flow through an AI voice agent (ElevenLabs, Retell, Vapi), that's configured on the Surge phone number itself — see Channels Roadmap → Voice. The dialpad doesn't need to know whether the number on the other end is a human or an agent. --- ## Unread Count Component URL: /docs/embeddable/unread-count Embed a live unread conversation count badge that updates in real time as new messages arrive. Unread Count Component The Unread Count component renders a badge showing the number of unread conversations for the current End User. Use it in navigation elements, icon buttons, or any element where you want to surface unread state. !Unread Count badge rendered next to a navigation icon, showing a numeric unread total Mount the component The component updates in real time when new messages arrive. Options | Option | Type | Required | Default | Description | |---|---|---|---|---| | target | HTMLElement | Yes | | The DOM element to mount the badge into | | token | string | Yes | | A signed JWT for the current End User | | onChange | function | No | | Callback when the unread count changes; receives the new count as a number | | hideWhenZero | boolean | No | true | Hide the badge when count is zero | | theme | object | No | | Custom color and font overrides | Reading the count in your own code If you want to render the count yourself instead of using the component: --- ## Overview URL: /docs/overview Overview The Platform, Account, and User hierarchy, what each level owns, and how the API is structured. Send your first SMS in under five minutes using Surge's demo number. No registration or phone number purchase required. An introduction to Surge, the SMS and voice API for developers. --- ## How Surge is Organised URL: /docs/overview/how-surge-is-organised The Platform, Account, and User hierarchy, what each level owns, and how the API is structured. How Surge is Organised Surge organizes everything into three levels: Platform, Account, and User. Understanding this hierarchy will help you make sense of the API, because most endpoints are scoped to one of these levels. Platform is your company or product. You get one when you sign up. Your API keys, billing configuration, webhook endpoints, and settings all live at the platform level. This means a single API key can manage everything underneath it. Account is the business entity that sends messages. It holds your campaigns, phone numbers, contacts, conversations, and messages. If you're a single developer or a small team, you'll have one account and that's all you need. If you're a SaaS platform registering brands for your own customers, you create one account per customer to keep compliance, billing, and message history isolated. See One Account per Customer for that pattern. User is optional. You only need Users if you're building with Surge's embedded UI components (inbox, conversation view, dialpad). A User represents a person inside an account who sends and receives messages through those components. If you're calling the API directly from your backend, you can skip Users entirely. Before your account can send messages at scale, you'll need to register a campaign through The Campaign Registry (TCR) or, for toll-free numbers, through a separate verification process. Register Your Business covers both paths. Two kinds of "user" This is the most common source of confusion for new developers, so it's worth being explicit. | | Dashboard User | End User | |---|---|---| | What they are | A person who logs in to hq.surge.app to manage API keys, billing, and webhooks | A person provisioned via the API, shown inside embedded UI components | | Who creates them | They sign up or receive an invitation | You create them via POST /accounts/{account_id}/users | | Where they appear | The Surge dashboard | Inside embeddable inbox and conversation components | | Scope | Can belong to multiple platforms | Scoped to a single account | The API reference uses "user" to mean End User throughout. If you're not using embedded components, you won't interact with End Users at all. For a full guide to provisioning them, see Working with End Users. The API in brief Surge is a REST API. All requests and responses are JSON. Authenticate with a bearer token: Account-scoped endpoints take the account ID in the request path. Because your API key is platform-scoped, this is how you target a specific account: The base URL is https://api.surge.app. Official SDKs are available for Python, TypeScript, Ruby, and Elixir with pagination helpers, error handling, and webhook signature verification built in. --- ## Send Your First Message URL: /docs/overview/send-your-first-message Send your first SMS in under five minutes using Surge's demo number. No registration or phone number purchase required. Send Your First Message Send an SMS in under five minutes using Surge's demo number. No carrier registration, no phone number purchase required. Prerequisites - Surge account: Sign up at hq.surge.app. - API key: Find it under API Keys in the left sidebar of the dashboard. - Account ID: Shown at the top of the dashboard, format acct_01j.... - A phone number you can receive texts on: Used as the to value in your first send. 1. Send a message Every outbound message goes through the same endpoint. Replace the account ID, API key, and to number with your own values. You don't need a from number. New accounts send from a demo number automatically. 2. Check the response A successful request returns the message object: Within a few seconds, the phone number you specified in to receives a text. If nothing arrives after 60 seconds, check that the number is in E.164 format: starting with + and country code, like +18015551234. 3. Watch for the demo limit The demo number has a cap of 25 outbound messages per account. When you hit the limit, the API returns a 403 with: Next steps Two things unlock production sending: register your business and purchase a phone number. Pick an SDK while you wait for review to clear. Get carrier approval for your brand and campaign. Pagination, error handling, and webhook signature verification out of the box. Buy a local or toll-free number and let Surge attach it to your campaign. Verify signatures and handle inbound messages, deliveries, and opt-outs. --- ## What is Surge URL: /docs/overview/what-is-surge An introduction to Surge, the SMS and voice API for developers. What is Surge Surge is an SMS and voice API that handles carrier registration for you. If you've used Twilio, Sinch, or Vonage, you know the pain: weeks of waiting for carrier approval, XML-based voice configuration, and support tickets that go nowhere. Surge gives you a demo number to test with immediately, and production registration takes hours instead of weeks. Why Surge? - 24-to-48-hour carrier registration with a guaranteed 72-hour response. Twilio and Sinch typically take two to four weeks. - JSON everywhere. REST API, voice call configuration, webhooks. No XML, no TwiML, no legacy protocols. - One-click voice agent integrations with ElevenLabs, Retell, and Vapi. - Embeddable UI components you drop into your app: inbox, conversation, dialpad, unread count. - SDKs for Python, TypeScript, Ruby, and Elixir with full API parity. - Responsive support when registrations get flagged or carriers block messages. Who Surge is for Developers adding messaging to a product. Appointment reminders, order notifications, 2FA, conversational SMS agents. Surge handles TCR paperwork and carrier registration so you don't build compliance infrastructure. Vertical SaaS platforms. Create one Account per customer, register each brand separately, and keep compliance, billing, and message history isolated. See One Account per Customer for the full pattern. What you can build Send one-to-one messages, schedule sends, or blast to large lists. Track ongoing threads with the Contacts and Conversations APIs. Deliver six-digit OTP codes for login flows and onboarding. Call POST /verifications to send a code and POST /verifications/{verification_id}/checks to verify it. Make outbound calls with recording and voicemail. Integrate with AI voice agents from ElevenLabs, Retell, or Vapi for agent-driven workflows. Add an inbox, conversation view, phone dialpad, or unread count badge to any web application. Your users send and receive messages without you building a messaging UI. Try it now Send a test message with a single API call. No registration or phone number purchase required: The quickstart walks through this step by step, including how to get your API key and account ID. Next steps See Surge working end to end with a demo number. Nothing to register or purchase yet. The Platform, Account, and User hierarchy and the API conventions that flow from it. What carriers require and how to submit it through Surge when you're ready for production. Python, TypeScript, Ruby, and Elixir, each with pagination, retries, and webhook verification. --- ## Phone Numbers URL: /docs/phone-numbers Phone Numbers How Surge automatically attaches phone numbers to active campaigns and when to listen for the attachment event. Port an existing phone number into Surge or host an external number to route its messaging traffic through Surge. US and Canada numbers are available via API. Numbers in other countries are available through support on request. Purchase local, toll-free, or short code phone numbers via API and understand per-plan phone number limits. --- ## Attaching a Number to a Campaign URL: /docs/phone-numbers/attaching How Surge automatically attaches phone numbers to active campaigns and when to listen for the attachment event. Attaching a Number to a Campaign Before a phone number can send production traffic, it must be attached to an active campaign. The campaign tells carriers who's behind the number and what use case it's registered for. How attachment works Surge handles campaign attachment automatically. There's no separate API call to make. Attachment is triggered in two situations: When you purchase a number while a campaign is already active. Surge attaches the number to the active campaign as part of the purchase transaction. The phone number response will show the campaign ID: When a campaign becomes active while numbers already exist. Once Surge approves a campaign and moves it to active status, any unattached phone numbers on the account are attached to the campaign automatically. Knowing when attachment completes Attachment happens asynchronously after campaign approval. Once complete, Surge fires the phonenumber.attachedto_campaign webhook event: Wait for this event before sending production messages from the number. You can also check whether a number is attached by listing your phone numbers (GET /accounts/{accountid}/phonenumbers) and inspecting the campaign_id field. The typical sequence 1. Create an account, submit a campaign (POST /accounts/{account_id}/campaigns) 2. Purchase one or more phone numbers (POST /accounts/{accountid}/phonenumbers), campaign_id is null at this point if the campaign isn't yet active 3. Campaign moves through pending → in_review → active 4. Surge auto-attaches all unattached numbers on the account to the newly active campaign 5. phonenumber.attachedto_campaign fires for each number — numbers are now ready for production traffic If you purchase a number after the campaign is already active, steps 4 and 5 happen during the purchase in step 2. Multiple numbers on one campaign A campaign can have multiple phone numbers attached. This is common when you need to distribute sending volume or provide dedicated numbers for different teams or use cases under the same registered campaign. Why there's no manual attachment endpoint Carriers require that every number be registered under a specific campaign. Surge enforces this by managing attachment automatically during campaign approval, ensuring numbers are never left in a permanently unattached state after a campaign becomes active. The webhook is the intended signal for readiness — listen for phonenumber.attachedto_campaign rather than polling. --- ## Importing a Number URL: /docs/phone-numbers/importing Port an existing phone number into Surge or host an external number to route its messaging traffic through Surge. Importing a Number If you have an existing phone number with another provider, you have two options for bringing it into Surge: porting (transferring ownership) or hosting (keeping ownership with your current provider while routing traffic through Surge). Both paths are currently handled through Surge support. API support for number imports is planned. Porting Porting transfers your number from another carrier or provider into Surge's ownership. After porting, Surge owns and manages the number directly: you pay Surge for the number, and your previous provider's contract for that number ends. When to port: - You want to consolidate all messaging under a single provider - Your previous provider relationship is ending - You want full Surge management including TCR campaign registration What to prepare: - Your current provider account information - Proof of ownership (typically the latest bill or account statement) - The specific phone number(s) you want to port Expected timeline: Number porting typically takes 2–4 weeks depending on your current provider's responsiveness. Hosting Hosting lets you keep the number registered with its current provider while routing its messaging traffic through Surge. The number stays in your current provider's account; you configure it to forward inbound messages and calls to Surge. When to use hosting: - You want to try Surge without committing to a full port - Your number has contractual requirements that prevent porting - You're using the same number for purposes outside of SMS How to start Contact Surge support at support@surge.app with: - The phone number(s) you want to import - Whether you want to port or host - Your current provider name Support will walk you through the specific requirements for your situation. --- ## International Numbers URL: /docs/phone-numbers/international US and Canada numbers are available via API. Numbers in other countries are available through support on request. International Numbers Surge's API supports purchasing and managing phone numbers in the United States and Canada. Numbers in other countries are available on a case-by-case basis through support. US and Canada Local numbers for US and Canadian area codes are available via the API: Canadian numbers follow the same 10-digit NANP format as US numbers. The identifier_type for Canadian business registration is cbn (Canadian Business Number). See the Schema Reference for details. Other countries For phone numbers outside the US and Canada, contact Surge support. International numbers are reviewed individually and availability varies by country. What to include in your support request: - The country and region you need a number in - Your use case (SMS, voice, or both) - Expected monthly message volume - Your existing business registration in that country (if applicable) Regulatory considerations Carrier regulations differ significantly by country. SMS spam laws, opt-in requirements, and registration requirements outside North America vary, and in some markets they're stricter than the US A2P 10DLC framework. Surge's support team can advise on what's required for your target country before you begin the purchasing process. --- ## Purchasing a Number URL: /docs/phone-numbers/purchasing Purchase local, toll-free, or short code phone numbers via API and understand per-plan phone number limits. Purchasing a Number Surge supports three number types. Local and toll-free numbers are available via API. Short codes require a support request. Prerequisites - Active account and API key: Both are required to call any phone number endpoint. - Plan that allows the number count you want: Hobby is capped at 1, Starter at 25, Growth and Custom are unlimited. - A campaign to attach to (eventually): Numbers can be purchased before a campaign is active, but they can't send production traffic until they're attached. See Attaching to a Campaign. Number types | Type | Purchase path | Use case | |---|---|---| | Local | API | Standard SMS/MMS, most campaigns | | Toll-free | API | High-volume transactional, customer care | | Short code | Support | Very high-volume or marketing to large audiences | The dashboard at hq.surge.app walks you through the same purchase via UI, useful for one-off numbers or for confirming pricing and approval timelines: !Dashboard "Select number type" page comparing toll-free vs local with prices, setup fees, and registration approval times After choosing the type, the dashboard shows currently-available numbers with city/state for local numbers: !Dashboard local-number search results with phone numbers and city/state Purchase a local number Pass type: "local" and an area_code to find an available number in that area: If no numbers are available for the area code you specified, the API returns a nomatchingnumbers error. Try a nearby area code or omit the area_code to get any available number. If your account has reached its phone number limit, the API returns a phonenumberlimit error. Limits by plan: | Plan | Phone number limit | |---|---| | Hobby | 1 | | Starter | 25 | | Growth | Unlimited | | Custom | Unlimited | Contact support to upgrade your plan if you need more numbers. Purchase a toll-free number Toll-free numbers need to go through verification before they can send production traffic. See Toll-Free Verification. List existing numbers Retrieve all phone numbers on an account: Attaching to a campaign A freshly purchased number has campaign_id: null. Surge attaches phone numbers to campaigns automatically. You don't call a separate endpoint. Two things trigger attachment: - Purchase while a campaign is active: Surge attaches the number immediately. - Campaign becomes active after purchase: Surge attaches all existing unattached numbers on the account when the campaign is approved. When attachment completes, Surge fires a phonenumber.attachedto_campaign webhook event. Once you receive that event, the number can send production traffic. See Attaching to a Campaign. Short codes Short codes (5-6 digit numbers like 12345) are available to all customers via a support request. They support very high message volumes and are the standard for large-scale marketing programs. Contact Surge support to start a short code request. --- ## Receiving Messages & Webhooks URL: /docs/receiving Configure a webhook endpoint, verify Surge webhook signatures, and handle incoming events in Python, TypeScript, Ruby, and Elixir. Receiving Messages & Webhooks Surge sends HTTP POST requests to a URL you control whenever something happens: a message arrives, a campaign is approved, a contact opts out. You configure the URL in the dashboard and Surge takes care of delivery, retries, and ordering. 1. Configure your webhook endpoint Go to Settings → Webhooks in the dashboard at hq.surge.app. Add the URL where you want to receive events. Surge sends all event types to a single endpoint (you filter by type in your handler). Your endpoint must: - Be publicly accessible over https:// (or http://, but HTTPS is strongly recommended) - Resolve to a public IP address - Return a 2xx status code within the request timeout - Handle duplicate deliveries (Surge retries failed attempts, so your handler should be idempotent) Webhook URL restrictions. Surge blocks webhook URLs that resolve to private IP ranges (10.x.x.x, 172.16.x.x, 192.168.x.x, 127.x.x.x) or reserved TLDs (.local, .internal, .localhost, .test) to prevent server-side request forgery. This means http://localhost:4000/webhooks won't work — use a tunneling service like ngrok or Cloudflare Tunnel in development. 2. Verify the signature Every webhook request includes a webhook-signature header. Verifying it confirms the request came from Surge and wasn't tampered with in transit. Surge signs requests using HMAC-SHA256 following the Standard Webhooks specification. Use the standardwebhooks library in Python, TypeScript, or Ruby, or compute the signature directly in any language. Surge sets three headers on every request: webhook-id, webhook-timestamp, and webhook-signature. The library handles them for you in Python / TypeScript / Ruby; you read them directly when computing the signature manually (Elixir example below). Where to find your webhook secret Your signing secret is in Settings → Webhooks in the dashboard, next to your webhook endpoint URL. 3. Handle events A webhook event has this shape: A minimal handler checks type and dispatches: Return 200 quickly. If your processing takes time, enqueue the work and return immediately — Surge will retry if your handler times out. Replay protection The Standard Webhooks spec includes a timestamp in the signature payload. Surge rejects replays outside a tolerance window. If you're testing with recorded payloads, make sure your test framework handles timestamp validation or disables it appropriately. Older signature header Earlier versions of Surge used surge-signature as the header name. This header is deprecated and being phased out. If your handler still reads surge-signature, migrate to webhook-signature. See Deprecation Notices. Next steps Every event type, its payload shape, and when it fires. Reply to inbound messages and keep conversation state in sync. Reference for the Standard Webhooks signature scheme Surge uses. Migrating from the older surge-signature header. --- ## Webhook Events URL: /docs/receiving/events The complete catalogue of 13 webhook event types with full payload shapes and when each fires. Webhook Events Surge sends 13 event types. All events arrive at your configured webhook endpoint as HTTP POST requests with a JSON body. Voice events (call.ended, recording.completed, voicemail.received) are flag-gated and only sent to accounts with voice enabled. See Receiving Messages & Webhooks for endpoint configuration and signature verification. Event structure Every event has the same top-level shape: The type field tells you what happened. The data field contains the resource that changed. Message events message.sent Fires when an outbound message is accepted by the carrier network. Includes sent_at (ISO 8601 timestamp), the full conversation thread, and any attachments. message.delivered Fires when the carrier confirms delivery to the handset. Not all carriers send delivery receipts — if you don't receive this event, it doesn't necessarily mean delivery failed. message.failed Fires when a message cannot be delivered. The failure_reason field explains why. message.received Fires when an inbound message arrives on one of your numbers. This is how you implement two-way messaging: your handler receives the conversation.id and the body, and you can reply by sending a message to the same conversation. Contact events contact.opted_in Fires when a contact sends a recognized opt-in keyword. Surge recognises these exact words (case-insensitive): | Keyword | Notes | |---|---| | START | Standard re-opt-in keyword | | YES | Common confirmation keyword | | UNSTOP | Used to re-subscribe after a STOP | When this event fires, the conversation status returns to active and you can send to the contact again. contact.opted_out Fires when a contact sends a recognized opt-out keyword. Surge recognises these exact words (case-insensitive): | Keyword | Notes | |---|---| | STOP | Standard carrier opt-out keyword | | STOPALL | Variant of STOP | | UNSUBSCRIBE | Common unsubscribe keyword | | CANCEL | Alternative opt-out keyword | | END | Alternative opt-out keyword | | QUIT | Alternative opt-out keyword | | OPTOUT | Alternative opt-out keyword | | REVOKE | Alternative opt-out keyword | Once opted out, sending to this contact returns an opted_out error. Do not attempt to re-subscribe contacts without their explicit new consent. Wait for them to send an opt-in keyword or otherwise affirmatively re-consent. Opt-out state is per-conversation, not per-contact. A contact can opt out of messages from one of your numbers while remaining active on another. This matters for multi-number accounts: a contact who texts STOP to +18015556789 can still receive messages from +18025557890 if they haven't opted out of that thread. Conversation events conversation.created Fires when a new conversation thread starts, either when a contact sends you their first message, or when you initiate a conversation with a contact for the first time. The event includes the phone number and contact on the new thread. Campaign events campaign.approved Fires when a campaign's status changes to active, meaning it's been approved by the US carriers and is cleared for production sending. Surge automatically attaches any existing phone numbers on the account to the newly active campaign. Phone number events phonenumber.attachedto_campaign Fires when a phone number is successfully attached to a campaign. Once you receive this event, the number is ready for production traffic. Link events link.followed Fires when a recipient clicks a tracked link in a message. Only fires on the first click: subsequent clicks on the same link by the same contact don't trigger additional events. Link tracking requires link shortening to be enabled on your account. When enabled, Surge replaces the original URLs in your messages with short links at Surge's link-shortener domain. When a recipient clicks one of these links, Surge fires the link.followed event and then redirects them to the original URL. Contact support to enable link shortening. If link shortening is not enabled on your account, no link.followed events will fire, even if your messages contain URLs. The top-level message_id field on link.followed is deprecated. Use message.id instead. See Deprecation Notices. Voice events (flag-gated) These events are only sent to accounts with voice enabled. call.ended Fires when a voice call completes. recording.completed Fires when a call recording is processed and ready for download. voicemail.received Fires when a voicemail is left on one of your numbers and the recording is ready. --- ## Two-Way Messaging Patterns URL: /docs/receiving/two-way Receive inbound messages, reply in the same thread, handle opt-outs inline, and avoid duplicate event processing. Two-Way Messaging Patterns Two-way messaging means your application can both send messages to users and receive replies from them. Here's how to set it up and handle the common failure modes. How inbound messages arrive When a contact texts your number, Surge receives the message from the carrier and forwards it to your webhook endpoint as a message.received event. The first time a contact messages your number, Surge also fires a conversation.created event. Use that event to initialize a conversation record in your system. Reply in the same thread Reply into the same conversation by passing the conversation ID as the conversation field in your outbound message. Surge routes the reply from the same number the contact previously reached. Handle opt-outs inline When a contact replies STOP (or any recognized opt-out keyword), Surge fires contact.opted_out before the next inbound message arrives. Update your database and stop sending to that contact. Surge automatically blocks sends to opted-out contacts — you'll get an opted_out error if you try. Tracking the state in your own system is still good practice so you don't attempt sends unnecessarily. Common setup failures No inbound messages arriving: 1. Verify your webhook URL is saved in Settings → Webhooks in the dashboard 2. Confirm the URL is publicly accessible (not localhost or behind a firewall) 3. Check that your endpoint returns 2xx within the timeout. If it returns 4xx or 5xx, Surge retries but logs the failure 4. Look at the webhook delivery logs in the dashboard for error details Inbound messages arriving but replies not delivering: 1. Confirm the conversation ID you're replying to matches the one from the event 2. Check that the phone number associated with the conversation is still active and attached to an approved campaign Duplicate event delivery: Surge retries failed webhook deliveries, which means your handler may receive the same event more than once. Make your handler idempotent — use the message ID as a deduplication key before processing. Handling concurrent inbound messages If your system creates records on inbound messages and a burst of messages arrives simultaneously, you may see duplicate conversation records. Use the conversation.id from the webhook event (not the contact phone number) as your primary key for deduplication. Surge guarantees a single conversation ID per thread. --- ## Register Your Business URL: /docs/registration Carrier registration overview: what you need, how the review process works, and how to choose your registration path. Register Your Business To crack down on spam and other unwanted messaging, carriers must know the legal entity responsible for every text message sent from software to a device on their network. Before your messages will be delivered at scale from a dedicated number, you need to register two things: a brand (your business identity) and a campaign (what you're sending and why). Surge's Account → Campaign model maps directly to what carriers call a brand and its associated campaigns. | Surge term | Carrier term | What it represents | |---|---|---| | Account | Brand | A legal business entity | | Campaign | Campaign | A registered use case for sending messages | Registration typically completes in 24–48 hours, with a 72-hour maximum response window. Two paths to registration The preferred path. Use it if you're provisioning accounts for customers, automating onboarding, or just prefer working in code. Covers account creation through phone number purchase. The form-based alternative at hq.surge.app. Mirrors the API fields. Toll-free number verification is currently only available here (API support is planned). Pick the API path unless you need toll-free verification today, in which case start with the dashboard. What you'll need To complete registration you need: - Your business's legal name, EIN (US) or CBN (Canada), and registered address - A publicly accessible website that clearly reflects your business - A privacy policy and terms of service (both must be publicly accessible, not behind a login) - A clear description of how users opt in to receive your messages - 2–5 sample messages that represent what you'll actually send What happens after you submit After you submit a campaign, Surge sends it to The Campaign Registry (TCR) and your carrier network for review. The campaign enters in_review immediately and moves to active once approved: If reviewers find issues with your registration, the status becomes rejected with details about what needs to change. See Fixing a Rejected Campaign for how to read that feedback and resubmit. Once your campaign reaches active, Surge fires a campaign.approved webhook event and starts attaching any phone numbers on the account to the campaign. Phone numbers can't send production messages until they're attached to a campaign: even on an active campaign, attachment runs as a follow-up step. Listen for the phonenumber.attachedto_campaign webhook event for each number; that's the signal a specific number is ready for production traffic. Before you register If you're building a platform that registers brands on behalf of your own customers, read Building on Surge: One Account per Customer before you start. The architecture decisions you make at this stage are difficult to undo. Want to understand what trips up most registrations? Avoiding Rejection covers the seven most common failure patterns with real reviewer feedback. --- ## Register via API URL: /docs/registration/api-walkthrough Step-by-step: create an account, submit a campaign, purchase a phone number, and reach production via the Surge API. Register via API This walkthrough takes you from a fresh Surge platform to a fully registered campaign with a dedicated phone number. If you submit all required fields in step 1, you can skip steps 2 and 3 entirely. Sole proprietor without an EIN? This walkthrough assumes you have a tax ID (EIN or CBN). If you don't, follow the Sole Proprietor Path — different fields, no EIN required, and dashboard-first. Prerequisites - Surge platform and API key: Create the platform at hq.surge.app and copy a key from API Keys. - Business tax ID: Your EIN (US) or CBN (Canada). - Privacy policy and terms of service URLs: Both must be live and publicly indexable when reviewers visit them. 1. Create an account An account represents a brand in carrier terms. Create one with your business details: The response includes your new account ID. Save it; all subsequent requests use it. If you're running this for each of your own customers, create one account per customer. See One Account per Customer for the reasoning. Optional: 2. Check what's missing The status endpoint tells you whether your account is ready to register a campaign and what fields are still needed: The response includes a fields_needed array listing any missing or invalid fields: An empty fields_needed array and a "ready" status means you can proceed to create a campaign. Optional: 3. Fill the gaps If fields_needed is non-empty, patch the account to add the missing information. The field names in the response are dot-delimited paths into the account object: Repeat steps 2 and 3 until capabilities.local_messaging.status is "ready". 4. Create a campaign A campaign tells carriers what you'll send and how users opted in. Every field here is read by human reviewers, so write for clarity. The campaign goes straight to in_review: A few things about campaign fields: - volume: "low" allows up to 2,000 SMS segments/day to T-Mobile. "high" allows up to 200,000/day, depending on the trust score assigned by The Campaign Registry. - use_cases: The types of messages you'll send. Common values: marketing, customercare, accountnotification, twofactorauthentication. See the Schema Reference for the full list. - message_samples: 2–5 examples of what you'll actually send. The first should be a compliance opt-in confirmation. Use real brand names, not placeholders. When the status changes to active, Surge fires a campaign.approved webhook event. The campaign is approved, but a number isn't ready to send production messages until it's also been attached to the campaign. Surge starts attaching numbers automatically as soon as the campaign goes active; the phonenumber.attachedto_campaign webhook fires per number once the attachment completes. Wait for that event before sending real traffic from a given number. 5. Purchase a phone number You can buy a number while your campaign is in review. You just can't send production messages from it until two things have happened: the campaign is active, and the number has been attached to it. After the campaign is approved, attachment runs automatically; you'll receive a phonenumber.attachedto_campaign webhook for each number once it's ready. campaign_id is null because the campaign isn't active yet. Surge attaches phone numbers to campaigns automatically. Two things trigger attachment: - Purchase while a campaign is active: Surge attaches the number immediately. - Campaign becomes active after purchase: Surge attaches all existing unattached numbers on the account when the campaign is approved. When attachment completes, Surge fires a phonenumber.attachedto_campaign webhook event. Once you receive that event, the number can send production traffic. What's next - Review the Schema Reference for every valid value of type, industry, use_cases, and other enum fields - Learn what trips up most registrations in Avoiding Rejection - Start sending messages with your new number --- ## Avoiding Rejection: Patterns from Real Registration Reviews URL: /docs/registration/avoiding-rejection The seven most common campaign rejection reasons with real reviewer quotes and specific fixes for each. Avoiding Rejection: Patterns from Real Registration Reviews Most rejections and "changes needed" outcomes fall into seven categories, drawn from clustering real reviewer feedback in registration review emails. Addressing these before you submit is faster than iterating after rejection. Ready to submit? See Register via API or Register via Dashboard. Opt-in flow issues This is the most common rejection reason, and it covers two related problems: missing consent disclosures at the point of collection, and making SMS consent a condition of signup. What reviewers look for: At minimum, a phone number input with explicit consent disclosures visible nearby. The disclosures must include message frequency, "Msg&data rates apply", and "Reply STOP to opt out." Real reviewer feedback: - "Typically we expect at the very least a phone number input and the consent disclosures shown in the registration flow." - "The carriers don't allow SMS consent to be a requirement for signup." - "It collects a phone number, but it would need to have checkboxes for transactional and marketing SMS consent." - "Looking at the contact form on [domain], it doesn't contain the necessary consent disclosures to be able to collect SMS consent." What to fix: - Add an unchecked opt-in checkbox specifically for SMS (do not bundle it with general terms agreement) - Display the three required disclosures near the phone number field - Never pre-check the consent box Business identity consistency Carriers expect the name, domain, and contact information on your registration to match what they can verify publicly. Mismatches between your website, your email domain, and your registered business name cause rejections. Real reviewer feedback: - "The website doesn't give much info." - "The email address you provided doesn't match the business domain, and they expect that to match or to use a common email domain like gmail." What to fix: - Use an email address at your business domain (hello@acme.com, not acme_hello@gmail.com) - Make sure your website clearly describes what your business does, a blank site, a coming-soon page, or a generic template fails review - Ensure your registered business name, website, and social media profiles all use the same name Privacy policy issues Your privacy policy must be publicly accessible and must not contain language that implies SMS opt-in through agreeing to the policy itself. Real reviewer feedback: - "Right now you don't appear to have a privacy policy or terms page." - "The first two paragraphs should be removed because they say that anyone agreeing to the terms and conditions is opting in to receive text messages, and that won't work because they have to explicitly opt in to receive messages." What to fix: - Publish a privacy policy at a publicly accessible URL (not behind a login) - Remove any language implying that agreeing to the policy constitutes SMS consent - Link to the privacy policy directly from your registration form Terms of service issues Same principle as the privacy policy: publicly accessible, not behind a login. Real reviewer feedback: - "The privacy policy and terms of service are both behind a login." What to fix: - Host both documents at public URLs - The terms page should be indexable, no robots.txt exclusions Sample message issues Message samples are read literally by reviewers. Placeholder tokens left unreplaced are flagged immediately. Real reviewer feedback: - "The sample message says 'this is \{Agent Name\} from \{Agency\}'." What to fix: - Replace every template token with realistic example values - Make samples reflect what you'll actually send. Don't submit generic lorem ipsum messages - If you're sending from an agent or on behalf of a client, use the real brand name Brand vs sender identity mismatch Every message you send must come from the brand registered on that account. If you're sending on behalf of a client, the registration must reflect the client's brand, not yours. Real reviewer feedback: - "Every message must match the brand to which the phone number is registered." What to fix: - For each client or brand, create a separate account registered under that client's legal entity - Do not use one campaign to send messages under multiple brand names See One Account per Customer for the architectural pattern. Business type / EIN mismatch Your business type must match how you're actually incorporated. Registering as sole_proprietor when you have an LLC or corporation is a mismatch that reviewers catch. Real reviewer feedback: - "Because your business name is [X] LLC, you will need to register as a private corporation rather than a sole proprietor, which means you will need to provide an EIN." What to fix: - If your business name includes "LLC", use llc - If it includes "Inc." or "Corp.", use privatecorporation (or publiccorporation if publicly traded) - sole_proprietor is for unincorporated individuals only and does not require an EIN --- ## Register via Dashboard URL: /docs/registration/dashboard Register your brand and campaign using the Surge dashboard form, the current path for toll-free verification. Register via Dashboard The dashboard registration form at hq.surge.app walks you through the same fields as the API, with step-by-step guidance and inline validation. It's the fastest path for direct businesses registering once. It's also the only path for toll-free number verification today — there is no API equivalent. Prerequisites Before you start, have these ready: - Surge dashboard access: Sign in at hq.surge.app. - An account selected: Pick one from the Accounts page or create a new one. - Business and campaign details: Tax ID, website URL, privacy and terms URLs, opt-in flow description, 2–5 sample messages. Open the registration form On the chosen account, click Register or Complete Registration to launch the form. !The Accounts list in the Surge dashboard The form is a 10-step wizard with a progress sidebar on the left. 1. Business type Carriers require a tax ID for every business that sends A2P text messages. If your business has a US Employer Identification Number (EIN), choose Yes, I have an EIN. Sole proprietors without an EIN choose No, I do not and follow the Sole Proprietor Path. !Business type step asking whether the business has an EIN 2. Authorized representative The person who has authority to register this brand for messaging. If you're registering on behalf of one of your customers (a vertical SaaS pattern), this must be a representative of the customer's brand, not someone from your platform. They may be contacted by Surge or carrier partners if reviewers have questions. !Authorized representative form with name, title, email, and phone fields 3. Business information Legal business name, EIN, website URL, industry, and regions of operation. The website must be publicly accessible and clearly reflect the brand, reviewers visit it and reject brands whose sites don't match the registration. See Avoiding Rejection → Business identity consistency for the most common pitfalls here. !Business information form with EIN, business name, website, industry, and regions 4. Business address The legal address registered to the business. This must match what's on the EIN/CBN registration record carriers cross-check against. 5. Campaign What you're sending: use cases (marketing, customer care, two-factor auth, etc.), expected volume, whether messages contain links or phone numbers, and 2–5 sample messages. !Campaign step with use cases, low/high volume tiers 6. Privacy policy The URL of your published privacy policy. Must be publicly accessible (not behind a login) and must reference your text-messaging program. !Privacy policy URL field 7. Terms and conditions The URL of your published terms of service. Same accessibility rules as the privacy policy. !Terms and conditions URL field 8. Opt-in method How users consent to receive messages. Pick from web form, mobile QR code, paper form, verbal consent, or a keyword reply. This maps to the consent_flow field in the API. !Opt-in method selection 9. Opt-in details A plain-English description of your opt-in flow plus the URL where the opt-in happens. Reviewers visit this URL, so it must be live and show the consent flow you described. See Schema Reference → consent_flow for what makes a strong description. !Opt-in details with description and URL 10. Confirm A read-only summary of every value you've entered. Edit any section directly from this page; only submit once everything looks right. !Campaign Summary review page Sole proprietor path If you selected No, I do not on Step 1, you're routed through a sole-proprietor variant of the form with different disclosure requirements. See Sole Proprietor Path for details. After you submit The form submits everything to Surge, which sends it to the carrier network. You'll receive an email when the campaign status changes, and you can track status in the dashboard under Accounts → [Account name] → Campaign. If the campaign status becomes changes_needed, Surge sends an email with reviewer feedback — see Fixing a Rejected or Changes-Needed Campaign for how to respond. Toll-free registration Toll-free verification is dashboard-only and uses a separate flow from 10DLC. See Toll-Free Verification. --- ## Fixing a Rejected or Changes-Needed Campaign URL: /docs/registration/fixing-rejected How to read reviewer feedback, update a campaign in a valid status, and get back to production after rejection. Fixing a Rejected or Changes-Needed Campaign Campaign reviews are done by human reviewers, not automated systems. When they need something from you, the campaign status changes to changes_needed or rejected. Here's how to read the feedback and get your campaign approved. Campaign status lifecycle The diagram shows the typical review lifecycle. deactivated and canceled are administrative states reachable from active (and earlier states for canceled); see the list below. - created: you submitted the campaign, Surge has received it - pending: queued for submission to the carrier network - in_review: actively being reviewed by TCR and carrier reviewers - active: approved; you can attach phone numbers and send production traffic - changes_needed: reviewer flagged issues; you need to update and resubmit - rejected: campaign was rejected; see notes on appealing below - deactivated: previously active campaign suspended by carrier - canceled: you or Surge canceled the campaign API status values. The campaign status field in API responses uses a condensed set of public values: created, inreview, active, rejected, canceled, deactivated. Both the pending and inreview internal states surface as "inreview" in the API. Both the changesneeded and rejected internal states surface as "rejected". Keep this in mind when polling for status or using the update table below. Understanding "changes needed" feedback When a campaign moves to changes_needed, Surge sends you an email with reviewer notes. Read it carefully — reviewers include specific details about what needs to change. Common patterns and what they mean: | Reviewer says | What to fix | |---|---| | "The website doesn't give much info" | Add clear business description and contact information to your homepage | | "The opt-in page doesn't show required disclosures" | Add message frequency notice, "Msg&data rates apply", and "Reply STOP to opt out" next to the opt-in checkbox | | "The sample message contains placeholder text" | Replace {brand name}, {Agent Name}, etc. with actual values | | "The privacy policy is behind a login" | Move the privacy policy to a publicly accessible URL | | "Your business email doesn't match the domain" | Use an email at your business domain, not a personal or generic address | Updating a campaign To address feedback, update the relevant campaign fields and resubmit. Campaigns can only be updated in certain statuses: | Status (API value) | Can update? | |---|---| | created | Yes | | inreview | **Usually no**: returns campaignlocked (409) once a reviewer picks it up. A brief window exists while the campaign is still queued (internally pending) before review begins. | | rejected | Yes, both "changes needed" and hard-rejected campaigns can be updated. | | canceled | Yes | | active | No: returns campaign_locked (409) | | deactivated | No: returns campaign_locked (409) | If you try to update while inreview, wait for the review to complete. The reviewer will either approve it, request changes (moving it to changesneeded), or reject it. Only then can you update. The campaign update endpoint requires the full field set, not a partial body. Send consentflow, description, messagesamples (≥2 items), privacypolicyurl, termsandconditionsurl, usecases, and volume together. Missing any field returns a validation_error. After updating a changesneeded, rejected, or canceled campaign, the campaign moves back to pending and then inreview. Checking campaign status Poll the campaign endpoint to track the current status: Or subscribe to the campaign.approved webhook event to get notified the moment a campaign goes active. This avoids polling. See Webhook Events. Rejected campaigns A rejected status is more serious than changesneeded. Unlike changesneeded, a rejection doesn't automatically move the campaign back into the queue when you update it — you may need to create a new campaign entirely. Rejections related to spam or misuse may also affect your account's ability to register future campaigns. If your campaign is rejected: Reviewers include the specific reason. Don't guess. Partial fixes typically lead to another rejection. Confirm whether to resubmit the existing campaign or start a new one — the right move depends on the reason. Make sure your opt-in flow, sample messages, and business identity are solid before resubmitting. If you're unsure what went wrong, Avoiding Rejection covers the seven most common patterns. --- ## Building on Surge: One Account per Customer URL: /docs/registration/one-account-per-customer Why vertical SaaS platforms should create one Surge account per customer and how to implement the pattern. Building on Surge: One Account per Customer If you're building a SaaS platform and you want your customers to send messages under their own brand, the right architecture is one Surge account per customer. This pattern is called multi-tenant messaging, and the design decisions you make here are much harder to undo than to get right from the start. The core expectation When you send messages through Surge, carriers ask: whose brand is behind this message? The brand registered on the account must match the brand the message recipient expects to see as the sender. If your customers are distinct businesses, a real estate agency, a dental practice, a law firm, each of those businesses needs its own account with its own registration. Sending all their messages through one shared account means you can only register one brand, and messages from "Sarah's Dental" arriving from "AcmeSaaS Corp" will get flagged. Why separate accounts matter Compliance partitioning. If one of your customers gets flagged for spam, whether it's a rogue employee or a misunderstanding, only their account is affected. Their campaign suspension doesn't touch your other customers. A single shared account means a flag on one customer's campaign can take down messaging for every customer. Cost attribution. Each account bills independently, so you can pass usage costs through to your customers without building a message-counting system on your end. Surge's usage reports are already broken down by account. Scaling isolation. A customer with a sudden volume spike doesn't consume rate-limit headroom for your other customers. Message delivery, error rates, and carrier relationships are isolated per account. The biggest gotcha The brand you register must match what message recipients see. Say you're building a platform for real estate agents. You create an account for agent Jane Smith, register it under "Smith Realty LLC," and purchase a number for her. Every message Jane sends through that number should identify itself as coming from Smith Realty, not from your platform's brand. If the registration says "Smith Realty" but the message says "Powered by YourPlatform," or if you use a shared number across multiple agents' accounts, reviewers will flag the mismatch. How to implement the pattern Call POST /accounts with the customer's business name. Store the returned id alongside their record in your database — every subsequent call uses it. Save acct_01jrzhe8d9enptypyx360pcmxl against your customer's row. Collect EIN, website, address, and authorized representative from the customer, then submit via PATCH /accounts/{account_id}. Submit it all at once — partial submissions can't enter review. See Register via API → Account fields for the full body shape. POST /accounts/{account_id}/campaigns with their use case, opt-in flow, and sample messages. The campaign reflects their brand, not yours. See Schema Reference → Campaign fields for required values. POST /accounts/{accountid}/phonenumbers. Once the campaign reaches active, Surge auto-attaches the number — see Attaching numbers. Use the customer's account ID in the path. The platform API key authorizes the call: See POST /messages for the full request and response shapes. You don't need separate API keys per customer. One platform-level API key authorizes every account under your platform. Checking registration status programmatically When you create an account for a customer, you may not have all their business information up front. The status endpoint lets you check what's still needed: The fields_needed array tells you exactly what to collect before you can submit a campaign. Build this check into your onboarding flow so customers see a guided setup experience rather than a registration failure. Further reading The same pattern is covered from a different angle in Twilio's Direct Customer to ISV Rearchitecture Guide, which documents why Twilio's largest platform customers moved away from shared accounts. The problems are identical across providers. --- ## Canonical Schema Reference URL: /docs/registration/schema-reference All valid enum values for organization type, industry, regions, campaign use cases, content includes, and volume. Canonical Schema Reference Valid values for all enum fields used in account and campaign registration. Every field listed here is read by a human reviewer, so use the most accurate value, not the most convenient one. For context on how these fields fit into the registration flow, see Register via API. Account fields organization.type The legal structure of your business. | Value | Description | |---|---| | co_op | Cooperative | | government | Government or government agency | | llc | Limited Liability Company | | non_profit | Non-profit organisation | | partnership | General or limited partnership | | private_corporation | Privately held corporation | | public_corporation | Publicly traded corporation | | sole_proprietor | Sole proprietorship (no EIN required; see Sole Proprietor Path) | If your business name ends in "LLC" or "Inc.", use llc or privatecorporation respectively — not soleproprietor. Sole proprietors don't have an EIN and aren't incorporated. This mismatch is one of the most common rejection reasons. organization.identifier_type The type of business registration number you're providing. | Value | Description | |---|---| | ein | US Employer Identification Number (format: XX-XXXXXXX or XXXXXXXXX) | | cbn | Canadian Business Number (exactly 9 digits) | More identifier types are on the roadmap. Contact support if your business uses a different identifier. organization.industry Choose the category that best matches your primary business activity. | | | | |---|---|---| | agriculture | automotive | banking | | construction | consumer | education | | electronics | energy | engineering | | fastmovingconsumer_goods | financial | fintech | | foodandbeverage | government | healthcare | | hospitality | insurance | jewelry | | legal | manufacturing | media | | notforprofit | oilandgas | online | | professionalservices | rawmaterials | real_estate | | religion | retail | technology | | telecommunications | transportation | travel | organization.regionsofoperation The geographic regions where your business operates. Accepts an array. | Value | Description | |---|---| | africa | Africa | | asia | Asia | | australia | Australia and Oceania | | europe | Europe | | latin_america | Latin America | | usaandcanada | United States and Canada | Most US-based businesses use ["usaandcanada"]. organization.contact.title The job title of the authorised representative submitting the registration. | Value | Description | |---|---| | ceo | Chief Executive Officer | | cfo | Chief Financial Officer | | director | Director | | gm | General Manager | | vp | Vice President | | general_counsel | General Counsel | | other | Any other title (set title_other to the specific title text) | Campaign fields use_cases Accepts an array. Choose all that apply to your campaign. If your messages cover multiple cases, list them all. | Value | Description | |---|---| | account_notification | Account status updates (balance alerts, statement ready, etc.) | | customer_care | Support conversations, help desk, case updates | | delivery_notification | Shipping and delivery status | | fraud_alert | Fraud warnings and suspicious activity notices | | higher_education | Messages from colleges and universities | | marketing | Promotional content, offers, sales | | polling_voting | Polls, surveys, and voting campaigns | | publicserviceannouncement | Informational messages from government or non-profit orgs | | security_alert | Two-factor codes, login alerts | | twofactorauthentication | OTP codes for authentication | For special use cases (political, proxy, sweepstakes, etc.) that don't appear here, contact support. includes Content types present in your messages. Required when applicable; leaving a flag unset when the content is present is a common reason for rejection. | Value | Description | |---|---| | links | Messages contain URLs | | phone_numbers | Messages contain phone numbers | | age_gated | Messages contain age-restricted content | | direct_lending | Messages are related to lending or financial products | volume Expected daily send volume to T-Mobile (the network with the strictest volume caps). | Value | Description | |---|---| | low | Up to 2,000 SMS segments per day to T-Mobile | | high | Up to 200,000 segments per day, subject to TCR trust score | Start with low unless you're certain you'll exceed 2,000 messages per day. You can request a volume increase after your campaign is approved. consent_flow A written description of how recipients opted in to receive your messages. Must be between 40 and 4,096 characters. Reviewers check this against your actual opt-in flow, so be specific and accurate, vague descriptions like "users opt in on our website" are reliably rejected. What to include: - Where the opt-in happens (checkout page, signup form, paper form, etc.) - The exact wording of the consent prompt or checkbox - Whether consent is pre-checked (it shouldn't be) or unchecked by default - The disclosures shown at the point of opt-in See Consent Flows for templates and examples. link_sample A URL representing a link that will appear in your messages. Optional, but recommended if your campaign uses includes: ["links"]. Carriers use this field to establish your campaign's link domain. If you send links from a domain different from what you registered here, carriers are significantly more likely to filter those messages. If link shortening is enabled on your account, Surge substitutes the link shortener URL automatically. Contact support to coordinate if you have a custom link-sample URL. Example: "https://acme.com/track/12345" Max length: 255 characters. message_samples An array of 2–5 real examples of messages you'll send. Each sample must be 20–1,024 characters. The canonical opt-out sample is: "You are now opted in to messages from \{brand name\}. Frequency varies. Msg&data rates apply. Reply STOP to opt out." Replace \{brand name\} with your actual brand name. Leaving template tokens like \{Agent Name\} or \{Agency\} unreplaced is a common rejection reason. Reviewers read every sample literally. Variable content in templates should be indicated with square brackets rather than curly braces: [customer name], [order number]. Curly braces suggest placeholder tokens that weren't filled in. --- ## Sole Proprietor Path URL: /docs/registration/sole-proprietor How sole proprietors register on Surge: no EIN required, dashboard-first flow, and lower throughput expectations. Sole Proprietor Path Sole proprietors are unincorporated individuals operating a business without a separate legal entity. They don't have an EIN and register differently from corporations and LLCs. When to use sole_proprietor Use organization.type: "sole_proprietor" when: - You're an individual operating under your own name or a DBA (doing business as) - You don't have a business EIN. You operate under your personal Social Security Number - You're not incorporated Do not use soleproprietor if your business name includes "LLC", "Inc.", "Corp.", or "Ltd." Those are incorporated entities and require llc, privatecorporation, or public_corporation respectively. This is a common, easily caught rejection — see Avoiding rejection → Business type / EIN mismatch. What's different No EIN required. Sole proprietors skip the identifier_type and identifier fields. Surge and the carriers use other verification methods for identity. Additional disclosures. Carrier regulations require additional reporting and disclosure language for sole proprietor campaigns. Surge handles these requirements automatically. You don't need to add anything to your messages. Dashboard-first. The dashboard registration form has a dedicated flow for the sole proprietor path, with questions and guidance specific to this business type. We recommend completing sole proprietor registration through the dashboard. Lower volume. Sole proprietor campaigns typically have lower throughput caps than corporation campaigns. If you expect high message volume, incorporating your business may be worth considering. What to prepare - Your full legal name (as it appears on government-issued ID) - A live, public-facing website or social media profile in your business's name — reviewers visit it, and a coming-soon page or one with the wrong name fails review - Your opt-in flow and consent disclosures - A valid business email address (ideally at your business domain, not personal Gmail) - Sample messages The rest of the registration process follows the same pattern as standard registration: create a campaign, add sample messages, link your privacy policy and terms, and wait for review. See Register via API for the full step-by-step walkthrough. --- ## Toll-Free Verification URL: /docs/registration/toll-free How toll-free number verification works on Surge and how it differs from standard 10DLC local number registration. Toll-Free Verification Toll-free numbers (8xx area codes) go through a separate verification process from standard 10DLC local number registration. The two processes are independent — you can have both a local campaign (10DLC) and a verified toll-free number on the same account. Surge can purchase numbers in 833, 844, 855, 866, 877, and 888 via API. The original 800 prefix is mostly exhausted — Surge can port in an existing 1-800 number on request, but new purchases use one of the other prefixes. Toll-free vs 10DLC | | Toll-free verification | 10DLC (standard) | |---|---|---| | Number type | Toll-free (8xx area codes) | Local 10-digit numbers | | Registration | Per-number verification | Brand + campaign, then attach numbers | | Use case | High-volume transactional, customer care | Any messaging use case | | API support | Purchase via API; verification dashboard-only | Full API support | | Review time | No published SLA | 24–48 hours typical | How to verify a toll-free number POST /accounts/{accountid}/phonenumbers with type: "toll_free". This part has full API support. Navigate to Phone Numbers and find the number you just purchased. Opens the toll-free verification form. Provide your business information, opt-in description, and sample messages. The number is marked as verified once the carrier approves. Until then, throughput is restricted. Changes-needed on toll-free If the carrier requests changes, Surge sends you a notification with details. Use the verification form in the dashboard to update and resubmit. The issues reviewers flag for toll-free are similar to 10DLC — opt-in flow clarity, business identity, and sample message quality. See Avoiding Rejection for the full breakdown. API support Today: purchasing toll-free numbers via API is supported. Coming: full toll-free verification via API. Until it ships, use the dashboard for the verification step. --- ## Sending Messages URL: /docs/sending Sending Messages Every send-time and delivery-time error code, retryability, and specific remediation steps for each failure type. How contacts and conversation threads work, when to use them, and how to manage opt-out state per thread. Blasts vs Audiences: when to use each, how to create and send to both, and campaign volume caps. Send SMS and MMS to a single recipient: required fields, attachments, scheduling, personalization, and common errors. How message delivery statuses work, webhook-based tracking, and how to list messages with cursor pagination. --- ## Ongoing Conversations URL: /docs/sending/conversations How contacts and conversation threads work, when to use them, and how to manage opt-out state per thread. Ongoing Conversations When the same person sends and receives multiple messages over time, Surge tracks them as a conversation thread. Understanding how conversations and contacts work helps you build two-way messaging flows, surface message history, and manage opt-out state correctly. Contacts and conversations A contact represents a phone number in the context of a specific account. When someone messages your number for the first time, or when you first message them, Surge creates a contact record and a conversation thread. A conversation is the thread of all messages between your number and a contact. Every message you send or receive includes a conversation.id in the response. When to use contacts vs raw messaging Raw messaging (passing a phone number directly in the to field) is fine for simple one-way notifications where you don't need to track who sent what or manage opt-out state per person. Blast sends work this way. Contacts become useful when: - You're building two-way messaging and need to correlate inbound replies with specific customers - You need to check or manage opt-out state for an individual - You want to associate metadata (name, account ID in your system) with a phone number - You're building embeddable UI components that need to show a contact's message history Create a contact explicitly to attach metadata before the first message: Contacts support these fields: phonenumber (required), firstname, lastname, email, dateof_birth, and metadata. All contact PII is encrypted at rest. firstname, lastname, email, dateofbirth, phone_number, and metadata are stored encrypted in the database. You can safely store customer-identifying information here without it being readable if the database is compromised. Surge also automatically populates carrier data (phonecarrier, phonecarriername, phonecountry) on contacts based on carrier lookups. This data is populated asynchronously after the first message send or verification — it is not available at contact creation time. Following a conversation When a contact sends you an inbound message, the message.received webhook event includes the conversation ID. Use that ID to fetch the full thread or send a reply into the same conversation. When you send into a conversation, Surge routes the message from the same number the contact previously reached. Conversation status A conversation has one of three statuses: | Status | Meaning | |---|---| | active | The conversation is open; messages can be sent and received | | archived | The conversation has been closed in the dashboard but the contact has not opted out. Sending to an archived conversation re-opens it automatically | | optedout | The contact replied with a recognised opt-out keyword. Sending returns an optedout error until they re-opt-in | The optedoutat timestamp is set when the conversation moves to opted_out status. Opt-out state Opt-out state lives on the conversation (one contact might opt out of one number but remain active on another). When a contact replies STOP, the conversation.optedoutat timestamp is set and the contact.opted_out webhook event fires. Sending to an opted-out conversation returns an opted_out error. Do not attempt to send around opt-outs. Doing so violates carrier policies and can result in your campaign being deactivated. A contact re-opts in by texting START, YES, or UNSTOP to the same number. When that happens, contact.opted_in fires and the conversation status returns to active. Audiences If you want to group contacts into named lists for repeated sends, use audiences. An audience is a collection of contacts that you blast together. See Send to Many People for the full pattern. Cross-channel tracking (roadmap) Conversations and contacts today are SMS-specific. As Surge adds RCS, WhatsApp, and other channels, the contact model will extend to track communication history across all channels in a single thread. See Channels Roadmap. --- ## Handle Failures URL: /docs/sending/handle-failures Every send-time and delivery-time error code, retryability, and specific remediation steps for each failure type. Handle Failures When a message can't be sent or delivered, the API returns a structured error object. Here's what each error means and what to do about it. Error structure All errors follow the same shape: Some errors include a detail object with field-level information: Errors at send time These errors come back synchronously when you call POST /accounts/{account_id}/messages. opted_out (422) The recipient has previously replied STOP. Carriers require you to honour opt-outs. Do not attempt to send to this contact until they explicitly re-opt-in by texting START. Fix: Check contact opt-out state before sending. Subscribe to contact.opted_out webhook events to update your database in real time. demomessagelimit (403) You've sent 25 messages from the demo number. The demo is for exploration — it's not for production use. Fix: Register your business and purchase a phone number. linksizelimit (422) The message body contains more URLs than the campaign's registered includes allows, or the total URL content exceeds carrier limits. Fix: Reduce the number of links in the message, or enable link shortening on your account. nophonenumber (422) No from number was specified and the account has no default phone number configured. Fix: Either set a default phone number on the account in the dashboard, or always pass a from value. phonenumbernot_found (422) The phone number ID passed as from doesn't exist or doesn't belong to this account. Fix: Confirm the phone number ID is correct and belongs to the account you're sending from. Errors at delivery (via webhooks) These appear in message.failed webhook events after a message was initially accepted. The failure_reason field on the event contains the slug. unreachable_destination The recipient's number is disconnected, out of service, or unreachable on the carrier network. What to do: Flag the contact as undeliverable in your system. Continued attempts to an unreachable number don't help and count against your volume. Permanent undeliverability. When Surge receives unreachabledestination, invalidmobilenumber, or unreachablecarrier for a number, that number is permanently flagged as undeliverable in Surge's carrier database. All future sends to it (from any account) are cancelled immediately without hitting the carrier. If a number becomes reachable again (e.g., the subscriber reactivates their SIM), the flag clears when a successful delivery is recorded. Stop sending to failed numbers — it has no effect, and your campaign volume quota is still consumed. invalidcontenttype An MMS attachment's content type isn't supported by the recipient's carrier or handset. What to do: Retry with a different format (prefer image/jpeg or image/png) or send the content as a link instead of an attachment. message_filtered The carrier's spam filter blocked the message. This can happen when message content matches spam patterns, when a campaign isn't fully approved, or when a number isn't attached to an active campaign. What to do: 1. Confirm the sending number is attached to an active, approved campaign 2. Check that message content matches your registered campaign description and sample messages 3. Avoid link shorteners that aren't trusted by carriers (use Surge's built-in link shortening instead) Complete failure reason reference All possible values for failure_reason in message.failed events: | Reason | Meaning | Retryable? | |---|---|---| | account_archived | The sending account has been archived | No | | account_locked | The platform has been locked due to a policy violation | No. Contact support | | attachmentretrievalerror | Surge couldn't fetch the attachment URL when building the MMS | Yes. Fix the URL or hosting | | blocked | The number has been explicitly blocked at the carrier level | No | | carrier_error | A transient error from the carrier network | Yes. Surge retries automatically | | configuration_error | The account's carrier credentials are misconfigured | No. Contact support | | dailymessagecap_reached | The campaign's daily T-Mobile volume cap was hit | Yes. Automatically next day | | geonotenabled | The destination country isn't in the platform's allowed regions | No. Contact support to enable the region | | invalidattachmenturl | The attachment URL is malformed or not publicly accessible | Yes. Fix the URL | | invalidcontenttype | MMS attachment type unsupported by the recipient's carrier | Yes. Use a supported format | | invalidmobilenumber | The number is not a valid mobile number (e.g., a landline) | No. Permanently undeliverable | | matchingsenderand_receiver | The from and to numbers are the same | No. Sending to yourself isn't supported | | message_filtered | Blocked by carrier spam filters or Surge's content policy | Investigate content before retrying | | messagetoobig | The message body exceeds the 1,600-character limit | Yes. Shorten the message | | mmsnotsupported | The recipient's carrier or handset doesn't support MMS | Yes. Surge falls back to SMS automatically | | opted_out | The contact has opted out of this conversation | No. Wait for explicit opt-in | | premiumratenumber | The destination is a premium-rate number | No | | suspected_fraud | Surge's fraud detection flagged the send | No. Contact support | | unknown_destination | The number doesn't exist on any carrier network | No. Permanently undeliverable | | unknown_error | An error occurred that Surge couldn't categorise | Investigate with support | | unreachable_carrier | The destination carrier network is unreachable | No. Permanently undeliverable | | unreachable_destination | The number is disconnected or out of service | No | | unregistered_number | The sending number isn't registered with the carrier for this message type | No. Contact support | | unsupported_route | No valid carrier route exists for this number pair | No. Contact support | Validation errors validation_error (422) A request field is missing or invalid. The detail map contains per-field errors. Fix: Correct the flagged fields. Phone numbers must include the country code with a + prefix (e.g. +18015551234, not 8015551234). Full error reference See the Error Reference for the complete list of error types, HTTP status codes, and additional context. --- ## Send to Many People URL: /docs/sending/send-many Blasts vs Audiences: when to use each, how to create and send to both, and campaign volume caps. Send to Many People Surge has two mechanisms for sending to multiple recipients: Blasts for one-time sends and Audiences for reusable contact lists. The choice between them depends on whether the same group of people will receive messages more than once. Blasts vs Audiences Use a Blast when you have a list of recipients you're assembling specifically for this send. You pass the recipient list at send time and don't need to maintain it afterward — weekly flash sales, event announcements, one-time notifications. Use an Audience when the same group of people receives messages regularly. You maintain the audience over time (adding and removing contacts), then blast the whole audience — weekly newsletters, customer cohorts, or geographic segments. Both send the same underlying message; the difference is how you manage the recipient list. Blasts Send to a list of phone numbers The simplest case: pass E.164 phone numbers directly in the to array. Send to contacts Pass contact IDs (with ctc_ prefix) instead of phone numbers. This links the blast to your contact records for tracking and opt-out management. Schedule a blast Add send_at to queue the blast for a future time: Audiences Create an audience An audience is a named contact list. Create it first, then populate it. Add contacts to the audience Repeat for each contact, or build this into the workflow that creates or updates contacts. Blast the audience Pass the audience ID in the to array. Surge expands the audience at send time and applies opt-out status automatically — opted-out contacts are skipped, so you never need to filter the list yourself. You can mix audience IDs, contact IDs, and raw phone numbers in the same to array. Volume caps Campaign volume limits apply to all sends, including blasts: | Campaign volume | T-Mobile limit | |---|---| | low | 2,000 SMS segments per day | | high | Up to 200,000 segments per day (subject to TCR trust score) | Surge queues messages within your daily cap and delivers them in order. If you regularly approach the low cap, request a volume increase through support after your campaign is approved. Tracking a blast Blast-level delivery metrics (deliverability rate, opt-out rate) are available in the dashboard under the Blasts section. Per-message delivery status comes through the message.sent, message.delivered, and message.failed webhook events — see Track Message Delivery for the full mechanism. --- ## Send to One Person URL: /docs/sending/send-one Send SMS and MMS to a single recipient: required fields, attachments, scheduling, personalization, and common errors. Send to One Person All outbound messages — SMS and MMS — go through the same endpoint: Send an SMS The minimum required fields are to (the recipient's phone number in E.164 format) and body (the message text). If you've configured a default phone number on the account, you don't need to specify from — otherwise you'll get a nophonenumber error. To send from a specific number, include its ID or E.164 number as from. (Python and Ruby use from_ to avoid the reserved keyword; the wire field is still from.) The response includes the message ID, body, and the conversation thread the message belongs to: Tracking delivery Message delivery status isn't a field on the REST response — it comes through webhook events. Subscribe to these event types: | Event | Meaning | |---|---| | message.sent | Carrier accepted the message for transmission | | message.delivered | Carrier confirmed the message reached the handset (or was handed off to the carrier for local numbers) | | message.failed | The message could not be delivered; the reason field in the event explains why | Not all carriers send delivery receipts, so message.delivered isn't guaranteed for every carrier. See Webhook Events for the full payload shapes. Send an MMS Include an attachments array to send MMS. Each item needs a url pointing to a publicly accessible file. The response includes the attachment IDs. To retrieve an attachment file later, use GET /attachments/{attachment_id}/file. MMS gotchas - Content type: some carriers reject attachment types they don't support. Common safe types are image/jpeg, image/png, and image/gif. PDFs deliver reliably to most handsets but not all. - File size: very large files may be silently truncated or fail delivery on certain networks. Keep attachments under 1 MB where possible. - Body is optional: you can send an MMS with attachments only, no body text. Attachment URL restrictions. Surge fetches the attachment URL to build the MMS, so the URL must be publicly accessible over https:// or http:// (no other schemes). The following are blocked: - Private IP addresses: 10.x.x.x, 172.16.x.x, 192.168.x.x, 127.x.x.x, etc. - Reserved TLDs: .local, .internal, .localhost, .test You cannot use localhost or internal network URLs for attachments, even in development. Host test files on a public URL or use a tunneling service like ngrok. Automatic SMS-to-MMS conversion Surge can automatically upgrade a plain SMS to MMS in two situations: 1. Message exceeds 10 segments: any message over 10 segments (around 1,530 GSM-7 characters) is automatically sent as MMS regardless of whether you included attachments. 2. Message is 3 or more segments and auto_mms_enabled is on for your platform: contact support to enable this platform-level setting if you want long messages to convert automatically. When a message is upgraded to MMS, it sends as a single media message rather than a concatenated SMS sequence. If the recipient's carrier doesn't support MMS (mmsnotsupported), Surge falls back to SMS. You never need to detect this yourself — the message.sent and message.delivered events reflect the actual protocol used. Schedule a message Set send_at to an ISO 8601 timestamp to schedule a message for later delivery. The maximum scheduling window is 65 days from now. A scheduled message is accepted and queued immediately. Delivery events (message.delivered, message.failed) fire when the message is actually sent at the scheduled time. Personalise messages with contact variables If the recipient has a contact record, Surge substitutes these tokens in the message body at send time: | Token | Replaced with | |---|---| | {first_name} | Contact's first name, or empty string if not set | | {last_name} | Contact's last name, or empty string if not set | | {full_name} | First and last name joined, or empty string if neither is set | If the contact's firstname is Jordan, the recipient receives Hi Jordan, your order has shipped!. If the contact has no firstname, the {first_name} token is replaced with an empty string, so the recipient gets Hi , your order has shipped! — set names on your contacts before sending personalised messages to avoid awkward blanks. Tokens are replaced when you pass to (a phone number or contact ID); they are not replaced on blast sends that bypass contact resolution. Add metadata metadata is a free-form JSON object you can attach to any message. It's returned in webhook events and list responses, which makes it useful for correlating Surge messages with records in your own system. Metadata is encrypted at rest. Message metadata is stored encrypted in Surge's database. Store any data here you'd normally want protected — internal IDs, tier markers, contextual details about the send. Keep metadata values below a few KB. Very large metadata objects have no hard size limit enforced at the API layer, but they are embedded in every webhook payload that references the message, which can make your webhook handler payload processing heavier than necessary. Common errors | Error type | Cause | Fix | |---|---|---| | opted_out | The recipient has replied STOP | Do not send to opted-out contacts | | demomessagelimit | You've hit the 25-message demo cap | Register and buy a number | | linksizelimit | The message body contains too many URLs | Reduce links or use link shortening | | invalidcontenttype | Attachment type unsupported by the carrier | Use a supported image format | Full error reference is in Handle Failures and the Error Reference. --- ## Track Message Delivery URL: /docs/sending/track-delivery How message delivery statuses work, webhook-based tracking, and how to list messages with cursor pagination. Track Message Delivery Message delivery status comes through webhook events, not the REST API response. The create endpoint (POST /accounts/{account_id}/messages) returns the message object immediately — the status at that point is "sent" (queued), and what happens next depends on the carrier. How delivery works | Status | Meaning | |---|---| | pending | Message created and accepted by the API | | queued | Picked up by the send worker and queued for dispatch | | sending | Currently being transmitted to the carrier | | sent | Carrier accepted the message (message.sent webhook fires) | | delivered | Carrier confirmed delivery to the handset (message.delivered fires) | | failed | Delivery failed at any stage (message.failed fires with failure_reason) | Status advances in one direction only. If Surge receives an out-of-order carrier status report (a rare but real occurrence on some networks), it is discarded rather than rolling the status back. A message in delivered state will never revert to sent. Not all carriers send delivery receipts. If you never receive message.delivered after message.sent, it doesn't mean the message wasn't delivered. The carrier simply didn't confirm it. Verizon, in particular, batches delivery confirmations, which can arrive several minutes late. Webhook-based tracking (recommended) Configure a webhook endpoint (Settings → Webhooks in the dashboard) and handle the events you care about: Return 200 quickly. If your processing takes time, enqueue the work and return immediately — Surge retries if your handler times out. See Receiving Messages & Webhooks for endpoint setup and signature verification. Retrieving a message by ID The retrieve-message response does not include a status field. It returns the message body, attachments, conversation thread, and metadata. For status, rely on the webhook events. You can retrieve a sent message to check its current fields: Use this endpoint to fetch the message body or attachment URLs if you didn't store them at send time. For status, rely on the webhook events. Listing messages To retrieve all messages for an account, use the list endpoint with cursor-based pagination: Pass after=CURSOR or before=CURSOR as query parameters to page through results. The list returns messages in reverse chronological order (most recent first). --- ## Verifications URL: /docs/verifications Verifications Send a six-digit OTP to a phone number and verify the user's code with two API calls, expiry and retry limits included. Opted-out contacts, international quirks, carrier filtering, and security tips for OTP verification in production. --- ## Send and verify a code URL: /docs/verifications/send-and-verify Send a six-digit OTP to a phone number and verify the user's code with two API calls, expiry and retry limits included. Send and verify a code Surge's verification API sends a six-digit code to a phone number and then checks the code the user provides. It handles code generation, delivery, retry limits, and expiration. You don't manage any of that yourself. How it works Verification is a two-step flow: 1. Create a verification: Surge sends a six-digit code to the number 2. Check the code: Surge validates the code the user entered 1. Send the code The user receives a text like: "Your verification code is 847291. It expires in 10 minutes." 2. Check the code When the user submits the code in your UI, send it to Surge for verification: The response includes a result field indicating whether the verification succeeded: Limits - Expiration: 10 minutes from creation. After that, the verification moves to expired status and all check attempts return expired. - Max attempts: 3 incorrect guesses per verification. After 3 incorrect attempts, the verification moves to exhausted status. Create a new verification and prompt the user to resend the code. Result values | Result | Meaning | What to do | |---|---|---| | ok | Code matched | Grant access | | incorrect | Wrong code | Show an error, let the user retry | | expired | Code is more than 10 minutes old | Prompt user to request a new code | | already_verified | This verification was already approved | Treat as ok (idempotent) | | exhausted | 3 incorrect attempts reached | Create a new verification; do not retry the same one | Putting it together A complete flow in Python: For deliverability quirks and edge cases, see Usage patterns. --- ## Usage patterns and delivery gotchas URL: /docs/verifications/usage-patterns Opted-out contacts, international quirks, carrier filtering, and security tips for OTP verification in production. Usage patterns and delivery gotchas Verification codes are sent as SMS, which inherits SMS's quirks. Here's what to plan for in production. Opted-out contacts If the phone number has previously replied STOP to your number, the API returns an opted_out error on the create call. You cannot send verification codes to opted-out numbers. The right behaviour in your UI is to surface a friendly error and offer the user a different verification path (email, support contact). Don't retry on the same number. International numbers Codes deliver to most international numbers, but delivery speed varies by carrier. If a user doesn't receive the code within 60 seconds: - Prompt them to request a resend (create a new verification). - The original verification is now wasted from the user's perspective; if the new code arrives and matches, treat it as success. - For a small number of countries / carriers, delivery can be intermittent. Monitor result: expired rates by region as a leading indicator. Carrier filtering Verification codes are generally exempt from spam filtering, carriers know OTP traffic is high-trust by pattern. But: - Very high volumes of verifications to a single area code or carrier can trigger rate limits. - A handful of carriers periodically tighten their filters and OTP messages get caught. If you see a spike in incorrect results that's actually "user never got the code," check the per-carrier delivery rate in your message logs. Sender number Verification codes are sent from a shared pool of Surge demo numbers, not from any phone number on your account. The sending number varies between verifications, users may see different "from" numbers across OTP flows. This is expected behaviour. You cannot customise the sender number for verifications. This also means that if a user texts STOP to the sender number from a verification, it opts them out of that specific Surge number, not out of your campaign numbers. Contact support if this creates operational issues. Implementation tips - Don't show the verification ID in your UI. The id is opaque; storing it in your session is fine, but exposing it client-side gives an attacker the input to brute-force /checks. - Lock retries on your side too. Surge enforces an exhausted threshold, but you should also rate-limit resends and re-checks at your application layer to prevent one user from racking up SMS costs. - Log the result, not the code. When debugging, log verification.id and result — never the six-digit code. Treat the code like a password. - Carrier lookup runs automatically. When you create a verification, Surge enqueues a carrier lookup in the background. This improves future delivery decisions for that number but adds a small delay before the first code is sent. If delivery speed is critical, ensure your infrastructure can handle codes that may arrive 2–5 seconds after the API response. ---