diff --git a/docs/TurboSign/API Bulk Signatures.md b/docs/TurboSign/API Bulk Signatures.md
new file mode 100644
index 0000000..f6f044a
--- /dev/null
+++ b/docs/TurboSign/API Bulk Signatures.md
@@ -0,0 +1,713 @@
+---
+title: TurboSign Bulk API Integration
+sidebar_position: 4
+description: Send documents for signature at scale using TurboSign Bulk API. Process hundreds or thousands of signature requests in batches with comprehensive tracking and management capabilities.
+keywords:
+ - turbosign bulk api
+ - bulk signature api
+ - batch signature sending
+ - mass document signing
+ - bulk e-signature
+ - batch processing api
+ - bulk upload api
+ - signature automation
+ - batch document workflow
+ - turbodocx bulk api
+ - bulk signature integration
+ - api batch processing
+ - mass signature collection
+ - enterprise signature api
+ - bulk sending api
+ - batch management api
+ - signature tracking api
+ - bulk document processing
+ - scalable signature api
+ - high-volume signatures
+---
+
+import ScriptLoader from '@site/src/components/ScriptLoader';
+
+# TurboSign Bulk API Integration
+
+Send documents for electronic signature at scale. The TurboSign Bulk API allows you to process hundreds or thousands of signature requests in organized batches, with comprehensive tracking and management capabilities.
+
+
+
+## Overview
+
+The Bulk API is designed for high-volume signature collection scenarios. Instead of sending documents one at a time, you can create batches that process multiple signature requests efficiently.
+
+### What is Bulk Sending?
+
+Bulk sending allows you to send the **same document to multiple recipients** in a single batch operation. Each recipient gets their own personalized copy with customized signature fields and recipient information.
+
+**Common Use Cases:**
+
+- **Employment Contracts**: Send offer letters or contracts to multiple new hires
+- **Client Agreements**: Distribute service agreements to hundreds of clients
+- **Policy Updates**: Send updated terms or policies requiring acknowledgment
+- **Event Registrations**: Collect signatures from event participants
+- **Real Estate**: Send disclosure forms to multiple buyers or sellers
+- **Insurance**: Distribute policy documents requiring signatures
+
+### Key Benefits
+
+- **Efficiency**: Create one batch instead of hundreds of individual requests
+- **Cost Effective**: Batch processing optimizes credit usage
+- **Tracking**: Monitor progress across all jobs in a batch
+- **Management**: Cancel, retry, or review jobs as needed
+- **Scalability**: Handle thousands of signature requests seamlessly
+- **Organization**: Group related requests together logically
+
+## Key Concepts
+
+Understanding these core concepts will help you effectively use the Bulk API.
+
+### Batches
+
+A **batch** is a container for multiple document send operations. All documents in a batch share the same source file (PDF, template, or deliverable) but can have different recipients and field configurations.
+
+### Jobs
+
+A **job** represents a single document send operation within a batch. Each job corresponds to one set of recipients receiving the shared document.
+
+### Source Types
+
+The Bulk API supports four source types for your documents:
+
+| Source Type | Description | When to Use |
+| --------------- | ------------------------------ | ---------------------------------- |
+| `file` | Upload PDF directly | One-time documents, custom content |
+| `deliverableId` | Reference existing deliverable | Documents generated from templates |
+| `templateId` | Use template directly | Reusable document structures |
+| `fileLink` | URL to document | Documents hosted externally |
+
+### Batch-Level vs Document-Level Configuration
+
+**Batch-Level:**
+
+- Source file (all jobs use same document)
+- Default metadata (documentName, documentDescription)
+- Default sender information
+
+**Document-Level:**
+
+- Recipients (unique per job)
+- Signature fields (customized per job)
+- Optional metadata overrides per job
+
+## Prerequisites
+
+Before using the Bulk API, ensure you have:
+
+- **API Access Token**: Bearer token for authentication
+- **Organization ID**: Your organization identifier
+- **Bulk Sending Enabled**: Feature must be enabled for your organization
+- **Sufficient Credits**: Credits will be reserved when creating batches
+
+### Getting Your Credentials
+
+1. **Login to TurboDocx**: Visit [https://www.turbodocx.com](https://www.turbodocx.com)
+2. **Navigate to Settings**: Access your organization settings
+3. **API Keys Section**: Generate or retrieve your API access token
+4. **Organization ID**: Copy your organization ID from the settings
+
+
+
+
+## Authentication
+
+All TurboSign Bulk API requests require authentication using a Bearer token:
+
+```http
+Authorization: Bearer YOUR_API_TOKEN
+```
+
+Additional required headers for all requests:
+
+```http
+x-rapiddocx-org-id: YOUR_ORGANIZATION_ID
+User-Agent: TurboDocx API Client
+```
+
+:::tip Authentication
+These authentication headers are identical to the single-step API. If you're already using the single-step API, you can use the same credentials for bulk operations.
+:::
+
+## API Endpoints
+
+The Bulk API provides four endpoints for complete batch management:
+
+| Endpoint | Method | Purpose |
+| --------------------------------------- | ------ | --------------------------------- |
+| `/turbosign/bulk/ingest` | POST | Create and ingest a new batch |
+| `/turbosign/bulk/batches` | GET | List all batches for organization |
+| `/turbosign/bulk/batch/:batchId/jobs` | GET | List jobs within a specific batch |
+| `/turbosign/bulk/batch/:batchId/cancel` | POST | Cancel a batch and pending jobs |
+
+---
+
+## Endpoint 1: Ingest Bulk Batch
+
+Create a new batch and ingest multiple document send operations. This is the primary endpoint for initiating bulk sending.
+
+### Endpoint
+
+```http
+POST https://api.turbodocx.com/turbosign/bulk/ingest
+```
+
+### Headers
+
+```http
+Content-Type: multipart/form-data
+Authorization: Bearer YOUR_API_TOKEN
+x-rapiddocx-org-id: YOUR_ORGANIZATION_ID
+User-Agent: TurboDocx API Client
+```
+
+### Request Body (multipart/form-data)
+
+⚠️ **Important**: `documents` must be sent as a JSON string in form-data
+
+| Field | Type | Required | Description |
+| ------------------- | ----------------- | ------------- | ----------------------------------------------------------------- |
+| sourceType | String | **Yes** | Source type: `file`, `deliverableId`, `templateId`, or `fileLink` |
+| file | File | Conditional\* | PDF file upload (required when sourceType is `file`) |
+| sourceValue | String (UUID/URL) | Conditional\* | UUID or URL (required for deliverableId/templateId/fileLink) |
+| batchName | String | **Yes** | Name for this batch (max 255 chars) |
+| documentName | String | No | Default document name for all jobs (max 255 chars) |
+| documentDescription | String | No | Default description for all jobs (max 1000 chars) |
+| senderName | String | No | Name of sender (max 255 chars) |
+| senderEmail | String (email) | No | Email of sender |
+| documents | String (JSON) | **Yes** | JSON string array of document objects |
+
+\* **File Source**: Must provide exactly ONE of:
+
+- `file` (upload) when sourceType is `file`
+- `sourceValue` (UUID) when sourceType is `deliverableId` or `templateId`
+- `sourceValue` (URL) when sourceType is `fileLink`
+
+### Documents Array Format
+
+The `documents` parameter must be a JSON string containing an array of document objects:
+
+```json
+[
+ {
+ "recipients": [
+ {
+ "name": "John Smith",
+ "email": "john.smith@company.com",
+ "signingOrder": 1
+ }
+ ],
+ "fields": [
+ {
+ "recipientEmail": "john.smith@company.com",
+ "type": "signature",
+ "page": 1,
+ "x": 100,
+ "y": 200,
+ "width": 200,
+ "height": 80,
+ "required": true
+ },
+ {
+ "recipientEmail": "john.smith@company.com",
+ "type": "date",
+ "page": 1,
+ "x": 100,
+ "y": 300,
+ "width": 150,
+ "height": 30,
+ "required": true
+ }
+ ],
+ "documentName": "Employment Contract - John Smith",
+ "documentDescription": "Please review and sign your employment contract"
+ },
+ {
+ "recipients": [
+ {
+ "name": "Jane Doe",
+ "email": "jane.doe@company.com",
+ "signingOrder": 1
+ }
+ ],
+ "fields": [
+ {
+ "recipientEmail": "jane.doe@company.com",
+ "type": "signature",
+ "page": 1,
+ "x": 100,
+ "y": 200,
+ "width": 200,
+ "height": 80,
+ "required": true
+ }
+ ],
+ "documentName": "Employment Contract - Jane Doe"
+ }
+]
+```
+
+**Document Object Properties:**
+
+| Property | Type | Required | Description |
+| ------------------- | ------ | -------- | ----------------------------------------------------------- |
+| recipients | Array | **Yes** | Array of recipient objects (same format as single-step API) |
+| fields | Array | **Yes** | Array of field objects (same format as single-step API) |
+| documentName | String | No | Override batch-level document name for this job |
+| documentDescription | String | No | Override batch-level description for this job |
+
+:::tip Field Types
+All field types from the single-step API are supported: `signature`, `initial`, `date`, `full_name`, `first_name`, `last_name`, `title`, `company`, `email`, `text`, `checkbox`. See the [single-step API documentation](/docs/TurboSign/API%20Signatures) for details.
+:::
+
+### Response (Success)
+
+```json
+{
+ "success": true,
+ "batchId": "550e8400-e29b-41d4-a716-446655440000",
+ "batchName": "Q4 Employment Contracts",
+ "totalJobs": 50,
+ "status": "pending",
+ "message": "Batch created successfully with 50 jobs. Processing will begin shortly."
+}
+```
+
+### Response (Error)
+
+```json
+{
+ "error": "Validation failed for 3 documents",
+ "code": "BulkValidationFailed",
+ "data": {
+ "invalidDocuments": [
+ {
+ "index": 0,
+ "errors": ["recipients[0].email is required"]
+ },
+ {
+ "index": 5,
+ "errors": ["fields[0].recipientEmail does not match any recipient"]
+ }
+ ]
+ }
+}
+```
+
+### Code Example
+
+
+
+---
+
+## Endpoint 2: List All Batches
+
+Retrieve all batches for your organization with pagination and filtering capabilities.
+
+### Endpoint
+
+```http
+GET https://api.turbodocx.com/turbosign/bulk/batches
+```
+
+### Headers
+
+```http
+Authorization: Bearer YOUR_API_TOKEN
+x-rapiddocx-org-id: YOUR_ORGANIZATION_ID
+User-Agent: TurboDocx API Client
+```
+
+### Query Parameters
+
+| Parameter | Type | Required | Description |
+| --------- | ------------------ | -------- | ----------------------------------------------------------------------------- |
+| limit | Number | No | Number of batches to return (default: 20, max: 100) |
+| offset | Number | No | Number of batches to skip (default: 0) |
+| query | String | No | Search query to filter batches by name |
+| status | String or String[] | No | Filter by status: `pending`, `processing`, `completed`, `failed`, `cancelled` |
+| startDate | String (ISO 8601) | No | Filter batches created on or after this date |
+| endDate | String (ISO 8601) | No | Filter batches created on or before this date |
+
+### Response
+
+```json
+{
+ "data": {
+ "batches": [
+ {
+ "id": "550e8400-e29b-41d4-a716-446655440000",
+ "name": "Q4 Employment Contracts",
+ "status": "completed",
+ "totalJobs": 50,
+ "succeededJobs": 48,
+ "failedJobs": 2,
+ "pendingJobs": 0,
+ "createdOn": "2025-01-15T10:30:00Z",
+ "updatedOn": "2025-01-15T14:25:00Z",
+ "metadata": {}
+ },
+ {
+ "id": "660e8400-e29b-41d4-a716-446655440001",
+ "name": "Client Agreements - January 2025",
+ "status": "processing",
+ "totalJobs": 100,
+ "succeededJobs": 75,
+ "failedJobs": 5,
+ "pendingJobs": 20,
+ "createdOn": "2025-01-16T09:00:00Z",
+ "updatedOn": "2025-01-16T12:30:00Z",
+ "metadata": {}
+ }
+ ],
+ "totalRecords": 2
+ }
+}
+```
+
+### Code Example
+
+
+
+---
+
+## Endpoint 3: List Jobs in Batch
+
+Retrieve all jobs within a specific batch with detailed status information.
+
+### Endpoint
+
+```http
+GET https://api.turbodocx.com/turbosign/bulk/batch/:batchId/jobs
+```
+
+### Headers
+
+```http
+Authorization: Bearer YOUR_API_TOKEN
+x-rapiddocx-org-id: YOUR_ORGANIZATION_ID
+User-Agent: TurboDocx API Client
+```
+
+### Path Parameters
+
+| Parameter | Type | Required | Description |
+| --------- | ------------- | -------- | --------------- |
+| batchId | String (UUID) | **Yes** | ID of the batch |
+
+### Query Parameters
+
+| Parameter | Type | Required | Description |
+| --------- | ------------------ | -------- | ------------------------------------------------ |
+| limit | Number | No | Number of jobs to return (default: 20, max: 100) |
+| offset | Number | No | Number of jobs to skip (default: 0) |
+| status | String or String[] | No | Filter by job status |
+| query | String | No | Search query to filter jobs |
+
+### Response
+
+```json
+{
+ "data": {
+ "batchId": "550e8400-e29b-41d4-a716-446655440000",
+ "batchName": "Q4 Employment Contracts",
+ "batchStatus": "completed",
+ "jobs": [
+ {
+ "id": "770e8400-e29b-41d4-a716-446655440000",
+ "batchId": "550e8400-e29b-41d4-a716-446655440000",
+ "documentId": "880e8400-e29b-41d4-a716-446655440000",
+ "documentName": "Employment Contract - John Smith",
+ "status": "SUCCEEDED",
+ "recipientEmails": ["john.smith@company.com"],
+ "attempts": 0,
+ "errorCode": null,
+ "errorMessage": null,
+ "createdOn": "2025-01-15T10:30:00Z",
+ "updatedOn": "2025-01-15T11:45:00Z",
+ "lastAttemptedAt": "2025-01-15T11:45:00Z"
+ },
+ {
+ "id": "770e8400-e29b-41d4-a716-446655440001",
+ "batchId": "550e8400-e29b-41d4-a716-446655440000",
+ "documentId": null,
+ "documentName": "Employment Contract - Jane Doe",
+ "status": "FAILED",
+ "recipientEmails": ["jane.doe@company.com"],
+ "attempts": 1,
+ "errorCode": "INVALID_EMAIL",
+ "errorMessage": "Invalid recipient email address",
+ "createdOn": "2025-01-15T10:30:00Z",
+ "updatedOn": "2025-01-15T10:31:00Z",
+ "lastAttemptedAt": "2025-01-15T10:31:00Z"
+ }
+ ],
+ "totalJobs": 50,
+ "totalRecords": 50,
+ "succeededJobs": 48,
+ "failedJobs": 2,
+ "pendingJobs": 0
+ }
+}
+```
+
+### Code Example
+
+
+
+---
+
+## Endpoint 4: Cancel Batch
+
+Cancel a batch and stop all pending jobs. Already completed jobs are not affected.
+
+### Endpoint
+
+```http
+POST https://api.turbodocx.com/turbosign/bulk/batch/:batchId/cancel
+```
+
+### Headers
+
+```http
+Authorization: Bearer YOUR_API_TOKEN
+x-rapiddocx-org-id: YOUR_ORGANIZATION_ID
+User-Agent: TurboDocx API Client
+```
+
+### Path Parameters
+
+| Parameter | Type | Required | Description |
+| --------- | ------------- | -------- | ------------------------- |
+| batchId | String (UUID) | **Yes** | ID of the batch to cancel |
+
+### Response
+
+```json
+{
+ "success": true,
+ "message": "Batch cancelled. 20 job(s) stopped, 20 credit(s) refunded.",
+ "batchId": "550e8400-e29b-41d4-a716-446655440000",
+ "cancelledJobs": 20,
+ "succeededJobs": 30,
+ "refundedCredits": 20,
+ "status": "cancelled"
+}
+```
+
+### Code Example
+
+
+
+:::warning Cancellation Notice
+
+- Only pending and processing jobs will be cancelled
+- Already succeeded jobs are not affected
+- Credits are refunded only for cancelled jobs
+- Cancellation cannot be undone
+ :::
+
+---
+
+## Best Practices
+
+### Batch Size
+
+**Recommended batch sizes:**
+
+- Small batches (10-50 jobs): Faster processing, easier to monitor
+- Medium batches (50-200 jobs): Balanced efficiency
+- Large batches (200-1000 jobs): Maximum efficiency for large-scale operations
+
+:::tip Batch Size Strategy
+For first-time use, start with smaller batches (10-20 jobs) to validate your configuration. Once confident, scale up to larger batches.
+:::
+
+### Error Handling
+
+**Always check for validation errors:**
+
+```javascript
+if (response.data.code === "BulkValidationFailed") {
+ const errors = response.data.data.invalidDocuments;
+ errors.forEach((err) => {
+ console.log(`Document ${err.index}: ${err.errors.join(", ")}`);
+ });
+}
+```
+
+**Monitor job failures:**
+
+- Regularly check job status
+- Review error messages for failed jobs
+- Fix issues and retry with corrected data
+
+### Credit Management
+
+**Credits are reserved when batch is created:**
+
+- 1 credit per recipient per job
+- Credits are consumed as jobs succeed
+- Credits are refunded for failed or cancelled jobs
+
+**Example**: Batch with 50 jobs, 1 recipient each = 50 credits reserved
+
+### Performance Tips
+
+1. **Use templateId or deliverableId**: Faster than file uploads
+2. **Batch similar documents**: Group documents with similar configurations
+3. **Validate data before submission**: Prevent validation errors
+4. **Use pagination**: When listing large result sets
+5. **Implement webhooks**: For real-time status updates (see [Webhooks documentation](/docs/TurboSign/Webhooks))
+
+### Data Validation
+
+**Before creating a batch, validate:**
+
+- All email addresses are valid
+- Field positioning doesn't overlap
+- RecipientEmail in fields matches actual recipients
+- Required fields are present
+
+---
+
+## Error Codes
+
+Common error codes you may encounter:
+
+| Code | Description | Solution |
+| ---------------------- | ------------------------------------------- | --------------------------------------- |
+| `FileUploadRequired` | No file provided when sourceType is 'file' | Include file in form-data |
+| `SourceValueRequired` | No sourceValue for deliverableId/templateId | Provide valid UUID |
+| `InvalidSourceValue` | sourceValue provided with file upload | Remove sourceValue when using file |
+| `InvalidDocumentsJSON` | Documents JSON is malformed | Validate JSON structure |
+| `BulkValidationFailed` | One or more documents failed validation | Check data.invalidDocuments for details |
+| `DeliverableNotFound` | deliverableId doesn't exist | Verify deliverable exists |
+| `TemplateNotFound` | templateId doesn't exist | Verify template exists |
+| `BatchNotFound` | batchId doesn't exist | Check batch ID |
+| `BatchNotCancellable` | Batch already completed | Cannot cancel completed batches |
+| `InsufficientCredits` | Not enough credits for batch | Add credits to organization |
+
+---
+
+## Limits and Quotas
+
+### Batch Limits
+
+| Limit | Value |
+| -------------------------- | -------------- |
+| Maximum jobs per batch | 1,000 |
+| Maximum document size | 25 MB |
+| Maximum recipients per job | 50 |
+| Maximum fields per job | 100 |
+| Maximum batch name length | 255 characters |
+
+### Rate Limits
+
+| Endpoint | Rate Limit |
+| ------------ | ------------------ |
+| Ingest batch | 10 requests/minute |
+| List batches | 60 requests/minute |
+| List jobs | 60 requests/minute |
+| Cancel batch | 10 requests/minute |
+
+:::warning Rate Limiting
+If you exceed rate limits, you'll receive a `429 Too Many Requests` response. Implement exponential backoff in your retry logic.
+:::
+
+### Credit Consumption
+
+- **1 credit per recipient**: Each recipient in each job consumes 1 credit
+- **Reserved on creation**: Credits are reserved when batch is created
+- **Consumed on success**: Credits are consumed when job succeeds
+- **Refunded on failure/cancellation**: Credits are refunded for failed or cancelled jobs
+
+**Example Calculations:**
+
+- 100 jobs × 1 recipient = 100 credits
+- 50 jobs × 2 recipients = 100 credits
+- 200 jobs × 1 recipient = 200 credits
+
+---
+
+## Troubleshooting
+
+### Batch Stuck in "Processing"
+
+**Possible causes:**
+
+- High system load (normal during peak times)
+- Large batch size
+- Complex document processing
+
+**Solutions:**
+
+- Wait for processing to complete (usually < 30 minutes)
+- Check job status to see progress
+- Contact support if stuck for > 1 hour
+
+### Jobs Failing with "Invalid Email"
+
+**Cause**: Email validation failed
+
+**Solutions:**
+
+- Verify email format
+- Check for typos
+- Ensure no special characters in local part
+
+### Credits Not Refunded
+
+**Cause**: Jobs succeeded before cancellation
+
+**Solution**: Credits are only refunded for cancelled jobs. Already succeeded jobs consume credits.
+
+### "DeliverableNotFound" Error
+
+**Possible causes:**
+
+- Wrong organization ID
+- Deliverable deleted
+- Incorrect deliverable ID
+
+**Solutions:**
+
+- Verify deliverable exists in your organization
+- Check organization ID matches
+- Generate new deliverable if needed
+
+---
+
+## Next Steps
+
+Now that you understand the Bulk API, you might want to:
+
+- **Set up webhooks**: Get real-time notifications for batch and job status changes. See [Webhooks documentation](/docs/TurboSign/Webhooks)
+- **Explore single-step API**: For individual document sending. See [Single-Step API documentation](/docs/TurboSign/API%20Signatures)
+- **Review field types**: Learn about all available signature field types
+- **Integrate with your app**: Build bulk sending into your application workflow
+
+---
+
+_Have questions? Contact our support team at support@turbodocx.com_
diff --git a/docs/TurboSign/API Signatures.md b/docs/TurboSign/API Signatures.md
index 0426deb..b73de02 100644
--- a/docs/TurboSign/API Signatures.md
+++ b/docs/TurboSign/API Signatures.md
@@ -1,12 +1,15 @@
---
title: TurboSign API Integration
sidebar_position: 3
-description: Complete guide for integrating TurboSign API to upload documents, add recipients, and prepare documents for electronic signatures. Learn the 3-step process with detailed examples and code samples.
+description: Complete guide for integrating TurboSign API using single-step document preparation. Send documents for electronic signatures in one API call with our simplified workflow.
keywords:
- turbosign api
+ - single-step signature api
- document upload api
- electronic signature api
- - recipients api
+ - multipart form data
+ - prepare for review
+ - prepare for signing
- signature preparation
- api integration
- document signing workflow
@@ -35,26 +38,31 @@ import ScriptLoader from '@site/src/components/ScriptLoader';
# TurboSign API Integration
-This comprehensive guide walks you through the TurboSign API integration process. Learn how to programmatically upload documents, add recipients, and prepare documents for electronic signatures using our RESTful API.
+This comprehensive guide walks you through the TurboSign single-step API integration. Learn how to programmatically upload documents, configure recipients, set up signature fields, and send documents for electronic signatures using a single, streamlined API call.

## Overview
-The TurboSign API follows a simple 3-step process to prepare documents for electronic signatures:
+The TurboSign API provides a simplified single-step process to prepare documents for electronic signatures. Instead of multiple API calls, you can now accomplish everything in one request.
-1. **Upload Document** - Upload your PDF document to TurboSign
-2. **Add Recipients** - Configure who needs to sign and their signing order
-3. **Prepare for Signing** - Set up signature fields and send for signing
+### Two Endpoint Options
-
+TurboSign offers two single-step endpoints to fit different workflows:
+
+1. **Prepare for Review** - Upload and get preview URL (no emails sent)
+2. **Prepare for Signing** - Upload and send immediately (emails sent)
+
+
### Key Features
-- **RESTful API**: Standard HTTP methods with JSON payloads
+- **Single API Call**: Upload document, add recipients, and configure fields in one request
+- **RESTful API**: Standard HTTP methods with multipart/form-data
- **Bearer Token Authentication**: Secure API access using JWT tokens
- **Multiple Recipients**: Support for multiple signers with custom signing order
- **Flexible Field Placement**: Position signature fields using anchors or coordinates
+- **Multiple File Sources**: Upload file, or reference deliverableId, templateId, or fileLink
- **Real-time Status Updates**: Track document status throughout the signing process
- **Webhook Integration**: Receive notifications when signing is complete
@@ -64,58 +72,48 @@ Don't want to read all the details? Here's what you need to know:
### Available Field Types
-| Type | Description | Use Case |
-| ----------- | -------------------------- | ------------------------------------------------- |
-| `signature` | Electronic signature field | Legal signatures |
-| `initial` | Initial field | Document initials, paragraph acknowledgments |
-| `date` | Date picker field | Signing date, agreement date |
-| `full_name` | Full name field | Automatically fills signer's complete name |
-| `first_name`| First name field | Automatically fills signer's first name |
-| `last_name` | Last name field | Automatically fills signer's last name |
-| `title` | Title/job title field | Professional title or position |
-| `company` | Company name field | Organization or company name |
-| `email` | Email address field | Signer's email address |
-| `text` | Generic text input field | Custom text, notes, or any other text input |
-
-### Complete 3-Step Workflow
+| Type | Description | Use Case |
+| ------------ | -------------------------- | -------------------------------------------- |
+| `signature` | Electronic signature field | Legal signatures |
+| `initial` | Initial field | Document initials, paragraph acknowledgments |
+| `date` | Date picker field | Signing date, agreement date |
+| `full_name` | Full name field | Automatically fills signer's complete name |
+| `first_name` | First name field | Automatically fills signer's first name |
+| `last_name` | Last name field | Automatically fills signer's last name |
+| `title` | Title/job title field | Professional title or position |
+| `company` | Company name field | Organization or company name |
+| `email` | Email address field | Signer's email address |
+| `text` | Generic text input field | Custom text, notes, or any other text input |
+| `checkbox` | Checkbox field | Acknowledgments, consent, agreements |
+
+### Quick Start: Prepare for Signing (Most Common)
+
+Use this endpoint to send documents immediately for signing:
-### Quick Coordinate Example
+### Alternative: Prepare for Review
-If you prefer using exact coordinates instead of text anchors:
+Use this endpoint when you need a preview URL to verify field placement:
-```json
-// Step 3 payload using coordinates instead of templates
-[
- {
- "recipientId": "5f673f37-9912-4e72-85aa-8f3649760f6b",
- "type": "signature",
- "page": 1,
- "x": 100,
- "y": 200,
- "width": 200,
- "height": 80,
- "pageWidth": 612,
- "pageHeight": 792
- },
- {
- "recipientId": "5f673f37-9912-4e72-85aa-8f3649760f6b",
- "type": "date",
- "page": 1,
- "x": 350,
- "y": 300,
- "width": 150,
- "height": 30,
- "pageWidth": 612,
- "pageHeight": 792
- }
-]
-```
+
+
+### Quick Comparison
+
+| Feature | prepare-for-review | prepare-for-signing |
+| -------------------- | ------------------------------ | ------------------------- |
+| Sends emails? | ❌ No | ✅ Yes |
+| Returns preview URL? | ✅ Yes | ❌ No |
+| Final status | REVIEW_READY | UNDER_REVIEW |
+| Use when | Need to verify field placement | Ready to send immediately |
Now that you've seen the whole thing, let's dive into the details...
@@ -134,8 +132,8 @@ Before you begin, ensure you have:
3. **API Keys Section**: Generate or retrieve your API access token
4. **Organization ID**: Copy your organization ID from the settings
-
-
+
+
## Authentication
@@ -152,14 +150,40 @@ x-rapiddocx-org-id: YOUR_ORGANIZATION_ID
User-Agent: TurboDocx API Client
```
-## Step 1: Upload Document
+## Choosing Your Endpoint
+
+TurboSign offers two single-step endpoints to fit different workflows. Choose the one that best matches your use case.
-The first step is to upload your PDF document to TurboSign. This creates a new document record and returns a document ID that you'll use in subsequent steps.
+### When to Use prepare-for-review
+
+✅ **Use this endpoint when you want to:**
+
+- Verify field placement before sending to recipients
+- Get a preview URL to review the document in TurboSign's interface
+- Manually trigger email sending after verifying field placement
+- Ensure correct field positioning before recipients receive emails
+
+**Workflow**: Upload → Get preview URL → Review in browser → Manually send when ready
+
+### When to Use prepare-for-signing
+
+✅ **Use this endpoint when you want to:**
+
+- Send documents immediately without preview step
+- Automate the entire signature process end-to-end
+- Use with verified templates or confident field positioning
+- Skip manual review and send directly to recipients
+
+**Workflow**: Upload → Emails sent automatically → Recipients sign
+
+## Endpoint 1: Prepare for Review
+
+Creates a signature request and returns a preview URL. No emails are sent to recipients.
### Endpoint
```http
-POST https://www.turbodocx.com/turbosign/documents/upload
+POST https://api.turbodocx.com/turbosign/single/prepare-for-review
```
### Headers
@@ -171,379 +195,628 @@ x-rapiddocx-org-id: YOUR_ORGANIZATION_ID
User-Agent: TurboDocx API Client
```
-### Request Body (Form Data)
+### Request Body (multipart/form-data)
+
+⚠️ **Important**: Recipients and fields must be sent as JSON strings in form-data
+
+| Field | Type | Required | Description |
+| ------------------- | -------------- | ------------- | ------------------------------------------ |
+| file | File | Conditional\* | PDF, DOCX, or PPTX file to upload |
+| deliverableId | String (UUID) | Conditional\* | Reference to existing deliverable |
+| templateId | String (UUID) | Conditional\* | Reference to existing template |
+| fileLink | String (URL) | Conditional\* | URL to download file from |
+| documentName | String | No | Document name in TurboSign (max 255 chars) |
+| documentDescription | String | No | Document description (max 1000 chars) |
+| recipients | String (JSON) | **Yes** | JSON string array of recipient objects |
+| fields | String (JSON) | **Yes** | JSON string array of field objects |
+| senderName | String | No | Name of sender (max 255 chars) |
+| senderEmail | String (email) | No | Email of sender |
+| ccEmails | String (JSON) | No | JSON string array of CC email addresses |
+
+\* **File Source**: Must provide exactly ONE of: file, deliverableId, templateId, or fileLink
+
+### Recipients JSON Format
+
+Recipients must be stringified before adding to form-data:
```javascript
-{
- "name": "Contract Agreement",
- "file": [PDF_FILE_BINARY],
- // Optional: triggerMeta for advanced configurations
- // "triggerMeta": "{\"url\": \"callback_url\"}"
-}
+const recipients = JSON.stringify([
+ {
+ name: "John Smith",
+ email: "john.smith@company.com",
+ signingOrder: 1,
+ metadata: {
+ color: "hsl(200, 75%, 50%)",
+ lightColor: "hsl(200, 75%, 93%)",
+ },
+ },
+ {
+ name: "Jane Doe",
+ email: "jane.doe@partner.com",
+ signingOrder: 2,
+ metadata: {
+ color: "hsl(270, 75%, 50%)",
+ lightColor: "hsl(270, 75%, 93%)",
+ },
+ },
+]);
+formData.append("recipients", recipients);
+```
+
+### Fields JSON Format
+
+Fields reference recipients by **email** (not recipientId) and must be stringified:
+
+#### Template-based (recommended):
+
+```javascript
+const fields = JSON.stringify([
+ {
+ recipientEmail: "john.smith@company.com",
+ type: "signature",
+ template: {
+ anchor: "{Signature1}",
+ placement: "replace",
+ size: { width: 200, height: 80 },
+ offset: { x: 0, y: 0 },
+ },
+ required: true,
+ },
+ {
+ recipientEmail: "john.smith@company.com",
+ type: "date",
+ template: {
+ anchor: "{Date1}",
+ placement: "replace",
+ size: { width: 150, height: 30 },
+ },
+ required: true,
+ },
+]);
+formData.append("fields", fields);
+```
+
+#### Coordinate-based:
+
+```javascript
+const fields = JSON.stringify([
+ {
+ recipientEmail: "john.smith@company.com",
+ type: "signature",
+ page: 1,
+ x: 100,
+ y: 200,
+ width: 200,
+ height: 80,
+ pageWidth: 612,
+ pageHeight: 792,
+ required: true,
+ },
+]);
+formData.append("fields", fields);
```
### Response
```json
{
- "data": {
- "id": "4a20eca5-7944-430c-97d5-fcce4be24296",
- "name": "Contract Agreement",
- "description": "",
- "status": "draft",
- "createdOn": "2025-09-17T13:24:57.083Z"
- }
+ "success": true,
+ "documentId": "4a20eca5-7944-430c-97d5-fcce4be24296",
+ "status": "REVIEW_READY",
+ "previewUrl": "https://www.turbodocx.com/sign/preview/abc123...",
+ "recipients": [
+ {
+ "id": "5f673f37-9912-4e72-85aa-8f3649760f6b",
+ "name": "John Smith",
+ "email": "john.smith@company.com",
+ "signingOrder": 1,
+ "metadata": {
+ "color": "hsl(200, 75%, 50%)",
+ "lightColor": "hsl(200, 75%, 93%)"
+ }
+ }
+ ],
+ "message": "Document prepared for review. Use the preview URL to review and assign fields."
}
```
### Response Fields
-| Field | Type | Description |
-| ------------------ | ------ | ----------------------------------------------------- |
-| `data.id` | string | Unique document identifier (save this for next steps) |
-| `data.name` | string | Document name as provided |
-| `data.description` | string | Document description (empty by default) |
-| `data.status` | string | Document status (`draft` after upload) |
-| `data.createdOn` | string | ISO 8601 timestamp of document creation |
-
-
+| Field | Type | Description |
+| ---------- | ------------- | ---------------------------------------------- |
+| success | Boolean | Request success status |
+| documentId | String (UUID) | Unique document identifier - save for tracking |
+| status | String | Document status (REVIEW_READY) |
+| previewUrl | String (URL) | URL to preview and verify document |
+| recipients | Array | Array of recipient objects with generated IDs |
+| message | String | Human-readable success message |
### Code Examples
-
-## Step 2: Add Recipients
+### Next Steps After Review
+
+Once you've reviewed the document via the preview URL click "Send for Signing" button on the preview page to send emails to recipients
-After uploading your document, add the recipients who need to sign. You can specify multiple recipients with their signing order and customize their signature appearance.
+## Endpoint 2: Prepare for Signing
+
+Creates a signature request and immediately sends emails to recipients. Use this for production workflows when you're confident in your field positioning.
### Endpoint
```http
-POST https://www.turbodocx.com/turbosign/documents/{documentId}/update-with-recipients
+POST https://api.turbodocx.com/turbosign/single/prepare-for-signing
```
### Headers
```http
-Content-Type: application/json
+Content-Type: multipart/form-data
Authorization: Bearer YOUR_API_TOKEN
x-rapiddocx-org-id: YOUR_ORGANIZATION_ID
User-Agent: TurboDocx API Client
```
-### Request Body
+### Request Body (multipart/form-data)
-```json
-{
- "document": {
- "name": "Contract Agreement - Updated",
- "description": "This document requires electronic signatures from both parties. Please review all content carefully before signing."
- },
- "recipients": [
- {
- "name": "John Smith",
- "email": "john.smith@company.com",
- "signingOrder": 1,
- "metadata": {
- "color": "hsl(200, 75%, 50%)",
- "lightColor": "hsl(200, 75%, 93%)"
- },
- "documentId": "4a20eca5-7944-430c-97d5-fcce4be24296"
- },
- {
- "name": "Jane Doe",
- "email": "jane.doe@partner.com",
- "signingOrder": 2,
- "metadata": {
- "color": "hsl(270, 75%, 50%)",
- "lightColor": "hsl(270, 75%, 93%)"
- },
- "documentId": "4a20eca5-7944-430c-97d5-fcce4be24296"
- }
- ]
-}
-```
+The request format is **identical** to prepare-for-review. See the "Endpoint 1: Prepare for Review" section above for detailed field documentation.
### Response
```json
{
- "data": {
- "document": {
- "id": "4a20eca5-7944-430c-97d5-fcce4be24296",
- "name": "Contract Agreement - Updated",
- "description": "This document requires electronic signatures from both parties. Please review all content carefully before signing.",
- "status": "setup_complete",
- "updatedOn": "2025-09-17T13:26:10.000Z"
- },
- "recipients": [
- {
- "id": "5f673f37-9912-4e72-85aa-8f3649760f6b",
- "name": "John Smith",
- "email": "john.smith@company.com",
- "signingOrder": 1,
- "metadata": {
- "color": "hsl(200, 75%, 50%)",
- "lightColor": "hsl(200, 75%, 93%)"
- }
- },
- {
- "id": "a8b9c1d2-3456-7890-abcd-ef1234567890",
- "name": "Jane Doe",
- "email": "jane.doe@partner.com",
- "signingOrder": 2,
- "metadata": {
- "color": "hsl(270, 75%, 50%)",
- "lightColor": "hsl(270, 75%, 93%)"
- }
- }
- ]
- }
+ "success": true,
+ "documentId": "4a20eca5-7944-430c-97d5-fcce4be24296",
+ "message": "Document sent for signing. Emails are being sent to recipients."
}
```
-### Request Fields
+### Response Fields
-| Field | Type | Required | Description |
-| ---------------------------------- | ------ | -------- | --------------------------------------------------- |
-| `document.name` | string | Yes | Updated document name |
-| `document.description` | string | No | Document description for recipients |
-| `recipients[].name` | string | Yes | Full name of the signer |
-| `recipients[].email` | string | Yes | Email address for signing notifications |
-| `recipients[].signingOrder` | number | Yes | Order in which recipients should sign (1, 2, 3...) |
-| `recipients[].metadata.color` | string | No | Primary color for this recipient's signature fields |
-| `recipients[].metadata.lightColor` | string | No | Light color variant for highlights |
-| `recipients[].documentId` | string | Yes | Document ID from Step 1 |
+| Field | Type | Description |
+| ---------- | ------------- | ---------------------------------------------- |
+| success | Boolean | Request success status |
+| documentId | String (UUID) | Unique document identifier - save for tracking |
+| message | String | Human-readable success message |
-
+⚠️ **Note**: This endpoint returns immediately after creating the document. Email sending happens asynchronously in the background. Use webhooks to receive notification when the document is fully signed.
### Code Examples
-
-## Step 3: Prepare for Signing
+## Recipients Reference
-The final step configures signature fields and sends the document to recipients for signing. You can position fields using text anchors or absolute coordinates.
+### Recipient Properties
-### Endpoint
+Each recipient object in the `recipients` array should contain the following properties:
-```http
-POST https://www.turbodocx.com/turbosign/documents/{documentId}/prepare-for-signing
-```
+| Property | Type | Required | Description |
+| ------------ | -------------- | -------- | ---------------------------------------------------------- |
+| name | String | Yes | Full name of the recipient/signer |
+| email | String (email) | Yes | Email address of the recipient (must be unique) |
+| signingOrder | Number | Yes | Order in which recipient should sign (starts at 1) |
+| metadata | Object | No | Optional metadata for UI customization (color, lightColor) |
-### Headers
+### Metadata Object (Optional)
-```http
-Content-Type: application/json
-Authorization: Bearer YOUR_API_TOKEN
-x-rapiddocx-org-id: YOUR_ORGANIZATION_ID
-User-Agent: TurboDocx API Client
-```
+The `metadata` object allows you to customize the recipient's UI appearance:
-### Request Body
+| Property | Type | Description | Example |
+| ---------- | ------ | -------------------------------------------------- | ---------------------- |
+| color | String | Primary color for recipient in HSL format | `"hsl(200, 75%, 50%)"` |
+| lightColor | String | Light background color for recipient in HSL format | `"hsl(200, 75%, 93%)"` |
+
+### Example Recipients Array
```json
[
{
- "recipientId": "5f673f37-9912-4e72-85aa-8f3649760f6b",
- "type": "signature",
- "template": {
- "anchor": "{Signature1}",
- "placement": "replace",
- "size": { "width": 200, "height": 80 },
- "offset": { "x": 0, "y": 0 },
- "caseSensitive": true,
- "useRegex": false
- },
- "defaultValue": "",
- "required": true
+ "name": "John Smith",
+ "email": "john.smith@company.com",
+ "signingOrder": 1
},
{
- "recipientId": "5f673f37-9912-4e72-85aa-8f3649760f6b",
- "type": "date",
- "template": {
- "anchor": "{Date1}",
- "placement": "replace",
- "size": { "width": 150, "height": 30 },
- "offset": { "x": 0, "y": 0 },
- "caseSensitive": true,
- "useRegex": false
- },
- "defaultValue": "",
- "required": true
- },
- {
- "recipientId": "a8b9c1d2-3456-7890-abcd-ef1234567890",
- "type": "signature",
- "template": {
- "anchor": "{Signature2}",
- "placement": "replace",
- "size": { "width": 200, "height": 80 },
- "offset": { "x": 0, "y": 0 },
- "caseSensitive": true,
- "useRegex": false
- },
- "defaultValue": "",
- "required": true
- },
+ "name": "Jane Doe",
+ "email": "jane.doe@partner.com",
+ "signingOrder": 2
+ }
+]
+```
+
+### With Optional Metadata
+
+```json
+[
{
- "recipientId": "a8b9c1d2-3456-7890-abcd-ef1234567890",
- "type": "text",
- "template": {
- "anchor": "{Title2}",
- "placement": "replace",
- "size": { "width": 200, "height": 30 },
- "offset": { "x": 0, "y": 0 },
- "caseSensitive": true,
- "useRegex": false
- },
- "defaultValue": "Business Partner",
- "required": false
+ "name": "John Smith",
+ "email": "john.smith@company.com",
+ "signingOrder": 1,
+ "metadata": {
+ "color": "hsl(200, 75%, 50%)",
+ "lightColor": "hsl(200, 75%, 93%)"
+ }
}
]
```
-### Response
+## Field Types Reference
+
+### Complete Field Type List
+
+| Type | Description | Auto-filled | Use Case |
+| ------------ | -------------------------- | ----------- | -------------------------------------------- |
+| `signature` | Electronic signature field | No | Legal signatures, agreements |
+| `initial` | Initial field | No | Document initials, paragraph acknowledgments |
+| `date` | Date picker field | No | Signing date, agreement date |
+| `full_name` | Full name field | Yes | Automatically fills signer's complete name |
+| `first_name` | First name field | Yes | Automatically fills signer's first name |
+| `last_name` | Last name field | Yes | Automatically fills signer's last name |
+| `title` | Title/job title field | No | Professional title or position |
+| `company` | Company name field | No | Organization or company name |
+| `email` | Email address field | Yes | Signer's email address |
+| `text` | Generic text input field | No | Custom text, notes, or any other text input |
+| `checkbox` | Checkbox field | No | Acknowledgments, consent, agreements |
+
+### Field Configuration Properties
+
+#### Common Properties (All Field Types)
+
+| Property | Type | Required | Description |
+| --------------- | ------- | -------- | -------------------------------------------------------------- |
+| recipientEmail | String | Yes | Email address of recipient (matches email in recipients array) |
+| type | String | Yes | Field type (see table above) |
+| required | Boolean | No | Whether field must be completed (default: true) |
+| defaultValue | String | No | Pre-filled value for the field |
+| isReadonly | Boolean | No | Makes field non-editable (for prefilled values) |
+| backgroundColor | String | No | Custom background color (hex or rgba) |
+
+#### Template-based Properties
+
+| Property | Type | Required | Description |
+| ---------------------- | ------- | -------- | --------------------------------------------------------------- |
+| template.anchor | String | Yes | Text anchor to find in document (e.g., "{Signature1}") |
+| template.placement | String | Yes | How to place field: "replace", "before", "after" |
+| template.size | Object | Yes | Field dimensions: { width: number, height: number } |
+| template.offset | Object | No | Position offset: { x: number, y: number } (default: {x:0, y:0}) |
+| template.caseSensitive | Boolean | No | Whether anchor search is case-sensitive (default: true) |
+| template.useRegex | Boolean | No | Whether to treat anchor as regex pattern (default: false) |
+
+#### Coordinate-based Properties
+
+| Property | Type | Required | Description |
+| ---------- | ------ | -------- | ------------------------------------------------------------------ |
+| page | Number | Yes | Page number (starts at 1) |
+| x | Number | Yes | Horizontal position from left edge (pixels) |
+| y | Number | Yes | Vertical position from top edge (pixels) |
+| width | Number | Yes | Field width in pixels |
+| height | Number | Yes | Field height in pixels |
+| pageWidth | Number | No | Total page width in pixels (optional, for responsive positioning) |
+| pageHeight | Number | No | Total page height in pixels (optional, for responsive positioning) |
+
+### Field Type Special Behaviors
+
+**signature & initial**
+
+- Draws a signature pad for user input
+- Can be text-based or drawn
+- Cryptographically signed and hashed for legal validity
+
+**date**
+
+- Shows date picker interface
+- Format: MM/DD/YYYY (US) or DD/MM/YYYY (configurable)
+- Can set defaultValue to "today" for auto-population
+
+**full_name, first_name, last_name, email**
+
+- Auto-populated from recipient profile
+- Can be overridden by recipient if needed
+- Useful for legal compliance and form filling
+
+**text**
+
+- Single-line text input by default
+- Supports defaultValue for prefilled content
+- Use for titles, company names, custom fields
+
+**checkbox**
+
+- Boolean true/false value
+- Useful for acknowledgments and consent
+- Can have label text next to checkbox
+
+## Field Positioning Methods
+
+TurboSign supports two methods for positioning signature fields in your documents.
+
+### Method 1: Template-based Positioning (Recommended)
+
+Uses text anchors in your PDF as placeholders. TurboSign searches for these anchors and places fields accordingly.
+
+#### Advantages
+
+✅ Easy to update field positions (just edit the PDF)
+✅ No need to measure exact coordinates
+✅ Works across different page sizes
+✅ More maintainable for non-technical users
+✅ Handles document variations gracefully
+
+#### How it Works
+
+1. **Add anchor text to your PDF**: Place text like `{Signature1}`, `{Date1}`, `{Initial1}` where you want fields
+2. **Configure fields with anchor references**: Tell TurboSign what to search for
+3. **TurboSign finds and replaces**: Anchors are found and replaced with interactive fields
+
+#### Anchor Configuration Example
+
+```json
+{
+ "recipientEmail": "john.smith@company.com",
+ "type": "signature",
+ "template": {
+ "anchor": "{Signature1}",
+ "placement": "replace",
+ "size": { "width": 200, "height": 80 },
+ "offset": { "x": 0, "y": 0 },
+ "caseSensitive": true,
+ "useRegex": false
+ },
+ "required": true
+}
+```
+
+#### Placement Options
+
+- **replace**: Removes the anchor text and places the field in its position
+- **before**: Places field before the anchor text (anchor remains visible)
+- **after**: Places field after the anchor text (anchor remains visible)
+
+#### Offset Usage
+
+Offset allows fine-tuning field position relative to the anchor:
+
+- `x`: Positive moves right, negative moves left (pixels)
+- `y`: Positive moves down, negative moves up (pixels)
```json
{
- "success": true
+ "anchor": "{Signature1}",
+ "placement": "replace",
+ "size": { "width": 200, "height": 80 },
+ "offset": { "x": 10, "y": -5 } // 10px right, 5px up from anchor
}
```
-### Field Positioning Options
+### Method 2: Coordinate-based Positioning
-TurboSign supports two field positioning methods:
+Uses exact pixel coordinates to position fields on specific pages. Best for precise control or when anchors aren't feasible.
-1. **Template-based positioning** (recommended) - Uses text anchors in your PDF
-2. **Coordinate-based positioning** - Uses exact pixel coordinates
+#### Advantages
-#### Template-based Field Configuration
+✅ Pixel-perfect precision
+✅ Works with PDFs that can't be edited
+✅ Programmatically generated positions
+✅ Useful for form-filling scenarios
+✅ Consistent placement across documents
-| Field | Type | Required | Description |
-| ------------------------ | ------- | -------- | ------------------------------------------------------ |
-| `recipientId` | string | Yes | Recipient ID from Step 2 |
-| `type` | string | Yes | Field type - see Available Field Types in TLDR section |
-| `template.anchor` | string | Yes | Text anchor to find in document (e.g., "{Signature1}") |
-| `template.placement` | string | Yes | How to place field ("replace", "before", "after") |
-| `template.size` | object | Yes | Field dimensions (width, height in pixels) |
-| `template.offset` | object | No | Position offset from anchor (x, y in pixels) |
-| `template.caseSensitive` | boolean | No | Whether anchor search is case-sensitive |
-| `template.useRegex` | boolean | No | Whether to treat anchor as regex pattern |
-| `defaultValue` | string | No | Pre-filled value for the field |
-| `required` | boolean | No | Whether field must be completed |
+#### How it Works
-#### Coordinate-based Field Configuration
+1. **Measure exact x,y coordinates** in your PDF (using PDF editor or viewer)
+2. **Provide page number, coordinates, and dimensions**
+3. **TurboSign places fields at exact positions**
-For precise positioning when text anchors aren't suitable, use coordinate-based fields:
+#### Coordinate Configuration Example
```json
-[
- {
- "recipientId": "5f673f37-9912-4e72-85aa-8f3649760f6b",
- "type": "signature",
- "page": 1,
- "x": 100,
- "y": 200,
- "width": 200,
- "height": 80,
- "pageWidth": 612,
- "pageHeight": 792
- },
- {
- "recipientId": "5f673f37-9912-4e72-85aa-8f3649760f6b",
- "type": "date",
- "page": 1,
- "x": 350,
- "y": 200,
- "width": 150,
- "height": 30,
- "pageWidth": 612,
- "pageHeight": 792
- }
-]
+{
+ "recipientEmail": "john.smith@company.com",
+ "type": "signature",
+ "page": 1,
+ "x": 100,
+ "y": 200,
+ "width": 200,
+ "height": 80,
+ "pageWidth": 612,
+ "pageHeight": 792,
+ "required": true
+}
```
-| Field | Type | Required | Description |
-| ------------ | ------ | -------- | ----------------------------------------------- |
-| `recipientId`| string | Yes | Recipient ID from Step 2 |
-| `type` | string | Yes | Field type - see Available Field Types section |
-| `page` | number | Yes | Page number (starts at 1) |
-| `x` | number | Yes | Horizontal position from left edge (pixels) |
-| `y` | number | Yes | Vertical position from top edge (pixels) |
-| `width` | number | Yes | Field width in pixels |
-| `height` | number | Yes | Field height in pixels |
-| `pageWidth` | number | Yes | Total page width in pixels |
-| `pageHeight` | number | Yes | Total page height in pixels |
-
-**Coordinate System Notes:**
-- Origin (0,0) is at the top-left corner of the page
-- Standard US Letter page size is 612 x 792 pixels (8.5" x 11" at 72 DPI)
-- Fields cannot extend beyond page boundaries: `x + width ≤ pageWidth` and `y + height ≤ pageHeight`
-- All coordinate values must be non-negative numbers
-
-**When to use coordinate-based positioning:**
-- Precise pixel-perfect placement needed
-- PDF doesn't contain suitable text anchors
-- Programmatically generated field positions
-- Converting from existing coordinate-based systems
-
-
+#### Coordinate System Reference
-### Code Examples
+- **Origin (0,0)**: Top-left corner of the page
+- **X-axis**: Increases from left to right
+- **Y-axis**: Increases from top to bottom
+- **Standard US Letter**: 612 x 792 pixels (8.5" x 11" at 72 DPI)
+- **Standard A4**: 595 x 842 pixels (210mm x 297mm at 72 DPI)
-
+#### Coordinate Validation
+
+Fields must stay within page boundaries:
+
+- `x ≥ 0`
+- `y ≥ 0`
+- `x + width ≤ pageWidth`
+- `y + height ≤ pageHeight`
+
+#### Measuring Coordinates
+
+**Adobe Acrobat Pro**:
+
+1. View → Show/Hide → Rulers & Grids → Rulers
+2. Hover over location to see coordinates
+
+**Browser Developer Tools**:
+
+1. Open PDF in browser
+2. Right-click → Inspect
+3. Use element inspector to measure positions
+
+**PDF Editing Software**:
+
+- Use built-in coordinate display
+- Draw rectangles to measure dimensions
+
+#### Quick Coordinate Example
+
+Position a signature field at bottom-right of a US Letter page:
+
+```json
+{
+ "recipientEmail": "john@example.com",
+ "type": "signature",
+ "page": 1,
+ "x": 362, // 612 - 250 = 362 (right aligned with 50px margin)
+ "y": 662, // 792 - 130 = 662 (bottom aligned with 50px margin)
+ "width": 200,
+ "height": 80,
+ "pageWidth": 612,
+ "pageHeight": 792
+}
+```
## Best Practices
+### Workflow Selection
+
+**When You Need Field Verification**:
+
+- ✅ Use `prepare-for-review` to get preview URLs
+- ✅ Verify field placement in browser before sending
+- ✅ Manually trigger sending after review
+- ✅ Useful for new document templates or complex field layouts
+
+**When Field Placement Is Verified**:
+
+- ✅ Use `prepare-for-signing` to send immediately
+- ✅ Implement webhook handlers for completion notifications
+- ✅ Use proper error handling and retry logic
+- ✅ Monitor API rate limits
+- ✅ Log all document IDs for tracking
+
+**General Tips**:
+
+- ✅ Use deliverableId or templateId to avoid repeated uploads
+- ✅ Test with your own email addresses first
+- ✅ Both endpoints are production-ready
+
### Security
-- **Never expose API tokens**: Store tokens securely in environment variables
-- **Use HTTPS only**: All API calls must use HTTPS in production
-- **Validate inputs**: Always validate recipient emails and document names
+- **Never expose API tokens**: Store tokens securely in environment variables or secrets management
+- **Use HTTPS only**: All API calls must use HTTPS in production (API enforces this)
+- **Validate inputs**: Always validate recipient emails and document names before submission
- **Implement rate limiting**: Respect API rate limits to avoid throttling
+- **Rotate tokens regularly**: Generate new API tokens periodically
+- **Use webhook signatures**: Verify webhook payloads using HMAC signatures
+- **Sanitize user inputs**: Validate and sanitize all user-provided data
### Error Handling
- **Check HTTP status codes**: Always verify response status before processing
-- **Handle timeouts**: Implement retry logic for network failures
+- **Handle timeouts**: Implement retry logic with exponential backoff for network failures
- **Log API responses**: Keep detailed logs for debugging and monitoring
- **Validate responses**: Check response structure before accessing data
+- **Graceful degradation**: Have fallback behavior for API failures
+- **User-friendly errors**: Display helpful error messages to end users
### Performance
-- **Upload optimization**: Compress PDFs when possible to reduce upload time
-- **Batch operations**: Group multiple recipients in single API calls
-- **Async processing**: Use webhooks instead of polling for status updates
-- **Connection pooling**: Reuse HTTP connections for multiple requests
+**File Upload Optimization**:
+
+- Compress PDFs when possible (aim for <5MB)
+- Use fileLink for files already in cloud storage (S3, GCS, etc.)
+- Use deliverableId/templateId to reference existing documents
+- Avoid uploading the same document multiple times
+
+**API Efficiency**:
+
+- Single-step endpoints reduce API calls from 3 to 1 (3x faster)
+- Batch multiple documents in parallel requests when possible
+- Use connection pooling for multiple requests
+- Implement exponential backoff for retries
+- Cache responses when appropriate
+
+**Network Optimization**:
+
+- Use CDN for document hosting when using fileLink
+- Enable gzip compression for API requests
+- Minimize payload sizes by only including required fields
### Document Preparation
-- **Use text anchors**: Place anchor text like `{Signature1}` in your PDFs for precise field positioning
-- **Consistent naming**: Use consistent anchor naming conventions across documents
-- **Test coordinates**: Verify field positions with test documents before production
-- **Document validation**: Ensure PDFs are not password-protected or corrupted
+**Text Anchors (Template-based)**:
+
+- Use consistent anchor naming: `{FieldType}{Number}` (e.g., `{Signature1}`, `{Date1}`)
+- Place anchors exactly where you want fields
+- Use unique anchors (avoid duplicates)
+- Test anchor placement with prepare-for-review first
+- Document your anchor naming convention
+
+**Coordinate-based**:
+
+- Verify coordinates work across different PDF viewers
+- Account for page margins and headers/footers
+- Use standard page sizes when possible
+- Test on actual page dimensions (don't assume)
+- Validate boundaries before submission
+
+**Document Validation**:
-### Coordinate-based Positioning Tips
+- Ensure PDFs are not password-protected or corrupted
+- Verify all pages are readable
+- Test with actual documents before production
+- Keep backup copies of source documents
-- **Measure accurately**: Use PDF viewers with coordinate display to find exact positions
-- **Account for margins**: Consider document margins when calculating field positions
-- **Test on different devices**: Verify coordinates work across different PDF viewers
-- **Use standard page sizes**: Stick to common page dimensions (612x792 for US Letter)
-- **Validate boundaries**: Ensure fields don't extend beyond page edges
-- **Consider scaling**: Be aware that different DPI settings may affect coordinates
+### JSON String Formatting
+
+⚠️ **Critical**: Recipients and fields must be valid JSON strings when added to form-data.
+
+**Correct**:
+
+```javascript
+const recipients = JSON.stringify([
+ { name: "John", email: "john@example.com", signingOrder: 1 },
+]);
+formData.append("recipients", recipients);
+```
+
+**Incorrect**:
+
+```javascript
+// Don't send object/array directly!
+formData.append("recipients", recipientsArray); // ❌ Wrong
+formData.append("recipients", "[{...}]"); // ❌ Wrong (string literal, not stringified)
+```
+
+**Python Example**:
+
+```python
+import json
+recipients = json.dumps([
+ {"name": "John", "email": "john@example.com", "signingOrder": 1}
+])
+form_data['recipients'] = recipients
+```
+
+**C# Example**:
+
+```csharp
+using System.Text.Json;
+var recipients = JsonSerializer.Serialize(new[] {
+ new { name = "John", email = "john@example.com", signingOrder = 1 }
+});
+formData.Add(new StringContent(recipients), "recipients");
+```
## Error Handling & Troubleshooting
@@ -555,61 +828,132 @@ For precise positioning when text anchors aren't suitable, use coordinate-based
| `400` | Bad Request | Check request body format and required fields |
| `401` | Unauthorized | Verify API token and headers |
| `403` | Forbidden | Check organization ID and permissions |
-| `404` | Not Found | Verify document ID and endpoint URLs |
+| `404` | Not Found | Verify endpoint URLs are correct |
| `422` | Unprocessable Entity | Validate field values and constraints |
| `429` | Too Many Requests | Implement rate limiting and retry logic |
| `500` | Internal Server Error | Contact support if persistent |
### Common Issues
-#### Authentication Failures
+#### JSON String Formatting Errors
-**Symptoms**: 401 Unauthorized responses
+**Symptoms**: 400 Bad Request with message "Invalid JSON in recipients/fields"
**Solutions**:
-- Verify API token is correct and not expired
-- Check that `x-rapiddocx-org-id` header matches your organization
-- Ensure Bearer token format: `Bearer YOUR_TOKEN`
+- ✅ Verify JSON.stringify() or equivalent is used for recipients, fields, ccEmails
+- ✅ Check JSON is valid using JSONLint or similar validator
+- ✅ Ensure proper escaping of quotes in JSON strings
+- ✅ Test with minimal example first (1 recipient, 1 field)
-#### Document Upload Failures
+**Example Error Response**:
-**Symptoms**: Upload returns error or times out
+```json
+{
+ "error": "Invalid JSON string in recipients field",
+ "code": "JSONParseError",
+ "details": "Unexpected token at position 45"
+}
+```
+
+**Debug Steps**:
+
+1. Log the JSON string before sending
+2. Validate JSON with online validator
+3. Check for special characters or unescaped quotes
+4. Test with hardcoded valid JSON first
+
+#### File Source Errors
+
+**Symptoms**: 400 Bad Request with message about file source
**Solutions**:
-- Verify PDF file is not corrupted or password-protected
-- Check file size is under the maximum limit (typically 10MB)
-- Ensure file is actually a PDF (check MIME type)
-- Verify network connection and try again
+- ✅ Provide exactly ONE of: file, deliverableId, templateId, fileId, fileLink
+- ✅ Verify UUIDs are valid format (8-4-4-4-12 characters)
+- ✅ Check file upload isn't corrupted or empty
+- ✅ Ensure fileLink is accessible (not behind auth)
-#### Recipient Configuration Issues
+**Example Error Response**:
-**Symptoms**: Recipients not receiving signing invitations
+```json
+{
+ "error": "Must provide exactly one file source",
+ "code": "InvalidFileSource"
+}
+```
+
+#### Recipients/Fields Mismatch
+
+**Symptoms**: 400 Bad Request about missing recipient or email mismatch
+
+**Solutions**:
+
+- ✅ Verify recipientEmail in fields matches email in recipients array **exactly**
+- ✅ Check for typos in email addresses
+- ✅ Ensure all fields reference valid recipients
+- ✅ Email matching is case-sensitive
+
+**Example**:
+
+```javascript
+// Recipients array
+[{ email: "john.smith@company.com", ... }]
+
+// Fields array - must match exactly
+[{ recipientEmail: "john.smith@company.com", ... }] // ✅ Correct
+[{ recipientEmail: "John.Smith@company.com", ... }] // ❌ Wrong (case mismatch)
+```
+
+#### Authentication Failures
+
+**Symptoms**: 401 Unauthorized responses
+
+**Solutions**:
+
+- ✅ Verify API token is correct and not expired
+- ✅ Check that `x-rapiddocx-org-id` header matches your organization
+- ✅ Ensure Bearer token format: `Bearer YOUR_TOKEN` (with space)
+- ✅ Confirm token has necessary permissions
+
+**Example Correct Headers**:
+
+```http
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
+x-rapiddocx-org-id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
+```
+
+#### Document Upload Failures
+
+**Symptoms**: Upload returns error or times out
**Solutions**:
-- Verify email addresses are valid and correctly formatted
-- Check signing order numbers are sequential (1, 2, 3...)
-- Ensure document ID from Step 1 is used correctly
-- Verify recipient metadata format is correct
+- ✅ Verify PDF file is not corrupted or password-protected
+- ✅ Check file size is under maximum limit (typically 10MB)
+- ✅ Ensure file is actually a PDF (check MIME type)
+- ✅ Verify network connection and try again
+- ✅ For fileLink, ensure URL is accessible
#### Field Positioning Problems
-**Symptoms**: Signature fields appear in wrong locations
+**Symptoms**: Signature fields appear in wrong locations or not at all
**Template-based Solutions**:
-- Verify anchor text exists in the PDF document
-- Check anchor text matches exactly (case-sensitive by default)
-- Test with `caseSensitive: false` if having matching issues
-- Use PDF coordinates as fallback if anchors don't work
+
+- ✅ Verify anchor text exists in the PDF document
+- ✅ Check anchor text matches exactly (case-sensitive by default)
+- ✅ Test with `caseSensitive: false` if having matching issues
+- ✅ Try different placement options (replace, before, after)
+- ✅ Use prepare-for-review to visually verify placement
**Coordinate-based Solutions**:
-- Verify page dimensions match your PDF's actual size
-- Check that x,y coordinates are within page boundaries
-- Ensure coordinates account for any PDF margins or headers
-- Test with different page numbers if multi-page document
-- Validate that `x + width ≤ pageWidth` and `y + height ≤ pageHeight`
+
+- ✅ Verify page dimensions match your PDF's actual size
+- ✅ Check that x,y coordinates are within page boundaries
+- ✅ Ensure coordinates account for any PDF margins or headers
+- ✅ Test with different page numbers if multi-page document
+- ✅ Validate that `x + width ≤ pageWidth` and `y + height ≤ pageHeight`
#### Webhook Integration Issues
@@ -617,26 +961,44 @@ For precise positioning when text anchors aren't suitable, use coordinate-based
**Solutions**:
-- Verify webhook URLs are accessible and return 200 OK
-- Check webhook configuration in organization settings
-- Review webhook delivery history for error details
-- Test webhook endpoints with external tools
+- ✅ Verify webhook URLs are accessible and return 200 OK
+- ✅ Check webhook configuration in organization settings
+- ✅ Review webhook delivery history for error details
+- ✅ Test webhook endpoints with external tools (webhook.site, ngrok)
+- ✅ Implement HMAC signature verification
### Debugging Tips
-1. **Enable request logging**: Log all API requests and responses
-2. **Test step by step**: Isolate issues by testing each step individually
-3. **Use Postman**: Import examples and test manually before coding
-4. **Check network**: Verify connectivity to `turbodocx.com`
-5. **Validate JSON**: Ensure request bodies are valid JSON format
-
-
+1. **Test with prepare-for-review first**: Visual confirmation before sending emails
+2. **Use preview URLs**: Verify field placement and document appearance
+3. **Check response documentId**: Save this for tracking and debugging
+4. **Enable request logging**: Log all requests and responses with timestamps
+5. **Test with minimal payloads**: Start simple (1 recipient, 1 field), add complexity incrementally
+6. **Validate JSON before sending**: Use JSON validators to check format
+7. **Use Postman/Insomnia**: Test manually before writing code
+8. **Check API status page**: Verify TurboDocx services are operational
+9. **Review error messages carefully**: Error responses include specific details
+10. **Monitor rate limits**: Track API usage to avoid throttling
+
+### Example Debug Request
+
+```bash
+# Test with curl to isolate issues
+curl -X POST https://api.turbodocx.com/turbosign/single/prepare-for-review \
+ -H "Authorization: Bearer YOUR_TOKEN" \
+ -H "x-rapiddocx-org-id: YOUR_ORG_ID" \
+ -F "file=@document.pdf" \
+ -F "documentName=Test Document" \
+ -F 'recipients=[{"name":"Test User","email":"test@example.com","signingOrder":1}]' \
+ -F 'fields=[{"recipientEmail":"test@example.com","type":"signature","page":1,"x":100,"y":200,"width":200,"height":80,"pageWidth":612,"pageHeight":792}]' \
+ -v
+```
## Next Steps
### Webhooks - The Next Logical Step
-Now that you've integrated the basic signing flow, the next step is setting up webhooks to receive real-time notifications when documents are signed. This eliminates the need for polling and provides instant updates about document status changes.
+Now that you've integrated the single-step signing flow, the next step is setting up webhooks to receive real-time notifications when documents are signed. This eliminates the need for polling and provides instant updates about document status changes.
📖 **[Learn how to configure Webhooks →](/docs/TurboSign/Webhooks)**
@@ -656,4 +1018,4 @@ Need help with your integration?
---
-Ready to get started? Follow the step-by-step guide above to integrate TurboSign API into your application and start collecting electronic signatures programmatically!
+Ready to get started? Follow the guide above to integrate TurboSign single-step API into your application and start collecting electronic signatures programmatically with a single API call!
diff --git a/docs/TurboSign/Setting up TurboSign.md b/docs/TurboSign/Setting up TurboSign.md
index 24e6cdf..d55c08c 100644
--- a/docs/TurboSign/Setting up TurboSign.md
+++ b/docs/TurboSign/Setting up TurboSign.md
@@ -199,6 +199,8 @@ For each recipient, you'll need:

+**CC Emails (Optional):** You can also add CC email addresses for people who should receive a copy of the document. These individuals will get the final signed copy when everyone has completed signing, but they won't need to sign the document themselves.
+
:::tip Recipient Best Practices
diff --git a/static/img/turbosign/api/bulk-api.jpg b/static/img/turbosign/api/bulk-api.jpg
new file mode 100644
index 0000000..870cabc
Binary files /dev/null and b/static/img/turbosign/api/bulk-api.jpg differ
diff --git a/static/img/turbosign/api/types.svg b/static/img/turbosign/api/types.svg
new file mode 100644
index 0000000..5486791
--- /dev/null
+++ b/static/img/turbosign/api/types.svg
@@ -0,0 +1,19 @@
+
diff --git a/static/scripts/turbosign/api/bulk/cancel-batch/nodejs/express.js b/static/scripts/turbosign/api/bulk/cancel-batch/nodejs/express.js
new file mode 100644
index 0000000..d4edf5e
--- /dev/null
+++ b/static/scripts/turbosign/api/bulk/cancel-batch/nodejs/express.js
@@ -0,0 +1,37 @@
+const fetch = require('node-fetch');
+
+// Configuration - Update these values
+const API_TOKEN = "YOUR_API_TOKEN";
+const ORG_ID = "YOUR_ORGANIZATION_ID";
+const BASE_URL = "https://api.turbodocx.com";
+const BATCH_ID = "YOUR_BATCH_ID"; // Replace with actual batch ID to cancel
+
+// Send POST request to cancel batch
+const response = await fetch(`${BASE_URL}/turbosign/bulk/batch/${BATCH_ID}/cancel`, {
+ method: 'POST',
+ headers: {
+ 'Authorization': `Bearer ${API_TOKEN}`,
+ 'x-rapiddocx-org-id': ORG_ID,
+ 'User-Agent': 'TurboDocx API Client',
+ 'Content-Type': 'application/json'
+ }
+});
+
+const result = await response.json();
+
+if (result.success) {
+ console.log('✅ Batch cancelled successfully');
+ console.log('Batch ID:', result.batchId);
+ console.log('Status:', result.status);
+ console.log('\n📊 Cancellation Summary:');
+ console.log('Cancelled Jobs:', result.cancelledJobs);
+ console.log('Succeeded Jobs (already completed):', result.succeededJobs);
+ console.log('Refunded Credits:', result.refundedCredits);
+ console.log('\n💰 Credits have been refunded to your account for cancelled jobs');
+ console.log('✅ Jobs that already succeeded remain completed');
+} else {
+ console.error('❌ Error:', result.error || result.message);
+ if (result.code) {
+ console.error('Error Code:', result.code);
+ }
+}
diff --git a/static/scripts/turbosign/api/bulk/cancel-batch/nodejs/fastify.js b/static/scripts/turbosign/api/bulk/cancel-batch/nodejs/fastify.js
new file mode 100644
index 0000000..5cc197f
--- /dev/null
+++ b/static/scripts/turbosign/api/bulk/cancel-batch/nodejs/fastify.js
@@ -0,0 +1,53 @@
+const fetch = require('node-fetch');
+
+// Configuration - Update these values
+const API_TOKEN = "YOUR_API_TOKEN";
+const ORG_ID = "YOUR_ORGANIZATION_ID";
+const BASE_URL = "https://api.turbodocx.com";
+const BATCH_ID = "YOUR_BATCH_ID"; // Replace with actual batch ID to cancel
+
+// Fastify route handler example
+async function cancelBatch(request, reply) {
+ try {
+ // Send POST request to cancel batch
+ const response = await fetch(`${BASE_URL}/turbosign/bulk/batch/${BATCH_ID}/cancel`, {
+ method: 'POST',
+ headers: {
+ 'Authorization': `Bearer ${API_TOKEN}`,
+ 'x-rapiddocx-org-id': ORG_ID,
+ 'User-Agent': 'TurboDocx API Client',
+ 'Content-Type': 'application/json'
+ }
+ });
+
+ const result = await response.json();
+
+ if (result.success) {
+ return reply.send({
+ success: true,
+ batchId: result.batchId,
+ status: result.status,
+ message: result.message,
+ cancelledJobs: result.cancelledJobs,
+ succeededJobs: result.succeededJobs,
+ refundedCredits: result.refundedCredits
+ });
+ } else {
+ return reply.code(400).send({
+ success: false,
+ error: result.error || result.message,
+ code: result.code
+ });
+ }
+ } catch (error) {
+ console.error('Error cancelling batch:', error);
+ return reply.code(500).send({
+ success: false,
+ error: 'Internal server error',
+ message: error.message
+ });
+ }
+}
+
+// Export for use in Fastify app
+module.exports = { cancelBatch };
diff --git a/static/scripts/turbosign/api/bulk/cancel-batch/python/fastapi.py b/static/scripts/turbosign/api/bulk/cancel-batch/python/fastapi.py
new file mode 100644
index 0000000..5bd16db
--- /dev/null
+++ b/static/scripts/turbosign/api/bulk/cancel-batch/python/fastapi.py
@@ -0,0 +1,71 @@
+import json
+import requests
+from fastapi import FastAPI, HTTPException
+from pydantic import BaseModel
+
+# Configuration - Update these values
+API_TOKEN = "YOUR_API_TOKEN"
+ORG_ID = "YOUR_ORGANIZATION_ID"
+BASE_URL = "https://api.turbodocx.com"
+BATCH_ID = "YOUR_BATCH_ID" # Replace with actual batch ID to cancel
+
+app = FastAPI()
+
+class CancelBatchResponse(BaseModel):
+ success: bool
+ batchId: str
+ status: str
+ message: str
+ cancelledJobs: int
+ succeededJobs: int
+ refundedCredits: int
+
+@app.post('/cancel-batch', response_model=CancelBatchResponse)
+async def cancel_batch():
+ try:
+ # Prepare headers
+ headers = {
+ 'Authorization': f'Bearer {API_TOKEN}',
+ 'x-rapiddocx-org-id': ORG_ID,
+ 'User-Agent': 'TurboDocx API Client',
+ 'Content-Type': 'application/json'
+ }
+
+ # Send POST request to cancel batch
+ response = requests.post(
+ f'{BASE_URL}/turbosign/bulk/batch/{BATCH_ID}/cancel',
+ headers=headers
+ )
+
+ result = response.json()
+
+ if result.get('success'):
+ return CancelBatchResponse(
+ success=True,
+ batchId=result['batchId'],
+ status=result['status'],
+ message=result['message'],
+ cancelledJobs=result['cancelledJobs'],
+ succeededJobs=result['succeededJobs'],
+ refundedCredits=result['refundedCredits']
+ )
+ else:
+ raise HTTPException(
+ status_code=400,
+ detail={
+ 'error': result.get('error', 'Failed to cancel batch'),
+ 'code': result.get('code')
+ }
+ )
+
+ except HTTPException:
+ raise
+ except Exception as error:
+ print(f'Error cancelling batch: {error}')
+ raise HTTPException(
+ status_code=500,
+ detail={
+ 'error': 'Internal server error',
+ 'message': str(error)
+ }
+ )
diff --git a/static/scripts/turbosign/api/bulk/cancel-batch/python/flask.py b/static/scripts/turbosign/api/bulk/cancel-batch/python/flask.py
new file mode 100644
index 0000000..72300de
--- /dev/null
+++ b/static/scripts/turbosign/api/bulk/cancel-batch/python/flask.py
@@ -0,0 +1,58 @@
+import json
+import requests
+from flask import Flask, jsonify
+
+# Configuration - Update these values
+API_TOKEN = "YOUR_API_TOKEN"
+ORG_ID = "YOUR_ORGANIZATION_ID"
+BASE_URL = "https://api.turbodocx.com"
+BATCH_ID = "YOUR_BATCH_ID" # Replace with actual batch ID to cancel
+
+app = Flask(__name__)
+
+@app.route('/cancel-batch', methods=['POST'])
+def cancel_batch():
+ try:
+ # Prepare headers
+ headers = {
+ 'Authorization': f'Bearer {API_TOKEN}',
+ 'x-rapiddocx-org-id': ORG_ID,
+ 'User-Agent': 'TurboDocx API Client',
+ 'Content-Type': 'application/json'
+ }
+
+ # Send POST request to cancel batch
+ response = requests.post(
+ f'{BASE_URL}/turbosign/bulk/batch/{BATCH_ID}/cancel',
+ headers=headers
+ )
+
+ result = response.json()
+
+ if result.get('success'):
+ return jsonify({
+ 'success': True,
+ 'batchId': result['batchId'],
+ 'status': result['status'],
+ 'message': result['message'],
+ 'cancelledJobs': result['cancelledJobs'],
+ 'succeededJobs': result['succeededJobs'],
+ 'refundedCredits': result['refundedCredits']
+ }), 200
+ else:
+ return jsonify({
+ 'success': False,
+ 'error': result.get('error', 'Failed to cancel batch'),
+ 'code': result.get('code')
+ }), 400
+
+ except Exception as error:
+ print(f'Error cancelling batch: {error}')
+ return jsonify({
+ 'success': False,
+ 'error': 'Internal server error',
+ 'message': str(error)
+ }), 500
+
+if __name__ == '__main__':
+ app.run(debug=True)
diff --git a/static/scripts/turbosign/api/bulk/ingest/nodejs/express.js b/static/scripts/turbosign/api/bulk/ingest/nodejs/express.js
new file mode 100644
index 0000000..68e1598
--- /dev/null
+++ b/static/scripts/turbosign/api/bulk/ingest/nodejs/express.js
@@ -0,0 +1,121 @@
+const FormData = require('form-data');
+const fs = require('fs');
+const fetch = require('node-fetch');
+
+// Configuration - Update these values
+const API_TOKEN = "YOUR_API_TOKEN";
+const ORG_ID = "YOUR_ORGANIZATION_ID";
+const BASE_URL = "https://api.turbodocx.com";
+
+// Prepare documents array - each document represents one signing job
+const documents = [
+ {
+ recipients: [
+ {
+ name: 'John Smith',
+ email: 'john.smith@company.com',
+ signingOrder: 1
+ }
+ ],
+ fields: [
+ {
+ recipientEmail: 'john.smith@company.com',
+ type: 'signature',
+ page: 1,
+ x: 100,
+ y: 200,
+ width: 200,
+ height: 80,
+ required: true
+ },
+ {
+ recipientEmail: 'john.smith@company.com',
+ type: 'date',
+ page: 1,
+ x: 100,
+ y: 300,
+ width: 150,
+ height: 30,
+ required: true
+ }
+ ],
+ documentName: 'Employment Contract - John Smith',
+ documentDescription: 'Please review and sign your employment contract'
+ },
+ {
+ recipients: [
+ {
+ name: 'Jane Doe',
+ email: 'jane.doe@company.com',
+ signingOrder: 1
+ }
+ ],
+ fields: [
+ {
+ recipientEmail: 'jane.doe@company.com',
+ type: 'signature',
+ page: 1,
+ x: 100,
+ y: 200,
+ width: 200,
+ height: 80,
+ required: true
+ },
+ {
+ recipientEmail: 'jane.doe@company.com',
+ type: 'date',
+ page: 1,
+ x: 100,
+ y: 300,
+ width: 150,
+ height: 30,
+ required: true
+ }
+ ],
+ documentName: 'Employment Contract - Jane Doe',
+ documentDescription: 'Please review and sign your employment contract'
+ }
+];
+
+// Create form data
+const formData = new FormData();
+formData.append('sourceType', 'file');
+formData.append('file', fs.createReadStream('./contract_template.pdf'));
+formData.append('batchName', 'Q4 Employment Contracts');
+formData.append('documentName', 'Employment Contract');
+formData.append('documentDescription', 'Please review and sign your employment contract');
+formData.append('senderName', 'HR Department');
+formData.append('senderEmail', 'hr@company.com');
+formData.append('documents', JSON.stringify(documents));
+
+// Send request to bulk ingest endpoint
+const response = await fetch(`${BASE_URL}/turbosign/bulk/ingest`, {
+ method: 'POST',
+ headers: {
+ 'Authorization': `Bearer ${API_TOKEN}`,
+ 'x-rapiddocx-org-id': ORG_ID,
+ 'User-Agent': 'TurboDocx API Client',
+ ...formData.getHeaders()
+ },
+ body: formData
+});
+
+const result = await response.json();
+
+if (result.success) {
+ console.log('✅ Bulk batch created successfully');
+ console.log('Batch ID:', result.batchId);
+ console.log('Batch Name:', result.batchName);
+ console.log('Total Jobs:', result.totalJobs);
+ console.log('Status:', result.status);
+ console.log('\n📧 Documents will be sent to recipients asynchronously');
+ console.log('💡 Tip: Use the batch ID to monitor progress and list jobs');
+} else {
+ console.error('❌ Error:', result.error || result.message);
+ if (result.code) {
+ console.error('Error Code:', result.code);
+ }
+ if (result.data?.invalidDocuments) {
+ console.error('Invalid Documents:', JSON.stringify(result.data.invalidDocuments, null, 2));
+ }
+}
diff --git a/static/scripts/turbosign/api/bulk/ingest/nodejs/fastify.js b/static/scripts/turbosign/api/bulk/ingest/nodejs/fastify.js
new file mode 100644
index 0000000..b2c063f
--- /dev/null
+++ b/static/scripts/turbosign/api/bulk/ingest/nodejs/fastify.js
@@ -0,0 +1,136 @@
+const FormData = require('form-data');
+const fs = require('fs');
+const fetch = require('node-fetch');
+
+// Configuration - Update these values
+const API_TOKEN = "YOUR_API_TOKEN";
+const ORG_ID = "YOUR_ORGANIZATION_ID";
+const BASE_URL = "https://api.turbodocx.com";
+
+// Fastify route handler example
+async function bulkIngest(request, reply) {
+ try {
+ // Prepare documents array - each document represents one signing job
+ const documents = [
+ {
+ recipients: [
+ {
+ name: 'John Smith',
+ email: 'john.smith@company.com',
+ signingOrder: 1
+ }
+ ],
+ fields: [
+ {
+ recipientEmail: 'john.smith@company.com',
+ type: 'signature',
+ page: 1,
+ x: 100,
+ y: 200,
+ width: 200,
+ height: 80,
+ required: true
+ },
+ {
+ recipientEmail: 'john.smith@company.com',
+ type: 'date',
+ page: 1,
+ x: 100,
+ y: 300,
+ width: 150,
+ height: 30,
+ required: true
+ }
+ ],
+ documentName: 'Employment Contract - John Smith',
+ documentDescription: 'Please review and sign your employment contract'
+ },
+ {
+ recipients: [
+ {
+ name: 'Jane Doe',
+ email: 'jane.doe@company.com',
+ signingOrder: 1
+ }
+ ],
+ fields: [
+ {
+ recipientEmail: 'jane.doe@company.com',
+ type: 'signature',
+ page: 1,
+ x: 100,
+ y: 200,
+ width: 200,
+ height: 80,
+ required: true
+ },
+ {
+ recipientEmail: 'jane.doe@company.com',
+ type: 'date',
+ page: 1,
+ x: 100,
+ y: 300,
+ width: 150,
+ height: 30,
+ required: true
+ }
+ ],
+ documentName: 'Employment Contract - Jane Doe',
+ documentDescription: 'Please review and sign your employment contract'
+ }
+ ];
+
+ // Create form data
+ const formData = new FormData();
+ formData.append('sourceType', 'file');
+ formData.append('file', fs.createReadStream('./contract_template.pdf'));
+ formData.append('batchName', 'Q4 Employment Contracts');
+ formData.append('documentName', 'Employment Contract');
+ formData.append('documentDescription', 'Please review and sign your employment contract');
+ formData.append('senderName', 'HR Department');
+ formData.append('senderEmail', 'hr@company.com');
+ formData.append('documents', JSON.stringify(documents));
+
+ // Send request to bulk ingest endpoint
+ const response = await fetch(`${BASE_URL}/turbosign/bulk/ingest`, {
+ method: 'POST',
+ headers: {
+ 'Authorization': `Bearer ${API_TOKEN}`,
+ 'x-rapiddocx-org-id': ORG_ID,
+ 'User-Agent': 'TurboDocx API Client',
+ ...formData.getHeaders()
+ },
+ body: formData
+ });
+
+ const result = await response.json();
+
+ if (result.success) {
+ return reply.send({
+ success: true,
+ batchId: result.batchId,
+ batchName: result.batchName,
+ totalJobs: result.totalJobs,
+ status: result.status,
+ message: 'Bulk batch created successfully'
+ });
+ } else {
+ return reply.code(400).send({
+ success: false,
+ error: result.error || 'Failed to create bulk batch',
+ code: result.code,
+ data: result.data
+ });
+ }
+ } catch (error) {
+ console.error('Error creating bulk batch:', error);
+ return reply.code(500).send({
+ success: false,
+ error: 'Internal server error',
+ message: error.message
+ });
+ }
+}
+
+// Export for use in Fastify app
+module.exports = { bulkIngest };
diff --git a/static/scripts/turbosign/api/bulk/ingest/python/fastapi.py b/static/scripts/turbosign/api/bulk/ingest/python/fastapi.py
new file mode 100644
index 0000000..18e14f2
--- /dev/null
+++ b/static/scripts/turbosign/api/bulk/ingest/python/fastapi.py
@@ -0,0 +1,156 @@
+import json
+import requests
+from fastapi import FastAPI, HTTPException, UploadFile, File
+from pydantic import BaseModel
+
+# Configuration - Update these values
+API_TOKEN = "YOUR_API_TOKEN"
+ORG_ID = "YOUR_ORGANIZATION_ID"
+BASE_URL = "https://api.turbodocx.com"
+
+app = FastAPI()
+
+class BulkIngestResponse(BaseModel):
+ success: bool
+ batchId: str
+ batchName: str
+ totalJobs: int
+ status: str
+ message: str
+
+@app.post('/bulk-ingest', response_model=BulkIngestResponse)
+async def bulk_ingest(file: UploadFile = File(...)):
+ try:
+ # Prepare documents array - each document represents one signing job
+ documents = [
+ {
+ 'recipients': [
+ {
+ 'name': 'John Smith',
+ 'email': 'john.smith@company.com',
+ 'signingOrder': 1
+ }
+ ],
+ 'fields': [
+ {
+ 'recipientEmail': 'john.smith@company.com',
+ 'type': 'signature',
+ 'page': 1,
+ 'x': 100,
+ 'y': 200,
+ 'width': 200,
+ 'height': 80,
+ 'required': True
+ },
+ {
+ 'recipientEmail': 'john.smith@company.com',
+ 'type': 'date',
+ 'page': 1,
+ 'x': 100,
+ 'y': 300,
+ 'width': 150,
+ 'height': 30,
+ 'required': True
+ }
+ ],
+ 'documentName': 'Employment Contract - John Smith',
+ 'documentDescription': 'Please review and sign your employment contract'
+ },
+ {
+ 'recipients': [
+ {
+ 'name': 'Jane Doe',
+ 'email': 'jane.doe@company.com',
+ 'signingOrder': 1
+ }
+ ],
+ 'fields': [
+ {
+ 'recipientEmail': 'jane.doe@company.com',
+ 'type': 'signature',
+ 'page': 1,
+ 'x': 100,
+ 'y': 200,
+ 'width': 200,
+ 'height': 80,
+ 'required': True
+ },
+ {
+ 'recipientEmail': 'jane.doe@company.com',
+ 'type': 'date',
+ 'page': 1,
+ 'x': 100,
+ 'y': 300,
+ 'width': 150,
+ 'height': 30,
+ 'required': True
+ }
+ ],
+ 'documentName': 'Employment Contract - Jane Doe',
+ 'documentDescription': 'Please review and sign your employment contract'
+ }
+ ]
+
+ # Prepare file
+ files = {
+ 'file': (file.filename, file.file, file.content_type)
+ }
+
+ # Prepare form data
+ data = {
+ 'sourceType': 'file',
+ 'batchName': 'Q4 Employment Contracts',
+ 'documentName': 'Employment Contract',
+ 'documentDescription': 'Please review and sign your employment contract',
+ 'senderName': 'HR Department',
+ 'senderEmail': 'hr@company.com',
+ 'documents': json.dumps(documents)
+ }
+
+ # Prepare headers
+ headers = {
+ 'Authorization': f'Bearer {API_TOKEN}',
+ 'x-rapiddocx-org-id': ORG_ID,
+ 'User-Agent': 'TurboDocx API Client'
+ }
+
+ # Send request
+ response = requests.post(
+ f'{BASE_URL}/turbosign/bulk/ingest',
+ headers=headers,
+ data=data,
+ files=files
+ )
+
+ result = response.json()
+
+ if result.get('success'):
+ return BulkIngestResponse(
+ success=True,
+ batchId=result['batchId'],
+ batchName=result['batchName'],
+ totalJobs=result['totalJobs'],
+ status=result['status'],
+ message=result['message']
+ )
+ else:
+ raise HTTPException(
+ status_code=400,
+ detail={
+ 'error': result.get('error', 'Failed to create bulk batch'),
+ 'code': result.get('code'),
+ 'data': result.get('data')
+ }
+ )
+
+ except HTTPException:
+ raise
+ except Exception as error:
+ print(f'Error creating bulk batch: {error}')
+ raise HTTPException(
+ status_code=500,
+ detail={
+ 'error': 'Internal server error',
+ 'message': str(error)
+ }
+ )
diff --git a/static/scripts/turbosign/api/bulk/ingest/python/flask.py b/static/scripts/turbosign/api/bulk/ingest/python/flask.py
new file mode 100644
index 0000000..15c506f
--- /dev/null
+++ b/static/scripts/turbosign/api/bulk/ingest/python/flask.py
@@ -0,0 +1,144 @@
+import json
+import requests
+from flask import Flask, jsonify
+
+# Configuration - Update these values
+API_TOKEN = "YOUR_API_TOKEN"
+ORG_ID = "YOUR_ORGANIZATION_ID"
+BASE_URL = "https://api.turbodocx.com"
+
+app = Flask(__name__)
+
+@app.route('/bulk-ingest', methods=['POST'])
+def bulk_ingest():
+ try:
+ # Prepare documents array - each document represents one signing job
+ documents = [
+ {
+ 'recipients': [
+ {
+ 'name': 'John Smith',
+ 'email': 'john.smith@company.com',
+ 'signingOrder': 1
+ }
+ ],
+ 'fields': [
+ {
+ 'recipientEmail': 'john.smith@company.com',
+ 'type': 'signature',
+ 'page': 1,
+ 'x': 100,
+ 'y': 200,
+ 'width': 200,
+ 'height': 80,
+ 'required': True
+ },
+ {
+ 'recipientEmail': 'john.smith@company.com',
+ 'type': 'date',
+ 'page': 1,
+ 'x': 100,
+ 'y': 300,
+ 'width': 150,
+ 'height': 30,
+ 'required': True
+ }
+ ],
+ 'documentName': 'Employment Contract - John Smith',
+ 'documentDescription': 'Please review and sign your employment contract'
+ },
+ {
+ 'recipients': [
+ {
+ 'name': 'Jane Doe',
+ 'email': 'jane.doe@company.com',
+ 'signingOrder': 1
+ }
+ ],
+ 'fields': [
+ {
+ 'recipientEmail': 'jane.doe@company.com',
+ 'type': 'signature',
+ 'page': 1,
+ 'x': 100,
+ 'y': 200,
+ 'width': 200,
+ 'height': 80,
+ 'required': True
+ },
+ {
+ 'recipientEmail': 'jane.doe@company.com',
+ 'type': 'date',
+ 'page': 1,
+ 'x': 100,
+ 'y': 300,
+ 'width': 150,
+ 'height': 30,
+ 'required': True
+ }
+ ],
+ 'documentName': 'Employment Contract - Jane Doe',
+ 'documentDescription': 'Please review and sign your employment contract'
+ }
+ ]
+
+ # Prepare file
+ files = {
+ 'file': ('contract_template.pdf', open('./contract_template.pdf', 'rb'), 'application/pdf')
+ }
+
+ # Prepare form data
+ data = {
+ 'sourceType': 'file',
+ 'batchName': 'Q4 Employment Contracts',
+ 'documentName': 'Employment Contract',
+ 'documentDescription': 'Please review and sign your employment contract',
+ 'senderName': 'HR Department',
+ 'senderEmail': 'hr@company.com',
+ 'documents': json.dumps(documents)
+ }
+
+ # Prepare headers
+ headers = {
+ 'Authorization': f'Bearer {API_TOKEN}',
+ 'x-rapiddocx-org-id': ORG_ID,
+ 'User-Agent': 'TurboDocx API Client'
+ }
+
+ # Send request
+ response = requests.post(
+ f'{BASE_URL}/turbosign/bulk/ingest',
+ headers=headers,
+ data=data,
+ files=files
+ )
+
+ result = response.json()
+
+ if result.get('success'):
+ return jsonify({
+ 'success': True,
+ 'batchId': result['batchId'],
+ 'batchName': result['batchName'],
+ 'totalJobs': result['totalJobs'],
+ 'status': result['status'],
+ 'message': result['message']
+ }), 200
+ else:
+ return jsonify({
+ 'success': False,
+ 'error': result.get('error', 'Failed to create bulk batch'),
+ 'code': result.get('code'),
+ 'data': result.get('data')
+ }), 400
+
+ except Exception as error:
+ print(f'Error creating bulk batch: {error}')
+ return jsonify({
+ 'success': False,
+ 'error': 'Internal server error',
+ 'message': str(error)
+ }), 500
+
+if __name__ == '__main__':
+ app.run(debug=True)
diff --git a/static/scripts/turbosign/api/bulk/list-batches/nodejs/express.js b/static/scripts/turbosign/api/bulk/list-batches/nodejs/express.js
new file mode 100644
index 0000000..eb0c760
--- /dev/null
+++ b/static/scripts/turbosign/api/bulk/list-batches/nodejs/express.js
@@ -0,0 +1,53 @@
+const fetch = require('node-fetch');
+
+// Configuration - Update these values
+const API_TOKEN = "YOUR_API_TOKEN";
+const ORG_ID = "YOUR_ORGANIZATION_ID";
+const BASE_URL = "https://api.turbodocx.com";
+
+// Prepare query parameters
+const queryParams = new URLSearchParams({
+ limit: '20',
+ offset: '0',
+ status: 'pending,processing,completed' // Can be single value or comma-separated
+ // startDate: '2024-01-01', // Optional: Filter by start date
+ // endDate: '2024-12-31', // Optional: Filter by end date
+ // query: 'Q4' // Optional: Search batches by name
+});
+
+// Send GET request to list batches endpoint
+const response = await fetch(`${BASE_URL}/turbosign/bulk/batches?${queryParams}`, {
+ method: 'GET',
+ headers: {
+ 'Authorization': `Bearer ${API_TOKEN}`,
+ 'x-rapiddocx-org-id': ORG_ID,
+ 'User-Agent': 'TurboDocx API Client'
+ }
+});
+
+const result = await response.json();
+
+if (result.data) {
+ console.log('✅ Successfully retrieved batches');
+ console.log('Total Records:', result.data.totalRecords);
+ console.log('Batches in this page:', result.data.batches.length);
+ console.log('\n📦 Batches:');
+
+ result.data.batches.forEach((batch, index) => {
+ console.log(`\n${index + 1}. ${batch.batchName}`);
+ console.log(' Batch ID:', batch.id);
+ console.log(' Status:', batch.status);
+ console.log(' Total Jobs:', batch.totalJobs);
+ console.log(' Succeeded:', batch.succeededJobs);
+ console.log(' Failed:', batch.failedJobs);
+ console.log(' Pending:', batch.pendingJobs);
+ console.log(' Created:', new Date(batch.createdOn).toLocaleString());
+ });
+
+ console.log('\n💡 Tip: Use limit and offset parameters for pagination');
+} else {
+ console.error('❌ Error:', result.error || 'Failed to retrieve batches');
+ if (result.code) {
+ console.error('Error Code:', result.code);
+ }
+}
diff --git a/static/scripts/turbosign/api/bulk/list-batches/nodejs/fastify.js b/static/scripts/turbosign/api/bulk/list-batches/nodejs/fastify.js
new file mode 100644
index 0000000..518afb1
--- /dev/null
+++ b/static/scripts/turbosign/api/bulk/list-batches/nodejs/fastify.js
@@ -0,0 +1,57 @@
+const fetch = require('node-fetch');
+
+// Configuration - Update these values
+const API_TOKEN = "YOUR_API_TOKEN";
+const ORG_ID = "YOUR_ORGANIZATION_ID";
+const BASE_URL = "https://api.turbodocx.com";
+
+// Fastify route handler example
+async function listBatches(request, reply) {
+ try {
+ // Prepare query parameters
+ const queryParams = new URLSearchParams({
+ limit: '20',
+ offset: '0',
+ status: 'pending,processing,completed' // Can be single value or comma-separated
+ // startDate: '2024-01-01', // Optional: Filter by start date
+ // endDate: '2024-12-31', // Optional: Filter by end date
+ // query: 'Q4' // Optional: Search batches by name
+ });
+
+ // Send GET request to list batches endpoint
+ const response = await fetch(`${BASE_URL}/turbosign/bulk/batches?${queryParams}`, {
+ method: 'GET',
+ headers: {
+ 'Authorization': `Bearer ${API_TOKEN}`,
+ 'x-rapiddocx-org-id': ORG_ID,
+ 'User-Agent': 'TurboDocx API Client'
+ }
+ });
+
+ const result = await response.json();
+
+ if (result.data) {
+ return reply.send({
+ success: true,
+ totalRecords: result.data.totalRecords,
+ batches: result.data.batches
+ });
+ } else {
+ return reply.code(400).send({
+ success: false,
+ error: result.error || 'Failed to retrieve batches',
+ code: result.code
+ });
+ }
+ } catch (error) {
+ console.error('Error retrieving batches:', error);
+ return reply.code(500).send({
+ success: false,
+ error: 'Internal server error',
+ message: error.message
+ });
+ }
+}
+
+// Export for use in Fastify app
+module.exports = { listBatches };
diff --git a/static/scripts/turbosign/api/bulk/list-batches/python/fastapi.py b/static/scripts/turbosign/api/bulk/list-batches/python/fastapi.py
new file mode 100644
index 0000000..5f068fd
--- /dev/null
+++ b/static/scripts/turbosign/api/bulk/list-batches/python/fastapi.py
@@ -0,0 +1,88 @@
+import json
+import requests
+from fastapi import FastAPI, HTTPException, Query
+from pydantic import BaseModel
+from typing import List, Optional
+
+# Configuration - Update these values
+API_TOKEN = "YOUR_API_TOKEN"
+ORG_ID = "YOUR_ORGANIZATION_ID"
+BASE_URL = "https://api.turbodocx.com"
+
+app = FastAPI()
+
+class BatchInfo(BaseModel):
+ id: str
+ batchName: str
+ status: str
+ totalJobs: int
+ succeededJobs: int
+ failedJobs: int
+ pendingJobs: int
+ createdOn: str
+ updatedOn: str
+
+class ListBatchesResponse(BaseModel):
+ success: bool
+ totalRecords: int
+ batches: List[BatchInfo]
+
+@app.get('/list-batches', response_model=ListBatchesResponse)
+async def list_batches(
+ limit: Optional[int] = Query(20, description="Number of batches to return"),
+ offset: Optional[int] = Query(0, description="Number of batches to skip"),
+ status: Optional[str] = Query(None, description="Filter by status")
+):
+ try:
+ # Prepare query parameters
+ params = {
+ 'limit': str(limit),
+ 'offset': str(offset)
+ }
+
+ if status:
+ params['status'] = status
+
+ # Prepare headers
+ headers = {
+ 'Authorization': f'Bearer {API_TOKEN}',
+ 'x-rapiddocx-org-id': ORG_ID,
+ 'User-Agent': 'TurboDocx API Client'
+ }
+
+ # Send GET request
+ response = requests.get(
+ f'{BASE_URL}/turbosign/bulk/batches',
+ headers=headers,
+ params=params
+ )
+
+ result = response.json()
+
+ if result.get('data'):
+ batches_data = result['data']
+ return ListBatchesResponse(
+ success=True,
+ totalRecords=batches_data['totalRecords'],
+ batches=[BatchInfo(**batch) for batch in batches_data['batches']]
+ )
+ else:
+ raise HTTPException(
+ status_code=400,
+ detail={
+ 'error': result.get('error', 'Failed to retrieve batches'),
+ 'code': result.get('code')
+ }
+ )
+
+ except HTTPException:
+ raise
+ except Exception as error:
+ print(f'Error retrieving batches: {error}')
+ raise HTTPException(
+ status_code=500,
+ detail={
+ 'error': 'Internal server error',
+ 'message': str(error)
+ }
+ )
diff --git a/static/scripts/turbosign/api/bulk/list-batches/python/flask.py b/static/scripts/turbosign/api/bulk/list-batches/python/flask.py
new file mode 100644
index 0000000..9e0ac23
--- /dev/null
+++ b/static/scripts/turbosign/api/bulk/list-batches/python/flask.py
@@ -0,0 +1,79 @@
+import json
+import requests
+from flask import Flask, jsonify, request
+
+# Configuration - Update these values
+API_TOKEN = "YOUR_API_TOKEN"
+ORG_ID = "YOUR_ORGANIZATION_ID"
+BASE_URL = "https://api.turbodocx.com"
+
+app = Flask(__name__)
+
+@app.route('/list-batches', methods=['GET'])
+def list_batches():
+ try:
+ # Prepare query parameters
+ params = {
+ 'limit': '20',
+ 'offset': '0',
+ 'status': 'pending,processing,completed' # Can be single value or comma-separated
+ # 'startDate': '2024-01-01', # Optional: Filter by start date
+ # 'endDate': '2024-12-31', # Optional: Filter by end date
+ # 'query': 'Q4' # Optional: Search batches by name
+ }
+
+ # Prepare headers
+ headers = {
+ 'Authorization': f'Bearer {API_TOKEN}',
+ 'x-rapiddocx-org-id': ORG_ID,
+ 'User-Agent': 'TurboDocx API Client'
+ }
+
+ # Send GET request
+ response = requests.get(
+ f'{BASE_URL}/turbosign/bulk/batches',
+ headers=headers,
+ params=params
+ )
+
+ result = response.json()
+
+ if result.get('data'):
+ batches_data = result['data']
+ formatted_batches = []
+
+ for batch in batches_data['batches']:
+ formatted_batches.append({
+ 'id': batch['id'],
+ 'batchName': batch['batchName'],
+ 'status': batch['status'],
+ 'totalJobs': batch['totalJobs'],
+ 'succeededJobs': batch['succeededJobs'],
+ 'failedJobs': batch['failedJobs'],
+ 'pendingJobs': batch['pendingJobs'],
+ 'createdOn': batch['createdOn'],
+ 'updatedOn': batch['updatedOn']
+ })
+
+ return jsonify({
+ 'success': True,
+ 'totalRecords': batches_data['totalRecords'],
+ 'batches': formatted_batches
+ }), 200
+ else:
+ return jsonify({
+ 'success': False,
+ 'error': result.get('error', 'Failed to retrieve batches'),
+ 'code': result.get('code')
+ }), 400
+
+ except Exception as error:
+ print(f'Error retrieving batches: {error}')
+ return jsonify({
+ 'success': False,
+ 'error': 'Internal server error',
+ 'message': str(error)
+ }), 500
+
+if __name__ == '__main__':
+ app.run(debug=True)
diff --git a/static/scripts/turbosign/api/bulk/list-jobs/nodejs/express.js b/static/scripts/turbosign/api/bulk/list-jobs/nodejs/express.js
new file mode 100644
index 0000000..b205481
--- /dev/null
+++ b/static/scripts/turbosign/api/bulk/list-jobs/nodejs/express.js
@@ -0,0 +1,64 @@
+const fetch = require('node-fetch');
+
+// Configuration - Update these values
+const API_TOKEN = "YOUR_API_TOKEN";
+const ORG_ID = "YOUR_ORGANIZATION_ID";
+const BASE_URL = "https://api.turbodocx.com";
+const BATCH_ID = "YOUR_BATCH_ID"; // Replace with actual batch ID
+
+// Prepare query parameters
+const queryParams = new URLSearchParams({
+ limit: '20',
+ offset: '0',
+ // status: 'pending,processing,succeeded,failed', // Optional: Filter by status
+ // query: 'john' // Optional: Search by recipient name/email
+});
+
+// Send GET request to list jobs for a batch
+const response = await fetch(`${BASE_URL}/turbosign/bulk/batch/${BATCH_ID}/jobs?${queryParams}`, {
+ method: 'GET',
+ headers: {
+ 'Authorization': `Bearer ${API_TOKEN}`,
+ 'x-rapiddocx-org-id': ORG_ID,
+ 'User-Agent': 'TurboDocx API Client'
+ }
+});
+
+const result = await response.json();
+
+if (result.data) {
+ console.log('✅ Successfully retrieved jobs for batch');
+ console.log('Batch ID:', result.data.batchId);
+ console.log('Batch Name:', result.data.batchName);
+ console.log('Batch Status:', result.data.batchStatus);
+ console.log('\n📊 Statistics:');
+ console.log('Total Jobs:', result.data.totalJobs);
+ console.log('Total Records:', result.data.totalRecords);
+ console.log('Succeeded:', result.data.succeededJobs);
+ console.log('Failed:', result.data.failedJobs);
+ console.log('Pending:', result.data.pendingJobs);
+
+ console.log('\n📋 Jobs in this page:', result.data.jobs.length);
+
+ result.data.jobs.forEach((job, index) => {
+ console.log(`\n${index + 1}. Job ID: ${job.id}`);
+ console.log(' Document:', job.documentName);
+ console.log(' Document ID:', job.documentId || 'N/A');
+ console.log(' Status:', job.status);
+ console.log(' Recipients:', job.recipientEmails.join(', '));
+ console.log(' Attempts:', job.attempts);
+ if (job.errorMessage) {
+ console.log(' Error:', job.errorMessage);
+ console.log(' Error Code:', job.errorCode);
+ }
+ console.log(' Created:', new Date(job.createdOn).toLocaleString());
+ console.log(' Updated:', new Date(job.updatedOn).toLocaleString());
+ });
+
+ console.log('\n💡 Tip: Use status filter to find failed or pending jobs');
+} else {
+ console.error('❌ Error:', result.error || 'Failed to retrieve jobs');
+ if (result.code) {
+ console.error('Error Code:', result.code);
+ }
+}
diff --git a/static/scripts/turbosign/api/bulk/list-jobs/nodejs/fastify.js b/static/scripts/turbosign/api/bulk/list-jobs/nodejs/fastify.js
new file mode 100644
index 0000000..d294bd0
--- /dev/null
+++ b/static/scripts/turbosign/api/bulk/list-jobs/nodejs/fastify.js
@@ -0,0 +1,67 @@
+const fetch = require('node-fetch');
+
+// Configuration - Update these values
+const API_TOKEN = "YOUR_API_TOKEN";
+const ORG_ID = "YOUR_ORGANIZATION_ID";
+const BASE_URL = "https://api.turbodocx.com";
+const BATCH_ID = "YOUR_BATCH_ID"; // Replace with actual batch ID
+
+// Fastify route handler example
+async function listJobs(request, reply) {
+ try {
+ // Prepare query parameters
+ const queryParams = new URLSearchParams({
+ limit: '20',
+ offset: '0'
+ // status: 'pending,processing,succeeded,failed', // Optional: Filter by status
+ // query: 'john' // Optional: Search by recipient name/email
+ });
+
+ // Send GET request to list jobs for a batch
+ const response = await fetch(`${BASE_URL}/turbosign/bulk/batch/${BATCH_ID}/jobs?${queryParams}`, {
+ method: 'GET',
+ headers: {
+ 'Authorization': `Bearer ${API_TOKEN}`,
+ 'x-rapiddocx-org-id': ORG_ID,
+ 'User-Agent': 'TurboDocx API Client'
+ }
+ });
+
+ const result = await response.json();
+
+ if (result.data) {
+ // Jobs include: id, batchId, documentId, documentName, status, recipientEmails,
+ // attempts, errorCode, errorMessage, createdOn, updatedOn, lastAttemptedAt
+ return reply.send({
+ success: true,
+ batchId: result.data.batchId,
+ batchName: result.data.batchName,
+ batchStatus: result.data.batchStatus,
+ statistics: {
+ totalJobs: result.data.totalJobs,
+ totalRecords: result.data.totalRecords,
+ succeededJobs: result.data.succeededJobs,
+ failedJobs: result.data.failedJobs,
+ pendingJobs: result.data.pendingJobs
+ },
+ jobs: result.data.jobs
+ });
+ } else {
+ return reply.code(400).send({
+ success: false,
+ error: result.error || 'Failed to retrieve jobs',
+ code: result.code
+ });
+ }
+ } catch (error) {
+ console.error('Error retrieving jobs:', error);
+ return reply.code(500).send({
+ success: false,
+ error: 'Internal server error',
+ message: error.message
+ });
+ }
+}
+
+// Export for use in Fastify app
+module.exports = { listJobs };
diff --git a/static/scripts/turbosign/api/bulk/list-jobs/python/fastapi.py b/static/scripts/turbosign/api/bulk/list-jobs/python/fastapi.py
new file mode 100644
index 0000000..84e0e7a
--- /dev/null
+++ b/static/scripts/turbosign/api/bulk/list-jobs/python/fastapi.py
@@ -0,0 +1,111 @@
+import json
+import requests
+from fastapi import FastAPI, HTTPException, Path, Query
+from pydantic import BaseModel
+from typing import List, Optional, Any
+
+# Configuration - Update these values
+API_TOKEN = "YOUR_API_TOKEN"
+ORG_ID = "YOUR_ORGANIZATION_ID"
+BASE_URL = "https://api.turbodocx.com"
+BATCH_ID = "YOUR_BATCH_ID" # Replace with actual batch ID
+
+app = FastAPI()
+
+class JobStatistics(BaseModel):
+ totalJobs: int
+ totalRecords: int
+ succeededJobs: int
+ failedJobs: int
+ pendingJobs: int
+
+class JobInfo(BaseModel):
+ id: str
+ batchId: str
+ documentId: Optional[str]
+ documentName: str
+ status: str
+ recipientEmails: List[str]
+ attempts: int
+ errorCode: Optional[str] = None
+ errorMessage: Optional[str] = None
+ createdOn: str
+ updatedOn: str
+ lastAttemptedAt: Optional[str] = None
+
+class ListJobsResponse(BaseModel):
+ success: bool
+ batchId: str
+ batchName: str
+ batchStatus: str
+ statistics: JobStatistics
+ jobs: List[JobInfo]
+
+@app.get('/list-jobs', response_model=ListJobsResponse)
+async def list_jobs(
+ limit: Optional[int] = Query(20, description="Number of jobs to return"),
+ offset: Optional[int] = Query(0, description="Number of jobs to skip"),
+ status: Optional[str] = Query(None, description="Filter by status")
+):
+ try:
+ # Prepare query parameters
+ params = {
+ 'limit': str(limit),
+ 'offset': str(offset)
+ }
+
+ if status:
+ params['status'] = status
+
+ # Prepare headers
+ headers = {
+ 'Authorization': f'Bearer {API_TOKEN}',
+ 'x-rapiddocx-org-id': ORG_ID,
+ 'User-Agent': 'TurboDocx API Client'
+ }
+
+ # Send GET request
+ response = requests.get(
+ f'{BASE_URL}/turbosign/bulk/batch/{BATCH_ID}/jobs',
+ headers=headers,
+ params=params
+ )
+
+ result = response.json()
+
+ if result.get('data'):
+ jobs_data = result['data']
+ return ListJobsResponse(
+ success=True,
+ batchId=jobs_data['batchId'],
+ batchName=jobs_data['batchName'],
+ batchStatus=jobs_data['batchStatus'],
+ statistics=JobStatistics(
+ totalJobs=jobs_data['totalJobs'],
+ totalRecords=jobs_data['totalRecords'],
+ succeededJobs=jobs_data['succeededJobs'],
+ failedJobs=jobs_data['failedJobs'],
+ pendingJobs=jobs_data['pendingJobs']
+ ),
+ jobs=jobs_data['jobs']
+ )
+ else:
+ raise HTTPException(
+ status_code=400,
+ detail={
+ 'error': result.get('error', 'Failed to retrieve jobs'),
+ 'code': result.get('code')
+ }
+ )
+
+ except HTTPException:
+ raise
+ except Exception as error:
+ print(f'Error retrieving jobs: {error}')
+ raise HTTPException(
+ status_code=500,
+ detail={
+ 'error': 'Internal server error',
+ 'message': str(error)
+ }
+ )
diff --git a/static/scripts/turbosign/api/bulk/list-jobs/python/flask.py b/static/scripts/turbosign/api/bulk/list-jobs/python/flask.py
new file mode 100644
index 0000000..a581c17
--- /dev/null
+++ b/static/scripts/turbosign/api/bulk/list-jobs/python/flask.py
@@ -0,0 +1,93 @@
+import json
+import requests
+from flask import Flask, jsonify, request
+
+# Configuration - Update these values
+API_TOKEN = "YOUR_API_TOKEN"
+ORG_ID = "YOUR_ORGANIZATION_ID"
+BASE_URL = "https://api.turbodocx.com"
+BATCH_ID = "YOUR_BATCH_ID" # Replace with actual batch ID
+
+app = Flask(__name__)
+
+@app.route('/list-jobs', methods=['GET'])
+def list_jobs():
+ try:
+ # Prepare query parameters
+ params = {
+ 'limit': '20',
+ 'offset': '0'
+ # 'status': 'pending,processing,succeeded,failed', # Optional: Filter by status
+ # 'query': 'john' # Optional: Search by recipient name/email
+ }
+
+ # Prepare headers
+ headers = {
+ 'Authorization': f'Bearer {API_TOKEN}',
+ 'x-rapiddocx-org-id': ORG_ID,
+ 'User-Agent': 'TurboDocx API Client'
+ }
+
+ # Send GET request
+ response = requests.get(
+ f'{BASE_URL}/turbosign/bulk/batch/{BATCH_ID}/jobs',
+ headers=headers,
+ params=params
+ )
+
+ result = response.json()
+
+ if result.get('data'):
+ jobs_data = result['data']
+ formatted_jobs = []
+
+ for job in jobs_data['jobs']:
+ job_info = {
+ 'id': job['id'],
+ 'batchId': job['batchId'],
+ 'documentId': job.get('documentId'),
+ 'documentName': job['documentName'],
+ 'status': job['status'],
+ 'recipientEmails': job['recipientEmails'],
+ 'attempts': job['attempts'],
+ 'createdOn': job['createdOn'],
+ 'updatedOn': job['updatedOn'],
+ 'lastAttemptedAt': job.get('lastAttemptedAt')
+ }
+ if job.get('errorMessage'):
+ job_info['errorMessage'] = job['errorMessage']
+ job_info['errorCode'] = job['errorCode']
+
+ formatted_jobs.append(job_info)
+
+ return jsonify({
+ 'success': True,
+ 'batchId': jobs_data['batchId'],
+ 'batchName': jobs_data['batchName'],
+ 'batchStatus': jobs_data['batchStatus'],
+ 'statistics': {
+ 'totalJobs': jobs_data['totalJobs'],
+ 'totalRecords': jobs_data['totalRecords'],
+ 'succeededJobs': jobs_data['succeededJobs'],
+ 'failedJobs': jobs_data['failedJobs'],
+ 'pendingJobs': jobs_data['pendingJobs']
+ },
+ 'jobs': formatted_jobs
+ }), 200
+ else:
+ return jsonify({
+ 'success': False,
+ 'error': result.get('error', 'Failed to retrieve jobs'),
+ 'code': result.get('code')
+ }), 400
+
+ except Exception as error:
+ print(f'Error retrieving jobs: {error}')
+ return jsonify({
+ 'success': False,
+ 'error': 'Internal server error',
+ 'message': str(error)
+ }), 500
+
+if __name__ == '__main__':
+ app.run(debug=True)
diff --git a/static/scripts/turbosign/api/single-step/prepare-for-review/csharp/controller.cs b/static/scripts/turbosign/api/single-step/prepare-for-review/csharp/controller.cs
new file mode 100644
index 0000000..3d5f49d
--- /dev/null
+++ b/static/scripts/turbosign/api/single-step/prepare-for-review/csharp/controller.cs
@@ -0,0 +1,146 @@
+using System;
+using System.IO;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Text.Json;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+
+namespace TurboSign.Controllers
+{
+ [ApiController]
+ [Route("api/[controller]")]
+ public class SignatureController : ControllerBase
+ {
+ private const string API_TOKEN = "YOUR_API_TOKEN";
+ private const string ORG_ID = "YOUR_ORGANIZATION_ID";
+ private const string BASE_URL = "https://api.turbodocx.com";
+ private readonly HttpClient _httpClient;
+
+ public SignatureController(IHttpClientFactory httpClientFactory)
+ {
+ _httpClient = httpClientFactory.CreateClient();
+ }
+
+ [HttpPost("prepare-for-review")]
+ public async Task PrepareForReview(IFormFile file)
+ {
+ try
+ {
+ using var formData = new MultipartFormDataContent();
+
+ // Add file
+ var fileContent = new StreamContent(file.OpenReadStream());
+ fileContent.Headers.ContentType = new MediaTypeHeaderValue(file.ContentType);
+ formData.Add(fileContent, "file", file.FileName);
+
+ // Add document metadata
+ formData.Add(new StringContent("Contract Agreement"), "documentName");
+ formData.Add(new StringContent("Please review and sign this contract"), "documentDescription");
+ formData.Add(new StringContent("Your Company"), "senderName");
+ formData.Add(new StringContent("sender@company.com"), "senderEmail");
+
+ // Add recipients (as JSON string)
+ var recipients = JsonSerializer.Serialize(new[]
+ {
+ new
+ {
+ name = "John Smith",
+ email = "john.smith@company.com",
+ signingOrder = 1},
+ new
+ {
+ name = "Jane Doe",
+ email = "jane.doe@partner.com",
+ signingOrder = 2}
+ });
+ formData.Add(new StringContent(recipients), "recipients");
+
+ // Add fields (as JSON string) - Coordinate-based
+ var fields = JsonSerializer.Serialize(new[]
+ {
+ new
+ {
+ recipientEmail = "john.smith@company.com",
+ type = "signature",
+ page = 1,
+ x = 100,
+ y = 200,
+ width = 200,
+ height = 80,
+ required = true
+ },
+ new
+ {
+ recipientEmail = "john.smith@company.com",
+ type = "date",
+ page = 1,
+ x = 100,
+ y = 300,
+ width = 150,
+ height = 30,
+ required = true
+ },
+ new
+ {
+ recipientEmail = "jane.doe@partner.com",
+ type = "signature",
+ page = 1,
+ x = 350,
+ y = 200,
+ width = 200,
+ height = 80,
+ required = true
+ }
+ });
+ formData.Add(new StringContent(fields), "fields");
+
+ // Set headers
+ var request = new HttpRequestMessage(HttpMethod.Post, $"{BASE_URL}/turbosign/single/prepare-for-review")
+ {
+ Content = formData
+ };
+ request.Headers.Add("Authorization", $"Bearer {API_TOKEN}");
+ request.Headers.Add("x-rapiddocx-org-id", ORG_ID);
+ request.Headers.Add("User-Agent", "TurboDocx API Client");
+
+ // Send request
+ var response = await _httpClient.SendAsync(request);
+ var responseContent = await response.Content.ReadAsStringAsync();
+ var result = JsonSerializer.Deserialize(responseContent);
+
+ if (result.GetProperty("success").GetBoolean())
+ {
+ return Ok(new
+ {
+ success = true,
+ documentId = result.GetProperty("documentId").GetString(),
+ status = result.GetProperty("status").GetString(),
+ previewUrl = result.GetProperty("previewUrl").GetString(),
+ recipients = result.GetProperty("recipients"),
+ message = "Document prepared for review successfully"
+ });
+ }
+ else
+ {
+ return BadRequest(new
+ {
+ success = false,
+ error = result.GetProperty("error").GetString(),
+ code = result.TryGetProperty("code", out var code) ? code.GetString() : null
+ });
+ }
+ }
+ catch (Exception ex)
+ {
+ return StatusCode(500, new
+ {
+ success = false,
+ error = "Internal server error",
+ message = ex.Message
+ });
+ }
+ }
+ }
+}
diff --git a/static/scripts/turbosign/api/single-step/prepare-for-review/csharp/minimal.cs b/static/scripts/turbosign/api/single-step/prepare-for-review/csharp/minimal.cs
new file mode 100644
index 0000000..c22b3fc
--- /dev/null
+++ b/static/scripts/turbosign/api/single-step/prepare-for-review/csharp/minimal.cs
@@ -0,0 +1,109 @@
+using System.Net.Http.Headers;
+using System.Text.Json;
+
+// Configuration - Update these values
+const string API_TOKEN = "YOUR_API_TOKEN";
+const string ORG_ID = "YOUR_ORGANIZATION_ID";
+const string BASE_URL = "https://api.turbodocx.com";
+
+var builder = WebApplication.CreateBuilder(args);
+builder.Services.AddHttpClient();
+
+var app = builder.Build();
+
+app.MapPost("/prepare-for-review", async (IFormFile file, IHttpClientFactory httpClientFactory) =>
+{
+ try
+ {
+ var httpClient = httpClientFactory.CreateClient();
+ using var formData = new MultipartFormDataContent();
+
+ // Add file
+ var fileContent = new StreamContent(file.OpenReadStream());
+ fileContent.Headers.ContentType = new MediaTypeHeaderValue(file.ContentType);
+ formData.Add(fileContent, "file", file.FileName);
+
+ // Add document metadata
+ formData.Add(new StringContent("Contract Agreement"), "documentName");
+ formData.Add(new StringContent("Please review and sign this contract"), "documentDescription");
+
+ // Add recipients (as JSON string)
+ var recipients = JsonSerializer.Serialize(new[]
+ {
+ new
+ {
+ name = "John Smith",
+ email = "john.smith@company.com",
+ signingOrder = 1}
+ });
+ formData.Add(new StringContent(recipients), "recipients");
+
+ // Add fields (as JSON string) - Coordinate-based
+ var fields = JsonSerializer.Serialize(new[]
+ {
+ new
+ {
+ recipientEmail = "john.smith@company.com",
+ type = "signature",
+ page = 1,
+ x = 100,
+ y = 200,
+ width = 200,
+ height = 80,
+ required = true
+ },
+ new
+ {
+ recipientEmail = "john.smith@company.com",
+ type = "date",
+ page = 1,
+ x = 350,
+ y = 200,
+ width = 150,
+ height = 30,
+ required = true
+ }
+ });
+ formData.Add(new StringContent(fields), "fields");
+
+ // Set headers
+ var request = new HttpRequestMessage(HttpMethod.Post, $"{BASE_URL}/turbosign/single/prepare-for-review")
+ {
+ Content = formData
+ };
+ request.Headers.Add("Authorization", $"Bearer {API_TOKEN}");
+ request.Headers.Add("x-rapiddocx-org-id", ORG_ID);
+ request.Headers.Add("User-Agent", "TurboDocx API Client");
+
+ // Send request
+ var response = await httpClient.SendAsync(request);
+ var responseContent = await response.Content.ReadAsStringAsync();
+ var result = JsonSerializer.Deserialize(responseContent);
+
+ if (result.GetProperty("success").GetBoolean())
+ {
+ return Results.Ok(new
+ {
+ success = true,
+ documentId = result.GetProperty("documentId").GetString(),
+ status = result.GetProperty("status").GetString(),
+ previewUrl = result.GetProperty("previewUrl").GetString(),
+ message = "Document prepared for review successfully"
+ });
+ }
+ else
+ {
+ return Results.BadRequest(new
+ {
+ success = false,
+ error = result.GetProperty("error").GetString()
+ });
+ }
+ }
+ catch (Exception ex)
+ {
+ return Results.Problem(ex.Message);
+ }
+});
+
+app.Run();
diff --git a/static/scripts/turbosign/api/single-step/prepare-for-review/go.go b/static/scripts/turbosign/api/single-step/prepare-for-review/go.go
new file mode 100644
index 0000000..d385c30
--- /dev/null
+++ b/static/scripts/turbosign/api/single-step/prepare-for-review/go.go
@@ -0,0 +1,188 @@
+package main
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "io"
+ "mime/multipart"
+ "net/http"
+ "os"
+)
+
+// Configuration - Update these values
+const (
+ API_TOKEN = "YOUR_API_TOKEN"
+ ORG_ID = "YOUR_ORGANIZATION_ID"
+ BASE_URL = "https://api.turbodocx.com"
+)
+
+type Recipient struct {
+ Name string `json:"name"`
+ Email string `json:"email"`
+ SigningOrder int `json:"signingOrder"`
+}
+
+type Field struct {
+ RecipientEmail string `json:"recipientEmail"`
+ Type string `json:"type"`
+ Page int `json:"page,omitempty"`
+ X int `json:"x,omitempty"`
+ Y int `json:"y,omitempty"`
+ Width int `json:"width,omitempty"`
+ Height int `json:"height,omitempty"`
+ Required bool `json:"required"`
+}
+
+type Response struct {
+ Success bool `json:"success"`
+ DocumentID string `json:"documentId"`
+ Status string `json:"status"`
+ PreviewURL string `json:"previewUrl"`
+ Recipients []map[string]interface{} `json:"recipients"`
+ Message string `json:"message"`
+ Error string `json:"error,omitempty"`
+ Code string `json:"code,omitempty"`
+}
+
+func prepareDocumentForReview() error {
+ // Prepare recipients
+ recipients := []Recipient{
+ {
+ Name: "John Smith",
+ Email: "john.smith@company.com",
+ SigningOrder: 1,
+ },
+ {
+ Name: "Jane Doe",
+ Email: "jane.doe@partner.com",
+ SigningOrder: 2,
+ },
+ }
+
+ recipientsJSON, err := json.Marshal(recipients)
+ if err != nil {
+ return fmt.Errorf("failed to marshal recipients: %w", err)
+ }
+
+ // Prepare fields - Coordinate-based
+ fields := []Field{
+ {
+ RecipientEmail: "john.smith@company.com",
+ Type: "signature",
+ Page: 1,
+ X: 100,
+ Y: 200,
+ Width: 200,
+ Height: 80,
+ Required: true,
+ },
+ {
+ RecipientEmail: "john.smith@company.com",
+ Type: "date",
+ Page: 1,
+ X: 100,
+ Y: 300,
+ Width: 150,
+ Height: 30,
+ Required: true,
+ },
+ {
+ RecipientEmail: "jane.doe@partner.com",
+ Type: "signature",
+ Page: 1,
+ X: 350,
+ Y: 200,
+ Width: 200,
+ Height: 80,
+ Required: true,
+ },
+ }
+
+ fieldsJSON, err := json.Marshal(fields)
+ if err != nil {
+ return fmt.Errorf("failed to marshal fields: %w", err)
+ }
+
+ // Create multipart form
+ body := &bytes.Buffer{}
+ writer := multipart.NewWriter(body)
+
+ // Add file
+ file, err := os.Open("./contract.pdf")
+ if err != nil {
+ return fmt.Errorf("failed to open file: %w", err)
+ }
+ defer file.Close()
+
+ part, err := writer.CreateFormFile("file", "contract.pdf")
+ if err != nil {
+ return fmt.Errorf("failed to create form file: %w", err)
+ }
+ if _, err := io.Copy(part, file); err != nil {
+ return fmt.Errorf("failed to copy file: %w", err)
+ }
+
+ // Add form fields
+ writer.WriteField("documentName", "Contract Agreement")
+ writer.WriteField("documentDescription", "Please review and sign this contract")
+ writer.WriteField("senderName", "Your Company")
+ writer.WriteField("senderEmail", "sender@company.com")
+ writer.WriteField("recipients", string(recipientsJSON))
+ writer.WriteField("fields", string(fieldsJSON))
+
+ err = writer.Close()
+ if err != nil {
+ return fmt.Errorf("failed to close writer: %w", err)
+ }
+
+ // Create request
+ req, err := http.NewRequest("POST", BASE_URL+"/turbosign/single/prepare-for-review", body)
+ if err != nil {
+ return fmt.Errorf("failed to create request: %w", err)
+ }
+
+ // Set headers
+ req.Header.Set("Authorization", "Bearer "+API_TOKEN)
+ req.Header.Set("x-rapiddocx-org-id", ORG_ID)
+ req.Header.Set("User-Agent", "TurboDocx API Client")
+ req.Header.Set("Content-Type", writer.FormDataContentType())
+
+ // Send request
+ client := &http.Client{}
+ resp, err := client.Do(req)
+ if err != nil {
+ return fmt.Errorf("failed to send request: %w", err)
+ }
+ defer resp.Body.Close()
+
+ // Parse response
+ var result Response
+ if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
+ return fmt.Errorf("failed to decode response: %w", err)
+ }
+
+ if result.Success {
+ fmt.Println("✅ Document prepared for review")
+ fmt.Printf("Document ID: %s\n", result.DocumentID)
+ fmt.Printf("Status: %s\n", result.Status)
+ fmt.Printf("Preview URL: %s\n", result.PreviewURL)
+ fmt.Println("\n🔍 Open this URL to preview the document:")
+ fmt.Println(result.PreviewURL)
+ } else {
+ fmt.Printf("❌ Error: %s\n", result.Error)
+ if result.Code != "" {
+ fmt.Printf("Error Code: %s\n", result.Code)
+ }
+ return fmt.Errorf("API error: %s", result.Error)
+ }
+
+ return nil
+}
+
+func main() {
+ if err := prepareDocumentForReview(); err != nil {
+ fmt.Fprintf(os.Stderr, "Error: %v\n", err)
+ os.Exit(1)
+ }
+}
diff --git a/static/scripts/turbosign/api/single-step/prepare-for-review/java.java b/static/scripts/turbosign/api/single-step/prepare-for-review/java.java
new file mode 100644
index 0000000..8550ebe
--- /dev/null
+++ b/static/scripts/turbosign/api/single-step/prepare-for-review/java.java
@@ -0,0 +1,128 @@
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import okhttp3.*;
+
+public class TurboSignPrepareForReview {
+
+ // Configuration - Update these values
+ private static final String API_TOKEN = "YOUR_API_TOKEN";
+ private static final String ORG_ID = "YOUR_ORGANIZATION_ID";
+ private static final String BASE_URL = "https://api.turbodocx.com";
+
+ private final OkHttpClient client = new OkHttpClient();
+ private final ObjectMapper objectMapper = new ObjectMapper();
+
+ public void prepareDocumentForReview(File pdfFile) throws IOException {
+ // Prepare recipients
+ List