calisero / laravel-sms
Laravel package for sending SMS through Calisero API
Installs: 61
Dependents: 0
Suggesters: 0
Security: 0
Stars: 1
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/calisero/laravel-sms
Requires
- php: ^8.2 || ^8.4
- calisero/calisero-php: ^2.0
- illuminate/console: ^12.0
- illuminate/notifications: ^12.0
- illuminate/routing: ^12.0
- illuminate/support: ^12.0
- illuminate/validation: ^12.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.59
- mockery/mockery: ^1.6
- orchestra/testbench: ^8.0|^9.0|^10.0
- phpstan/phpstan: ^1.11
- phpunit/phpunit: ^11.5 || ^12.0
README
A first-class Laravel 12 package that wraps the Calisero PHP SDK and provides idiomatic Laravel features for sending SMS messages through the Calisero API.
Features
- ๐ Laravel 12 ready with full support for the latest features
- ๐ฑ Easy SMS sending via Facade, Notification channels, or direct client usage
- ๐ Two-Factor Authentication with verification codes API
- ๐ Webhook handling with token-based security
- โ Validation rules for phone numbers (E.164) and sender IDs
- ๐ฏ Queue support for reliable message delivery
- ๐งช Artisan commands for testing and development
- ๐๏ธ PSR-4 compliant with full test coverage
Internal package logging was removed. Add your own logging in event listeners/subscribers.
Installation
You can install the package via Composer:
composer require calisero/laravel-sms
Publish the configuration file
php artisan vendor:publish --provider="Calisero\\LaravelSms\\ServiceProvider" --tag="calisero-config"
Configure your environment
Add your Calisero API credentials to your .env file:
# Required: API Configuration CALISERO_API_KEY=your-api-key-here CALISERO_BASE_URI=https://rest.calisero.ro/api/v1 # Optional: Account ID for balance queries CALISERO_ACCOUNT_ID=your-account-id # Optional: Connection Settings CALISERO_TIMEOUT=10.0 CALISERO_CONNECT_TIMEOUT=3.0 CALISERO_RETRIES=5 CALISERO_RETRY_BACKOFF_MS=200 # Optional: Webhook Configuration CALISERO_WEBHOOK_ENABLED=true CALISERO_WEBHOOK_PATH=calisero/webhook CALISERO_WEBHOOK_TOKEN=your-shared-secret # Optional: Credit Monitoring CALISERO_CREDIT_LOW=500 CALISERO_CREDIT_CRITICAL=100
Usage
Quick Start
Sending SMS via Facade
use Calisero\LaravelSms\Facades\Calisero; $response = Calisero::sendSms([ 'to' => '+40712345678', 'text' => 'Hello from Laravel!', // 'from' => 'MyBrand' // Include ONLY if approved by Calisero ]);
Verification Codes (2FA)
Send and verify one-time codes for two-factor authentication:
use Calisero\LaravelSms\Facades\Calisero; // Send a verification code (with brand) $response = Calisero::sendVerification([ 'to' => '+40712345678', 'brand' => 'MyApp', // Required if no template 'expires_in' => 5, // Optional: 1-10 minutes, default 5 ]); // OR send with custom template $response = Calisero::sendVerification([ 'to' => '+40712345678', 'template' => 'Your verification code is {code}. Valid for 5 minutes.', 'expires_in' => 5, ]); // The response includes expiration time echo "Code expires at: " . $response->expires_at; // Check/verify the code entered by user $result = Calisero::checkVerification([ 'to' => '+40712345678', 'code' => '123456', // Code entered by user (6 characters) ]); if ('verified' === $result->status) { // Code is valid, proceed with authentication echo "Verification successful!"; } else { // Code is invalid or expired echo "Verification failed!"; }
Note: Either
brandORtemplateis required when sending verification codes. The template must contain{code}placeholder. Codes are 6 characters and sent via SMS.
Using Notifications
Create a notification class:
use Calisero\LaravelSms\Notification\SmsMessage; use Illuminate\Notifications\Notification; class WelcomeNotification extends Notification { public function via($notifiable): array { return ['calisero']; } public function toCalisero($notifiable): SmsMessage { return SmsMessage::create('Welcome to our app!') // ->from('MyBrand') // Uncomment only after approval ; } }
Send the notification:
use App\Models\User; use App\Notifications\WelcomeNotification; $user = User::find(1); $user->notify(new WelcomeNotification());
For the notification to work, your User model should implement the routeNotificationForCalisero method:
public function routeNotificationForCalisero($notification): string { return $this->phone; // Return the user's phone number }
Using the Direct Client
For more control, inject the client directly:
use Calisero\LaravelSms\Contracts\SmsClient; class SmsService { public function __construct(private SmsClient $client) {} public function sendWelcomeSms(string $phone): void { $this->client->sendSms([ 'to' => $phone, 'text' => 'Welcome to our service!', 'from' => 'MyApp', 'idempotencyKey' => 'welcome-' . uniqid(), ]); } }
Validation Rules
Use the included validation rules in your form requests:
use Calisero\LaravelSms\Validation\Rule; use Illuminate\Foundation\Http\FormRequest; class SendSmsRequest extends FormRequest { public function rules(): array { return [ 'phone' => ['required', Rule::phoneE164()], 'sender_id' => ['required', Rule::senderId()], 'message' => ['required', 'string', 'max:1600'], ]; } }
Webhook Handling
Enable webhooks by setting CALISERO_WEBHOOK_ENABLED=true. The package will register a POST endpoint at /calisero/webhook (or your configured CALISERO_WEBHOOK_PATH).
Securing the Webhook (Query Token)
If you also set CALISERO_WEBHOOK_TOKEN=your-shared-secret, the package will:
- Automatically append
?token=your-shared-secretto the injectedcallback_urlsent to Calisero (only when you did not supply a customcallback_url). - Register a middleware that rejects any incoming webhook request not containing the correct
tokenquery parameter.
Requirements when token security is enabled:
- Each webhook request from Calisero must include the query parameter
tokenwith the exact configured value. - If you manually override
callback_url, you are responsible for including the?token=...segment yourself. - If your explicit URL already contains a
token=parameter, the library will not modify it.
Environment example:
CALISERO_WEBHOOK_ENABLED=true CALISERO_WEBHOOK_PATH=calisero/webhook CALISERO_WEBHOOK_TOKEN=super-secret-value
Example of explicit override (token already present, no modification by the library):
Calisero::sendSms([ 'to' => '+1234567890', 'text' => 'Custom secured callback', 'callback_url' => 'https://example.com/custom-hook?token=' . urlencode(config('calisero.webhook.token')), ]);
Rotation tip: rotate the token by
- Adding a temporary second endpoint (optional) or a maintenance window.
- Updating
CALISERO_WEBHOOK_TOKEN. - Redeploying and updating the callback URL in Calisero (or sending a new message to propagate the injected URL).
If you leave CALISERO_WEBHOOK_TOKEN empty, no token middleware is attached and the endpoint is publicly accessible (POST only). Consider other controls (IP allow-list, WAF) if you opt out of the token.
Listen for the events:
Event::listen(MessageSent::class, fn (MessageSent $e) => ...); Event::listen(MessageDelivered::class, fn (MessageDelivered $e) => ...); Event::listen(MessageFailed::class, fn (MessageFailed $e) => ...);
Statuses currently emitted (lifecycle):
sentโ the message was accepted and dispatched to the networkdeliveredโ the handset/network confirmed deliveryfailedโ delivery permanently failed
Webhook payload example (flat structure):
{
"price": 0.0378,
"sender": "CALISERO",
"sentAt": "2025-09-19T11:59:44.000000Z",
"status": "sent",
"messageId": "019961d8-3338-700c-be17-10d061f03a5c",
"recipient": "+40742***350",
"scheduleAt": "2025-09-19T11:59:42.000000Z",
"deliveredAt": null,
"remainingBalance": 999.43
}
When the same message is later delivered you will receive another webhook with:
{
"price": 0.0378,
"sender": "CALISERO",
"sentAt": "2025-09-19T11:59:44.000000Z",
"status": "delivered",
"messageId": "019961d8-3338-700c-be17-10d061f03a5c",
"recipient": "+40742***350",
"scheduleAt": "2025-09-19T11:59:42.000000Z",
"deliveredAt": "2025-09-19T12:00:24.000000Z",
"remainingBalance": 999.43
}
A failed attempt would have "status": "failed" and usually a deliveredAt of null.
Automatic callback_url Injection
If CALISERO_WEBHOOK_ENABLED=true, every sendSms() call without an explicit callback_url (or callbackUrl) automatically includes one pointing to the named route calisero.webhook (if registered) or a URL built from app.url + the configured path.
To override, supply your own callback_url parameter.
To disable injection, set CALISERO_WEBHOOK_ENABLED=false or omit the env variable.
Edge cases:
- If
app.urlis not set and the route helper fails, a root-relative path like/calisero/webhookis used. - Passing either
callback_urlorcallbackUrlprevents injection.
Example (override):
Calisero::sendSms([ 'to' => '+1234567890', 'text' => 'Custom callback', 'callback_url' => 'https://example.com/custom-hook', ]);
Artisan Commands
The package provides several Artisan commands for testing and development:
Test SMS Sending
php artisan calisero:sms:test +40712345678 --from=YourApp --text="Test message"
SMS Status
php artisan calisero:sms:status 019961d8-3338-700c-be17-10d061f03a5c
Verification Commands
Send a verification code with brand:
php artisan calisero:verification:send +40712345678 --brand=MyApp
Send a verification code with custom template:
php artisan calisero:verification:send +40712345678 --template="Your code is {code}" --expires-in=5
Check a verification code:
php artisan calisero:verification:check +40712345678 123456
Webhook Verification
Enable webhook handling by setting CALISERO_WEBHOOK_ENABLED=true. The package will register a POST endpoint at /calisero/webhook (or your configured CALISERO_WEBHOOK_PATH).
If you also set CALISERO_WEBHOOK_TOKEN=your-shared-secret, the package will:
- Automatically append
?token=your-shared-secretto the injectedcallback_urlsent to Calisero (only when you did not supply a customcallback_url). - Register a middleware that rejects any incoming webhook request not containing the correct
tokenquery parameter.
Requirements when token security is enabled:
- Each webhook request from Calisero must include the query parameter
tokenwith the exact configured value. - If you manually override
callback_url, you are responsible for including the?token=...segment yourself. - If your explicit URL already contains a
token=parameter, the library will not modify it.
Environment example:
CALISERO_WEBHOOK_ENABLED=true CALISERO_WEBHOOK_PATH=calisero/webhook CALISERO_WEBHOOK_TOKEN=super-secret-value
Example of explicit override (token already present, no modification by the library):
Calisero::sendSms([ 'to' => '+1234567890', 'text' => 'Custom secured callback', 'callback_url' => 'https://example.com/custom-hook?token=' . urlencode(config('calisero.webhook.token')), ]);
Rotation tip: rotate the token by
- Adding a temporary second endpoint (optional) or a maintenance window.
- Updating
CALISERO_WEBHOOK_TOKEN. - Redeploying and updating the callback URL in Calisero (or sending a new message to propagate the injected URL).
If you leave CALISERO_WEBHOOK_TOKEN empty, no token middleware is attached and the endpoint is publicly accessible (POST only). Consider other controls (IP allow-list, WAF) if you opt out of the token.
Listen for the events:
Event::listen(MessageSent::class, fn (MessageSent $e) => ...); Event::listen(MessageDelivered::class, fn (MessageDelivered $e) => ...); Event::listen(MessageFailed::class, fn (MessageFailed $e) => ...);
Statuses currently emitted (lifecycle):
sentโ the message was accepted and dispatched to the networkdeliveredโ the handset/network confirmed deliveryfailedโ delivery permanently failed
Webhook payload example (flat structure):
{
"price": 0.0378,
"sender": "CALISERO",
"sentAt": "2025-09-19T11:59:44.000000Z",
"status": "sent",
"messageId": "019961d8-3338-700c-be17-10d061f03a5c",
"recipient": "+40742***350",
"scheduleAt": "2025-09-19T11:59:42.000000Z",
"deliveredAt": null,
"remainingBalance": 999.43
}
When the same message is later delivered you will receive another webhook with:
{
"price": 0.0378,
"sender": "CALISERO",
"sentAt": "2025-09-19T11:59:44.000000Z",
"status": "delivered",
"messageId": "019961d8-3338-700c-be17-10d061f03a5c",
"recipient": "+40742***350",
"scheduleAt": "2025-09-19T11:59:42.000000Z",
"deliveredAt": "2025-09-19T12:00:24.000000Z",
"remainingBalance": 999.43
}
A failed attempt would have "status": "failed" and usually a deliveredAt of null.
Automatic callback_url Injection
If CALISERO_WEBHOOK_ENABLED=true, every sendSms() call without an explicit callback_url (or callbackUrl) automatically includes one pointing to the named route calisero.webhook (if registered) or a URL built from app.url + the configured path.
To override, supply your own callback_url parameter.
To disable injection, set CALISERO_WEBHOOK_ENABLED=false or omit the env variable.
Edge cases:
- If
app.urlis not set and the route helper fails, a root-relative path like/calisero/webhookis used. - Passing either
callback_urlorcallbackUrlprevents injection.
Example (override):
Calisero::sendSms([ 'to' => '+1234567890', 'text' => 'Custom callback', 'callback_url' => 'https://example.com/custom-hook', ]);
Artisan Commands
The package provides several Artisan commands for testing and development:
Test SMS Sending
php artisan calisero:sms:test +40712345678 --from=YourApp --text="Test message"
SMS Status
php artisan calisero:sms:status 019961d8-3338-700c-be17-10d061f03a5c
Verification Commands
Send a verification code with brand:
php artisan calisero:verification:send +40712345678 --brand=MyApp
Send a verification code with custom template:
php artisan calisero:verification:send +40712345678 --template="Your code is {code}" --expires-in=5
Check a verification code:
php artisan calisero:verification:check +40712345678 123456
Webhook Verification
Enable webhook handling by setting CALISERO_WEBHOOK_ENABLED=true. The package will register a POST endpoint at /calisero/webhook (or your configured CALISERO_WEBHOOK_PATH).
If you also set CALISERO_WEBHOOK_TOKEN=your-shared-secret, the package will:
- Automatically append
?token=your-shared-secretto the injectedcallback_urlsent to Calisero (only when you did not supply a customcallback_url). - Register a middleware that rejects any incoming webhook request not containing the correct
tokenquery parameter.
Requirements when token security is enabled:
- Each webhook request from Calisero must include the query parameter
tokenwith the exact configured value. - If you manually override
callback_url, you are responsible for including the?token=...segment yourself. - If your explicit URL already contains a
token=parameter, the library will not modify it.
Environment example:
CALISERO_WEBHOOK_ENABLED=true CALISERO_WEBHOOK_PATH=calisero/webhook CALISERO_WEBHOOK_TOKEN=super-secret-value
Example of explicit override (token already present, no modification by the library):
Calisero::sendSms([ 'to' => '+1234567890', 'text' => 'Custom secured callback', 'callback_url' => 'https://example.com/custom-hook?token=' . urlencode(config('calisero.webhook.token')), ]);
Rotation tip: rotate the token by
- Adding a temporary second endpoint (optional) or a maintenance window.
- Updating
CALISERO_WEBHOOK_TOKEN. - Redeploying and updating the callback URL in Calisero (or sending a new message to propagate the injected URL).
If you leave CALISERO_WEBHOOK_TOKEN empty, no token middleware is attached and the endpoint is publicly accessible (POST only). Consider other controls (IP allow-list, WAF) if you opt out of the token.
Listen for the events:
Event::listen(MessageSent::class, fn (MessageSent $e) => ...); Event::listen(MessageDelivered::class, fn (MessageDelivered $e) => ...); Event::listen(MessageFailed::class, fn (MessageFailed $e) => ...);
Statuses currently emitted (lifecycle):
sentโ the message was accepted and dispatched to the networkdeliveredโ the handset/network confirmed deliveryfailedโ delivery permanently failed
Webhook payload example (flat structure):
{
"price": 0.0378,
"sender": "CALISERO",
"sentAt": "2025-09-19T11:59:44.000000Z",
"status": "sent",
"messageId": "019961d8-3338-700c-be17-10d061f03a5c",
"recipient": "+40742***350",
"scheduleAt": "2025-09-19T11:59:42.000000Z",
"deliveredAt": null,
"remainingBalance": 999.43
}
When the same message is later delivered you will receive another webhook with:
{
"price": 0.0378,
"sender": "CALISERO",
"sentAt": "2025-09-19T11:59:44.000000Z",
"status": "delivered",
"messageId": "019961d8-3338-700c-be17-10d061f03a5c",
"recipient": "+40742***350",
"scheduleAt": "2025-09-19T11:59:42.000000Z",
"deliveredAt": "2025-09-19T12:00:24.000000Z",
"remainingBalance": 999.43
}
A failed attempt would have "status": "failed" and usually a deliveredAt of null.
Automatic callback_url Injection
If CALISERO_WEBHOOK_ENABLED=true, every sendSms() call without an explicit callback_url (or callbackUrl) automatically includes one pointing to the named route calisero.webhook (if registered) or a URL built from app.url + the configured path.
To override, supply your own callback_url parameter.
To disable injection, set CALISERO_WEBHOOK_ENABLED=false or omit the env variable.
Edge cases:
- If
app.urlis not set and the route helper fails, a root-relative path like/calisero/webhookis used. - Passing either
callback_urlorcallbackUrlprevents injection.
Example (override):
Calisero::sendSms([ 'to' => '+1234567890', 'text' => 'Custom callback', 'callback_url' => 'https://example.com/custom-hook', ]);
Artisan Commands
The package provides several Artisan commands for testing and development:
Test SMS Sending
php artisan calisero:sms:test +40712345678 --from=YourApp --text="Test message"
SMS Status
php artisan calisero:sms:status 019961d8-3338-700c-be17-10d061f03a5c
Verification Commands
Send a verification code with brand:
php artisan calisero:verification:send +40712345678 --brand=MyApp
Send a verification code with custom template:
php artisan calisero:verification:send +40712345678 --template="Your code is {code}" --expires-in=5
Check a verification code:
php artisan calisero:verification:check +40712345678 123456
Webhook Verification
Enable webhook handling by setting CALISERO_WEBHOOK_ENABLED=true. The package will register a POST endpoint at /calisero/webhook (or your configured CALISERO_WEBHOOK_PATH).
If you also set CALISERO_WEBHOOK_TOKEN=your-shared-secret, the package will:
- Automatically append
?token=your-shared-secretto the injectedcallback_urlsent to Calisero (only when you did not supply a customcallback_url). - Register a middleware that rejects any incoming webhook request not containing the correct
tokenquery parameter.
Requirements when token security is enabled:
- Each webhook request from Calisero must include the query parameter
tokenwith the exact configured value. - If you manually override
callback_url, you are responsible for including the?token=...segment yourself. - If your explicit URL already contains a
token=parameter, the library will not modify it.
Environment example:
CALISERO_WEBHOOK_ENABLED=true CALISERO_WEBHOOK_PATH=calisero/webhook CALISERO_WEBHOOK_TOKEN=super-secret-value
Example of explicit override (token already present, no modification by the library):
Calisero::sendSms([ 'to' => '+1234567890', 'text' => 'Custom secured callback', 'callback_url' => 'https://example.com/custom-hook?token=' . urlencode(config('calisero.webhook.token')), ]);
Rotation tip: rotate the token by
- Adding a temporary second endpoint (optional) or a maintenance window.
- Updating
CALISERO_WEBHOOK_TOKEN. - Redeploying and updating the callback URL in Calisero (or sending a new message to propagate the injected URL).
If you leave CALISERO_WEBHOOK_TOKEN empty, no token middleware is attached and the endpoint is publicly accessible (POST only). Consider other controls (IP allow-list, WAF) if you opt out of the token.
Listen for the events:
Event::listen(MessageSent::class, fn (MessageSent $e) => ...); Event::listen(MessageDelivered::class, fn (MessageDelivered $e) => ...); Event::listen(MessageFailed::class, fn (MessageFailed $e) => ...);
Statuses currently emitted (lifecycle):
sentโ the message was accepted and dispatched to the networkdeliveredโ the handset/network confirmed deliveryfailedโ delivery permanently failed
Webhook payload example (flat structure):
{
"price": 0.0378,
"sender": "CALISERO",
"sentAt": "2025-09-19T11:59:44.000000Z",
"status": "sent",
"messageId": "019961d8-3338-700c-be17-10d061f03a5c",
"recipient": "+40742***350",
"scheduleAt": "2025-09-19T11:59:42.000000Z",
"deliveredAt": null,
"remainingBalance": 999.43
}
When the same message is later delivered you will receive another webhook with:
{
"price": 0.0378,
"sender": "CALISERO",
"sentAt": "2025-09-19T11:59:44.000000Z",
"status": "delivered",
"messageId": "019961d8-3338-700c-be17-10d061f03a5c",
"recipient": "+40742***350",
"scheduleAt": "2025-09-19T11:59:42.000000Z",
"deliveredAt": "2025-09-19T12:00:24.000000Z",
"remainingBalance": 999.43
}
A failed attempt would have "status": "failed" and usually a deliveredAt of null.
Automatic callback_url Injection
If CALISERO_WEBHOOK_ENABLED=true, every sendSms() call without an explicit callback_url (or callbackUrl) automatically includes one pointing to the named route calisero.webhook (if registered) or a URL built from app.url + the configured path.
To override, supply your own callback_url parameter.
To disable injection, set CALISERO_WEBHOOK_ENABLED=false or omit the env variable.
Edge cases:
- If
app.urlis not set and the route helper fails, a root-relative path like/calisero/webhookis used. - Passing either
callback_urlorcallbackUrlprevents injection.
Example (override):
Calisero::sendSms([ 'to' => '+1234567890', 'text' => 'Custom callback', 'callback_url' => 'https://example.com/custom-hook', ]);
Artisan Commands
The package provides several Artisan commands for testing and development:
Test SMS Sending
php artisan calisero:sms:test +40712345678 --from=YourApp --text="Test message"
SMS Status
php artisan calisero:sms:status 019961d8-3338-700c-be17-10d061f03a5c
Verification Commands
Send a verification code with brand:
php artisan calisero:verification:send +40712345678 --brand=MyApp
Send a verification code with custom template:
php artisan calisero:verification:send +40712345678 --template="Your code is {code}" --expires-in=5
Check a verification code:
php artisan calisero:verification:check +40712345678 123456
Webhook Verification
Enable webhook handling by setting CALISERO_WEBHOOK_ENABLED=true. The package will register a POST endpoint at /calisero/webhook (or your configured CALISERO_WEBHOOK_PATH).
If you also set CALISERO_WEBHOOK_TOKEN=your-shared-secret, the package will:
- Automatically append
?token=your-shared-secretto the injectedcallback_urlsent to Calisero (only when you did not supply a customcallback_url). - Register a middleware that rejects any incoming webhook request not containing the correct
tokenquery parameter.
Requirements when token security is enabled:
- Each webhook request from Calisero must include the query parameter
tokenwith the exact configured value. - If you manually override
callback_url, you are responsible for including the?token=...segment yourself. - If your explicit URL already contains a
token=parameter, the library will not modify it.
Environment example:
CALISERO_WEBHOOK_ENABLED=true CALISERO_WEBHOOK_PATH=calisero/webhook CALISERO_WEBHOOK_TOKEN=super-secret-value
Example of explicit override (token already present, no modification by the library):
Calisero::sendSms([ 'to' => '+1234567890', 'text' => 'Custom secured callback', 'callback_url' => 'https://example.com/custom-hook?token=' . urlencode(config('calisero.webhook.token')), ]);
Rotation tip: rotate the token by
- Adding a temporary second endpoint (optional) or a maintenance window.
- Updating
CALISERO_WEBHOOK_TOKEN. - Redeploying and updating the callback URL in Calisero (or sending a new message to propagate the injected URL).
If you leave CALISERO_WEBHOOK_TOKEN empty, no token middleware is attached and the endpoint is publicly accessible (POST only). Consider other controls (IP allow-list, WAF) if you opt out of the token.
Listen for the events:
Event::listen(MessageSent::class, fn (MessageSent $e) => ...); Event::listen(MessageDelivered::class, fn (MessageDelivered $e) => ...); Event::listen(MessageFailed::class, fn (MessageFailed $e) => ...);
Statuses currently emitted (lifecycle):
sentโ the message was accepted and dispatched to the networkdeliveredโ the handset/network confirmed deliveryfailedโ delivery permanently failed
Webhook payload example (flat structure):
{
"price": 0.0378,
"sender": "CALISERO",
"sentAt": "2025-09-19T11:59:44.000000Z",
"status": "sent",
"messageId": "019961d8-3338-700c-be17-10d061f03a5c",
"recipient": "+40742***350",
"scheduleAt": "2025-09-19T11:59:42.000000Z",
"deliveredAt": null,
"remainingBalance": 999.43
}
When the same message is later delivered you will receive another webhook with:
{
"price": 0.0378,
"sender": "CALISERO",
"sentAt": "2025-09-19T11:59:44.000000Z",
"status": "delivered",
"messageId": "019961d8-3338-700c-be17-10d061f03a5c",
"recipient": "+40742***350",
"scheduleAt": "2025-09-19T11:59:42.000000Z",
"deliveredAt": "2025-09-19T12:00:24.000000Z",
"remainingBalance": 999.43
}
A failed attempt would have "status": "failed" and usually a deliveredAt of null.
Automatic callback_url Injection
If CALISERO_WEBHOOK_ENABLED=true, every sendSms() call without an explicit callback_url (or callbackUrl) automatically includes one pointing to the named route calisero.webhook (if registered) or a URL built from app.url + the configured path.
To override, supply your own callback_url parameter.
To disable injection, set CALISERO_WEBHOOK_ENABLED=false or omit the env variable.
Edge cases:
- If
app.urlis not set and the route helper fails, a root-relative path like/calisero/webhookis used. - Passing either
callback_urlorcallbackUrlprevents injection.
Example (override):
Calisero::sendSms([ 'to' => '+1234567890', 'text' => 'Custom callback', 'callback_url' => 'https://example.com/custom-hook', ]);
Artisan Commands
The package provides several Artisan commands for testing and development:
Test SMS Sending
php artisan calisero:sms:test +40712345678 --from=YourApp --text="Test message"
SMS Status
php artisan calisero:sms:status 019961d8-3338-700c-be17-10d061f03a5c
Verification Commands
Send a verification code with brand:
php artisan calisero:verification:send +40712345678 --brand=MyApp
Send a verification code with custom template:
php artisan calisero:verification:send +40712345678 --template="Your code is {code}" --expires-in=5
Check a verification code:
php artisan calisero:verification:check +40712345678 123456
Webhook Verification
Enable webhook handling by setting CALISERO_WEBHOOK_ENABLED=true. The package will register a POST endpoint at /calisero/webhook (or your configured CALISERO_WEBHOOK_PATH).
If you also set CALISERO_WEBHOOK_TOKEN=your-shared-secret, the package will:
- Automatically append
?token=your-shared-secretto the injectedcallback_urlsent to Calisero (only when you did not supply a customcallback_url). - Register a middleware that rejects any incoming webhook request not containing the correct
tokenquery parameter.
Requirements when token security is enabled:
- Each webhook request from Calisero must include the query parameter
tokenwith the exact configured value. - If you manually override
callback_url, you are responsible for including the?token=...segment yourself. - If your explicit URL already contains a
token=parameter, the library will not modify it.
Environment example:
CALISERO_WEBHOOK_ENABLED=true CALISERO_WEBHOOK_PATH=calisero/webhook CALISERO_WEBHOOK_TOKEN=super-secret-value
Example of explicit override (token already present, no modification by the library):
Calisero::sendSms([ 'to' => '+1234567890', 'text' => 'Custom secured callback', 'callback_url' => 'https://example.com/custom-hook?token=' . urlencode(config('calisero.webhook.token')), ]);
Rotation tip: rotate the token by
- Adding a temporary second endpoint (optional) or a maintenance window.
- Updating
CALISERO_WEBHOOK_TOKEN. - Redeploying and updating the callback URL in Calisero (or sending a new message to propagate the injected URL).
If you leave CALISERO_WEBHOOK_TOKEN empty, no token middleware is attached and the endpoint is publicly accessible (POST only). Consider other controls (IP allow-list, WAF) if you opt out of the token.
Listen for the events:
Event::listen(MessageSent::class, fn (MessageSent $e) => ...); Event::listen(MessageDelivered::class, fn (MessageDelivered $e) => ...); Event::listen(MessageFailed::class, fn (MessageFailed $e) => ...);
Statuses currently emitted (lifecycle):
sentโ the message was accepted and dispatched to the networkdeliveredโ the handset/network confirmed deliveryfailedโ delivery permanently failed
Webhook payload example (flat structure):
{
"price": 0.0378,
"sender": "CALISERO",
"sentAt": "2025-09-19T11:59:44.000000Z",
"status": "sent",
"messageId": "019961d8-3338-700c-be17-10d061f03a5c",
"recipient": "+40742***350",
"scheduleAt": "2025-09-19T11:59:42.000000Z",
"deliveredAt": null,
"remainingBalance": 999.43
}
When the same message is later delivered you will receive another webhook with:
{
"price": 0.0378,
"sender": "CALISERO",
"sentAt": "2025-09-19T11:59:44.000000Z",
"status": "delivered",
"messageId": "019961d8-3338-700c-be17-10d061f03a5c",
"recipient": "+40742***350",
"scheduleAt": "2025-09-19T11:59:42.000000Z",
"deliveredAt": "2025-09-19T12:00:24.000000Z",
"remainingBalance": 999.43
}
A failed attempt would have "status": "failed" and usually a deliveredAt of null.
Automatic callback_url Injection
If CALISERO_WEBHOOK_ENABLED=true, every sendSms() call without an explicit callback_url (or callbackUrl) automatically includes one pointing to the named route calisero.webhook (if registered) or a URL built from app.url + the configured path.
To override, supply your own callback_url parameter.
To disable injection, set CALISERO_WEBHOOK_ENABLED=false or omit the env variable.
Edge cases:
- If
app.urlis not set and the route helper fails, a root-relative path like/calisero/webhookis used. - Passing either
callback_urlorcallbackUrlprevents injection.
Example (override):
Calisero::sendSms([ 'to' => '+1234567890', 'text' => 'Custom callback', 'callback_url' => 'https://example.com/custom-hook', ]);
Artisan Commands
The package provides several Artisan commands for testing and development:
Test SMS Sending
php artisan calisero:sms:test +40712345678 --from=YourApp --text="Test message"
SMS Status
php artisan calisero:sms:status 019961d8-3338-700c-be17-10d061f03a5c
Verification Commands
Send a verification code with brand:
php artisan calisero:verification:send +40712345678 --brand=MyApp
Send a verification code with custom template:
php artisan calisero:verification:send +40712345678 --template="Your code is {code}" --expires-in=5
Check a verification code:
php artisan calisero:verification:check +40712345678 123456
Webhook Verification
Enable webhook handling by setting CALISERO_WEBHOOK_ENABLED=true. The package will register a POST endpoint at /calisero/webhook (or your configured CALISERO_WEBHOOK_PATH).
If you also set CALISERO_WEBHOOK_TOKEN=your-shared-secret, the package will:
- Automatically append
?token=your-shared-secretto the injectedcallback_urlsent to Calisero (only when you did not supply a customcallback_url). - Register a middleware that rejects any incoming webhook request not containing the correct
tokenquery parameter.
Requirements when token security is enabled:
- Each webhook request from Calisero must include the query parameter
tokenwith the exact configured value. - If you manually override
callback_url, you are responsible for including the?token=...segment yourself. - If your explicit URL already contains a
token=parameter, the library will not modify it.
Environment example:
CALISERO_WEBHOOK_ENABLED=true CALISERO_WEBHOOK_PATH=calisero/webhook CALISERO_WEBHOOK_TOKEN=super-secret-value
Example of explicit override (token already present, no modification by the library):
Calisero::sendSms([ 'to' => '+1234567890', 'text' => 'Custom secured callback', 'callback_url' => 'https://example.com/custom-hook?token=' . urlencode(config('calisero.webhook.token')), ]);
Rotation tip: rotate the token by
- Adding a temporary second endpoint (optional) or a maintenance window.
- Updating
CALISERO_WEBHOOK_TOKEN. - Redeploying and updating the callback URL in Calisero (or sending a new message to propagate the injected URL).
If you leave CALISERO_WEBHOOK_TOKEN empty, no token middleware is attached and the endpoint is publicly accessible (POST only). Consider other controls (IP allow-list, WAF) if you opt out of the token.
Listen for the events:
Event::listen(MessageSent::class, fn (MessageSent $e) => ...); Event::listen(MessageDelivered::class, fn (MessageDelivered $e) => ...); Event::listen(MessageFailed::class, fn (MessageFailed $e) => ...);
Statuses currently emitted (lifecycle):
sentโ the message was accepted and dispatched to the networkdeliveredโ the handset/network confirmed deliveryfailedโ delivery permanently failed
Webhook payload example (flat structure):
{
"price": 0.0378,
"sender": "CALISERO",
"sentAt": "2025-09-19T11:59:44.000000Z",
"status": "sent",
"messageId": "019961d8-3338-700c-be17-10d061f03a5c",
"recipient": "+40742***350",
"scheduleAt": "2025-09-19T11:59:42.000000Z",
"deliveredAt": null,
"remainingBalance": 999.43
}
When the same message is later delivered you will receive another webhook with:
{
"price": 0.0378,
"sender": "CALISERO",
"sentAt": "2025-09-19T11:59:44.000000Z",
"status": "delivered",
"messageId": "019961d8-3338-700c-be17-10d061f03a5c",
"recipient": "+40742***350",
"scheduleAt": "2025-09-19T11:59:42.000000Z",
"deliveredAt": "2025-09-19T12:00:24.000000Z",
"remainingBalance": 999.43
}
A failed attempt would have "status": "failed" and usually a deliveredAt of null.
Automatic callback_url Injection
If CALISERO_WEBHOOK_ENABLED=true, every sendSms() call without an explicit callback_url (or callbackUrl) automatically includes one pointing to the named route calisero.webhook (if registered) or a URL built from app.url + the configured path.
To override, supply your own callback_url parameter.
To disable injection, set CALISERO_WEBHOOK_ENABLED=false or omit the env variable.
Edge cases:
- If
app.urlis not set and the route helper fails, a root-relative path like/calisero/webhookis used. - Passing either
callback_urlorcallbackUrlprevents injection.
Example (override):
Calisero::sendSms([ 'to' => '+1234567890', 'text' => 'Custom callback', 'callback_url' => 'https://example.com/custom-hook', ]);
Artisan Commands
The package provides several Artisan commands for testing and development:
Test SMS Sending
php artisan calisero:sms:test +40712345678 --from=YourApp --text="Test message"
SMS Status
php artisan calisero:sms:status 019961d8-3338-700c-be17-10d061f03a5c
Verification Commands
Send a verification code with brand:
php artisan calisero:verification:send +40712345678 --brand=MyApp
Send a verification code with custom template:
php artisan calisero:verification:send +40712345678 --template="Your code is {code}" --expires-in=5
Check a verification code:
php artisan calisero:verification:check +40712345678 123456
Webhook Verification
Enable webhook handling by setting CALISERO_WEBHOOK_ENABLED=true. The package will register a POST endpoint at /calisero/webhook (or your configured CALISERO_WEBHOOK_PATH).
If you also set CALISERO_WEBHOOK_TOKEN=your-shared-secret, the package will:
- Automatically append
?token=your-shared-secretto the injectedcallback_urlsent to Calisero (only when you did not supply a customcallback_url). - Register a middleware that rejects any incoming webhook request not containing the correct
tokenquery parameter.
Requirements when token security is enabled:
- Each webhook request from Calisero must include the query parameter
tokenwith the exact configured value. - If you manually override
callback_url, you are responsible for including the?token=...segment yourself. - If your explicit URL already contains a
token=parameter, the library will not modify it.
Environment example:
CALISERO_WEBHOOK_ENABLED=true CALISERO_WEBHOOK_PATH=calisero/webhook CALISERO_WEBHOOK_TOKEN=super-secret-value
Example of explicit override (token already present, no modification by the library):
Calisero::sendSms([ 'to' => '+1234567890', 'text' => 'Custom secured callback', 'callback_url' => 'https://example.com/custom-hook?token=' . urlencode(config('calisero.webhook.token')), ]);
Rotation tip: rotate the token by
- Adding a temporary second endpoint (optional) or a maintenance window.
- Updating
CALISERO_WEBHOOK_TOKEN. - Redeploying and updating the callback URL in Calisero (or sending a new message to propagate the injected URL).
If you leave CALISERO_WEBHOOK_TOKEN empty, no token middleware is attached and the endpoint is publicly accessible (POST only). Consider other controls (IP allow-list, WAF) if you opt out of the token.
Listen for the events:
Event::listen(MessageSent::class, fn (MessageSent $e) => ...); Event::listen(MessageDelivered::class, fn (MessageDelivered $e) => ...); Event::listen(MessageFailed::class, fn (MessageFailed $e) => ...);
Statuses currently emitted (lifecycle):
sentโ the message was accepted and dispatched to the networkdeliveredโ the handset/network confirmed deliveryfailedโ delivery permanently failed
Webhook payload example (flat structure):
{
"price": 0.0378,
"sender": "CALISERO",
"sentAt": "2025-09-19T11:59:44.000000Z",
"status": "sent",
"messageId": "019961d8-3338-700c-be17-10d061f03a5c",
"recipient": "+40742***350",
"scheduleAt": "2025-09-19T11:59:42.000000Z",
"deliveredAt": null,
"remainingBalance": 999.43
}
When the same message is later delivered you will receive another webhook with:
{
"price": 0.0378,
"sender": "CALISERO",
"sentAt": "2025-09-19T11:59:44.000000Z",
"status": "delivered",
"messageId": "019961d8-3338-700c-be17-10d061f03a5c",
"recipient": "+40742***350",
"scheduleAt": "2025-09-19T11:59:42.000000Z",
"deliveredAt": "2025-09-19T12:00:24.000000Z",
"remainingBalance": 999.43
}
A failed attempt would have "status": "failed" and usually a deliveredAt of null.
Automatic callback_url Injection
If CALISERO_WEBHOOK_ENABLED=true, every sendSms() call without an explicit callback_url (or callbackUrl) automatically includes one pointing to the named route calisero.webhook (if registered) or a URL built from app.url + the configured path.
To override, supply your own callback_url parameter.
To disable injection, set CALISERO_WEBHOOK_ENABLED=false or omit the env variable.
Edge cases:
- If
app.urlis not set and the route helper fails, a root-relative path like/calisero/webhookis used. - Passing either
callback_urlorcallbackUrlprevents injection.
Example (override):
Calisero::sendSms([ 'to' => '+1234567890', 'text' => 'Custom callback', 'callback_url' => 'https://example.com/custom-hook', ]);
Artisan Commands
The package provides several Artisan commands for testing and development:
Test SMS Sending
php artisan calisero:sms:test +40712345678 --from=YourApp --text="Test message"
SMS Status
php artisan calisero:sms:status 019961d8-3338-700c-be17-10d061f03a5c
Verification Commands
Send a verification code with brand:
php artisan calisero:verification:send +40712345678 --brand=MyApp
Send a verification code with custom template:
php artisan calisero:verification:send +40712345678 --template="Your code is {code}" --expires-in=5
Check a verification code:
php artisan calisero:verification:check +40712345678 123456
Webhook Verification
Enable webhook handling by setting CALISERO_WEBHOOK_ENABLED=true. The package will register a POST endpoint at /calisero/webhook (or your configured CALISERO_WEBHOOK_PATH).
If you also set CALISERO_WEBHOOK_TOKEN=your-shared-secret, the package will:
- Automatically append
?token=your-shared-secretto the injectedcallback_urlsent to Calisero (only when you did not supply a customcallback_url). - Register a middleware that rejects any incoming webhook request not containing the correct
tokenquery parameter.
Requirements when token security is enabled:
- Each webhook request from Calisero must include the query parameter
tokenwith the exact configured value. - If you manually override
callback_url, you are responsible for including the?token=...segment yourself. - If your explicit URL already contains a
token=parameter, the library will not modify it.
Environment example:
CALISERO_WEBHOOK_ENABLED=true CALISERO_WEBHOOK_PATH=calisero/webhook CALISERO_WEBHOOK_TOKEN=super-secret-value
Example of explicit override (token already present, no modification by the library):
Calisero::sendSms([ 'to' => '+1234567890', 'text' => 'Custom secured callback', 'callback_url' => 'https://example.com/custom-hook?token=' . urlencode(config('calisero.webhook.token')), ]);
Rotation tip: rotate the token by
- Adding a temporary second endpoint (optional) or a maintenance window.
- Updating
CALISERO_WEBHOOK_TOKEN. - Redeploying and updating the callback URL in Calisero (or sending a new message to propagate the injected URL).
If you leave CALISERO_WEBHOOK_TOKEN empty, no token middleware is attached and the endpoint is publicly accessible (POST only). Consider other controls (IP allow-list, WAF) if you opt out of the token.
Listen for the events:
Event::listen(MessageSent::class, fn (MessageSent $e) => ...); Event::listen(MessageDelivered::class, fn (MessageDelivered $e) => ...); Event::listen(MessageFailed::class, fn (MessageFailed $e) => ...);
Statuses currently emitted (lifecycle):
sentโ the message was accepted and dispatched to the networkdeliveredโ the handset/network confirmed deliveryfailedโ delivery permanently failed
Webhook payload example (flat structure):
{
"price": 0.0378,
"sender": "CALISERO",
"sentAt": "2025-09-19T11:59:44.000000Z",
"status": "sent",
"messageId": "019961d8-3338-700c-be17-10d061f03a5c",
"recipient": "+40742***350",
"scheduleAt": "2025-09-19T11:59:42.000000Z",
"deliveredAt": null,
"remainingBalance": 999.43
}
When the same message is later delivered you will receive another webhook with:
{
"price": 0.0378,
"sender": "CALISERO",
"sentAt": "2025-09-19T11:59:44.000000Z",
"status": "delivered",
"messageId": "019961d8-3338-700c-be17-10d061f03a5c",
"recipient": "+40742***350",
"scheduleAt": "2025-09-19T11:59:42.000000Z",
"deliveredAt": "2025-09-19T12:00:24.000000Z",
"remainingBalance": 999.43
}
A failed attempt would have "status": "failed" and usually a deliveredAt of null.
Automatic callback_url Injection
If CALISERO_WEBHOOK_ENABLED=true, every sendSms() call without an explicit callback_url (or callbackUrl) automatically includes one pointing to the named route calisero.webhook (if registered) or a URL built from app.url + the configured path.
To override, supply your own callback_url parameter.
To disable injection, set CALISERO_WEBHOOK_ENABLED=false or omit the env variable.
Edge cases:
- If
app.urlis not set and the route helper fails, a root-relative path like/calisero/webhookis used. - Passing either
callback_urlorcallbackUrlprevents injection.
Example (override):
Calisero::sendSms([ 'to' => '+1234567890', 'text' => 'Custom callback', 'callback_url' => 'https://example.com/custom-hook', ]);
Artisan Commands
The package provides several Artisan commands for testing and development:
Test SMS Sending
php artisan calisero:sms:test +40712345678 --from=YourApp --text="Test message"
SMS Status
php artisan calisero:sms:status 019961d8-3338-700c-be17-10d061f03a5c
Verification Commands
Send a verification code with brand:
php artisan calisero:verification:send +40712345678 --brand=MyApp
Send a verification code with custom template:
php artisan calisero:verification:send +40712345678 --template="Your code is {code}" --expires-in=5
Check a verification code:
php artisan calisero:verification:check +40712345678 123456
Webhook Verification
Enable webhook handling by setting CALISERO_WEBHOOK_ENABLED=true. The package will register a POST endpoint at /calisero/webhook (or your configured CALISERO_WEBHOOK_PATH).
If you also set CALISERO_WEBHOOK_TOKEN=your-shared-secret, the package will:
- Automatically append
?token=your-shared-secretto the injectedcallback_urlsent to Calisero (only when you did not supply a customcallback_url). - Register a middleware that rejects any incoming webhook request not containing the correct
tokenquery parameter.
Requirements when token security is enabled:
- Each webhook request from Calisero must include the query parameter
tokenwith the exact configured value. - If you manually override
callback_url, you are responsible for including the?token=...segment yourself. - If your explicit URL already contains a
token=parameter, the library will not modify it.
Environment example:
CALISERO_WEBHOOK_ENABLED=true CALISERO_WEBHOOK_PATH=calisero/webhook CALISERO_WEBHOOK_TOKEN=super-secret-value
Example of explicit override (token already present, no modification by the library):
Calisero::sendSms([ 'to' => '+1234567890', 'text' => 'Custom secured callback', 'callback_url' => 'https://example.com/custom-hook?token=' . urlencode(config('calisero.webhook.token')), ]);
Rotation tip: rotate the token by
- Adding a temporary second endpoint (optional) or a maintenance window.
- Updating
CALISERO_WEBHOOK_TOKEN. - Redeploying and updating the callback URL in Calisero (or sending a new message to propagate the injected URL).
If you leave CALISERO_WEBHOOK_TOKEN empty, no token middleware is attached and the endpoint is publicly accessible (POST only). Consider other controls (IP allow-list, WAF) if you opt out of the token.
Listen for the events:
Event::listen(MessageSent::class, fn (MessageSent $e) => ...); Event::listen(MessageDelivered::class, fn (MessageDelivered $e) => ...); Event::listen(MessageFailed::class, fn (MessageFailed $e) => ...);
Statuses currently emitted (lifecycle):
sentโ the message was accepted and dispatched to the networkdeliveredโ the handset/network confirmed deliveryfailedโ delivery permanently failed
Webhook payload example (flat structure):
{
"price": 0.0378,
"sender": "CALISERO",
"sentAt": "2025-09-19T11:59:44.000000Z",
"status": "sent",
"messageId": "019961d8-3338-700c-be17-10d061f03a5c",
"recipient": "+40742***350",
"scheduleAt": "2025-09-19T11:59:42.000000Z",
"deliveredAt": null,
"remainingBalance": 999.43
}
When the same message is later delivered you will receive another webhook with:
{
"price": 0.0378,
"sender": "CALISERO",
"sentAt": "2025-09-19T11:59:44.000000Z",
"status": "delivered",
"messageId": "019961d8-3338-700c-be17-10d061f03a5c",
"recipient": "+40742***350",
"scheduleAt": "2025-09-19T11:59:42.000000Z",
"deliveredAt": "2025-09-19T12:00:24.000000Z",
"remainingBalance": 999.43
}
A failed attempt would have "status": "failed" and usually a deliveredAt of null.
Automatic callback_url Injection
If CALISERO_WEBHOOK_ENABLED=true, every sendSms() call without an explicit callback_url (or callbackUrl) automatically includes one pointing to the named route calisero.webhook (if registered) or a URL built from app.url + the configured path.
To override, supply your own callback_url parameter.
To disable injection, set CALISERO_WEBHOOK_ENABLED=false or omit the env variable.
Edge cases:
- If
app.urlis not set and the route helper fails, a root-relative path like/calisero/webhookis used. - Passing either
callback_urlorcallbackUrlprevents injection.
Example (override):
Calisero::sendSms([ 'to' => '+1234567890', 'text' => 'Custom callback', 'callback_url' => 'https://example.com/custom-hook', ]);
Artisan Commands
The package provides several Artisan commands for testing and development:
Test SMS Sending
php artisan calisero:sms:test +40712345678 --from=YourApp --text="Test message"
SMS Status
php artisan calisero:sms:status 019961d8-3338-700c-be17-10d061f03a5c
Verification Commands
Send a verification code with brand:
php artisan calisero:verification:send +40712345678 --brand=MyApp
Send a verification code with custom template:
php artisan calisero:verification:send +40712345678 --template="Your code is {code}" --expires-in=5
Check a verification code:
php artisan calisero:verification:check +40712345678 123456
Webhook Verification
Enable webhook handling by setting CALISERO_WEBHOOK_ENABLED=true. The package will register a POST endpoint at /calisero/webhook (or your configured CALISERO_WEBHOOK_PATH).
If you also set CALISERO_WEBHOOK_TOKEN=your-shared-secret, the package will:
- Automatically append
?token=your-shared-secretto the injectedcallback_urlsent to Calisero (only when you did not supply a customcallback_url). - Register a middleware that rejects any incoming webhook request not containing the correct
tokenquery parameter.
Requirements when token security is enabled:
- Each webhook request from Calisero must include the query parameter
tokenwith the exact configured value. - If you manually override
callback_url, you are responsible for including the?token=...segment yourself. - If your explicit URL already contains a
token=parameter, the library will not modify it.
Environment example:
CALISERO_WEBHOOK_ENABLED=true CALISERO_WEBHOOK_PATH=calisero/webhook CALISERO_WEBHOOK_TOKEN=super-secret-value
Example of explicit override (token already present, no modification by the library):
Calisero::sendSms([ 'to' => '+1234567890', 'text' => 'Custom secured callback', 'callback_url' => 'https://example.com/custom-hook?token=' . urlencode(config('calisero.webhook.token')), ]);
Rotation tip: rotate the token by
- Adding a temporary second endpoint (optional) or a maintenance window.
- Updating
CALISERO_WEBHOOK_TOKEN. - Redeploying and updating the callback URL in Calisero (or sending a new message to propagate the injected URL).
If you leave CALISERO_WEBHOOK_TOKEN empty, no token middleware is attached and the endpoint is publicly accessible (POST only). Consider other controls (IP allow-list, WAF) if you opt out of the token.
Listen for the events:
Event::listen(MessageSent::class, fn (MessageSent $e) => ...); Event::listen(MessageDelivered::class, fn (MessageDelivered $e) => ...); Event::listen(MessageFailed::class, fn (MessageFailed $e) => ...);
Statuses currently emitted (lifecycle):
sentโ the message was accepted and dispatched to the networkdeliveredโ the handset/network confirmed deliveryfailedโ delivery permanently failed
Webhook payload example (flat structure):
{
"price": 0.0378,
"sender": "CALISERO",
"sentAt": "2025-09-19T11:59:44.000000Z",
"status": "sent",
"messageId": "019961d8-3338-700c-be17-10d061f03a5c",
"recipient": "+40742***350",
"scheduleAt": "2025-09-19T11:59:42.000000Z",
"deliveredAt": null,
"remainingBalance": 999.43
}
When the same message is later delivered you will receive another webhook with:
{
"price": 0.0378,
"sender": "CALISERO",
"sentAt": "2025-09-19T11:59:44.000000Z",
"status": "delivered",
"messageId": "019961d8-3338-700c-be17-10d061f03a5c",
"recipient": "+40742***350",
"scheduleAt": "2025-09-19T11:59:42.000000Z",
"deliveredAt": "2025-09-19T12:00:24.000000Z",
"remainingBalance": 999.43
}
A failed attempt would have "status": "failed" and usually a deliveredAt of null.
Automatic callback_url Injection
If CALISERO_WEBHOOK_ENABLED=true, every sendSms() call without an explicit callback_url (or callbackUrl) automatically includes one pointing to the named route calisero.webhook (if registered) or a URL built from app.url + the configured path.
To override, supply your own callback_url parameter.
To disable injection, set CALISERO_WEBHOOK_ENABLED=false or omit the env variable.
Edge cases:
- If
app.urlis not set and the route helper fails, a root-relative path like/calisero/webhookis used. - Passing either
callback_urlorcallbackUrlprevents injection.
Example (override):
Calisero::sendSms([ 'to' => '+1234567890', 'text' => 'Custom callback', 'callback_url' => 'https://example.com/custom-hook', ]);
Artisan Commands
The package provides several Artisan commands for testing and development:
Test SMS Sending
php artisan calisero:sms:test +40712345678 --from=YourApp --text="Test message"
SMS Status
php artisan calisero:sms:status 019961d8-3338-700c-be17-10d061f03a5c
Verification Commands
Send a verification code with brand:
php artisan calisero:verification:send +40712345678 --brand=MyApp
Send a verification code with custom template:
php artisan calisero:verification:send +40712345678 --template="Your code is {code}" --expires-in=5
Check a verification code:
php artisan calisero:verification:check +40712345678 123456
Webhook Verification
Enable webhook handling by setting CALISERO_WEBHOOK_ENABLED=true. The package will register a POST endpoint at /calisero/webhook (or your configured CALISERO_WEBHOOK_PATH).
If you also set CALISERO_WEBHOOK_TOKEN=your-shared-secret, the package will:
- Automatically append
?token=your-shared-secretto the injectedcallback_urlsent to Calisero (only when you did not supply a customcallback_url). - Register a middleware that rejects any incoming webhook request not containing the correct
tokenquery parameter.
Requirements when token security is enabled:
- Each webhook request from Calisero must include the query parameter
tokenwith the exact configured value. - If you manually override
callback_url, you are responsible for including the?token=...segment yourself. - If your explicit URL already contains a
token=parameter, the library will not modify it.
Environment example:
CALISERO_WEBHOOK_ENABLED=true CALISERO_WEBHOOK_PATH=calisero/webhook CALISERO_WEBHOOK_TOKEN=super-secret-value
Example of explicit override (token already present, no modification by the library):
Calisero::sendSms([ 'to' => '+1234567890', 'text' => 'Custom secured callback', 'callback_url' => 'https://example.com/custom-hook?token=' . urlencode(config('calisero.webhook.token')), ]);
Rotation tip: rotate the token by
- Adding a temporary second endpoint (optional) or a maintenance window.
- Updating
CALISERO_WEBHOOK_TOKEN. - Redeploying and updating the callback URL in Calisero (or sending a new message to propagate the injected URL).
If you leave CALISERO_WEBHOOK_TOKEN empty, no token middleware is attached and the endpoint is publicly accessible (POST only). Consider other controls (IP allow-list, WAF) if you opt out of the token.
Listen for the events:
Event::listen(MessageSent::class, fn (MessageSent $e) => ...); Event::listen(MessageDelivered::class, fn (MessageDelivered $e) => ...); Event::listen(MessageFailed::class, fn (MessageFailed $e) => ...);
Statuses currently emitted (lifecycle):
sentโ the message was accepted and dispatched to the networkdeliveredโ the handset/network confirmed deliveryfailedโ delivery permanently failed
Webhook payload example (flat structure):
{
"price": 0.0378,
"sender": "CALISERO",
"sentAt": "2025-09-19T11:59:44.000000Z",
"status": "sent",
"messageId": "019961d8-3338-700c-be17-10d061f03a5c",
"recipient": "+40742***350",
"scheduleAt": "2025-09-19T11:59:42.000000Z",
"deliveredAt": null,
"remainingBalance": 999.43
}
When the same message is later delivered you will receive another webhook with:
{
"price": 0.0378,
"sender": "CALISERO",
"sentAt": "2025-09-19T11:59:44.000000Z",
"status": "delivered",
"messageId": "019961d8-3338-700c-be17-10d061f03a5c",
"recipient": "+40742***350",
"scheduleAt": "2025-09-19T11:59:42.000000Z",
"deliveredAt": "2025-09-19T12:00:24.000000Z",
"remainingBalance": 999.43
}
A failed attempt would have "status": "failed" and usually a deliveredAt of null.
Automatic callback_url Injection
If CALISERO_WEBHOOK_ENABLED=true, every sendSms() call without an explicit callback_url (or callbackUrl) automatically includes one pointing to the named route calisero.webhook (if registered) or a URL built from app.url + the configured path.
To override, supply your own callback_url parameter.
To disable injection, set `CALISERO_WEBHOOK_ENABLED=false