Skip to main content
A vendor is a connection out to whoever carries your traffic. Keys are set as outSms.instance.<name>.<key> — see Property tiers. Every worker also has the base settings.

Connecting

Bind counts

Counts apply across normal, extra.hosts and backup.hosts.
A session may carry traffic out if it is a transceiver, or a transmitter on a client session. A receiver-only vendor binds successfully and sends nothing — which is why the panel reports bound_transmittable separately from bound. If they differ, that is the reason.

Keep-alive and timeouts

Retry classification

maxRetries bounds the total submit_sm one message may cost at this vendor, across in-worker retries and router re-routes. Before that was true, a value of 1 alongside 20 routing attempts meant a vendor could see roughly 100 PDUs for one message. Failing over to a different vendor starts a fresh budget.
ESME_RSUBMITFAIL (0x45, decimal 69) is treated as permanent: a lane refused with it fails on the first answer rather than after roughly twenty. Carriers that mean transient overload by it are opted back in per vendor with status.retry.worker = 20,88,69.

Receipts

Turning it off means this vendor stops reporting outcomes, so every message through it settles as EXPIRED at the deadline rather than DELIVERED or FAILED — and your delivery rate for that vendor becomes meaningless rather than zero.

TLVs

The vendor-side mandatory.tlvs.submit is checked against what actually goes on the wire — after default.tlvs.submit, and after registered.tlvs.submit has filtered. A message missing one is refused with a FAILED receipt, a refund and a cdr_rejected row. It is not re-routed.Requiring a tag that is not also in registered.tlvs.submit can never be satisfied, and is warned about at load.

Delivery receipt error codes

Both are vendor properties, not listener ones: 630 from one carrier and 630 from another are unrelated numbers in unrelated spaces, so a gateway-wide table would be wrong for every carrier but the one it was written for. They answer different questions. dlr.errcodes says what 630 means. dlr.errcodes.passthrough says whether a code you have never named may reach the customer at all.

What happens to a code you have not listed

By default it becomes 018, unknown. FireFlo’s own vocabulary is a 38-value list and anything outside it is collapsed — so a telco that reported err:630 had that replaced with the one value a customer cannot act on, and the original was gone before anything recorded it. Set dlr.errcodes.passthrough = true and an unlisted code reaches the customer unchanged. A listed code is unaffected either way: mapping still applies with passthrough off, and passthrough does not change what a mapped code resolves to.
Off by default, and this one cannot be otherwise. Removing the collapse is not a setting that lies dormant until configured — the collapse is the default path. Turning it on gateway-wide would change the err: value in receipts customers already parse, and some have logic built on seeing 018. Enable it per carrier once you have checked those customers can cope with a code they do not recognise.
What the carrier said is always kept, whatever both settings say. cdr_final.vendor_err_code holds the raw code verbatim, beside err_code which holds FireFlo’s reading of it. So a mapping set wrongly is corrected by editing the line rather than by having lost the evidence. Rows written before 0.8.10 have vendor_err_code NULL — it was not backfilled, because those rows genuinely do not know.
The value the customer receives is the same over SMPP err: and over the REST webhook’s err field, so the two populations are told the same thing about one failure.

Inbound messages

mo.route is off by default because a gateway whose last routing rule is a catch-all would otherwise submit its own inbound messages straight back out to a vendor.It is independent of forward.mo.url: with both set, every MO is forwarded and routed.
See Webhooks for the egress settings that govern the forward.