Back to Blog
PHPAPIExample

PDF Signing API PHP Example: Working Code You Can Copy

PDFSignify TeamMarch 2, 202610 min read

This article is a collection of working PHP code examples for the PDFSignify API. Every example uses cURL and can be dropped into any PHP project. No frameworks required, no external dependencies beyond PHP's built-in cURL extension.

Before You Start

You need three things to use these examples:

  • A PDFSignify account — sign up at pdfsignify.com to get your AccessKey and SecretKey
  • A .pfx or .p12 digital certificate file with its password
  • PHP 7.4 or later with the cURL extension enabled

The API authenticates with two custom headers on every request: AccessKey and SecretKey. There are no tokens to refresh and no OAuth to configure.

Example 1: Sign a PDF (Minimal)

The simplest possible signing request. Send the PDF, the certificate, and the password. Get the signed PDF back.

php
<?php
$ch = curl_init('https://api.pdfsignify.com/api/v1/sign-pdf');

curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'AccessKey: YOUR_ACCESS_KEY',
        'SecretKey: YOUR_SECRET_KEY',
    ],
    CURLOPT_POSTFIELDS => [
        'certificate' => new CURLFile('certificate.pfx', 'application/x-pkcs12'),
        'certificatePassword' => 'your_certificate_password',
        'pdf' => new CURLFile('document.pdf', 'application/pdf'),
    ],
]);

$response = curl_exec($ch);
$contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
curl_close($ch);

if (strpos($contentType, 'application/pdf') !== false) {
    file_put_contents('signed.pdf', $response);
    echo 'Done. Signed PDF saved.';
} else {
    echo 'Error: ' . $response;
}

Example 2: Sign with Custom Signature Appearance

Add a logo, position the signature on the page, and customize the message text. This example builds the multipart body manually for full control:

php
<?php
$cert = file_get_contents('certificate.pfx');
$pdf = file_get_contents('document.pdf');
$logo = file_get_contents('logo.png');

$boundary = uniqid();
$delimiter = '--------------------' . $boundary;
$parts = [];

$parts[] = '--' . $delimiter;
$parts[] = 'Content-Disposition: form-data; name="certificate"; filename="certificate.pfx"';
$parts[] = 'Content-Type: application/x-pkcs12';
$parts[] = '';
$parts[] = $cert;

$parts[] = '--' . $delimiter;
$parts[] = 'Content-Disposition: form-data; name="certificatePassword"';
$parts[] = '';
$parts[] = 'your_certificate_password';

$parts[] = '--' . $delimiter;
$parts[] = 'Content-Disposition: form-data; name="pdf"; filename="document.pdf"';
$parts[] = 'Content-Type: application/pdf';
$parts[] = '';
$parts[] = $pdf;

$parts[] = '--' . $delimiter;
$parts[] = 'Content-Disposition: form-data; name="signatureBackgroundImage"; filename="logo.png"';
$parts[] = 'Content-Type: image/png';
$parts[] = '';
$parts[] = $logo;

$textFields = [
    'signaturePageAppearance' => '-1',
    'timezone' => 'America/New_York',
    'signatureMessage' => 'Signed by Acme Inc.',
    'signatureDateFormat' => 'Y-m-d H:i:s',
    'signatureHeight' => '80',
    'signatureWidth' => '200',
    'signatureYPosition' => '50',
    'signatureXPosition' => '350',
];

foreach ($textFields as $name => $value) {
    $parts[] = '--' . $delimiter;
    $parts[] = 'Content-Disposition: form-data; name="' . $name . '"';
    $parts[] = '';
    $parts[] = $value;
}

$parts[] = '--' . $delimiter . '--';
$parts[] = '';
$body = implode("\r\n", $parts);

$ch = curl_init('https://api.pdfsignify.com/api/v1/sign-pdf');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: multipart/form-data; boundary=' . $delimiter,
    'AccessKey: YOUR_ACCESS_KEY',
    'SecretKey: YOUR_SECRET_KEY',
]);

$response = curl_exec($ch);
$contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
curl_close($ch);

if (strpos($contentType, 'application/pdf') !== false) {
    file_put_contents('signed_with_logo.pdf', $response);
    echo 'Signed PDF with custom appearance saved.';
} else {
    echo 'Error: ' . $response;
}

Example 3: Validate a Certificate Before Signing

Use this to verify that a certificate and password combination is correct before attempting to sign. Useful when users upload their own certificates:

php
<?php
$ch = curl_init('https://api.pdfsignify.com/api/v1/check-certificate-password');

curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'AccessKey: YOUR_ACCESS_KEY',
        'SecretKey: YOUR_SECRET_KEY',
    ],
    CURLOPT_POSTFIELDS => [
        'certificate' => new CURLFile('certificate.pfx', 'application/x-pkcs12'),
        'certificatePassword' => 'test_password',
    ],
]);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response, true);
echo $result['success'] ? 'Valid certificate.' : 'Invalid certificate or password.';

Example 4: Set PDF Metadata

Update the author, title, keywords, and other metadata fields of a PDF without signing it:

php
<?php
$ch = curl_init('https://api.pdfsignify.com/api/v1/set-pdf-metadata');

curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'AccessKey: YOUR_ACCESS_KEY',
        'SecretKey: YOUR_SECRET_KEY',
    ],
    CURLOPT_POSTFIELDS => [
        'pdf' => new CURLFile('report.pdf', 'application/pdf'),
        'pdfMetadataAuthor' => 'Finance Team',
        'pdfMetadataTitle' => 'Annual Report 2026',
        'pdfMetadataSubject' => 'Financial Summary',
        'pdfMetadataKeywords' => 'annual,report,finance,2026',
        'pdfMetadataCreator' => 'Internal Tools',
        'pdfMetadataProducer' => 'PDFSignify',
    ],
]);

$response = curl_exec($ch);
$contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
curl_close($ch);

if (strpos($contentType, 'application/pdf') !== false) {
    file_put_contents('report_with_metadata.pdf', $response);
    echo 'Metadata updated.';
} else {
    echo 'Error: ' . $response;
}

Example 5: Sign and Stream to Browser

Instead of saving the signed PDF to disk, you can serve it directly as a download. This works well for web application endpoints:

php
<?php
$ch = curl_init('https://api.pdfsignify.com/api/v1/sign-pdf');

curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'AccessKey: YOUR_ACCESS_KEY',
        'SecretKey: YOUR_SECRET_KEY',
    ],
    CURLOPT_POSTFIELDS => [
        'certificate' => new CURLFile('certificate.pfx', 'application/x-pkcs12'),
        'certificatePassword' => 'your_certificate_password',
        'pdf' => new CURLFile('invoice.pdf', 'application/pdf'),
    ],
]);

$response = curl_exec($ch);
$contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($httpCode === 200 && strpos($contentType, 'application/pdf') !== false) {
    header('Content-Type: application/pdf');
    header('Content-Disposition: attachment; filename="signed_invoice.pdf"');
    header('Content-Length: ' . strlen($response));
    echo $response;
    exit;
}

http_response_code(500);
header('Content-Type: application/json');
echo json_encode(['error' => 'Signing failed', 'details' => $response]);

Example 6: Helper Function for Reuse

If you are signing PDFs in multiple places in your application, extract the logic into a reusable function:

php
<?php
function signPdf(
    string $pdfPath,
    string $certPath,
    string $certPassword,
    string $accessKey,
    string $secretKey
): string|false {
    $ch = curl_init('https://api.pdfsignify.com/api/v1/sign-pdf');

    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => [
            'AccessKey: ' . $accessKey,
            'SecretKey: ' . $secretKey,
        ],
        CURLOPT_POSTFIELDS => [
            'certificate' => new CURLFile($certPath, 'application/x-pkcs12'),
            'certificatePassword' => $certPassword,
            'pdf' => new CURLFile($pdfPath, 'application/pdf'),
        ],
    ]);

    $response = curl_exec($ch);
    $contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
    curl_close($ch);

    if (strpos($contentType, 'application/pdf') !== false) {
        return $response;
    }

    return false;
}

// Usage:
$signed = signPdf(
    'contract.pdf',
    'certificate.pfx',
    'password123',
    getenv('PDFSIGNIFY_ACCESS_KEY'),
    getenv('PDFSIGNIFY_SECRET_KEY')
);

if ($signed !== false) {
    file_put_contents('signed_contract.pdf', $signed);
}

Tips for Production Use

  • Store AccessKey and SecretKey in environment variables — never hardcode them in source files
  • The sign-pdf and set-pdf-metadata endpoints return binary PDF data, not JSON — always check Content-Type before parsing
  • Only check-certificate-password returns JSON
  • Use curl_getinfo($ch, CURLINFO_HTTP_CODE) to detect server errors (4xx/5xx)
  • Set CURLOPT_TIMEOUT to a reasonable value for large PDFs to avoid hanging requests
  • Log errors with context but never log the SecretKey

Every example on this page uses only PHP and cURL. No Composer packages, no frameworks, no external dependencies.