Docs

Docs / Start / PHP client

PHP client

signsealer/sdk for PHP 8.1 and later. Only PHP's own curl and json: no Guzzle, nothing to pin.

composer require signsealer/sdk

On its way to Packagist. The client is written and tested against this API; until its first release is published, the line above does not find it. Ask us and we will send the package. For a WordPress site, the WordPress plugin needs no code at all.

The same three promises as the Node client: one idempotency key through every retry of a write, a Retry-After honoured, and the API's own sentence, code, hint and request id on every error.

Send a document

use SignSealer\SignSealer;
use SignSealer\SignSealerError;

$signsealer = new SignSealer(getenv('SIGNSEALER_KEY'));

$doc = $signsealer->prepareDocument([
    'template_code' => 'rental-agreement',
    'values' => ['guest_name' => 'Dana Reyes', 'trip_date' => '2026-11-14'],
    'external_ref' => 'booking-1042',
]);
$sent = $signsealer->send($doc['document_id'], [['full_name' => 'Dana Reyes', 'email' => 'dana@example.com']]);
echo $sent['links'][0]['url'];

Every method

MethodCalls
account() · branding()GET /v1/account · /v1/branding
templates($code = null) · template($id)GET /v1/templates · /v1/templates/:id
documents($filter) · document($id) · documentSigners($id)GET /v1/documents · /:id · /:id/signers
prepareDocument($document, $idempotencyKey = null)POST /v1/documents
documentFromPdf($title, $pdfBytes, $fields = null, $textTags = false)POST /v1/documents/from-pdf
send($documentId, $signers) · remind($documentId, $signerId) · embedLink($documentId, $signerId) · void($documentId, $reason)POST /v1/documents/:id/send · /signers/:signer/remind · /embed · /void
certificate($id) · pdf($id) · bundle($id)GET /v1/documents/:id/certificate · /pdf · /bundle: the files come back as bytes
standing($template, $email = null, $phone = null)GET /v1/standing
workflows() · startWorkflow($workflowId, $participant)GET /v1/workflows · POST /v1/workflows/:id/start
webhooks() · addWebhook($url, $events = null) · ensureWebhook($url, $events = null) · removeWebhook($id, $reason)/v1/webhooks

Each answers with the API's JSON as an array, exactly as the API reference describes it.

Errors

try {
    $signsealer->prepareDocument(['template_code' => 'rental-agreement', 'values' => []]);
} catch (SignSealerError $e) {
    echo $e->status, ' ', $e->errorCode, ' ', $e->getMessage();   // 422 invalid no value for guest_name
    echo $e->hint, ' ', $e->requestId;                              // what to do, and the id to quote
}

Verifying a webhook

use SignSealer\Webhook;

$check = Webhook::verify(
    getenv('SIGNSEALER_WEBHOOK_SECRET'),
    file_get_contents('php://input'),                 // the raw body
    $_SERVER['HTTP_SIGNSEALER_SIGNATURE'] ?? null,
    $_SERVER['HTTP_SIGNSEALER_TIMESTAMP'] ?? null,
);
if (!$check['ok']) { http_response_code(400); exit($check['reason']); }

Ready to build? An API key takes a minute in the portal, and the free plan covers the first 10 agreements a month.

Get an API key