AWRA OpsHub Search

Three Fields, Nine Meanings

Three SMS providers, one settings screen, three stored fields. What those fields mean changes completely with the provider you pick — one of them holds a username, an account identifier and a web address depending on the answer to a dropdown.

Integrations & Data AWRA OpsHub Team 11 min read

Supporting several providers for one job always comes down to the same question: where do you put the seam? Put it too high and every provider gets its own everything, and the product has three of each screen. Put it too low and you have invented a lowest common denominator that serves none of them properly. The SMS connectors here put it in a specific place, and the consequences are visible on the settings screen.

Three fields, and what they hold

A workspace stores one SMS configuration: which provider, and three values. The three are read by whichever driver the provider names, and each driver reads them its own way.

Field Provider one Provider two Provider three
First An account username An account identifier Your API base address
Second An API key An auth token An API key
Third A sender name, optional A number or a service identifier A sender name, defaulted

The first row is the one that gives people pause: the same stored field holds a username on one provider and a web address on another. It is not a compromise or an accident — it is what a shared shape looks like when the things being shared genuinely differ.

The field is a slot, not a meaning. What goes in it is decided by the provider you chose, and the label on the screen follows.

The field is a slot. What belongs in it is decided by the provider, and the label follows.
The field is a slot. What belongs in it is decided by the provider, and the label follows.

What the shared shape actually buys

It is worth being concrete, because the alternative is not obviously worse until you count what it costs.

  1. One settings screen instead of three

    One place to look, one place to change, one place to gate behind a permission. Three screens means three of each of those, and one of them being subtly different.

  2. Switching provider changes nothing downstream

    Notification routing, module toggles and per-type preferences all sit above this and know nothing about which provider is chosen. Changing provider is changing a dropdown.

  3. One place asks whether SMS is configured

    Everything upstream asks a single question and gets one answer, rather than asking three and combining them — which is exactly the kind of place a fourth provider gets forgotten.

  4. A new provider is a driver, not a feature

    Adding one means writing something that reads three fields and sends. Nothing above the gateway has to hear about it.

The fourth point is the real one. The value of a seam is measured by what does not have to change when you add the next thing, and on that measure this one is in the right place.

And what it costs

Two things, and both are worth stating rather than glossing.

The first is that the field labels have to change with the provider, because a field asking for a username when it wants a web address is a field people will fill in wrongly. The screen carries per-provider labelling, which is one more thing to keep in step with the drivers.

The second is more interesting. The shared interface returns whether it worked and why not — and one of the three providers can internally report a reason per recipient, because it sends one request each. That detail exists inside the driver and does not survive the common return shape.

That is the ordinary cost of a common interface: it can only promise what all of its implementations can deliver. The alternative is a return type carrying optional per-recipient detail that is present on one provider and absent on two, which every caller would then have to handle — for a benefit only some workspaces would ever see.

It is a defensible trade and it is a real loss, and pretending otherwise is how interfaces quietly stop matching what is underneath them.

Where the seam is not

One thing deliberately does not sit in the shared layer, and its absence is the design.

There is no shared handling of the sender field, no shared idea of what to do when it is empty, no shared retry policy and no shared batching. Each driver does what its provider actually wants — one omits the field, one substitutes a default, one refuses; one loops per recipient, two send the list in a single call.

A shared policy for any of those would be right for one provider and wrong for the other two, and the wrongness would arrive as a rejected message rather than as a visible mistake. The gateway routes; it does not decide anything a provider is entitled to decide.

The one thing that is shared, and why

Recipient handling happens once, above the drivers. The list is accepted as text or as a list, split on commas and line breaks, trimmed, and emptied of blanks. An empty result is refused there with a reason naming it.

That belongs above the drivers because it is not about any provider. It is about what a person types into a text box — trailing commas, line breaks, a stray space after a number. Every driver would otherwise implement the same cleanup, and the third one would implement it slightly differently.

The test for what belongs in a shared layer is exactly that: is this about the job, or about the provider? Cleaning a list somebody typed is about the job. Deciding what to do with an empty sender field is about the provider.

What is in place, layer by layer

How three SMS providers share one connector

One stored configuration per workspace

A chosen provider and three values, read by whichever driver that provider names — so switching provider is a dropdown rather than a migration.

Built in

Per-provider labels on shared fields

The screen asks for what the chosen provider actually wants, because a field labelled for the wrong provider is a field filled in wrongly.

Built in

Routing by provider, deciding nothing else

The gateway picks a driver and gets out of the way. Sender handling, batching and error extraction stay with the provider that has an opinion about them.

Built in

Recipient cleanup done once, above

Text or list, split on commas and line breaks, trimmed, blanks dropped — because that is about what a person typed, not about any provider.

Built in

An empty list refused with a reason

Named before a driver is even chosen, rather than sent onward and returned as a confusing provider error.

Built in

Three genuinely different send shapes

A joined list, one request per recipient, and a structured destinations array — each driver doing what its provider is built for.

Built in

A common result the callers can rely on

Whether it worked and why not, in the same shape from all three, so nothing upstream has to know which provider is configured.

Built in

Credentials encrypted at rest

All three values stored through an encrypted cast on the row for one workspace, whichever provider they belong to.

Built in

One provider can internally attribute a failure to a specific recipient; the shared result does not carry that, because an interface can only promise what all of its implementations can deliver.

Three positions held on purpose

  • The shared layer routes and normalises input, and decides nothing a provider is entitled to decide. A common policy on sender handling or batching would be right for one of the three and would fail on the others as a rejected message.
  • The common result carries only what all three can deliver. The alternative — optional detail present on one provider and absent on two — pushes the difference onto every caller for a benefit most workspaces never see.
  • The cost of the shared shape is stated rather than hidden. An interface that quietly promises less than one implementation can provide is fine; one that nobody has written down is how an interface drifts from what is underneath it.

Five questions about multi-provider support

How hard is it to switch provider?

A good answer sounds like

A setting.

What ours actually is

A dropdown and three values. Nothing downstream knows which provider is configured.

Do the fields mean the same thing on each?

A good answer sounds like

No, and the labels say so.

What ours actually is

One field holds a username, an account identifier or a web address. The screen asks for the right one.

Is there a shared retry or batching policy?

A good answer sounds like

No — per provider.

What ours actually is

Each driver does what its provider is built for. A shared policy would be wrong on two of three.

What is shared, then?

A good answer sounds like

Input handling.

What ours actually is

Recipient cleanup and the empty-list refusal, because those are about what a person typed rather than about a provider.

Does anything get lost in the abstraction?

A good answer sounds like

Yes, and they say what.

What ours actually is

Per-recipient failure detail exists on one provider and is not carried by the shared result.

Our take

The question in any multi-provider feature is not whether to abstract but where, and the reliable test is to ask of each decision whether it belongs to the job or to the provider. Cleaning up a list somebody typed is the job. Choosing what to do with an empty sender field is the provider, and every provider answers it differently. Get that boundary right and adding a fourth provider is an afternoon. Get it wrong in the generous direction and you get a shared policy that is subtly incorrect for most of your providers, failing as rejected messages rather than as an obvious mistake — which is the version that takes a year to find.

Pick the provider, not the integration

Coverage and pricing in the markets you actually send to should decide this. Switching later is a dropdown and three values.

Talk through SMS alerts

Frequently asked questions

Can we switch SMS providers without reconfiguring our alerts?

Yes. Which events go to SMS, which modules are included and which notification types travel there all sit above the provider choice and know nothing about it. Switching is choosing a different provider and entering its three values.

Why does the first field ask for different things on different providers?

Because the providers genuinely want different things there — an account username, an account identifier, or your own API base address. The storage is shared and the labels follow the provider you chose, so the screen asks for the right value.

Can we use two SMS providers at once?

A workspace holds one SMS configuration, so one provider is active at a time. Using two would mean deciding per message which to use, which is a routing rule nobody has a good answer for — and the usual reason people want it, redundancy, is better handled inside a provider account than by splitting traffic.

Does the connector report which recipient failed?

That depends on the provider, and the shared result does not carry it. One of the three sends one request per recipient and can attribute a failure to a number; the other two send the list in a single call. The common result reports whether it worked and the reason it did not.

How do we add a provider that is not on the list?

It is a driver that reads three values and sends, plus the labels for its fields. Nothing above the gateway has to change, which is the whole point of the seam being where it is.

Help Center

Need a quick answer while you read?

Run inventory, procurement, assets, sales, and field work with approved AWRA guidance for setup, migration, integrations, security, pricing, and support.

Search all approved AWRA public help articles.

Open Help Center