laravel-notification-channels/clickatell
Send SMS notifications through Clickatell with this Laravel notification channel. Install via Composer, configure your API token, and deliver messages using a simple toClickatell method.
Architecture fit. The package is a thin adapter, not a subsystem. It plugs into Laravel's existing notification pipeline as one more channel alongside mail, database, and broadcast — meaning it inherits queueing, retries, via() routing, shouldSend() gating, and Notification::fake() testability for free. That is the right architectural shape for a first SMS capability: no new abstraction, no parallel messaging layer, no bespoke job queue. The integration surface is deliberately small — a channel class, a fluent message builder, and one config file — which means the blast radius of adopting it is limited to the notification classes that opt in. For a TPM, the important read is that this package does not commit the product to Clickatell architecturally; it commits it commercially and operationally, which is a cheaper and more reversible decision.
Integration feasibility. Feasible in days, not weeks, on a Laravel 8-era codebase. The work breaks into four tracks: (1) install and publish config, (2) add routeNotificationForClickatell() to notifiable models plus a phone field with E.164 normalisation, (3) author one or two notification classes, (4) wire credentials and sender ID in the Clickatell account. Only step 4 is outside engineering's control, and it is the one most likely to consume calendar time — alphanumeric sender ID registration is per-country and account-side. The build-vs-buy question is genuinely marginal here: the driver is roughly one small class plus config, so "buy" mostly buys a maintained naming convention rather than meaningful engineering. The honest framing is adopt temporarily, replace deliberately.
Technical risk. Four risks dominate, in descending order of business impact:
202 Accepted from Clickatell is not proof of delivery — per-message accepted flags can be false inside a successful HTTP response. "Did the OTP arrive?" is unanswerable with this dependency alone, which materially changes the effort estimate for anything on the auth path.via() reads. This is a launch prerequisite, not a follow-up ticket.env() directly rather than through config(), php artisan config:cache in production silently nulls the token. Must be validated in a config-cached staging environment before launch.Key questions.
composer why-not against the real lockfile?Stack fit. Strong on Laravel 8.x; progressively weaker on 9, 10, and 11. The value proposition depends entirely on reusing the existing notification pipeline rather than building a separate SMS path — the moment we fork into a bespoke sender, we lose the argument for the package. This means the channel should be adopted behind our own SmsSender seam from day one: an interface plus a ClickatellSmsSender implementation that delegates to notify(). That single hour of work is what converts the eventual vendor swap from an incident into a scheduled afternoon, and it also allows a second vendor for a second region without touching call sites.
Migration path. Three realistic routes, and the decision should be made explicitly rather than by default:
SmsSender seam makes this a swap rather than a rewrite.The recommendation is to adopt on 8.x if that is the current target, with a pre-agreed trigger — a Laravel upgrade, or a delivery-observability requirement — that converts the dependency into vendored or rebuilt code.
Compatibility. Compatibility is ours to prove, not the release notes'. Pre-1.0, single-maintainer, released for Laravel 8, zero dependents: there is no tested matrix. The gate is composer why-not laravel-notification-channels/clickatell run against the actual lockfile, not against documentation. --ignore-platform-reqs in production is not an acceptable answer. A second compatibility axis is the Clickatell account itself: the from sender ID must be provisioned and approved for every destination country in the user base. A failing send that looks like a code defect is frequently a sender-ID provisioning issue in the account, and that distinction needs to be visible in the runbook.
Sequencing. The package gives no reason to launch all SMS flows at once; treat each as a separate product bet with its own success criteria.
Notification::route('clickatell', ...) smoke test — no notifiable model, no migration, no product surface. This de-risks the account-side work before any code depends on it.Cross-cutting work that must run in parallel with step 2, not after it: consent capture and the suppression table that via() reads, phone normalisation to E.164 enforced at write time on the model, a base SmsNotification abstract class to stop seven near-identical notification classes proliferating, and an outbound send log (recipient, channel, body hash, response ID) driven by NotificationSent / NotificationFailed.
Maintenance. Low volume of work, non-zero probability of forced work. The package is small enough to read end-to-end in ten minutes, which means maintenance cost sitting on it is negligible — the risk is that it stops being installable on a future Laravel version and the fix arrives on the framework upgrade's critical path rather than on a schedule. Mitigation: pin the exact version, log it with an owner and a review date, and keep the SmsSender seam so remediation is a planned task. Budget the replacement as a known, small, scheduled cost rather than treating this as strategic infrastructure.
Support. Two support surfaces, and only one of them is engineering's. Internally, support load is mostly "I never got my code" investigations, which the outbound send log turns from a two-hour investigation into a five-minute answer. Externally, vendor support is Clickatell's, and its quality depends on the commercial tier — a self-serve account and an enterprise contract get very different response times on delivery issues and sender-ID provisioning. Someone must own the Clickatell account relationship: sender ID approvals, rate limits, billing, and the status-API work if delivery receipts are required.
Scaling. Throughput is a queue-and-plan question, not a library question. Concurrency is set by our workers and Clickatell's plan rate limit. Two scaling costs need to be forecast rather than discovered:
sms queue so a stuck worker cannot starve mail; add tries, backoff(), and RateLimited middleware on OTP flows specifically so a retry storm does not become a Clickatell bill.Failure modes. Ranked by how quietly they fail:
202 with per-message accepted: false — bad sender ID, blocked route, invalid destination — looks like a successful send in logs if the channel only asserts HTTP status. Highest-severity failure mode because it is silent. Mitigation: inspect and validate the response body, or wrap the call.routeNotificationForClickatell() returns null and the message has no explicit to(), the exception lands in the queue and disappears. Mitigation: gate in via() and shouldSend(), and make the phone field non-nullable for opted-in users.Ramp-up. Fast and bounded. Engineers comfortable with Laravel notifications need perhaps half a day to read the three relevant source files, publish config, and ship the smoke test. The real ramp-up cost is not technical — it is the compliance and account onboarding track: consent capture, STOP handling, suppression lists, sender ID approvals per country, and the decision on delivery observability. Those items carry regulatory consequences if skipped and calendar lead time if handled properly, and they sit on the critical path regardless of how quickly the code lands. Plan the launch around that track, not around the package install.
How can I help you explore Laravel packages today?