app_header is one approved sender ID, matched case-insensitively, scoped to an
account and a product. In several markets a commercial sender ID is a registration rather than a
string a customer picks — the carrier has approved ACMEIN for one company and expects the traffic
carrying it to be theirs.
Approved is not the same as deliverable
An approval does two jobs, and they fail independently. It decides whether the message is matched at this gateway, and it carries the TLVs that are stamped onto the message on the way out — under DLT, the entity identifier the carrier requires. The panel’s TLV coverage report exists for exactly this. Per approved row, it names which mandatory tags nothing will supply, and why:It is a report, never a guard. A login is not tied to a listener and the route rules pick the
vendor, so nothing here can be certain a given message meets a given listener. Refusing a save on it
would block configurations that work — and every sentence it produces says that a client sending the
tag itself makes the question moot.
vendorDrops is the one worth reading twice: a tag the vendor has not registered is discarded on the
way out, after the call record has already noted it as present. The record shows it sent and the
wire does not carry it. See TLVs on a connection.
Scope: account, product, and optionally one login
A row is per(account, product), with an empty product meaning every product. A product-specific
list is merged over the account’s any-product list — the two are unioned, not chosen between.
system_id narrows further. NULL means every login on the account, which is what every row meant
before the column existed, so an untouched deployment matches exactly as it did and no backfill was
needed. Two logins on the same account may hold the same sender ID.
Who proposes and who approves
1
The customer asks
From the portal. Everything a customer writes lands
PENDING, with their own note attached — under
DLT, their registration reference. The gateway serves a row only when it is enabled and
APPROVED, so a request is stored, visible to both sides, and inert.2
An operator decides
Approve or refuse, on the account page or on the cross-account list. A refusal keeps the row and
carries a reason the customer is shown — which is why there is no bulk refuse, only bulk approve.
3
It goes into service on the next poll
No reconnect. See below.
status and enabled are separate columns doing separate jobs. Approving writes status and records
the reviewer. Unticking writes enabled = false and leaves status alone: withdrawing something is
not un-approving it, and a withdrawn row put back into service should not need approving again.
A customer reading the API sees the withdrawal, from gateway 0.9.8. Because the two columns are
separate, the registration API used to report a withdrawn row as
APPROVED — it reads the approval
decision, and that had not changed. A customer polling it to decide what to send from was told the
sender was approved while the gateway refused every message from it.It now reports a fourth state, WITHDRAWN, so the API and the customer’s portal screen agree with
each other and with what the gateway actually does. Nothing about your side changed; unticking still
writes one column.The TLVs a customer may propose but not set
Both registration tables carry two TLV columns.
Under
replace the stamp overwrites whatever the client asserted, and that overwrite is the
approval. A customer who could write the served column could assert somebody else’s registration
while sending approved wording, and the call record would then report the value they chose as
approved. That behaviour is pinned by a test, so it will not drift.
The payoff is for the customer: a proposal changes nothing that is served, so unlike an edit to the
sender ID it does not suspend the row. An approved registration keeps working while an operator
considers the proposal. Accepting moves the value across and stamps the operator as its owner;
dismissing clears the request and leaves tlvs exactly as it was. Neither touches status.
What the panel refuses to store
64 is generous rather than correct. SMPP caps
source_addr at 21 octets, so anything past that cannot
be delivered over a bind — but refusing at 21 here would also refuse edits to rows that already exist,
and “you cannot deliver this” is a different decision from “you cannot save this”.Approving does not require a reconnect
The whitelist is re-read on the configuration poll and each snapshot carries a generation. An SMPP session bound for weeks picks up a newly approved sender ID without reconnecting — which matters, because the customer whose traffic is being refused is usually on the phone while somebody approves it.Content templates
The other half of the same approval, and the placeholder grammar.
Rolling whitelisting out
Report before enforce, and the diagnostics on
/ops/health.