Surge

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.

flowchart TD
  P["Your Platform<br/>(one API key)"]
  P --> A1["Account: Smith Realty<br/>org.type: llc"]
  P --> A2["Account: Jones Dental<br/>org.type: llc"]
  P --> A3["Account: Acme Co<br/>org.type: private_corporation"]
  A1 --> C1["Campaign + Phone Number<br/>brand: Smith Realty"]
  A2 --> C2["Campaign + Phone Number<br/>brand: Jones Dental"]
  A3 --> C3["Campaign + Phone Number<br/>brand: Acme Co"]

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

Important

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

Create an account when a customer signs up

Call POST /accounts with the customer's business name. Store the returned id alongside their record in your database — every subsequent call uses it.

curl -X POST https://api.surge.app/accounts \
  -H "Authorization: Bearer YOUR_PLATFORM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Smith Realty",
    "brand_name": "Smith Realty"
  }'
{
  "id": "acct_01jrzhe8d9enptypyx360pcmxl",
  "name": "Smith Realty",
  "brand_name": "Smith Realty"
}

Save acct_01jrzhe8d9enptypyx360pcmxl against your customer's row.

Submit business information

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.

Create a campaign for their account

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.

Purchase and attach a phone number

POST /accounts/{account_id}/phone_numbers. Once the campaign reaches active, Surge auto-attaches the number — see Attaching numbers.

Send messages on their behalf

Use the customer's account ID in the path. The platform API key authorizes the call:

curl -X POST https://api.surge.app/accounts/{account_id}/messages \
  -H "Authorization: Bearer YOUR_PLATFORM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+15551234567",
    "body": "Smith Realty: Showing confirmed for Saturday at 2pm."
  }'

See POST /messages for the full request and response shapes.

Note

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:

curl "https://api.surge.app/accounts/{account_id}/status?capabilities=local_messaging" \
  -H "Authorization: Bearer YOUR_PLATFORM_API_KEY"

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.