Skip to main content

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 call dispatch() 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 first track() of the process — see Boot signal.

Installation

No custom repository or credentials are required — Packagist is used by default.

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 } to POST /v1/events/track — plus occurredAt when you declare one.
  • Sends Authorization: Bearer <sdkSecretKey> and X-Idempotency-Key: <idempotencyKey>.
  • Retries transient failures twice (1s, then 2s) — three total attempts.
  • Rethrows the last GuzzleHttp\Exception\GuzzleException if 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.
Named arguments keep the call readable when you skip $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.
Returnsstring, 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:
Then inject it where you need it:

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 a ready() 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:
The full error catalogue (including HTTP status mappings) is in error catalogue.

FAQ

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.
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.
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.
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.
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.
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.
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.