PHPackages                             blueink/blueink-client-php - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [HTTP &amp; Networking](/categories/http)
4. /
5. blueink/blueink-client-php

ActiveLibrary[HTTP &amp; Networking](/categories/http)

blueink/blueink-client-php
==========================

A PHP client library for the BlueInk eSignature REST API

v2.0.0(1w ago)655[3 issues](https://github.com/blueinkhq/blueink-client-php/issues)[2 PRs](https://github.com/blueinkhq/blueink-client-php/pulls)MITPHPPHP &gt;=8.1CI passing

Since Feb 27Pushed 1w ago3 watchersCompare

[ Source](https://github.com/blueinkhq/blueink-client-php)[ Packagist](https://packagist.org/packages/blueink/blueink-client-php)[ Docs](https://github.com/blueinkhq/blueink-client-php)[ RSS](/packages/blueink-blueink-client-php/feed)WikiDiscussions master Synced 5d ago

READMEChangelog (3)Dependencies (8)Versions (10)Used By (0)

Blueink API Client for PHP
==========================

[](#blueink-api-client-for-php)

A PHP SDK for the [Blueink](https://blueink.com) eSignature REST API. For full API reference see the [Blueink API v2 docs](https://developer.blueink.com).

Requirements
------------

[](#requirements)

- PHP 8.1 or newer
- ext-json
- [Guzzle](http://docs.guzzlephp.org/en/stable/) 7.x (installed as a dependency)

Installation
------------

[](#installation)

```
composer require blueink/blueink-client-php
```

Quickstart
----------

[](#quickstart)

```
use Blueink\ClientSDK\Client;

// Provide the key directly...
$client = new Client('');

// ...or set BLUEINK_PRIVATE_API_KEY in the environment and call:
// $client = new Client();

$response = $client->bundles->list();
foreach ($response->data as $bundle) {
    echo $bundle['id'] . "\n";
}
```

Configuration
-------------

[](#configuration)

The `Client` constructor accepts:

ArgumentDefaultDescription`$private_api_key``getenv('BLUEINK_PRIVATE_API_KEY')`Blueink private API key.`$base_url``getenv('BLUEINK_API_URL')` ?? `https://api.blueink.com/api/v2`Override for sandbox or custom hosts.`$raise_exceptions``true`When `false`, 4XX/5XX responses are returned as `NormalizedResponse` instead of throwing a Guzzle exception.```
$client = new Client(
    private_api_key: 'sk_live_…',
    base_url: 'https://sandbox.blueink.com/api/v2',
    raise_exceptions: false,
);
```

Responses: `NormalizedResponse`
-------------------------------

[](#responses-normalizedresponse)

Every subclient method returns a `Blueink\ClientSDK\NormalizedResponse`:

```
$response = $client->bundles->retrieve('abc123');

$response->status;           // int  HTTP status code
$response->data;             // mixed Decoded JSON body (associative array) or raw string
$response->headers;          // array Response headers (last value per name)
$response->pagination;       // ?Pagination Parsed X-Blueink-Pagination header (list endpoints)
$response->originalResponse; // Psr\Http\Message\ResponseInterface
```

The most-recent response for a given subclient is also retained:

```
$last = $client->bundles->getLastResponse();
```

Subclients
----------

[](#subclients)

The `Client` exposes one property per resource:

PropertyClassResource`$client->bundles``BundleSubClient`Bundles (envelopes)`$client->persons``PersonSubClient`Persons (signers / contacts)`$client->packets``PacketSubClient`Packets (per-recipient)`$client->templates``TemplateSubClient`Document templates`$client->envelope_templates``EnvelopeTemplateSubClient`Envelope templates`$client->webhooks``WebhookSubClient`Webhooks, headers, events, deliveries### Bundles

[](#bundles)

Create a Bundle from a hand-built payload:

```
$response = $client->bundles->create([
    'label'   => 'A Test Bundle',
    'is_test' => true,
    'packets' => [[
        'key'   => 'signer-1',
        'name'  => 'Peter Gibbons',
        'email' => 'peter.gibbons@example.com',
    ]],
    'documents' => [[
        'key'         => 'doc-01',
        'file_url'    => 'https://example.com/contract.pdf',
        'fields'      => [],
    ]],
]);

$bundle_id = $response->data['id'];
```

Or use `BundleHelper` to assemble it. The helper exposes convenience methods for adding signers, documents, fields, and auto-placements without hand-rolling the nested payload:

```
use Blueink\ClientSDK\BundleHelper;

$bundle = new BundleHelper([
    'label'         => 'A Test Bundle',
    'email_subject' => 'Please sign this test bundle',
    'is_test'       => true,
]);

// Add signers (each requires email or phone). Returns the generated packet key.
$signer1 = $bundle->addSigner(name: 'Peter Gibbons', email: 'peter@example.com');
$signer2 = $bundle->addSigner(name: 'Bill Lumbergh', email: 'bill@example.com');

// Add a Document by URL, base64, file path (read at build time), file path
// (streamed as multipart at request time), or raw HTML.
$doc_key = $bundle->addDocumentByURL('https://example.com/contract.pdf');
// $bundle->addDocumentByPath('/tmp/nda.pdf');
// $bundle->addDocumentByFile('/tmp/large.pdf', 'application/pdf');
// $bundle->addDocumentByHTML('Statement of Work...');

// Add a field at fixed coordinates, scoped to a list of editor packet keys.
$bundle->addField(
    document_key: $doc_key,
    x: 1, y: 15, w: 60, h: 6, p: 1,
    kind: 'inp',
    editors: [$signer1, $signer2],
    label: 'Full name',
);

// Or let the API auto-place a field by searching the document text.
$bundle->addAutoPlacement(
    document_key: $doc_key,
    kind: 'sig',
    search: 'Signature:',
    w: 30, h: 6,
    offset_x: 8, offset_y: -1,
    editors: [$signer1],
);

$response = $client->bundles->createFromBundleHelper($bundle);
```

When the helper has files queued via `addDocumentByFile()`, the SDK transparently switches the request to `multipart/form-data`.

#### Building a Bundle from a document Template

[](#building-a-bundle-from-a-document-template)

`addDocumentTemplate()` adds a Document backed by an existing template, with optional role assignments and initial field values. `assignRole()` and `setValue()` append to the same Document after the fact:

```
$signer = $bundle->addSigner(
    name: 'Peter Gibbons',
    email: 'peter@example.com',
    key: 'signer-1',
);

$tmpl_key = $bundle->addDocumentTemplate(
    template_id: 'tmpl_abc123',
    assignments: ['signer' => 'signer-1'],          // role => signer key
    initial_field_values: ['agree' => true],        // field key => value
);

// Equivalent, post-hoc:
$bundle->assignRole($tmpl_key, 'signer-1', 'witness');
$bundle->setValue($tmpl_key, 'effective_date', '2026-01-01');

$response = $client->bundles->createFromBundleHelper($bundle);
```

Other Bundle operations:

```
$client->bundles->retrieve($bundle_id);
$client->bundles->retrieve($bundle_id, related_data: true); // attach events / files / data
$client->bundles->cancel($bundle_id);
$client->bundles->listEvents($bundle_id);
$client->bundles->listFiles($bundle_id);
$client->bundles->listData($bundle_id);
```

### Persons

[](#persons)

```
use Blueink\ClientSDK\PersonHelper;

$helper = new PersonHelper(['name' => 'Jane Doe']);
$helper->addEmail('jane@example.com');
$helper->addPhone('+15551234567');

$created = $client->persons->createFromPersonHelper($helper);
$person_id = $created->data['id'];

$client->persons->retrieve($person_id);
$client->persons->update($person_id, ['metadata' => ['vip' => true]], partial: true);
$client->persons->delete($person_id);
```

> The Blueink API normalizes `name` by splitting on whitespace into first/last tokens, so the round-tripped `name` may not be byte-identical to what was sent.

### Packets

[](#packets)

```
$client->packets->update($packet_id, ['email' => 'new@example.com']);
$client->packets->embedURL($packet_id);    // signed embedded-signing URL
$client->packets->retrieveCOE($packet_id); // Certificate of Evidence
$client->packets->remind($packet_id);
```

### Templates

[](#templates)

```
$client->templates->list();
$client->templates->retrieve($template_id);
```

### Envelope Templates

[](#envelope-templates)

Envelope templates are reusable, end-to-end Bundle workflows configured in the Blueink dashboard. The subclient is read-only:

```
$client->envelope_templates->list();
$client->envelope_templates->retrieve($envelope_template_id);

foreach ($client->envelope_templates->pagedList(per_page: 100) as $page) {
    foreach ($page->data as $tpl) {
        echo $tpl['id'] . "\n";
    }
}
```

To create a Bundle from an envelope template, configure a `BundleHelper` with `setEnvelopeTemplate()` (plus any signers and field overrides) and post it via `createFromEnvelopeTemplateHelper()`:

```
use Blueink\ClientSDK\BundleHelper;

$bundle = new BundleHelper([
    'label'   => 'Onboarding paperwork',
    'is_test' => true,
]);
$bundle->addSigner(name: 'Peter Gibbons', email: 'peter@example.com', key: 'signer-1');

$bundle->setEnvelopeTemplate(
    template_id: 'env_tmpl_abc123',
    field_values: ['company_name' => 'ACME Corp'],
);
$bundle->addEnvelopeTemplateFieldValue('start_date', '2026-01-15');

$response = $client->bundles->createFromEnvelopeTemplateHelper($bundle);
```

For pre-built payloads, `createFromEnvelopeTemplate(array $data)` posts the array directly to `/bundles/create_from_envelope_template/`.

### Webhooks

[](#webhooks)

```
$created = $client->webhooks->create([
    'name'        => 'My integration',
    'url'         => 'https://example.com/blueink-webhook',
    'event_types' => ['bundle_complete'],
]);

$client->webhooks->update($id, ['url' => '…'], partial: true);
$client->webhooks->delete($id);

// Custom request headers Blueink will send to your endpoint
$client->webhooks->createHeader([
    'webhook' => $id,
    'name'    => 'X-My-Token',
    'value'   => 'shhh',
    'order'   => 0,
]);

// Verification of incoming deliveries
$client->webhooks->retrieveSecret();
$client->webhooks->regenerateSecret();

// Inspection
$client->webhooks->listEvents();
$client->webhooks->listDeliveries();
$client->webhooks->retrieveDelivery($delivery_id);
```

Pagination
----------

[](#pagination)

List endpoints accept `page` and `per_page` and return a `Pagination` object on the response, parsed from the `X-Blueink-Pagination` header:

```
$response = $client->bundles->list(page: 1, per_page: 25);

$response->pagination->page_number;
$response->pagination->total_pages;
$response->pagination->per_page;
$response->pagination->total_results;
```

For automatic page-walking, every list-capable subclient exposes a `pagedList()`that returns a `Paginated` iterator. Each iteration yields the next page's `NormalizedResponse`:

```
foreach ($client->bundles->pagedList(per_page: 100) as $page) {
    foreach ($page->data as $bundle) {
        // …
    }
}
```

Error handling
--------------

[](#error-handling)

By default, 4XX/5XX responses raise a `Blueink\ClientSDK\BlueinkApiError`, a typed exception that parses the Blueink error body shape:

```
{
    "detail": "Invalid input.",
    "code": "invalid",
    "errors": [
        { "field": "channels", "message": "This field is required." }
    ]
}
```

```
use Blueink\ClientSDK\BlueinkApiError;

try {
    $client->persons->create(['name' => 'Jane Doe']);
} catch (BlueinkApiError $e) {
    $e->status_code;   // int    HTTP status (also $e->getCode())
    $e->detail;        // ?string body.detail
    $e->api_code;      // ?string body.code (named api_code to avoid clashing with getCode())
    $e->errors;        // array
    $e->body;          // decoded body (array) or raw string when not JSON
    $e->response;      // ?Psr\Http\Message\ResponseInterface
    $e->request;       // ?Psr\Http\Message\RequestInterface
    $e->getPrevious(); // GuzzleHttp\Exception\BadResponseException

    foreach ($e->errors as $error) {
        printf("%s: %s\n", $error['field'] ?? '_', $error['message'] ?? '');
    }
}
```

`BlueinkApiError` extends `\RuntimeException`, so a generic `catch (\Throwable $e)`still works. The original Guzzle `BadResponseException` (`ClientException` / `ServerException`) is preserved as the previous exception. Network-level failures (`GuzzleHttp\Exception\ConnectException`, `RequestException` without a response) are not wrapped and continue to bubble up unchanged.

To inspect failures without try/catch, construct the client with `raise_exceptions: false`. 4XX and 5XX responses then come back as `NormalizedResponse` objects with `status` and decoded `data`:

```
$client = new Client(raise_exceptions: false);

$response = $client->persons->create(['name' => 'Jane Doe']);
if ($response->status >= 400) {
    var_dump($response->data);
}
```

Testing
-------

[](#testing)

The SDK ships with two PHPUnit suites:

- **`unit`** (default) — fast, hermetic. Uses Guzzle's `MockHandler` to verify request shaping (verb, URL, headers, JSON / multipart body) without touching the network.
- **`integration`** — opt-in. Hits a real Blueink environment using the key in `BLUEINK_PRIVATE_API_KEY` (and optional `BLUEINK_API_URL`). Tests are skipped automatically when the key is absent. Use a sandbox account.

```
# Unit suite (runs by default)
./vendor/bin/phpunit

# Integration suite, against your sandbox
BLUEINK_PRIVATE_API_KEY=sk_sandbox_… \
BLUEINK_API_URL=https://sandbox.blueink.com/api/v2 \
    ./vendor/bin/phpunit --testsuite=integration
```

License
-------

[](#license)

[MIT](LICENSE).

###  Health Score

49

—

FairBetter than 95% of packages

Maintenance88

Actively maintained with recent releases

Popularity13

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity71

Established project with proven stability

 Bus Factor1

Top contributor holds 66.7% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~753 days

Total

4

Last Release

9d ago

Major Versions

1.1.0 → v2.0.02026-05-07

PHP version history (3 changes)1.0.0PHP &gt;=5.6

1.1.0PHP &gt;=7.3

v2.0.0PHP &gt;=8.1

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/6328121?v=4)[blueink](/maintainers/blueink)[@blueink](https://github.com/blueink)

---

Top Contributors

[![scottblueink](https://avatars.githubusercontent.com/u/58989132?v=4)](https://github.com/scottblueink "scottblueink (12 commits)")[![zlove](https://avatars.githubusercontent.com/u/108858?v=4)](https://github.com/zlove "zlove (6 commits)")

---

Tags

sdkGuzzleeSignaturee-signatureblueink

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/blueink-blueink-client-php/health.svg)

```
[![Health](https://phpackages.com/badges/blueink-blueink-client-php/health.svg)](https://phpackages.com/packages/blueink-blueink-client-php)
```

###  Alternatives

[huaweicloud/huaweicloud-sdk-php

Huawei Cloud SDK for PHP

1829.2k2](/packages/huaweicloud-huaweicloud-sdk-php)[hyperf/guzzle

Swoole coroutine handler for guzzle

102.7M292](/packages/hyperf-guzzle)[onesignal/onesignal-php-api

A powerful way to send personalized messages at scale and build effective customer engagement strategies. Learn more at onesignal.com

34170.2k2](/packages/onesignal-onesignal-php-api)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
