Notifizz PHP SDK reference
notifizz/php is the PHP SDK for tracking events. It exposes track(), the enricher subsystem, and a hashed-token helper for widget auth, and ships on Packagist. Node and Java carry the same surface since their 2.0.0.
TL;DR
new NotifizzClient($authSecretKey, $sdkSecretKey, $webhookSigningSecret = null, $clientOptions = [])— the third argument is required only when you calldispatch()to expose enrichers.$client->track($eventName, $properties = [], $idempotencyKey = null, $occurredAt = null)— emits one event; rethrows the last transport error after retries.- Each
track()retries twice (1s, then 2s) before bubbling the last exception. $client->enricher($name, $options),$client->enrichProfileData($handler)and$client->dispatch($body)give PHP the same enricher surface as the Node SDK, since 2.0.0.- No
ready()in PHP: the boot signal goes out inline on the firsttrack()of the process — see Boot signal.
Installation
Constructor
$client->track($eventName, $properties = [], $idempotencyKey = null, $occurredAt = null)
Emits a single event. Notifizz resolves campaigns by $eventName and runs each campaign’s orchestrator server-side to build the recipient list — there is no client-side workflow or recipient targeting.
Parameters
Behaviour
- Posts
{ eventName, properties, sdkSecretKey, idempotencyKey }toPOST /v1/events/track— plusoccurredAtwhen you declare one. - Sends
Authorization: Bearer <sdkSecretKey>andX-Idempotency-Key: <idempotencyKey>. - Retries transient failures twice (1s, then 2s) — three total attempts.
- Rethrows the last
GuzzleHttp\Exception\GuzzleExceptionif all attempts fail.
Event time (occurredAt)
By default, the moment Notifizz receives an event is the moment it happened — right, when you track at the point the thing occurs. Declare the event time when the two genuinely differ: replaying a queue, flushing a batch collected offline, backfilling history.
$idempotencyKey. Accepts an ISO 8601 string or any DateTimeInterface; left out of the request entirely when null — an absent field tells the API that reception time is the only truth.
A date in the future is refused with event/invalid-occurred-at; a 60-second tolerance absorbs client clock skew. The value feeds time-scoped resolutions server-side, so it must never claim a state that does not exist yet.
Available since 2.1.0.
$client->generateHashedToken($userId)
Generates the SHA-256 of $userId. $authSecretKey. Pass it to your frontend so the Notification Center widget can authenticate in backendToken mode.
Returns —
string, hex-encoded SHA-256.
See backend tokens for the widget side.
$client->config($opts)
Overrides default options. Currently only baseUrl is configurable.
Audience identity
identify() links two Subjects to the same Audience — the mechanism that makes an application user and an email address the same person. detach() puts a Subject back into an Audience of its own. Available since 2.0.0.
What the action means
Both methods return the decoded response body as an array and throw
GuzzleHttp\Exception\GuzzleException on a network failure or a >= 400 response. Unlike track(), they do not retry.
Links are always declared, never inferred: Notifizz merges two identities because your code said so, not because two payloads looked similar. In a production environment, an EmailSubject on a known disposable domain is refused (disposable_email_domain); outside production the call succeeds and the response carries a warnings entry instead.
Laravel example
In a Laravel application, register the client as a singleton:Enrichers
An enricher is a server-side function the Notifizz orchestrator calls to fetch live data at notification time. Your PHP service registers the handlers, exposes them on one public route, and Notifizz calls that route with an HMAC-signed webhook. Available since 2.0.0 — no Node service in the middle. Pass$webhookSigningSecret as the third constructor argument, otherwise dispatch() throws on the first call.
$client->enricher($name, $options)
JsonSchema is a small fluent builder shipped with the SDK; a hand-written schema array works just as well. Registering the same name twice throws Notifizz\EnricherRegistrationException.
$client->enrichProfileData($handler, $description = null, $cache = null)
Registers the system enricher that feeds the Audience profile panel in the dashboard. It differs from enricher() on three points: it takes the reserved name notifizz:profileResolver (hidden from the enricher catalog), its input is fixed to { id, email } — one user per call, no batch — and its output is free-form.
Whatever fields you return are rendered in the profile panel — there is no output schema to declare.
$client->enrichers()
Returns the discovery view of what is registered — handy in tests.
$client->dispatch($body)
The body-only webhook entry point. Your controller is a one-liner:
dispatch() accepts the parsed body (array) or a raw JSON string. It verifies the HMAC over the inner payload, rejects stale timestamps (anti-replay, ±5 min), and routes discovery versus enricher execution.
Errors are encoded inside the body, never thrown across the dispatch boundary — your framework writes 200 unconditionally and Notifizz translates
error.code back into a typed domain error. That is what keeps the controller one line.
dispatch() throws \RuntimeException when no $webhookSigningSecret was passed to the constructor. NotifizzClient::signDispatchPayload($secret, $payload) produces the same HMAC, for testing a dispatch route with synthetic signed requests.
Boot signal
The Node and Java SDKs expose aready() method: you call it once your HTTP server is listening, and a non-production environment re-runs discovery on the spot. The PHP SDK has no ready() — and does not need one, because a PHP process does not stay up between requests.
Instead, the SDK sends a best-effort boot signal inline, once per process, on the first track() call. It is fire-and-forget with a short timeout (1 s to connect, 2 s total) and never fails your request. It announces that the SDK key is alive; it does not ask for a re-discovery.
The practical consequence, and the only one worth remembering:
So after adding an enricher to a PHP service, expect the dashboard to pick it up on the next pass rather than instantly. Nothing to call, nothing to configure.
Error handling
track() throws once it gives up. Catch \Throwable — the exception class depends on the failure mode:
FAQ
What exception does a failed `track()` throw?
What exception does a failed `track()` throw?
It depends on the failure — a network failure surfaces the Guzzle
ConnectException, an API rejection a \RuntimeException. Catch \Throwable and look at the message; the table in Error handling lists the four cases. Handle it explicitly either way — silent network failures are a foot-gun.Should I generate the idempotency key myself?
Should I generate the idempotency key myself?
Yes when the same logical emit may be retried (queued jobs, retry middleware). Use a deterministic key derived from your domain —
"order-shipped:{$orderId}" is better than bin2hex(random_bytes(16)) from outside the SDK, which generates a new key per attempt and defeats the dedupe.Can I host an enricher from my PHP service?
Can I host an enricher from my PHP service?
Yes, since
2.0.0. Register handlers with $client->enricher($name, $options), pass a $webhookSigningSecret to the constructor, and expose $client->dispatch($body) on a public route — see Enrichers above. No Node service in the middle.Is there a `ready()` method like in Node and Java?
Is there a `ready()` method like in Node and Java?
No, and PHP does not need one — the process does not stay up between requests. The boot signal goes out inline on the first
track() of the process instead, and it does not request an immediate re-discovery. A newly added enricher is picked up on the next periodic discovery pass. See Boot signal.Is `track()` blocking?
Is `track()` blocking?
Yes —
track() is synchronous, and the SDK adds 1 s + 2 s of backoff between its three attempts. It does not set a response timeout of its own, so the network wait itself is bounded only by your PHP and cURL configuration: a hung backend holds the request. In Laravel, dispatch tracking from a queued job when latency matters — and never call it inside a database transaction, where the wait would hold a connection hostage.How do I configure a custom base URL for staging?
How do I configure a custom base URL for staging?
Call
$client->config(['baseUrl' => '...']) after construction. The SDK reads the option on every track() call. Use it for local mocks, regional endpoints, or staging environments — production is https://api.notifizz.com/v1.Can I batch several events in one call?
Can I batch several events in one call?
Not today. Each
track() call is one event. Use Laravel’s queue or a Symfony Messenger transport to fire many events in parallel; idempotency keys ensure retries dedupe at the backend.See also
Event Tracking reference
HTTP wire format, idempotency contract, error shapes.
Backend quickstart
Send your first event in under five minutes.
Event Tracking overview
Cross-language feature matrix.
Notification Center widget
Display the notifications your events drive.