fagforbundet/notification-api-sms-notifier-bundle
Symfony bundle for sending SMS notifications through the Notification API. Integrates with Symfony Notifier to dispatch text messages from your application, with configurable API credentials and delivery settings.
Architecture fit
This is a Symfony bundle, not a Laravel package — that mismatch is the single most important fact in the evaluation. What it ships is a symfony/notifier SMS transport: a TransportInterface implementation that POSTs to Fagforbundet's Notification API SMS endpoint, a transport factory, and a bundle extension that registers both in the Symfony DI container via a DSN. In a Symfony app that is clean, idiomatic, and exactly the right shape. In a Laravel app, the DI wiring, bundle extension, and DSN config loader are all dead weight — Laravel's Illuminate\Notifications channel contract has no knowledge of Notifier transports, and we would be adopting a second notification abstraction purely to reach one HTTP call. The reusable surface is genuinely tiny: essentially one transport class with a send() method and a message/response mapping. A TPM should treat this as "a small, single-purpose SMS client with a Symfony-shaped API," not as an integration.
Integration feasibility Two paths, with a clear recommendation.
symfony/notifier and symfony/http-client into the Laravel app, bootstrap a minimal container to register factory + transport, call it from a Laravel service. Highest fidelity, highest dependency and configuration cost, and we inherit the bundle's assumptions about config keys and HTTP wiring. Roughly a week with meaningful ongoing risk.SmsSender interface, and expose it through a custom Laravel notification channel. Two to five engineer-days including tests. Decouples us from the bundle's lifecycle, keeps the container idiomatic, and reduces the third-party surface to something small enough to contract-test.Technical risk The dominant risk is project health, not code quality: 0 stars, 0 dependents, single-organization authorship (a Norwegian union), no visible external consumers, and no signal on tests, release cadence, or security policy. Bus factor is effectively one team, and that team may not be on our route. Secondary risks: the real API we'd depend on is the undocumented configuration contract (config keys, transport naming), tight coupling to one vendor's request/response shape, PHP/Symfony version constraints that may lag or collide with Laravel's pinned Symfony components, and no obvious story for retries, idempotency, rate limits, or delivery receipts. MIT license is clean, so forking is legally unencumbered — that materially mitigates the abandonment scenario and is the main reason this stays on the table as a reference implementation.
Key questions
composer.json require — PHP version, symfony/notifier / symfony/http-client constraints, Symfony 7 support? Does it resolve cleanly against our Laravel skeleton?POST wrapper, or does it handle timeouts, retries, idempotency keys, and error mapping?Stack fit
PHP 8.x is fine. All friction sits at the framework boundary: Laravel 10/11 already ships Symfony components internally, and adding symfony/notifier plus transitive requirements can force pins on symfony/http-client or cause version-resolution conflicts. Run composer require --dry-run against the real application skeleton before writing any code — this is a 30-minute spike that decides the path. If resolution is clean, path 2 is straightforward: one adapter class, one Laravel notification channel, one config block, one service-provider binding.
Migration path
Wrap, don't wire. Define an internal SmsSender port with send(to, body, options) returning a result object carrying provider message id and status. Implement it once with the package's transport, once as a fake for tests, and — if we ever switch vendors — once more. Keep credentials in config/services.php and .env, never in bundle-style YAML. If we are currently sending SMS via raw Http::post or a vendor SDK, move that behind the interface first, then swap implementations behind a feature flag so cutover is a config change rather than a code change.
Compatibility
Pin the package explicitly with a caret range and a documented upgrade checkpoint against our Laravel minor version. If Symfony 7 is unsupported and we are on Laravel 11+, plan to fork the transport into app/Support/Sms/ rather than fight constraints. Record the upstream commit SHA so we can diff later.
Sequencing
composer.json and src/ end to end (2–4 hours); confirm vendor relevance. Go/no-go gate.SmsSender adapter, a contract test with a faked HTTP client, and a queue-backed job wrapper.Maintenance Assume we own it. Expect no timely upstream updates; plan for a vendored or thin fork with our own CI. Steady-state cost is low — a few hundred lines — but nonzero: re-verify against vendor API changes quarterly, watch upstream for security fixes on a defined cadence, and add a dependency-health review item so it does not silently rot.
Support No community, no forum, no Stack Overflow corpus. Support is internal owner plus the vendor. Confirm the vendor's support channel and SLA in writing before this touches anything customer-facing; if the vendor offers no SLA, that becomes a hard constraint on which notification types can use this path.
Scaling Throughput is bounded by the vendor's SMS API, not by this package. The TPM-relevant scaling concerns are (a) queueing — sends must go through a Laravel job, not synchronous request paths, so a vendor slowdown does not back up user-facing requests; (b) rate limiting and per-country throughput caps; (c) cost, which scales linearly per message and needs its own budget owner and alerting; and (d) observability — we need delivery status per message, which a thin transport may not return.
Failure modes Vendor 4xx/5xx surfaces as a failed notification with no built-in retry unless we add one; decide retry policy and dead-letter handling explicitly. Auth/credential expiry, sender-ID rejection in some countries, silent acceptance without delivery, and PII leakage into logs (phone numbers, message bodies) are the realistic failure classes. Each needs a mitigation we own: alerting on failure rate, redaction in logs, and a fallback channel for OTP-class messages.
Ramp-up Low for any PHP engineer — a day to read the transport and understand the vendor contract, another day to have an adapter and tests. The real ramp-up cost is organizational: GDPR/data-processor review (SMS to members is personal data), consent tracking for transactional vs. marketing sends, and phone-number retention rules. Those are TPM-owned workstreams, not engineering tasks, and they will likely take longer than the integration itself. Budget them before the pilot, not after.
How can I help you explore Laravel packages today?