PHP code example of turbodocx / sdk

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";

use TurboDocx\TurboSign;
use TurboDocx\Config\HttpClientConfig;

// Basic configuration (REQUIRED)
TurboSign::configure(new HttpClientConfig(
    apiKey: 'your-api-key',           // REQUIRED
    orgId: 'your-org-id',             // REQUIRED
    senderEmail: '[email protected]',   // REQUIRED - reply-to address for signature requests
    senderName: 'Your Company'        // OPTIONAL but strongly recommended
));

// With custom options
TurboSign::configure(new HttpClientConfig(
    apiKey: 'your-api-key',
    orgId: 'your-org-id',
    senderEmail: '[email protected]',
    senderName: 'Your Company',
    baseUrl: 'https://custom-api.example.com'  // Optional: custom API endpoint
));

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";

$audit = TurboSign::getAuditTrail('doc-uuid-here');

echo "Document: {$audit->document->name}\n";
echo "Audit Trail:\n";

foreach ($audit->auditTrail as $entry) {
    echo "  {$entry->actionType} - {$entry->timestamp}\n";
    if ($entry->user) {
        echo "    By: {$entry->user->name} ({$entry->user->email})\n";
    }
    if ($entry->recipient) {
        echo "    Recipient: {$entry->recipient->name}\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 

$tested = TurboWebhooks::testWebhook(
    eventType: 'signature.document.completed',
    payload: ['documentId' => 'doc-xyz', 'status' => 'completed'],
);
// $tested['summary']: ['total' => N, 'successful' => N, 'failed' => N]

$deliveries = TurboWebhooks::listWebhookDeliveries(limit: 10);
$replayed = TurboWebhooks::replayWebhookDelivery($deliveries['results'][0]['id']);

$rotated = TurboWebhooks::regenerateWebhookSecret();
// $rotated['secret'] is the new secret. Old signatures will fail immediately.

$stats = TurboWebhooks::getWebhookStats(days: 30);
// $stats['summary']['successRate'], $stats['eventBreakdown']

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]
    )
)

// Checkbox (pre-checked, readonly)
new Field(
    type: SignatureFieldType::CHECKBOX,
    recipientEmail: '[email protected]',
    page: 1,
    x: 100,
    y: 600,
    width: 20,
    height: 20,
    defaultValue: 'true',     // Pre-checked
    isReadonly: true          // Cannot be unchecked
)

// Multiline text field
new Field(
    type: SignatureFieldType::TEXT,
    recipientEmail: '[email protected]',
    page: 1,
    x: 100,
    y: 200,
    width: 400,
    height: 100,
    isMultiline: true,        // Allow multiple lines
    

$result = TurboSign::sendSignature(
    new SendSignatureRequest(
        file: file_get_contents('contract.pdf'),
        fileName: 'contract.pdf',  // Optional
        // ...
    )
);

$result = TurboSign::sendSignature(
    new SendSignatureRequest(
        fileLink: 'https://example.com/contract.pdf',
        // ...
    )
);

$result = TurboSign::sendSignature(
    new SendSignatureRequest(
        deliverableId: 'deliverable-uuid-from-turbodocx',
        // ...
    )
);

$result = 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')
    )
);

$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\CreateOrganizationRequest;

$result = TurboPartner::createOrganization(
    new CreateOrganizationRequest(
        name: 'Acme Corporation',
        features: ['maxUsers' => 50]  // Optional entitlements override
    )
);

echo "Organization ID: {$result->data->id}\n";

use TurboDocx\Types\Requests\Partner\ListOrganizationsRequest;

$result = TurboPartner::listOrganizations(
    new ListOrganizationsRequest(
        limit: 25,
        offset: 0,
        search: 'Acme'  // Optional search by name
    )
);

echo "Total: {$result->totalRecords}\n";
foreach ($result->results as $org) {
    echo "- {$org->name} (ID: {$org->id})\n";
}

$result = TurboPartner::getOrganizationDetails('org-uuid-here');

echo "Name: {$result->organization->name}\n";
if ($result->features) {
    echo "Max Users: {$result->features->maxUsers}\n";
}

use TurboDocx\Types\Requests\Partner\UpdateOrganizationRequest;

$result = TurboPartner::updateOrganizationInfo(
    'org-uuid-here',
    new UpdateOrganizationRequest(name: 'Acme Corp (Updated)')
);

use TurboDocx\Types\Requests\Partner\UpdateEntitlementsRequest;

$result = TurboPartner::updateOrganizationEntitlements(
    'org-uuid-here',
    new UpdateEntitlementsRequest(
        features: [
            'maxUsers' => 100,
            'maxStorage' => 10737418240,  // 10GB
            'maxSignatures' => 500,
            'hasTDAI' => true,
            'hasFileDownload' => true,
        ]
    )
);

$prefs = TurboPartner::getOrganizationPreferences('org-uuid-here')->preferences;

var_dump($prefs->hideSignatureOutline);   // false by default
var_dump($prefs->hideSignatureHash);      // false by default
var_dump($prefs->lockedFieldsBackground); // true by default
var_dump($prefs->allowDownloadBeforeSigning); // false by default

$result = TurboPartner::updateOrganizationPreferences('org-uuid-here', [
    'lockedFieldsBackground' => false,  // render locked fields as plain text
]);

var_dump($result->preferences->lockedFieldsBackground); // false

$result = TurboPartner::deleteOrganization('org-uuid-here');
echo "Success: " . ($result->success ? 'Yes' : 'No') . "\n";

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)
);

$result = TurboPartner::resendOrganizationInvitationToUser(
    'org-uuid-here',
    'user-uuid-here'
);

$result = TurboPartner::removeUserFromOrganization(
    'org-uuid-here',
    'user-uuid-here'
);

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')
);

$result = TurboPartner::revokeOrganizationApiKey(
    'org-uuid-here',
    'api-key-uuid-here'
);

use TurboDocx\Types\Requests\Partner\CreatePartnerApiKeyRequest;
use TurboDocx\Types\Enums\PartnerScope;

$result = TurboPartner::createPartnerApiKey(
    new CreatePartnerApiKeyRequest(
        name: 'Integration API Key',
        scopes: [
            PartnerScope::ORG_CREATE,
            PartnerScope::ORG_READ,
            PartnerScope::ORG_UPDATE,
            PartnerScope::ENTITLEMENTS_UPDATE,
            PartnerScope::AUDIT_READ,
        ],
        description: 'For third-party integration',
    )
);

echo "Key ID: {$result->data->id}\n";
echo "Full Key: {$result->data->key}\n";  // Only shown once!

use TurboDocx\Types\Requests\Partner\ListPartnerApiKeysRequest;

$result = TurboPartner::listPartnerApiKeys(
    new ListPartnerApiKeysRequest(limit: 50)
);

foreach ($result->results as $key) {
    echo "{$key->name}: {$key->key}\n"; // Shows masked preview
}

use TurboDocx\Types\Requests\Partner\UpdatePartnerApiKeyRequest;

$result = TurboPartner::updatePartnerApiKey(
    'partner-key-uuid-here',
    new UpdatePartnerApiKeyRequest(
        name: 'Updated Integration Key',
        description: 'Updated description'
    )
);

$result = TurboPartner::revokePartnerApiKey('partner-key-uuid-here');

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 = TurboPartner::resendPartnerPortalInvitationToUser('partner-user-uuid-here');

$result = TurboPartner::removeUserFromPartnerPortal('partner-user-uuid-here');

use TurboDocx\Types\Requests\Partner\ListAuditLogsRequest;

$result = TurboPartner::getPartnerAuditLogs(
    new ListAuditLogsRequest(
        limit: 50,
        offset: 0,
        action: 'ORG_CREATED',           // Optional filter
        resourceType: 'organization',    // Optional filter
        success: true,                   // Optional filter
        startDate: '2024-01-01',        // Optional filter
        endDate: '2024-12-31'           // Optional filter
    )
);

foreach ($result->results as $entry) {
    echo "{$entry->action} - {$entry->createdOn}\n";
}

use TurboDocx\TurboQuote;
use TurboDocx\Config\QuoteClientConfig;

TurboQuote::configure(new QuoteClientConfig(
    apiKey: getenv('TURBODOCX_API_KEY') ?: null,  // REQUIRED
    orgId: getenv('TURBODOCX_ORG_ID') ?: null,    // REQUIRED
    // baseUrl: 'https://api.turbodocx.com', // Optional
));

// Or auto-configure from environment
TurboQuote::configure(QuoteClientConfig::fromEnvironment());

use TurboDocx\TurboQuote;
use TurboDocx\Config\QuoteClientConfig;
use TurboDocx\Types\Requests\Quote\CreateCompanyRequest;
use TurboDocx\Types\Requests\Quote\CreateContactRequest;
use TurboDocx\Types\Requests\Quote\CreateQuoteRequest;
use TurboDocx\Types\Requests\Quote\AddLineItemRequest;
use TurboDocx\Types\Requests\Quote\SendQuoteRequest;

TurboQuote::configure(new QuoteClientConfig(
    apiKey: getenv('TURBODOCX_API_KEY') ?: null,
    orgId: getenv('TURBODOCX_ORG_ID') ?: null,
));

// Create company + contact
$company = TurboQuote::createCompany(new CreateCompanyRequest(
    name: 'Acme Corp',
    contacts: [['name' => 'Jane Smith', 'email' => '[email protected]']],
));
$contact = TurboQuote::createContact(new CreateContactRequest(
    name: 'Jane Smith',
    companyId: $company->id,
    email: '[email protected]',
));

// Create quote
$quote = TurboQuote::createQuote(new CreateQuoteRequest(
    name: 'Enterprise License Q3',
    companyId: $company->id,
    contactId: $contact->id,
    currency: 'USD',
    termDays: 60,  // Optional — defaults to 60
));

// Add line items
TurboQuote::addLineItems($quote->id, [
    new AddLineItemRequest(
        productId: null,
        productName: 'Platform Subscription',
        unitPrice: 499.00,
        billingFrequency: 'monthly',
        quantity: 1,
    ),
    new AddLineItemRequest(
        productId: null,
        productName: 'Professional Services',
        unitPrice: 2500.00,
        billingFrequency: 'one-time',
        quantity: 5,
        discountType: 'percent',
        discountPercent: 10,
    ),
]);

// Send to customer
$result = TurboQuote::sendQuote($quote->id, new SendQuoteRequest(
    validUntil: date('Y-m-d', strtotime('+30 days')),
));

echo "Quote sent! Status: {$result->quote->status}\n";

$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
);

use TurboDocx\Types\Requests\Quote\CreatePriceBookRequest;

$priceBook = TurboQuote::createPriceBook(new CreatePriceBookRequest(
    name: 'Partner Tier A',
    priceBookTypeId: 'pbt-uuid-here',
    validFrom: '2026-01-01',
    discountPercent: 15.0,
    productPricing: [
        // Per-product overrides
        [
            'productId' => 'prod-uuid',
            'discountType' => 'amount',
            'discountAmount' => 50.00,
        ],
    ],
));

// Apply to a quote
TurboQuote::applyPriceBook($quote->id, $priceBook->id);

use TurboDocx\Exceptions\AuthenticationException;
use TurboDocx\Exceptions\ValidationException;
use TurboDocx\Exceptions\NotFoundException;
use TurboDocx\Exceptions\RateLimitException;
use TurboDocx\Exceptions\NetworkException;

try {
    $result = TurboSign::sendSignature(/* ... */);
} catch (AuthenticationException $e) {
    // 401 - Invalid API key or access token
    echo "Authentication failed: {$e->getMessage()}\n";
} catch (ValidationException $e) {
    // 400 - Invalid request data
    echo "Validation error: {$e->getMessage()}\n";
} catch (NotFoundException $e) {
    // 404 - Document not found
    echo "Not found: {$e->getMessage()}\n";
} catch (RateLimitException $e) {
    // 429 - Rate limit exceeded
    echo "Rate limit: {$e->getMessage()}\n";
} catch (NetworkException $e) {
    // Network/connection error
    echo "Network error: {$e->getMessage()}\n";
}