1. Go to this page and download the library: Download turbodocx/sdk library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
turbodocx / sdk example snippets
use TurboDocx\TurboSign;
use TurboDocx\Config\HttpClientConfig;
use TurboDocx\Types\Recipient;
use TurboDocx\Types\Field;
use TurboDocx\Types\SignatureFieldType;
use TurboDocx\Types\TemplateConfig;
use TurboDocx\Types\FieldPlacement;
use TurboDocx\Types\Requests\SendSignatureRequest;
// 1. Configure from TURBODOCX_API_KEY, TURBODOCX_ORG_ID, TURBODOCX_SENDER_EMAIL (sult = TurboSign::sendSignature(
new SendSignatureRequest(
recipients: [
new Recipient('John Doe', '[email protected]', 1)
],
fields: [
new Field(
type: SignatureFieldType::SIGNATURE,
recipientEmail: '[email protected]',
template: new TemplateConfig(
anchor: '{signature1}',
placement: FieldPlacement::REPLACE,
size: ['width' => 100, 'height' => 30]
)
)
],
file: file_get_contents('contract.pdf'),
documentName: 'Partnership Agreement'
)
);
echo "Document ID: {$result->documentId}\n";
TurboSign::configure(new HttpClientConfig(
apiKey: getenv('TURBODOCX_API_KEY') ?: null, // getenv() returns false when unset; `?: null` keeps it null
orgId: getenv('TURBODOCX_ORG_ID') ?: null,
senderEmail: getenv('TURBODOCX_SENDER_EMAIL') ?: null,
senderName: getenv('TURBODOCX_SENDER_NAME') ?: null
));
// Or use auto-configuration from environment
TurboSign::configure(HttpClientConfig::fromEnvironment());
use TurboDocx\Types\Requests\CreateSignatureReviewLinkRequest;
$result = TurboSign::createSignatureReviewLink(
new CreateSignatureReviewLinkRequest(
recipients: [
new Recipient('John Doe', '[email protected]', 1)
],
fields: [
new Field(
type: SignatureFieldType::SIGNATURE,
recipientEmail: '[email protected]',
page: 1,
x: 100,
y: 500,
width: 200,
height: 50
)
],
fileLink: 'https://example.com/contract.pdf', // Or use file: for upload
documentName: 'Service Agreement', // Optional
documentDescription: 'Q4 Contract', // Optional
ccEmails: ['[email protected]'] // Optional
)
);
echo "Preview URL: {$result->previewUrl}\n";
echo "Document ID: {$result->documentId}\n";
use TurboDocx\Types\Requests\SendSignatureRequest;
$result = TurboSign::sendSignature(
new SendSignatureRequest(
recipients: [
new Recipient('Alice', '[email protected]', 1),
new Recipient('Bob', '[email protected]', 2) // Signs after Alice
],
fields: [
new Field(
type: SignatureFieldType::SIGNATURE,
recipientEmail: '[email protected]',
page: 1,
x: 100,
y: 500,
width: 200,
height: 50
),
new Field(
type: SignatureFieldType::SIGNATURE,
recipientEmail: '[email protected]',
page: 1,
x: 100,
y: 600,
width: 200,
height: 50
)
],
file: file_get_contents('contract.pdf')
)
);
// The created recipients come back on the send result itself, as a plain array of
// associative arrays (not objects). Each carries id, name and email.
if ($result->recipients !== null) {
foreach ($result->recipients as $recipient) {
echo "{$recipient['name']} <{$recipient['email']}> — {$recipient['id']}\n";
}
}
// For signing progress afterwards, use getRecipients():
$progress = TurboSign::getRecipients($result->documentId);
use TurboDocx\TurboSign;
use TurboDocx\Types\Recipient;
use TurboDocx\Types\Requests\SendSignatureRequest;
// The role keys come from the template (also shown under "Use via API" on the template page)
$setup = TurboSign::getTemplateSignatureSetup('your-template-id');
foreach ($setup->roles as $role) {
echo "{$role->key} (order {$role->order}, saved signer: " . ($role->hasSavedSigner ? 'yes' : 'no') . ")\n";
}
$result = TurboSign::sendSignature(
new SendSignatureRequest(
templateId: 'your-template-id',
recipients: [
new Recipient(name: 'Jane Doe', email: '[email protected]', role: 'client'),
// 'countersigner' left out: the signer saved on the template is used
],
)
);
use TurboDocx\Types\Requests\CreateSigningUrlRequest;
$result = TurboSign::sendSignature(
new SendSignatureRequest(
recipients: [new Recipient('Alice', '[email protected]', 1)],
fields: [
new Field(
type: SignatureFieldType::SIGNATURE,
recipientEmail: '[email protected]',
page: 1,
x: 100,
y: 500,
width: 200,
height: 50
)
],
file: file_get_contents('contract.pdf'),
fileName: 'contract.pdf',
sendEmail: false // no signing emails; you hand out the link yourself
)
);
// Mint the link when the signer is ready. Don't log or store it: it is a bearer link.
$recipientId = $result->recipients[0]['id'] ?? throw new RuntimeException('No recipient returned');
$link = TurboSign::createSigningUrl(
$result->documentId,
new CreateSigningUrlRequest(recipientId: $recipientId)
);
echo $link->url; // open in a new tab or redirect to it
// Remind everyone whose turn it is to sign.
$result = TurboSign::sendReminder('doc-uuid-here');
// The response is a plain array. Each entry reports a recipientId and what happened
// to it — signers not at the current order come back as skipped rather than emailed.
foreach ($result['results'] as $r) {
echo "{$r['recipientId']}: {$r['status']}\n";
}
// Optionally limit the reminder to specific recipients (all-or-nothing: every id must be
// a current-order pending signer, or the whole call is rejected).
TurboSign::sendReminder('doc-uuid-here', ['recipient-uuid-1', 'recipient-uuid-2']);
$result = TurboSign::sendSignature(
new SendSignatureRequest(
// ...recipients, fields, file, etc.
remindersEnabled: true,
reminderDelay: ['value' => 2, 'unit' => 'days'], // time to the first reminder
expirationEnabled: true,
expireAfter: ['value' => 14, 'unit' => 'days'] // how long the document stays signable
)
);
$status = TurboSign::getStatus('doc-uuid-here');
echo "Document Status: {$status->status}\n"; // 'under_review', 'completed', 'voided', 'expired', ...
// expiresAt is the signing-window deadline (ISO 8601), or null when expiration is off.
echo "Expires: " . ($status->expiresAt ?? 'never') . "\n";
$result = TurboSign::getRecipients('doc-uuid-here');
echo "Sent by {$result->document->sentBy->name} <{$result->document->sentBy->email}>\n";
echo "{$result->summary->completed} of {$result->summary->total} signed, ";
echo "still waiting on {$result->summary->waitingOn}\n";
foreach ($result->recipients as $recipient) {
// 'pending' | 'viewed' | 'completed' | 'voided' | 'expired'
echo " {$recipient->name} <{$recipient->email}>: {$recipient->effectiveStatus}\n";
if ($recipient->signedOn !== null) {
echo " Signed on: {$recipient->signedOn}\n";
}
echo " Emailed {$recipient->delivery->totalSent}x\n";
}
$pdfContent = TurboSign::download('doc-uuid-here');
// Save to file
file_put_contents('signed-contract.pdf', $pdfContent);
// Or send as HTTP response
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="signed.pdf"');
echo $pdfContent;
use TurboDocx\Types\Responses\VoidDocumentResponse;
$result = TurboSign::void('doc-uuid-here', 'Document needs to be revised');
echo "Status: {$result->status}\n";
echo "Voided at: {$result->voidedAt}\n";
// Resend to specific recipients (at least one ID is ient-id-1', 'recipient-id-2']);
echo "Message: {$result->message}\n";
use TurboDocx\TurboWebhooks;
// With TURBODOCX_API_KEY and TURBODOCX_ORG_ID set (and optionally TURBODOCX_BASE_URL), no call
// is needed: TurboWebhooks reads them with getenv() on first use and throws if either is missing.
// Or pass the credentials explicitly (both are
use TurboDocx\Types\Enums\WebhookEvent;
$created = TurboWebhooks::createWebhook(
urls: ['https://your-server.example.com/webhooks/turbodocx'], // HTTPS only; 1-10 URLs
events: [ // at least 1
WebhookEvent::SENT->value,
WebhookEvent::VIEWED->value,
WebhookEvent::RECIPIENT_SIGNED->value,
WebhookEvent::COMPLETED->value,
WebhookEvent::VOIDED->value,
],
// ...or subscribe to everything: events: WebhookEvent::all()
);
// `secret` is shown ONCE here. Store it securely; it cannot be retrieved later.
echo "Save this secret: {$created['secret']}";
$webhook = TurboWebhooks::getWebhook();
// $webhook['deliveryStats'] and $webhook['availableEvents'] are
use function TurboDocx\Utils\verifyWebhookSignature;
// In your webhook endpoint:
$rawBody = file_get_contents('php://input'); // CRITICAL: raw bytes only.
// Do NOT json_decode first; the
// signature is over the exact bytes.
$signature = $_SERVER['HTTP_X_TURBODOCX_SIGNATURE'] ?? '';
$timestamp = $_SERVER['HTTP_X_TURBODOCX_TIMESTAMP'] ?? '';
$secret = getenv('TURBODOCX_WEBHOOK_SECRET') ?: ''; // unset => '' => every check fails closed
if (!verifyWebhookSignature($rawBody, $signature, $timestamp, $secret)) {
http_response_code(401);
exit('invalid signature');
}
$event = json_decode($rawBody, true);
// ... process event ...
use TurboDocx\Types\SignatureFieldType;
SignatureFieldType::SIGNATURE // Signature field
SignatureFieldType::INITIAL // Initial field
SignatureFieldType::DATE // Date stamp
SignatureFieldType::TEXT // Free text input
SignatureFieldType::FULL_NAME // Full name
SignatureFieldType::FIRST_NAME // First name
SignatureFieldType::LAST_NAME // Last name
SignatureFieldType::EMAIL // Email address
SignatureFieldType::TITLE // Job title
SignatureFieldType::COMPANY // Company name
SignatureFieldType::CHECKBOX // Checkbox field (also the controlling field for conditional logic)
use TurboDocx\Types\Field;
use TurboDocx\Types\SignatureFieldType;
use TurboDocx\Types\TemplateConfig;
use TurboDocx\Types\FieldPlacement;
use TurboDocx\Types\FieldMetadata;
use TurboDocx\Types\FieldConditional;
use TurboDocx\Types\ConditionalOperator;
use TurboDocx\Types\ConditionalAction;
$fields = [
// Controlling checkbox — carries the fieldKey dependents reference
new Field(
type: SignatureFieldType::CHECKBOX,
recipientEmail: '[email protected]',
template: new TemplateConfig(anchor: '{request_changes}', placement: FieldPlacement::REPLACE, size: ['width' => 20, 'height' => 20]),
metadata: new FieldMetadata(fieldKey: 'request_changes')
),
// Dependent text field — hidden until the checkbox is checked ("If checked, explain")
new Field(
type: SignatureFieldType::TEXT,
recipientEmail: '[email protected]',
isMultiline: true,
template: new TemplateConfig(anchor: '{change_details}', placement: FieldPlacement::REPLACE, size: ['width' => 200, 'height' => 50]),
metadata: new FieldMetadata(
conditional: new FieldConditional(
controllingFieldKey: 'request_changes', // must equal the checkbox's fieldKey
operator: ConditionalOperator::IS_CHECKED, // IS_CHECKED | IS_NOT_CHECKED
action: ConditionalAction::SHOW // SHOW (hidden until met) | UNLOCK (read-only until met)
)
)
),
];
new Field(
type: SignatureFieldType::SIGNATURE,
recipientEmail: '[email protected]',
page: 1, // Page number (1-indexed)
x: 100, // X coordinate
y: 500, // Y coordinate
width: 200, // Width in pixels
height: 50 // Height in pixels
)
new Field(
type: SignatureFieldType::SIGNATURE,
recipientEmail: '[email protected]',
template: new TemplateConfig(
anchor: '{signature1}', // Text to find in PDF
placement: FieldPlacement::REPLACE, // How to place the field
size: ['width' => 100, 'height' => 30]
)
)
$result = TurboSign::sendSignature(
new SendSignatureRequest(
recipients: [
new Recipient('Alice', '[email protected]', 1), // Signs first
new Recipient('Bob', '[email protected]', 2), // Signs after Alice
new Recipient('Carol', '[email protected]', 3) // Signs last
],
fields: [
// Fields for each recipient...
],
file: file_get_contents('contract.pdf')
)
);
$result = TurboSign::sendSignature(/* ... */);
// Poll for completion
while (true) {
sleep(2);
$status = TurboSign::getStatus($result->documentId);
if ($status->status === 'completed') {
echo "Document completed!\n";
// Download signed document
$signedPdf = TurboSign::download($result->documentId);
file_put_contents('signed.pdf', $signedPdf);
break;
}
echo "Status: {$status->status}\n";
}
use TurboDocx\TurboPartner;
use TurboDocx\Config\PartnerClientConfig;
TurboPartner::configure(new PartnerClientConfig(
partnerApiKey: getenv('TURBODOCX_PARTNER_API_KEY') ?: '', // REQUIRED - must start with TDXP-
partnerId: getenv('TURBODOCX_PARTNER_ID') ?: '', // REQUIRED - your partner UUID
// (unset => '' => a clear "is
use TurboDocx\Types\Requests\Partner\AddOrgUserRequest;
use TurboDocx\Types\Enums\OrgUserRole;
$result = TurboPartner::addUserToOrganization(
'org-uuid-here',
new AddOrgUserRequest(
email: '[email protected]',
role: OrgUserRole::ADMIN // ADMIN, CONTRIBUTOR, USER, or VIEWER
)
);
echo "User ID: {$result->data->id}\n";
use TurboDocx\Types\Requests\Partner\ListOrgUsersRequest;
$result = TurboPartner::listOrganizationUsers(
'org-uuid-here',
new ListOrgUsersRequest(limit: 50, offset: 0)
);
echo "User Limit: {$result->userLimit}\n";
foreach ($result->results as $user) {
echo "- {$user->email} ({$user->role})\n";
}
use TurboDocx\Types\Requests\Partner\UpdateOrgUserRequest;
use TurboDocx\Types\Enums\OrgUserRole;
$result = TurboPartner::updateOrganizationUserRole(
'org-uuid-here',
'user-uuid-here',
new UpdateOrgUserRequest(role: OrgUserRole::CONTRIBUTOR)
);
use TurboDocx\Types\Requests\Partner\CreateOrgApiKeyRequest;
$result = TurboPartner::createOrganizationApiKey(
'org-uuid-here',
new CreateOrgApiKeyRequest(
name: 'Production API Key',
role: 'admin' // admin, contributor, or viewer
)
);
echo "Key ID: {$result->data->id}\n";
echo "Full Key: {$result->data->key}\n"; // Only shown once!
use TurboDocx\Types\Requests\Partner\ListOrgApiKeysRequest;
$result = TurboPartner::listOrganizationApiKeys(
'org-uuid-here',
new ListOrgApiKeysRequest(limit: 50)
);
use TurboDocx\Types\Requests\Partner\UpdateOrgApiKeyRequest;
$result = TurboPartner::updateOrganizationApiKey(
'org-uuid-here',
'api-key-uuid-here',
new UpdateOrgApiKeyRequest(name: 'Updated Key Name', role: 'contributor')
);
use TurboDocx\Types\Requests\Partner\AddPartnerUserRequest;
use TurboDocx\Types\Partner\PartnerPermissions;
$result = TurboPartner::addUserToPartnerPortal(
new AddPartnerUserRequest(
email: '[email protected]',
role: 'admin', // admin, member, or viewer
permissions: new PartnerPermissions(
canManageOrgs: true,
canManageOrgUsers: true,
canManagePartnerUsers: false,
canManageOrgAPIKeys: false,
canManagePartnerAPIKeys: false,
canUpdateEntitlements: true,
canViewAuditLogs: true
)
)
);
use TurboDocx\Types\Requests\Partner\ListPartnerUsersRequest;
$result = TurboPartner::listPartnerPortalUsers(
new ListPartnerUsersRequest(limit: 50)
);
use TurboDocx\Types\Requests\Partner\UpdatePartnerUserRequest;
use TurboDocx\Types\Partner\PartnerPermissions;
$result = TurboPartner::updatePartnerUserPermissions(
'partner-user-uuid-here',
new UpdatePartnerUserRequest(
role: 'admin',
permissions: new PartnerPermissions(
canManageOrgs: true,
canManageOrgUsers: true,
canManagePartnerUsers: true,
canManageOrgAPIKeys: true,
canManagePartnerAPIKeys: true,
canUpdateEntitlements: true,
canViewAuditLogs: true
)
)
);
$result = TurboQuote::sendQuote($quote->id, new SendQuoteRequest(
validUntil: date('Y-m-d', strtotime('+30 days')), // the expiry date (expireAfter is ignored)
remindersEnabled: true,
reminderDelay: ['value' => 3, 'unit' => 'days'], // time to the first reminder
reminderInterval: ['value' => 7, 'unit' => 'days'], // gap between later reminders
expirationEnabled: true,
expirationWarning: ['value' => 2, 'unit' => 'days'],// warn 2 days before validUntil
));
$quote = TurboQuote::getQuote($quoteId);
$prepared = $quote->preparedBy ?? [];
echo $prepared['name'] ?? ''; // e.g. "Acme Billing Integration" or the template sender
echo $prepared['email'] ?? ''; // may be absent — render a placeholder
use TurboDocx\Types\Requests\Quote\CreateQuoteRequest;
use TurboDocx\Types\Enums\RenewalPeriod;
// Fixed term (default 60 days) — do NOT send renewalPeriod
TurboQuote::createQuote(new CreateQuoteRequest(
name: 'Fixed Term Quote',
companyId: $company->id,
contactId: $contact->id,
termDays: 90,
));
// Auto-renewing quote — renewalPeriod is
use TurboDocx\Types\Requests\Quote\HandleExpiredQuoteRequest;
$reissued = TurboQuote::handleExpiredQuote($quote->id, new HandleExpiredQuoteRequest(
action: 'void', // 'void' or 'decline' — nothing else
reason: 'Pricing refreshed for the new fiscal year', //
use TurboDocx\Types\Enums\DiscountType;
// Percent discount (default)
new AddLineItemRequest(
productId: 'prod-uuid',
productName: 'Widget',
unitPrice: 100.00,
billingFrequency: 'monthly',
discountType: DiscountType::PERCENT->value, // 'percent'
discountPercent: 15, // 15% off
);
// Fixed-dollar discount
new AddLineItemRequest(
productId: 'prod-uuid',
productName: 'Widget',
unitPrice: 100.00,
billingFrequency: 'monthly',
discountType: DiscountType::AMOUNT->value, // 'amount'
discountAmount: 25.00, // $25 off per unit
);