| 1 |
# Email Notification SDK |
| 2 |
|
| 3 |
A reusable WordPress plugin notification flow handler with a REST API and a built-in WhatsApp channel. |
| 4 |
|
| 5 |
## 🛠 Installation |
| 6 |
|
| 7 |
1. **Require this package via Composer** |
| 8 |
|
| 9 |
Add the following configuration to your `composer.json`: |
| 10 |
|
| 11 |
```json |
| 12 |
{ |
| 13 |
"require": { |
| 14 |
"themewinter/email-notification-sdk": "dev-main" |
| 15 |
}, |
| 16 |
"repositories": [ |
| 17 |
{ |
| 18 |
"type": "vcs", |
| 19 |
"url": "https://github.com/themewinter/email-notification-sdk" |
| 20 |
} |
| 21 |
] |
| 22 |
} |
| 23 |
``` |
| 24 |
If you do not have Composer installed in your plugin, initialize it with: |
| 25 |
|
| 26 |
```bash |
| 27 |
composer init |
| 28 |
``` |
| 29 |
|
| 30 |
2. **Update Dependencies** |
| 31 |
|
| 32 |
```bash |
| 33 |
composer update |
| 34 |
composer dump-autoload |
| 35 |
``` |
| 36 |
|
| 37 |
3. **On composer update process, if you are asked to provide a token, follow these steps to generate one:** |
| 38 |
- Go to GitHub: https://github.com |
| 39 |
- Login to your account. |
| 40 |
- Navigate to Settings: |
| 41 |
- Click your profile picture (top right) → Settings |
| 42 |
- Access Developer Settings: |
| 43 |
- Scroll down in the left sidebar → Click Developer settings |
| 44 |
- Personal access tokens → Tokens (classic): |
| 45 |
- Click Personal access tokens, then choose Tokens (classic) |
| 46 |
- Click "Generate new token" → "Generate new token (classic)" |
| 47 |
- Set token details: |
| 48 |
- Note: Give your token a name (e.g., "Git CLI access") |
| 49 |
- Expiration: Choose an expiry time (e.g., 30 days or "No expiration") |
| 50 |
- Scopes: Select the permissions you need, for example: |
| 51 |
- repo (full control of private repositories) |
| 52 |
- workflow (for GitHub Actions) |
| 53 |
- read:org (if needed for organization access) |
| 54 |
- user (for profile info) |
| 55 |
- Click Generate Token |
| 56 |
- Copy the token immediately — it won't be shown again! |
| 57 |
|
| 58 |
## Configuration |
| 59 |
|
| 60 |
1. **In your plugin's main file, add this initialization code. Make sure it runs after all of your scripts have been enqueued.** |
| 61 |
|
| 62 |
```php |
| 63 |
if (file_exists(plugin_dir_path( __FILE__ ) . '/vendor/autoload.php')) { |
| 64 |
require_once plugin_dir_path( __FILE__ ) . '/vendor/autoload.php'; |
| 65 |
} |
| 66 |
|
| 67 |
if ( class_exists( \ENS\Core\Sdk::class ) ) { |
| 68 |
\ENS\Core\Sdk::get_instance() |
| 69 |
->setup([ |
| 70 |
'plugin_name' => 'Eventin', |
| 71 |
'plugin_slug' => 'eventin', |
| 72 |
'general_prefix' => 'eve', |
| 73 |
'text_domain' => 'eventin', |
| 74 |
'admin_script_handler' => 'etn-dashboard', |
| 75 |
'sub_menu_filter_hook' => 'eventin_menu', |
| 76 |
'sub_menu_details' => [ |
| 77 |
'title' => __( 'Automation', 'eventin' ), |
| 78 |
'capability' => 'manage_options', |
| 79 |
'url' => 'admin.php?page=' . 'eventin' . '#/automation', |
| 80 |
'position' => 10, |
| 81 |
], |
| 82 |
]) |
| 83 |
->init(); |
| 84 |
|
| 85 |
// here in "ens_eve_available_actions" "eve" is the general_prefix you have sent in the package earlier |
| 86 |
add_filter( 'ens_eve_available_actions', function ( $actions ) { |
| 87 |
$actions = [ // Array of all actions, on which you want to send email |
| 88 |
[ |
| 89 |
"trigger_label" => "Event Created", // Name of the event |
| 90 |
"trigger_value" => "event_created", // Event slug |
| 91 |
"trigger_data" => [ // Data you have after the event happened |
| 92 |
[ |
| 93 |
"label" => "Event Name", |
| 94 |
"value" => "event_name", |
| 95 |
"type" => "string", |
| 96 |
], |
| 97 |
[ |
| 98 |
"label" => "Event Date", |
| 99 |
"value" => "event_date", |
| 100 |
"type" => "date", |
| 101 |
], |
| 102 |
[ |
| 103 |
"label" => "User Email", |
| 104 |
"value" => "user_email", |
| 105 |
"type" => "string", |
| 106 |
] |
| 107 |
], |
| 108 |
"conditional_dependencies" => [ // Data you have after the event happened |
| 109 |
[ |
| 110 |
"label" => "Event Title", |
| 111 |
"value" => "event_title", |
| 112 |
"type" => "string", |
| 113 |
], |
| 114 |
], |
| 115 |
"delay_dependencies" => [ |
| 116 |
[ |
| 117 |
"label" => "Event Date", |
| 118 |
"value" => "event_date", |
| 119 |
], |
| 120 |
], |
| 121 |
"email_receivers" => [ |
| 122 |
[ |
| 123 |
"label" => "User Email", |
| 124 |
"value" => "user_email", |
| 125 |
] |
| 126 |
], |
| 127 |
], |
| 128 |
[ |
| 129 |
"trigger_label" => "Event Rescheduled", |
| 130 |
"trigger_value" => "event_rescheduled", |
| 131 |
"trigger_data" => [ |
| 132 |
[ |
| 133 |
"label" => "Event Name", |
| 134 |
"value" => "event_name", |
| 135 |
"type" => "string", |
| 136 |
], |
| 137 |
[ |
| 138 |
"label" => "Event Date", |
| 139 |
"value" => "event_date", |
| 140 |
"type" => "date", |
| 141 |
], |
| 142 |
[ |
| 143 |
"label" => "User Email", |
| 144 |
"value" => "user_email", |
| 145 |
"type" => "string", |
| 146 |
] |
| 147 |
], |
| 148 |
"delay_dependencies" => [ |
| 149 |
[ |
| 150 |
"label" => "Event Date", |
| 151 |
"value" => "before_event_date", // use before_ or after_ prefix according to your needs |
| 152 |
], |
| 153 |
], |
| 154 |
"email_receivers" => [ |
| 155 |
[ |
| 156 |
"label" => "User Email", |
| 157 |
"value" => "user_email", |
| 158 |
] |
| 159 |
], |
| 160 |
], |
| 161 |
]; |
| 162 |
|
| 163 |
return $actions; |
| 164 |
} ); |
| 165 |
} |
| 166 |
``` |
| 167 |
|
| 168 |
## Firing a notification event |
| 169 |
|
| 170 |
In the function where you handle the event, fire the SDK hook. The hook name is `{hook_prefix}_gln_hook` — replace `{hook_prefix}` with the value you passed to `setup()` (or with `general_prefix` if `hook_prefix` is omitted). Example for Eventin (no `hook_prefix`, falls back to `general_prefix = eve`): |
| 171 |
|
| 172 |
```php |
| 173 |
do_action( 'eve_gln_hook', 'event_rescheduled', [ |
| 174 |
'user_email' => '[email protected]', // user email |
| 175 |
'event_name' => 'World cup cricket', //event name |
| 176 |
'event_date_timestamp' => '17023658981', //timestamp of event date, use wp_timezone() function while converting into timestamp |
| 177 |
// here event_date is the key of your delay dependency. |
| 178 |
'event_title' => 'Test title' |
| 179 |
] ); |
| 180 |
``` |
| 181 |
|
| 182 |
## WhatsApp Channel |
| 183 |
|
| 184 |
The SDK ships built-in WhatsApp delivery via the Meta WhatsApp Cloud API. Any flow node with `name: "whatsapp"` (created from the frontend builder's "Send WhatsApp Message" action) is dispatched automatically alongside email actions — no extra wiring required. |
| 185 |
|
| 186 |
### Credentials |
| 187 |
|
| 188 |
The SDK does not ship a settings UI. The consumer plugin owns credential storage and must supply them via the `{hook_prefix}_ens_whatsapp_credentials` filter (see below) or by writing the option `{general_prefix}_ens_whatsapp_settings` itself (the built-in `MetaCloudProvider` reads it as a default before applying the filter). |
| 189 |
|
| 190 |
The built-in Meta provider expects this shape: |
| 191 |
|
| 192 |
```php |
| 193 |
[ |
| 194 |
'access_token' => '…', // Meta Cloud API permanent access token |
| 195 |
'phone_number_id' => '…', // registered sender phone number ID |
| 196 |
'business_id' => '…', // optional, for reference |
| 197 |
'admin_number' => '…', // optional admin recipient (E.164) |
| 198 |
] |
| 199 |
``` |
| 200 |
|
| 201 |
### Triggering WhatsApp messages |
| 202 |
|
| 203 |
Whenever you fire `do_action( '{hook_prefix}_gln_hook', $action, $hook_data )`, include the recipient phone numbers in `$hook_data` under the same keys the frontend builder exposes (`customer_number` and `admin_number`): |
| 204 |
|
| 205 |
```php |
| 206 |
do_action( 'eve_gln_hook', 'event_rescheduled', [ |
| 207 |
'user_email' => '[email protected]', // existing email recipient |
| 208 |
'admin_email' => get_option( 'admin_email' ), // existing email recipient |
| 209 |
'customer_number' => '+8801712345678', // WhatsApp recipient (E.164) |
| 210 |
'admin_number' => '+8801799999999', // WhatsApp recipient (E.164) |
| 211 |
'event_name' => 'World cup cricket', |
| 212 |
'event_title' => 'Test title', |
| 213 |
] ); |
| 214 |
``` |
| 215 |
|
| 216 |
If a flow contains a WhatsApp node with `receiverType = customer_number`, the SDK reads `$hook_data['customer_number']` as the destination, runs `{{placeholder}}` substitution on the message body, strips HTML, and posts to Meta Cloud API. |
| 217 |
|
| 218 |
### Available filters and actions |
| 219 |
|
| 220 |
| Hook | Type | Purpose | |
| 221 |
|------|------|---------| |
| 222 |
| `{hook_prefix}_ens_whatsapp_credentials` | filter | Override stored credentials for this consumer plugin only. Returns the array `[ access_token, phone_number_id, business_id, admin_number ]`. The prefix matches the `hook_prefix` you passed to `setup()` — same prefix as `{hook_prefix}_gln_hook`. If `hook_prefix` is omitted at setup, it falls back to `general_prefix` (e.g. `wpcafe_ens_whatsapp_credentials`, `eve_ens_whatsapp_credentials`). | |
| 223 |
| `ens_whatsapp_provider` | filter | Return `true` to short-circuit the built-in Meta provider (the consumer handles transport itself by listening to the `notification_sdk_send_whatsapp` action). | |
| 224 |
| `ens_whatsapp_payload` | filter | Mutate the Graph API request body before send (e.g. switch to `type=template`). | |
| 225 |
| `notification_sdk_send_whatsapp` | action | Fired by the SDK to dispatch a WhatsApp message. The built-in Meta provider listens to it; consumers can listen to it to integrate other providers (Twilio, Gupshup, etc.) when paired with the `ens_whatsapp_provider` short-circuit. | |
| 226 |
| `{hook_prefix}_notification_sdk_to_whatsapp_numbers` | filter | Mutate the recipient list for a flow. Mirrors `{hook_prefix}_notification_sdk_to_emails`. | |
| 227 |
| `notification_sdk_whatsapp_message` | filter | Mutate the resolved message body. Mirrors `notification_sdk_email_message`. | |
| 228 |
| `{hook_prefix}_ens_whatsapp_request` | action | Fired immediately before the Graph API POST. Args: `$endpoint, $payload, $message_type, $to`. Subscribe for outbound-request logging, request signing, or metrics. | |
| 229 |
| `{hook_prefix}_ens_whatsapp_send_success` | action | Fired on a 2xx response from Meta. Args: `$response_body, $to, $payload, $http_code`. Use to capture the WhatsApp message id (`wamid…`) from the response body. | |
| 230 |
| `{hook_prefix}_ens_whatsapp_send_error` | action | Fired on any failure: missing credentials, invalid recipient, invalid template config, WP transport error, or non-2xx Meta response. Args: `$error_body, $to, $payload, $http_code` (HTTP code is `0` for setup/transport errors). | |
| 231 |
|
| 232 |
Example — read credentials from environment: |
| 233 |
|
| 234 |
```php |
| 235 |
add_filter( 'wpcafe_ens_whatsapp_credentials', function ( $creds ) { |
| 236 |
if ( defined( 'WHATSAPP_ACCESS_TOKEN' ) ) { |
| 237 |
$creds['access_token'] = WHATSAPP_ACCESS_TOKEN; |
| 238 |
$creds['phone_number_id'] = WHATSAPP_PHONE_NUMBER_ID; |
| 239 |
} |
| 240 |
return $creds; |
| 241 |
} ); |
| 242 |
``` |
| 243 |
|
| 244 |
Example — switch to a Meta template message: |
| 245 |
|
| 246 |
```php |
| 247 |
add_filter( 'ens_whatsapp_payload', function ( $payload, $to, $message ) { |
| 248 |
return [ |
| 249 |
'messaging_product' => 'whatsapp', |
| 250 |
'to' => $to, |
| 251 |
'type' => 'template', |
| 252 |
'template' => [ |
| 253 |
'name' => 'order_update', |
| 254 |
'language' => [ 'code' => 'en_US' ], |
| 255 |
'components' => [ |
| 256 |
[ |
| 257 |
'type' => 'body', |
| 258 |
'parameters' => [ |
| 259 |
[ 'type' => 'text', 'text' => $message ], |
| 260 |
], |
| 261 |
], |
| 262 |
], |
| 263 |
], |
| 264 |
]; |
| 265 |
}, 10, 3 ); |
| 266 |
``` |
| 267 |
|
| 268 |
> **Note:** Meta only allows free-form `type=text` messages within 24 hours of the user's last inbound message. Outside that window you must use approved template messages — handle this via the `ens_whatsapp_payload` filter shown above. |
| 269 |
|
| 270 |
## License |
| 271 |
|
| 272 |
This package is licensed under the **GPL-2.0-or-later**. |
| 273 |
|
| 274 |
See the [](LICENSELICENSE](LICENSE](LICENSE) file for the full license text. |
| 275 |
|