upanupstudios/upaknee-php-client

Upaknee API client for PHP

Maintainers

Package info

github.com/upanupstudios/upaknee-php-client

pkg:composer/upanupstudios/upaknee-php-client

Transparency log

Statistics

Installs: 387

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

2.0 2026-08-25 21:40 UTC

This package is auto-updated.

Last update: 2026-08-25 21:41:22 UTC


README

A PHP client for the Upaknee (Cloud Messaging Stack Enterprise) REST API.

The client is scoped to what the Upaknee Mailout Drupal module needs: checking a token, reading the list's sending identity, resolving the recipients of a newsletter, and submitting a mailing. It is not a full binding of the API.

Requirements

  • PHP 8.1+
  • A PSR-18 HTTP client and PSR-17 factories. The library does not depend on a specific implementation — install any one you like, for example Guzzle:
composer require upanupstudios/upaknee-php-client guzzlehttp/guzzle

Authentication

The API uses HTTP Basic authentication. Your token (a 32-character alphanumeric string) and an optional security password are obtained from the Settings tab of Campaign Manager.

Usage

The HTTP client and factories are auto-discovered from whatever PSR-18/PSR-17 implementation you have installed:

use Upanupstudios\Upaknee\Php\Client\Upaknee;

$client = Upaknee::create('YOUR_API_TOKEN', 'OPTIONAL_PASSWORD');

// Verify a token — GET /version
$client->version(); // ['message' => 'API Version: 2.1.1.3']

Lists

A token is scoped to one contact list, which carries the account's sending identity.

// The list this token belongs to — GET /subscribers/summary, then GET /lists/{id}
$list = $client->lists()->current();
$list['name'];          // 'Primary Contact List'
$list['sender-name'];   // 'Auto Assign QA'
$list['sender-email'];  // 'qa-auto-assign@upaknee.com'

// Or address a list directly
$client->lists()->show('1851712');

// Subscriber counts by status — GET /subscribers/summary
$client->lists()->summary(); // ['active' => '10', 'pending' => '11', 'size' => '32', …]

Note: the sending identity is read-only. There is no route for changing a list, so the from name and from address are configured in Campaign Manager. The API models no reply-to address at all — it appears nowhere in the documented endpoints.

Newsletters

A newsletter is the subscription list a subscriber opts in to, and is the closest thing the API offers to a "group" of recipients.

// Total newsletters on the account — GET /newsletters-count
$client->newsletters()->count();

// Subscriptions belonging to a newsletter — GET /newsletters/{id}/subscriptions
$subscriptions = $client->newsletters()->subscriptions('15');
$subscriptions[0]['profile-id'];           // 'resubtest3'
$subscriptions[0]['name'];                 // 'general-communications'
$subscriptions[0]['subscription-status'];  // 'active'

// Optional filters
$client->newsletters()->subscriptions('15', status: 'active', limit: 500, offset: 0);

// Just the recipients, active by default
$profileIds = $client->newsletters()->profileIds('15'); // ['resubtest3', 'resubtest5']

subscriptions() always returns a list, including when the newsletter has exactly one subscriber — XML cannot distinguish a one-element collection from a single object, so the client normalizes it.

Note: the API publishes no route for listing the newsletters on an account — only the total count, and the subscriptions of a newsletter whose ID you already hold. Newsletter IDs have to come from Campaign Manager rather than from discovery. status, limit and offset are documented on the sibling /subscribers/{profile-id}/subscriptions route but not on this one; they are passed through for servers that honour them, and profileIds() re-filters on status client-side so an ignored filter cannot cause an unsubscribed contact to be mailed.

Mailings

Upaknee sends against a trigger — a message template configured in Campaign Manager. The API does not author content: it submits the recipients to mail and the merge-tag values to interpolate into the template. Anything variable in the message (subject line, body HTML) must be exposed as a merge tag on the trigger.

// Same content to many recipients — POST /triggers/{trigger-name}/mailings
$client->mailings()->submitToProfiles(
  'newsletter-broadcast',
  $profileIds,
  ['subject' => 'March update', 'body' => '<p>Hello!</p>'],
);

// Schedule it instead of sending now
$client->mailings()->submitToProfiles(
  'newsletter-broadcast',
  $profileIds,
  ['subject' => 'March update', 'body' => '<p>Hello!</p>'],
  '2026-09-01T22:15:00Z',
);

// Per-recipient merge data
$client->mailings()->submit('newsletter-broadcast', [
  ['profile-id' => 'A', 'data' => ['first_name' => 'Ada']],
  ['profile-id' => 'B', 'data' => ['first_name' => 'Grace']],
]);

Merge-tag values named in the serializer's CDATA list (body by default) are emitted as CDATA, so HTML survives intact. Pass a different list to XmlSerializer if your trigger names its HTML tag something else:

use Upanupstudios\Upaknee\Php\Client\XmlSerializer;

$client = new Upaknee($transport, new XmlSerializer(['body', 'html_content']));

$scheduledFor accepts the three formats the API documents:

Format Example
TZ (always UTC) 2026-09-01T22:15:00Z
GMT offset 2026-09-01 18:15:00 -0400
Timezone abbreviation 2026-09-01 17:15:00 EST

Bring your own HTTP client

For full control (custom middleware, timeouts, testing), construct the transport yourself:

use GuzzleHttp\Client as GuzzleClient;
use Nyholm\Psr7\Factory\Psr17Factory;
use Upanupstudios\Upaknee\Php\Client\Config;
use Upanupstudios\Upaknee\Php\Client\HttpClient;
use Upanupstudios\Upaknee\Php\Client\Upaknee;

$factory = new Psr17Factory();
$transport = new HttpClient(
  new GuzzleClient(),
  $factory,            // PSR-17 request factory
  $factory,            // PSR-17 stream factory
  new Config('YOUR_API_TOKEN', 'OPTIONAL_PASSWORD'),
);

$client = new Upaknee($transport);

Error handling

Successful responses are decoded from XML into associative arrays. Errors throw typed exceptions that all implement Upanupstudios\Upaknee\Php\Client\Exception\ExceptionInterface:

Exception When
AuthenticationException HTTP 401 / 403
NotFoundException HTTP 404
ValidationException HTTP 406 / 422 (e.g. duplicate or invalid email)
ApiException Any other HTTP error (base class; carries the PSR-7 response)
TransportException Network/transport failure (no response received)
InvalidArgumentException Bad arguments before a request is sent (e.g. a mailing with no recipients)
use Upanupstudios\Upaknee\Php\Client\Exception\ApiException;

try {
  $subscriptions = $client->newsletters()->subscriptions('15');
} catch (ApiException $e) {
  echo $e->getStatusCode();      // e.g. 404
  $response = $e->getResponse(); // PSR-7 ResponseInterface|null
}

Tests

composer install
composer test

License

MIT