A lightweight SMS notification plugin for WordPress, WooCommerce, and Dokan.
- Description
- Installation
- Development Setup
- Developer Documentation
- Frequently Asked Questions
- Changelog
Texty is a lightweight SMS notification plugin for WordPress. With so many emails coming to your inbox, often it becomes overwhelming to stay on top of things that matter most. A text notification on your phone may be more desired.
Texty integrates with 3rd party SMS providers to add support for text messaging. When events occur in WordPress, WooCommerce, or Dokan, Texty sends an SMS notification to the right people at the right time.
- Multiple SMS Gateways - Choose from Twilio, Vonage, Plivo, or Clickatell
- WordPress Notifications - New user registration and new comment alerts
- WooCommerce Notifications - Admin and customer order status notifications
- Dokan Notifications - Vendor order status notifications
- Customizable Messages - Use template tags to personalize every SMS
- Developer Friendly - 20+ action and filter hooks for full extensibility
- Twilio
- Vonage - Formerly Nexmo
- Plivo
- Clickatell
WordPress Core
- New User Registration
- New Comment
WooCommerce (Admin) - Processing, Complete, Cancelled, Failed, Refunded
WooCommerce (Customer) - On Hold, Processing, Complete, Cancelled, Failed, Refunded
Dokan (Vendor) - Processing, Complete, Cancelled, Failed, Refunded
- WordPress 6.8 or greater
- PHP 7.4 or greater
- Go to Plugins > Add New in your WordPress admin
- Search for "Texty"
- Click Install Now and then Activate
- Download the plugin zip file
- Extract and upload the
textyfolder to/wp-content/plugins/ - Activate the plugin from the Plugins page
- Navigate to Texty > Settings in your WordPress admin
- Select your SMS gateway and enter the API credentials
- Go to the Notifications tab to enable and configure alerts
- Use the Tools tab to send a test SMS
# Clone the repository into your WordPress plugins directory
cd /path/to/wordpress/wp-content/plugins
git clone https://github.com/getdokan/texty.git
cd texty
# Install PHP dependencies
composer install
# Install Node.js dependencies
npm install# Build assets for production
npm run build
# Start development mode with file watching
npm run start| Command | Description |
|---|---|
npm run build |
Build production assets |
npm run start |
Start development mode with file watching |
npm run clean |
Remove the dist directory |
npm run makepot |
Generate translation POT file |
npm run pot2json |
Convert POT file to JSON for JavaScript translations |
npm run readme |
Convert readme.txt to readme.md |
composer phpcs |
Run PHP CodeSniffer |
composer phpcbf |
Auto-fix PHP CodeSniffer issues |
texty/
├── includes/ # PHP source files
│ ├── Gateways/ # SMS gateway implementations
│ ├── Integrations/ # Third-party plugin integrations (WooCommerce, Dokan)
│ ├── Notifications/ # Notification types (WP, WC, Dokan)
│ ├── Dispatcher.php # Event dispatcher
│ ├── Gateways.php # Gateway manager/registry
│ ├── Notifications.php # Notification manager/registry
│ └── Settings.php # Plugin settings
├── src/ # JavaScript/React source files
├── dist/ # Built assets (generated)
├── dependencies/ # Mozart-managed PHP dependencies
├── languages/ # Translation files
├── texty.php # Plugin entry point
├── composer.json
└── package.json
Texty provides a comprehensive set of action and filter hooks that allow developers to extend every aspect of the plugin — from custom gateways and notifications to message modification and send pipeline control.
- Send Pipeline Hooks
- Notification Hooks
- Gateway Registry
- Notification Registry
- Integration Registry
- Settings Hooks
- Plugin Lifecycle
- Custom Gateway Guide
- Custom Notification Guide
- Hooks Reference
These hooks wrap every SMS sent through the Gateways::send() method, giving you control over the entire send lifecycle.
Modify the recipient phone number before sending.
add_filter( 'texty_sms_to', function ( $to, $message, $gateway ) {
// Normalize phone numbers to E.164 format
if ( strpos( $to, '+' ) !== 0 ) {
$to = '+1' . $to;
}
return $to;
}, 10, 3 );Parameters:
$to(string) — The recipient phone number$message(string) — The message body$gateway(GatewayInterface) — The active gateway instance
Modify the SMS body at the gateway level.
add_filter( 'texty_sms_message', function ( $message, $to, $gateway ) {
// Append a signature to all outgoing SMS
return $message . "\n— Sent via " . get_bloginfo( 'name' );
}, 10, 3 );Parameters:
$message(string) — The message body$to(string) — The recipient phone number$gateway(GatewayInterface) — The active gateway instance
Short-circuit SMS sending. Return a non-null value to skip the gateway call entirely.
add_filter( 'texty_pre_send_sms', function ( $pre_send, $to, $message, $gateway ) {
// Block SMS to specific numbers
$blocked = [ '+1234567890' ];
if ( in_array( $to, $blocked, true ) ) {
return new \WP_Error( 'blocked', 'This number is blocked.' );
}
return $pre_send; // Return null to continue sending
}, 10, 4 );Parameters:
$pre_send(null|mixed) — Return non-null to short-circuit$to(string) — The recipient phone number$message(string) — The message body$gateway(GatewayInterface) — The active gateway instance
Fires immediately before each SMS is sent. Useful for logging, rate limiting, or analytics.
add_action( 'texty_before_send_sms', function ( $to, $message, $gateway ) {
error_log( sprintf( 'Texty: Sending SMS to %s via %s', $to, $gateway->name() ) );
}, 10, 3 );Parameters:
$to(string) — The recipient phone number$message(string) — The message body$gateway(GatewayInterface) — The active gateway instance
Fires after each SMS is sent, with the result.
add_action( 'texty_after_send_sms', function ( $result, $to, $message, $gateway ) {
if ( is_wp_error( $result ) ) {
error_log( 'Texty: SMS failed - ' . $result->get_error_message() );
} else {
error_log( 'Texty: SMS sent successfully to ' . $to );
}
}, 10, 4 );Parameters:
$result(bool|WP_Error) — The send result$to(string) — The recipient phone number$message(string) — The message body$gateway(GatewayInterface) — The active gateway instance
Fires specifically when SMS sending returns a WP_Error.
add_action( 'texty_send_sms_failed', function ( $error, $to, $message, $gateway ) {
// Send failure alert to admin or log to external service
do_action( 'my_sms_failure_handler', $error, $to );
}, 10, 4 );Parameters:
$error(WP_Error) — The error object$to(string) — The recipient phone number$message(string) — The message body$gateway(GatewayInterface) — The active gateway instance
These hooks let you modify recipients, message content, and the notification lifecycle.
Modify the list of recipients for any notification.
add_filter( 'texty_notification_recipients', function ( $recipients, $notification ) {
// Add an extra number for WooCommerce admin notifications
if ( $notification->get_group() === 'wc' && $notification->get_type() === 'role' ) {
$recipients[] = '+1987654321';
}
return $recipients;
}, 10, 2 );Parameters:
$recipients(array) — Phone numbers$notification(Notification) — The notification instance
Modify the message content for all notifications.
add_filter( 'texty_notification_message', function ( $content, $notification ) {
return $content . "\n[" . $notification->get_id() . ']';
}, 10, 2 );Parameters:
$content(string) — The message content$notification(Notification) — The notification instance
Modify the message for a specific notification type. Replace {$id} with the notification ID (e.g., texty_notification_message_registration for the new user registration).
add_filter( 'texty_notification_message_registration', function ( $content, $notification ) {
return 'Welcome! ' . $content;
}, 10, 2 );Parameters:
$content(string) — The message content$notification(Notification) — The notification instance
Fires before the notification send loop begins.
add_action( 'texty_before_notification', function ( $notification, $recipients, $content ) {
error_log( sprintf(
'Texty: Sending "%s" notification to %d recipients',
$notification->get_id(),
count( $recipients )
) );
}, 10, 3 );Parameters:
$notification(Notification) — The notification instance$recipients(array) — Phone numbers$content(string) — The message content
Fires after the notification send loop completes.
add_action( 'texty_after_notification', function ( $notification, $recipients, $content ) {
// Track notification completion
}, 10, 3 );Parameters:
$notification(Notification) — The notification instance$recipients(array) — Phone numbers$content(string) — The message content
Override the enabled state of any notification.
add_filter( 'texty_notification_enabled', function ( $enabled, $id, $notification ) {
// Force-enable registration notifications
if ( $id === 'registration' ) {
return true;
}
return $enabled;
}, 10, 3 );Parameters:
$enabled(bool) — Whether the notification is enabled$id(string) — The notification ID$notification(Notification) — The notification instance
Modify the settings for any notification.
add_filter( 'texty_notification_settings', function ( $settings, $id, $notification ) {
// Override message for a specific notification
if ( $id === 'comment' ) {
$settings['message'] = 'New comment on your site!';
}
return $settings;
}, 10, 3 );Parameters:
$settings(array) — The notification settings$id(string) — The notification ID$notification(Notification) — The notification instance
Register custom SMS gateways. This is the recommended way to add new gateways.
add_action( 'texty_register_gateways', function ( $manager ) {
$manager->register( 'my_gateway', \MyPlugin\Gateways\MyGateway::class );
} );Parameters:
$manager(Gateways) — The gateway manager instance
See Custom Gateway Guide for a complete example.
Modify a gateway instance after it is created.
add_filter( 'texty_gateway_instance', function ( $instance, $gateway_key ) {
// Wrap the gateway with a decorator for logging
return $instance;
}, 10, 2 );Parameters:
$instance(GatewayInterface) — The gateway instance$gateway_key(string) — The gateway identifier
Filter the final list of available gateways. This is the legacy approach — prefer texty_register_gateways for new code.
add_filter( 'texty_available_gateways', function ( $gateways ) {
// Remove a built-in gateway
unset( $gateways['clickatell'] );
return $gateways;
} );Register custom notification types.
add_action( 'texty_register_notifications', function ( $manager ) {
$manager->register( 'my_event', \MyPlugin\Notifications\MyEvent::class );
} );Parameters:
$manager(Notifications) — The notifications manager instance
See Custom Notification Guide for a complete example.
Filter the final list of available notifications. This is the legacy approach — prefer texty_register_notifications for new code.
Fires during plugin initialization. Use this to bootstrap custom integrations.
add_action( 'texty_register_integrations', function () {
if ( class_exists( 'EDD' ) ) {
new \MyPlugin\Integrations\EasyDigitalDownloads();
}
} );Modify any settings value. Useful for loading credentials from environment variables.
add_filter( 'texty_setting', function ( $value, $key ) {
// Load Twilio credentials from environment
if ( $key === 'twilio' ) {
$sid = getenv( 'TEXTY_TWILIO_SID' );
$token = getenv( 'TEXTY_TWILIO_TOKEN' );
$from = getenv( 'TEXTY_TWILIO_FROM' );
if ( $sid && $token && $from ) {
return [
'sid' => $sid,
'token' => $token,
'from' => $from,
];
}
}
return $value;
}, 10, 2 );Parameters:
$value(mixed) — The setting value$key(string) — The setting key
Override the active gateway.
add_filter( 'texty_active_gateway_name', function ( $gateway ) {
// Force Twilio in production
if ( wp_get_environment_type() === 'production' ) {
return 'twilio';
}
return $gateway;
} );Parameters:
$gateway(string|false) — The active gateway identifier
Fires after the Texty plugin is fully initialized. Safe to use Texty APIs after this hook.
add_action( 'texty_loaded', function ( $texty ) {
// Plugin is ready, initialize your Texty extension
} );Parameters:
$texty(Texty) — The main plugin instance
To add a custom SMS gateway, implement the GatewayInterface and register it:
<?php
namespace MyPlugin\Gateways;
use Texty\Gateways\GatewayInterface;
class MyGateway implements GatewayInterface {
public function send( $to, $message ) {
$response = wp_remote_post( 'https://api.mygateway.com/send', [
'body' => [
'to' => $to,
'message' => $message,
'api_key' => $this->get_api_key(),
],
] );
if ( is_wp_error( $response ) ) {
return $response;
}
$code = wp_remote_retrieve_response_code( $response );
if ( $code !== 200 ) {
return new \WP_Error(
'mygateway_error',
'MyGateway API returned status ' . $code
);
}
return true;
}
public function logo() {
return 'https://example.com/logo.svg';
}
public function name() {
return 'MyGateway';
}
public function description() {
return 'Send SMS via MyGateway API.';
}
public function get_settings() {
return texty()->settings()->get( 'mygateway' );
}
public function validate( $request ) {
$creds = $request->get_param( 'mygateway' );
if ( empty( $creds['api_key'] ) ) {
return new \WP_Error(
'mygateway_api_key',
__( 'API key is required.', 'my-plugin' )
);
}
return true;
}
private function get_api_key() {
$settings = $this->get_settings();
return $settings['api_key'] ?? '';
}
}Register the gateway:
add_action( 'texty_register_gateways', function ( $manager ) {
$manager->register( 'mygateway', \MyPlugin\Gateways\MyGateway::class );
} );To add a custom notification, extend the Notification base class:
<?php
namespace MyPlugin\Notifications;
use Texty\Notifications\Notification;
class LowStock extends Notification {
protected $title = 'Low Stock Alert';
protected $id = 'low_stock';
protected $group = 'wc';
protected $default = 'Low stock alert: {product_name} has only {stock_qty} left.';
protected $default_recipients = [ 'administrator' ];
private $product;
public function set_product( $product ) {
$this->product = $product;
return $this;
}
public function get_message() {
$message = parent::get_message_raw();
if ( $this->product ) {
$message = str_replace( '{product_name}', $this->product->get_name(), $message );
$message = str_replace( '{stock_qty}', $this->product->get_stock_quantity(), $message );
}
return $this->replace_global_keys( $message );
}
public function get_recipients() {
return $this->get_numbers_by_roles();
}
public function replacement_keys() {
return [
'product_name' => 'get_name',
'stock_qty' => 'get_stock_quantity',
];
}
}Register the notification and add the notification group:
add_action( 'texty_register_notifications', function ( $manager ) {
$manager->register( 'low_stock', \MyPlugin\Notifications\LowStock::class );
} );
// If using a custom group, register it
add_filter( 'texty_notification_groups', function ( $groups ) {
$groups['inventory'] = [
'title' => __( 'Inventory', 'my-plugin' ),
'description' => '',
'available' => class_exists( 'WooCommerce' ),
];
return $groups;
} );Trigger the notification from your code:
$class = texty()->notifications()->get( 'low_stock' );
$notifier = new $class();
$notifier->set_product( $product );
$notifier->send();| Hook | Type | Location | Purpose |
|---|---|---|---|
texty_sms_to |
filter | Gateways::send() |
Modify recipient number |
texty_sms_message |
filter | Gateways::send() |
Modify SMS body |
texty_pre_send_sms |
filter | Gateways::send() |
Short-circuit sending |
texty_before_send_sms |
action | Gateways::send() |
Before each SMS |
texty_after_send_sms |
action | Gateways::send() |
After each SMS with result |
texty_send_sms_failed |
action | Gateways::send() |
On send failure |
texty_register_gateways |
action | Gateways::all() |
Register custom gateways |
texty_gateway_instance |
filter | Gateways::active_gateway() |
Modify gateway instance |
texty_available_gateways |
filter | Gateways::all() |
Filter gateway list (legacy) |
texty_notification_recipients |
filter | Notification::send() |
Modify recipients |
texty_notification_message |
filter | Notification::send() |
Modify notification message |
texty_notification_message_{$id} |
filter | Notification::send() |
Modify specific notification |
texty_before_notification |
action | Notification::send() |
Before send loop |
texty_after_notification |
action | Notification::send() |
After send loop |
texty_notification_enabled |
filter | Notification::enabled() |
Override enabled state |
texty_notification_settings |
filter | Notification::settings() |
Modify notification config |
texty_register_notifications |
action | Notifications::all() |
Register custom notifications |
texty_available_notifications |
filter | Notifications::all() |
Filter notification list (legacy) |
texty_register_integrations |
action | Dispatcher::__construct() |
Register custom integrations |
texty_setting |
filter | Settings::get() |
Modify any setting value |
texty_active_gateway_name |
filter | Settings::gateway() |
Override active gateway |
texty_loaded |
action | Texty::init_plugin() |
Plugin fully initialized |
Texty supports Twilio, Vonage (Nexmo), Plivo, and Clickatell. You can also register custom gateways via the texty_register_gateways action hook.
Yes. Texty supports admin and customer notifications for order status changes including processing, complete, on-hold, cancelled, failed, and refunded.
Yes. Dokan vendors receive SMS notifications when their order statuses change.
Yes. Implement the GatewayInterface and register your gateway using the texty_register_gateways action hook. See the Custom Gateway Guide for a full example.
Yes. Use the texty_sms_message filter to modify the SMS body, or texty_notification_message to modify notification content before sending.
- New: Added notifications for WooCommerce order statuses: cancelled, failed, and refunded
- New: Added notifications for Dokan Vendor order statuses: cancelled, failed, and refunded
- New: Added 21 action and filter hooks for developer extensibility
- New: Gateway registry system — register custom gateways via
texty_register_gateways - New: Notification registry system — register custom notifications via
texty_register_notifications - Update: Appsero updated for WordPress 6.8 compatibility
- Update: Mozart is integrated with the plugin
- Update: WordPress 6.9 compatibility added
- Update: WordPress 6.6.2 compatibility
- Update: WordPress 6.3.2 compatibility
- Fix: Fixed Appsero SDK security issue
- Fix: WordPress 6.0 compatibility
- Fix: Responsive issue in the settings panel where gateway names overflowed the viewport
- Fix: Remove duplicate numbers while sending messages
- New: Syncing of vendor phone number from Dokan during registration
- New: Added Plivo gateway
- New: Added Clickatell gateway
- New: Added Dokan integration for vendor order notifications
- New: Added
{items}shortcode for WooCommerce orders
- Initial Release