Server-side only
ZINDUA_API_KEY in .env or getenv(). Never expose in the browser or mobile app.

Laravel, Symfony, WordPress custom, cron scripts. Send OTP, then shop, bank, and hospital messages with zindua/sdk v1.1.1. Same API as Node.
composer require zindua/sdkZINDUA_API_KEY in .env or getenv(). Never expose in the browser or mobile app.

Email, E.164 WhatsApp, template slugs validated locally before calling the API.

One send() method for both channels. Same templates as Node and WordPress.

Uses PHP ext-curl and ext-json only. No Guzzle required.
Register Client as a singleton. Inject in controllers, jobs, and notifications.

Test sends with @zindua/cli before wiring PHP code.

Pick a use case. The PHP snippet updates. Composer, first OTP, then shop, bank, or hospital.
Install SDK
CLIAdd zindua/sdk with Composer and set your API key in .env.
terminal
1composer require zindua/sdk2 3# .env4ZINDUA_API_KEY=znd_test_your_key_hereFrom project key to CLI smoke test.
Composer install to first OTP from PHP.
Create a Zindua project
Sign up at zindua.run, create a project, copy the API key. Connect email and/or WhatsApp in the dashboard.
Methods and environment variables.
Parity with @zindua/sdk on Node.
send()
Queue email or WhatsApp message
Bind the client once. Then send OTP from a controller or an ajax hook. Two small files each.
Laravel
.env, bind once, inject, send.
<?php// config/zindua.phpreturn [ 'api_key' => env('ZINDUA_API_KEY'),]; // app/Providers/AppServiceProvider.php inside register()use Zindua\Client; $this->app->singleton(Client::class, function () { $key = config('zindua.api_key'); if (! $key) { throw new RuntimeException('Set ZINDUA_API_KEY in .env'); } return new Client(['apiKey' => $key]);});<?phpnamespace App\Http\Controllers; use Illuminate\Http\Request;use Illuminate\Support\Facades\Cache;use Illuminate\Support\Facades\Hash;use Zindua\Client; class OtpController extends Controller{ public function send(Request $request, Client $zindua) { $email = $request->validate(['email' => 'required|email'])['email']; $code = str_pad((string) random_int(0, 999999), 6, '0', STR_PAD_LEFT); $result = $zindua->send([ 'to' => $email, 'template' => 'otp-verification', 'variables' => [ 'code' => $code, 'appName' => config('app.name'), ], ]); Cache::put('otp:'.$email, Hash::make($code), now()->addMinutes(10)); return ['ok' => true, 'logId' => $result['logId']]; }}WordPress
mu-plugin, one helper, one ajax hook.
<?php// wp-config.phpdefine('ZINDUA_API_KEY', 'znd_test_your_key_here'); // wp-content/mu-plugins/zindua-client.phprequire_once ABSPATH . 'vendor/autoload.php'; function zindua(): \Zindua\Client{ static $client = null; if ($client instanceof \Zindua\Client) { return $client; } if (! defined('ZINDUA_API_KEY') || ! ZINDUA_API_KEY) { wp_die('Set ZINDUA_API_KEY in wp-config.php'); } $client = new \Zindua\Client(['apiKey' => ZINDUA_API_KEY]); return $client;}<?php// wp-content/mu-plugins/zindua-otp.phpadd_action('wp_ajax_nopriv_zindua_otp', 'zindua_send_otp');add_action('wp_ajax_zindua_otp', 'zindua_send_otp'); function zindua_send_otp(){ $email = sanitize_email($_POST['email'] ?? ''); if (! $email) { wp_send_json_error('Need an email', 400); } $code = str_pad((string) random_int(0, 999999), 6, '0', STR_PAD_LEFT); $result = zindua()->send([ 'to' => $email, 'template' => 'otp-verification', 'variables' => [ 'code' => $code, 'appName' => get_bloginfo('name'), ], ]); set_transient( 'zindua_otp_'.md5($email), wp_hash_password($code), 10 * MINUTE_IN_SECONDS ); wp_send_json_success(['logId' => $result['logId']]);}Smoke test your API key before writing PHP code.
npx @zindua/cli@latest doctorUse the official zindua-connect plugin for OTP login and contact forms.