Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Client Core Laravel Package

nexmo/client-core

Core PHP library for Nexmo/Vonage REST APIs, providing shared client infrastructure, request handling, and exception classes used by the Nexmo PHP SDK. Helps integrate SMS, voice, verification, and related communication services.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: nexmo/client-core is archived (last tagged releases are maintenance-only patches; the project's own README points at vonage/client-core). It still works against the Vonage API for the endpoints it knew about, but it receives no security patches and no new API features. Use it only to keep an existing integration alive; new code should start on vonage/client-core (where most of the API below survives with Nexmo\ → Vonage\ renamed).

The 2.9.2 patch is typical of post-archive maintenance: one surgical bugfix, no features. The release notes matter because they tell you which version floor is safe — 2.9.2 fixes JWT sub claim generation under newer PHP versions, so anything below it can silently produce bad tokens on modern PHP runtimes even though nothing appeared to break at install time.

For a legacy project, pin at or above the 2.9.2 patch:

composer require nexmo/client-core:^2.9.2

PHP 7.x-era code, Guzzle 6/7 + PSR-7 under the hood. Credentials come from the Vonage dashboard: an API key/secret pair for SMS, Verify, Numbers and Account APIs, and an application ID + private key for Voice and Applications.

Minimal bootstrap:

use Nexmo\Client;
use Nexmo\Client\Credentials\Basic;
use Nexmo\SMS\Message\SMS;

$client = new Client(new Basic(getenv('NEXMO_KEY'), getenv('NEXMO_SECRET')));

$sms = new SMS('447700900000', 'Acme', 'Your code is 1234');
$sent = $client->sms()->send($sms);
$id   = $sent->getMessageId();

Where to look first:

  1. The factory methods on Nexmo\Client (sms(), verify(), voice(), numbers(), account(), applications(), insights(), redact(), conversion()). Each maps 1:1 to a namespace and to an API family — if a method is missing, the corresponding split package (nexmo/messages, nexmo/verify, …) isn't installed.
  2. Nexmo\Client\Credentials\* — this is where 90% of "it just returns 401" problems live.
  3. Nexmo\Client\Exception\* — the exception hierarchy tells you what is retryable.

First real use case: send an SMS and then immediately wire a Verify flow (start → user enters code → check), because those two exercise both credential types and the two calling conventions (fire-and-forget vs. request-id lifecycle) you'll use everywhere else. Do the Voice/Applications path second — that's the one that depends on the 2.9.2 JWT fix, so it's a good canary that your pinned version is actually correct.

Implementation Patterns

One client, multiple credential types. Almost every non-trivial app needs both Basic (SMS/Numbers) and Keypair (Voice/Applications). Wrap them in a Container; it selects the credential that matches the request:

use Nexmo\Client\Credentials\{Container, Basic, Keypair};

$client = new Client(new Container(
    new Basic(config('services.vonage.key'), config('services.vonage.secret')),
    new Keypair(storage_path('app/vonage.key'), config('services.vonage.application_id')),
));

Register it as a singleton in a Laravel service provider and inject Nexmo\Client — never construct it per call, since it holds a Guzzle client and (for Keypair) JWT state.

Resource-object pattern. Nearly every endpoint follows "build a typed object → pass it to a client method → server-populated IDs appear on the object or its return value." Learn it once and the whole SDK is predictable:

$verification = new Nexmo\Verify\Verification('447700900000', 'Acme');
$client->verify()->start($verification);
$requestId = $verification->getRequestId();   // persist this

$client->verify()->check(new Nexmo\Verify\Check($requestId, $code));
// or ->cancel(new Nexmo\Verify\Verification($requestId))
// or ->search(...) for status polling

Voice via NCCO action objects. Don't hand-build NCCO JSON; compose actions and let them serialise. Your answer webhook returns the array:

use Nexmo\Voice\NCCO\NCCO;
use Nexmo\Voice\NCCO\Action\{Talk, Connect, Record};

$ncco = (new NCCO())
    ->addAction(new Talk('Connecting you now'))
    ->addAction(new Connect('447700900001'));

return response()->json($ncco->toArray());

Outbound calls go through $client->voice()->create(new Nexmo\Voice\Call($to, $from, $answerUrl)). Every one of these paths signs a short-lived JWT, so this is the code to smoke-test after any dependency bump.

Numbers are configuration, not just inventory. $client->numbers()->update($number, [...]) is how you attach moHttpUrl (inbound SMS webhook) and voiceCallbackType/voiceCallbackValue. Treat buying a number and configuring its webhooks as one operation.

Webhook signature verification. Inbound webhooks should be checked before you touch the payload:

$signature = new Nexmo\Client\Signature($signatureSecret, 'sha256');
if (! $signature->check($request->query())) {
    abort(403);
}

Note it takes the query params including sig, not the JSON body — a very common mix-up. (This is the shared-secret webhook signature, unrelated to the application JWT that 2.9.2 fixes; don't conflate them when debugging 403s.)

Error handling and retry. Catch Nexmo\Client\Exception\ExceptionInterface as the umbrella, then branch: Throttle (429) and Server (5xx) are retryable with backoff; Request (4xx) is not — a bad number stays a bad number. In Laravel, put sends on a queued job with $tries = 5 and backoff() and use release() rather than a sleep loop.

Testing. Don't hit the live API. Inject a Guzzle client with a MockHandler through the client options:

$handler = HandlerStack::create(new MockHandler([
    new Response(200, [], json_encode(['messages' => [['status' => '0', 'message-id' => 'abc']]])),
]));
$client = new Client($creds, ['http_client' => new GuzzleHttp\Client(['handler' => $handler])]);

Alternatively the SDK's MapFactory lets you substitute whole API clients ($client->sms()) with fakes. A mocked HTTP client will not catch a broken JWT — the token is assembled before the request is handed to Guzzle — so add one un-mocked smoke test against Voice/Applications in staging.

Observability. Listen to client events / attach Guzzle middleware to log the outbound URL and response body. The API's own error strings are terse ("Bad Request"), so the raw response body is the only useful debugging artefact you'll have.

Gotchas and Tips

  • Archived means frozen. No PHP 8.2+ official support, no deprecation fixes, no new endpoints (Conversations, Messages v1, Video, etc. are absent). Patches arrive rarely and are narrowly scoped — 2.9.2 is a single JWT fix with no other changes. Pin an exact-ish constraint and put the migration to vonage/client-core on the roadmap; it's largely a namespace rename.
  • Stay on ≥ 2.9.2 if you use Keypair credentials. Before 2.9.2, JWTs with a sub claim could be generated incorrectly under newer PHP versions. Symptom is nasty: the token looks fine locally on an older runtime and fails as a 401 from Vonage on a newer one, which reads like a credential problem and sends you chasing the wrong bug. If Voice/Applications suddenly broke after a PHP upgrade rather than a code change, this is the first thing to check.
  • v1 vs v2 naming split. Old tutorials use $client->message()->send(new Nexmo\Message\Text(...)); v2 uses $client->sms()->send(new Nexmo\SMS\Message\SMS(...)). Copy-pasting from blog posts is the #1 source of "class not found."
  • Missing $client->xyz() almost always means a split package isn't installed. nexmo/client-core alone is not the same as the nexmo/client meta-package that pulled everything in.
  • Keypair constructor is overloaded. First argument is either a path to the .key file or the PEM string itself. A relative path resolves against the CWD, which differs between CLI and FPM — always use storage_path()/base_path(). If the file is unreadable you get a generic signing error later, not an IO error at construction.
  • Container ordering matters when you hold both a key/secret and a keypair; the first matching credential wins for a given API call. Put Basic where you expect SMS, or be explicit.
  • The HTTP client option key changed across versions (http_client, and older base_api_url/api_url for the endpoint base). Check the installed version's Client::__construct before writing fake-API tests — passing an unknown key silently does nothing.
  • Base URL override is your friend for integration tests, but only if you set it on the client options, not by patching Guzzle middleware after the fact.
  • No client-side rate limiting. SMS throughput and Verify search are hard-throttled server-side; burst loops will produce Throttle exceptions. Throttle in the queue worker.
  • Exceptions hide the body. Request exceptions carry status and a short message; log $e->getResponse()->getBody() (or the equivalent getter for your version) or you'll be debugging blind.
  • Timestamps. Conversion/redact endpoints expect ISO-8601 in UTC — now() in a non-UTC app timezone silently produces rejected or misattributed events.
  • Verify check() failing is an exception, not a false return; code 6/16 semantics (wrong code vs. max attempts) are only distinguishable from the exception payload.
  • Deprecation warnings in Composer will appear on install/update output. Don't let a "clean up composer" ticket accidentally bump you to a vonage/* package mid-sprint — that's a coordinated namespace migration, not a patch. Conversely, a nexmo/client-core patch bump within 2.x is low-risk and worth taking, since the only changes landing there are fixes like 2.9.2.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core