TurboSign SDK
Let an agent scaffold this for you
Install the TurboDocx Quickstart Skill and let Claude Code, Cursor, Copilot, Codex, or any agent that speaks the Agent Skills standard install the SDK, wire it into your app, and write a working TurboSign integration end-to-end.
$npx skills add TurboDocx/quickstart›/turbodocx-sdk turbosign- JavaScript / TypeScript
- Python
- PHP
- Go
- Java
- Ruby
The official TurboDocx SDK for JavaScript and TypeScript applications. Build document generation and digital signature workflows with full TypeScript support, async/await patterns, and comprehensive error handling. Available on npm as @turbodocx/sdk.
The official TurboDocx SDK for Python applications. Build document generation and digital signature workflows with async/await patterns and comprehensive error handling. Available on PyPI as turbodocx-sdk.
The official TurboDocx SDK for PHP applications. Build document generation and digital signature workflows with modern PHP 8.1+ features, strong typing, and comprehensive error handling. Available on Packagist as turbodocx/sdk.
The official TurboDocx SDK for Go applications. Build document generation and digital signature workflows with idiomatic Go patterns, context support, and comprehensive error handling. Available as github.com/TurboDocx/SDK/packages/go-sdk.
The official TurboDocx SDK for Java applications. Build document generation and digital signature workflows with the Builder pattern, comprehensive error handling, and type-safe APIs. Available on Maven Central as com.turbodocx:turbodocx-sdk.
The official TurboDocx SDK for Ruby applications. Build document generation and digital signature workflows with a simple synchronous API and comprehensive error handling. Available on RubyGems as turbodocx-sdk.
Installation
- JavaScript / TypeScript
- Python
- PHP
- Go
- Java
- Ruby
- npm
- yarn
- pnpm
npm install @turbodocx/sdk
yarn add @turbodocx/sdk
pnpm add @turbodocx/sdk
- pip
- Poetry
- Pipenv
pip install turbodocx-sdk
poetry add turbodocx-sdk
pipenv install turbodocx-sdk
composer require turbodocx/sdk
go get github.com/TurboDocx/SDK/packages/go-sdk
- Maven
- Gradle (Kotlin)
- Gradle (Groovy)
<dependency>
<groupId>com.turbodocx</groupId>
<artifactId>turbodocx-sdk</artifactId>
<version>0.9.0</version>
</dependency>
implementation("com.turbodocx:turbodocx-sdk:0.9.0")
implementation 'com.turbodocx:turbodocx-sdk:0.9.0'
- gem
- Bundler
gem install turbodocx-sdk
Add to your Gemfile:
gem "turbodocx-sdk"
Then run:
bundle install
Requirements
- JavaScript / TypeScript
- Python
- PHP
- Go
- Java
- Ruby
- Node.js 18+ or modern browser
- TypeScript 4.7+ (optional, for type checking)
- Python 3.9+
httpx(installed automatically)
- PHP 8.1 or higher
- Composer
- ext-json
- ext-fileinfo
This SDK leverages PHP 8.1+ features including enums, named parameters, readonly properties, and match expressions for a superior developer experience.
- Go 1.21+
- Java 11+
- OkHttp 4.x (included)
- Gson 2.x (included)
- Ruby 2.7+
- No runtime dependencies (uses the standard library's
net/http)
Configuration
- JavaScript / TypeScript
- Python
- PHP
- Go
- Java
- Ruby
- JavaScript
- TypeScript
const { TurboSign } = require("@turbodocx/sdk");
// Configure globally (recommended for server-side)
TurboSign.configure({
apiKey: process.env.TURBODOCX_API_KEY, // Required : Your TurboDocx API key
orgId: process.env.TURBODOCX_ORG_ID, // Required: Your organization ID
senderEmail: process.env.TURBODOCX_SENDER_EMAIL, // Required: reply-to address for signature request emails
senderName: "Your Company Name", // Optional but recommended: appears as the sender name
// Optional: override base URL for testing
// baseUrl: 'https://api.turbodocx.com'
});
import { TurboSign } from "@turbodocx/sdk";
// Configure globally (recommended for server-side)
TurboSign.configure({
apiKey: process.env.TURBODOCX_API_KEY || "", // Required : Your TurboDocx API key
orgId: process.env.TURBODOCX_ORG_ID || "", // Required: Your organization ID
senderEmail: process.env.TURBODOCX_SENDER_EMAIL || "", // Required: reply-to address for signature request emails
senderName: "Your Company Name", // Optional but recommended: appears as the sender name
// Optional: override base URL for testing
// baseUrl: 'https://api.turbodocx.com'
});
Authenticate using apiKey. API keys are recommended for server-side applications.
from turbodocx_sdk import TurboSign
import os
# Configure globally (recommended)
TurboSign.configure(
api_key=os.environ["TURBODOCX_API_KEY"], # Required: Your TurboDocx API key
org_id=os.environ["TURBODOCX_ORG_ID"], # Required: Your organization ID
sender_email="contracts@yourcompany.com", # Required: Reply-to address for signature emails
sender_name="Your Company", # Recommended: Sender name shown in emails
# base_url="https://api.turbodocx.com" # Optional: Override base URL
)
Authenticate using api_key. API keys are recommended for server-side applications.
- Manual Configuration
- From Environment
<?php
use TurboDocx\TurboSign;
use TurboDocx\Config\HttpClientConfig;
// Configure with all options
TurboSign::configure(new HttpClientConfig(
apiKey: getenv('TURBODOCX_API_KEY') ?: null, // Required: Your TurboDocx API key
orgId: getenv('TURBODOCX_ORG_ID') ?: null, // Required: Your organization ID
senderEmail: getenv('TURBODOCX_SENDER_EMAIL') ?: null, // Required: Reply-to email for signature requests
senderName: getenv('TURBODOCX_SENDER_NAME') ?: null, // Optional: Sender name (strongly recommended)
baseUrl: 'https://api.turbodocx.com' // Optional: Custom API endpoint
));
<?php
use TurboDocx\TurboSign;
use TurboDocx\Config\HttpClientConfig;
// Auto-configure from environment variables
TurboSign::configure(HttpClientConfig::fromEnvironment());
// Reads from: TURBODOCX_API_KEY, TURBODOCX_ORG_ID,
// TURBODOCX_SENDER_EMAIL, TURBODOCX_SENDER_NAME
The senderEmail parameter is required for TurboSign. This email appears as the reply-to address in signature request emails. If you omit it, configure() throws a ValidationException before any request is sent (unless skipSenderValidation is enabled, which TurboSign does not use).
package main
import (
"log"
"os"
turbodocx "github.com/TurboDocx/SDK/packages/go-sdk"
)
func main() {
// Create a new client (reads SenderEmail from TURBODOCX_SENDER_EMAIL)
client, err := turbodocx.NewClient(
os.Getenv("TURBODOCX_API_KEY"),
os.Getenv("TURBODOCX_ORG_ID"),
)
if err != nil {
log.Fatal(err)
}
_ = client
// Or with custom configuration
client, err = turbodocx.NewClientWithConfig(turbodocx.ClientConfig{
APIKey: os.Getenv("TURBODOCX_API_KEY"),
OrgID: os.Getenv("TURBODOCX_ORG_ID"),
SenderEmail: os.Getenv("TURBODOCX_SENDER_EMAIL"), // Required for TurboSign
BaseURL: "https://api.turbodocx.com", // Optional custom base URL
})
if err != nil {
log.Fatal(err)
}
_ = client
}
import com.turbodocx.TurboDocxClient;
public class Main {
public static void main(String[] args) {
// Create client with Builder pattern
TurboDocxClient client = new TurboDocxClient.Builder()
.apiKey(System.getenv("TURBODOCX_API_KEY"))
.orgId(System.getenv("TURBODOCX_ORG_ID"))
.senderEmail(System.getenv("TURBODOCX_SENDER_EMAIL"))
.build();
// Or with custom base URL
TurboDocxClient customClient = new TurboDocxClient.Builder()
.apiKey(System.getenv("TURBODOCX_API_KEY"))
.orgId(System.getenv("TURBODOCX_ORG_ID"))
.senderEmail(System.getenv("TURBODOCX_SENDER_EMAIL"))
.baseUrl("https://api.turbodocx.com")
.build();
}
}
require "turbodocx_sdk"
# Configure globally (recommended)
TurboDocxSdk::TurboSign.configure(
api_key: ENV["TURBODOCX_API_KEY"], # Required: Your TurboDocx API key
org_id: ENV["TURBODOCX_ORG_ID"], # Required: Your organization ID
sender_email: "contracts@yourcompany.com", # Required: Reply-to address for signature emails
sender_name: "Your Company" # Recommended: Sender name shown in emails
# base_url: "https://api.turbodocx.com" # Optional: Override base URL
)
Authenticate using api_key. API keys are recommended for server-side applications.
Builder Options
- Java
| Method | Type | Required | Default | Description |
|---|---|---|---|---|
apiKey(String) | String | Yes* | - | Organization API key |
accessToken(String) | String | Yes* | - | Bearer access token (alternative to apiKey) |
orgId(String) | String | Yes | - | Organization ID |
senderEmail(String) | String | Yes | - | Reply-to address for signature request emails |
senderName(String) | String | No | - | Display name used on signature request emails |
baseUrl(String) | String | No | https://api.turbodocx.com | API base URL |
connectTimeoutSeconds(int) | int | No | 60 | Connection timeout |
readTimeoutSeconds(int) | int | No | 120 | Read timeout, raise it for large document uploads |
writeTimeoutSeconds(int) | int | No | 60 | Write timeout, raise it for large document uploads |
*Provide either apiKey or accessToken.
// Tune the timeouts for large documents
TurboDocxClient client = new TurboDocxClient.Builder()
.apiKey(System.getenv("TURBODOCX_API_KEY"))
.orgId(System.getenv("TURBODOCX_ORG_ID"))
.senderEmail(System.getenv("TURBODOCX_SENDER_EMAIL"))
.connectTimeoutSeconds(30)
.readTimeoutSeconds(300)
.writeTimeoutSeconds(300)
.build();
Closing the Client
- Java
TurboDocxClient implements AutoCloseable. Calling close() shuts down the underlying OkHttp dispatcher and connection pool, so long-running JVM services should close clients they no longer need. Use try-with-resources for short-lived clients:
try (TurboDocxClient client = new TurboDocxClient.Builder()
.apiKey(System.getenv("TURBODOCX_API_KEY"))
.orgId(System.getenv("TURBODOCX_ORG_ID"))
.senderEmail(System.getenv("TURBODOCX_SENDER_EMAIL"))
.build()) {
SendSignatureResponse result = client.turboSign().sendSignature(request);
}
Creating a client per request leaks OkHttp threads until they are closed. Prefer one long-lived client for the life of your application and call client.close() during shutdown.
Environment Variables
- JavaScript / TypeScript
- Python
- PHP
- Go
- Java
- Ruby
# .env
TURBODOCX_API_KEY=your_api_key_here
TURBODOCX_ORG_ID=your_org_id_here
TURBODOCX_SENDER_EMAIL=you@company.com # Required for TurboSign
TURBODOCX_SENDER_NAME=Your Company Name # Optional (strongly recommended)
# .env
TURBODOCX_API_KEY=your_api_key_here
TURBODOCX_ORG_ID=your_org_id_here
TURBODOCX_SENDER_EMAIL=contracts@yourcompany.com
TURBODOCX_SENDER_NAME=Your Company
api_key and org_id are required for all API requests. TurboSign additionally requires sender_email (set it on configure(), per call, or via the TURBODOCX_SENDER_EMAIL environment variable): configure() raises a ValidationError without it. sender_name is optional but strongly recommended. To get your credentials, follow the Get Your Credentials steps from the SDKs main page.
# .env
TURBODOCX_API_KEY=your_api_key_here
TURBODOCX_ORG_ID=your_org_id_here
TURBODOCX_SENDER_EMAIL=you@company.com
TURBODOCX_SENDER_NAME=Your Company Name
Both apiKey and orgId parameters are required for all API requests. To get your credentials, follow the Get Your Credentials steps from the SDKs main page.
export TURBODOCX_API_KEY=your_api_key_here
export TURBODOCX_ORG_ID=your_org_id_here
export TURBODOCX_SENDER_EMAIL=you@example.com # Required for TurboSign (reply-to address)
export TURBODOCX_API_KEY=your_api_key_here
export TURBODOCX_ORG_ID=your_org_id_here
export TURBODOCX_SENDER_EMAIL=sender@yourcompany.com
Three parameters are required for TurboSign operations: apiKey (or accessToken), orgId, and senderEmail. The senderEmail is used as the reply-to address for signature request emails. To get your credentials, follow the Get Your Credentials steps from the SDKs main page.
# .env
TURBODOCX_API_KEY=your_api_key_here
TURBODOCX_ORG_ID=your_org_id_here
TURBODOCX_SENDER_EMAIL=contracts@yourcompany.com
TURBODOCX_SENDER_NAME=Your Company
api_key and org_id are required for all API requests. TurboSign additionally requires sender_email (set it on configure, per call, or via the TURBODOCX_SENDER_EMAIL environment variable): configure raises a TurboDocxSdk::ValidationError without it. sender_name is optional but strongly recommended. To get your credentials, follow the Get Your Credentials steps from the SDKs main page.
Quick Start
Send a Document for Signature
- JavaScript / TypeScript
- Python
- PHP
- Go
- Java
- Ruby
- JavaScript
- TypeScript
const { TurboSign } = require("@turbodocx/sdk");
TurboSign.configure({
apiKey: process.env.TURBODOCX_API_KEY,
orgId: process.env.TURBODOCX_ORG_ID,
senderEmail: process.env.TURBODOCX_SENDER_EMAIL,
});
(async () => {
// Send document with coordinate-based fields
const result = await TurboSign.sendSignature({
fileLink: "https://www.turbodocx.com/examples/turbodocx.pdf",
documentName: "Service Agreement",
senderName: "Acme Corp",
senderEmail: "contracts@acme.com",
recipients: [
{ name: "Alice Smith", email: "alice@example.com", signingOrder: 1 },
{ name: "Bob Johnson", email: "bob@example.com", signingOrder: 2 },
],
fields: [
// Alice's signature
{
type: "signature",
page: 1,
x: 100,
y: 650,
width: 200,
height: 50,
recipientEmail: "alice@example.com",
},
{
type: "date",
page: 1,
x: 320,
y: 650,
width: 100,
height: 30,
recipientEmail: "alice@example.com",
// Pins a fixed date in MM/DD/YYYY; omit to auto-fill the signing date
defaultValue: "12/31/2026",
},
// Bob's signature
{
type: "signature",
page: 1,
x: 100,
y: 720,
width: 200,
height: 50,
recipientEmail: "bob@example.com",
},
{
type: "date",
page: 1,
x: 320,
y: 720,
width: 100,
height: 30,
recipientEmail: "bob@example.com",
},
],
});
console.log(JSON.stringify(result, null, 2));
})();
import { TurboSign } from "@turbodocx/sdk";
TurboSign.configure({
apiKey: process.env.TURBODOCX_API_KEY || "",
orgId: process.env.TURBODOCX_ORG_ID || "",
senderEmail: process.env.TURBODOCX_SENDER_EMAIL || "",
});
// Send document with coordinate-based fields
const result = await TurboSign.sendSignature({
fileLink: "https://www.turbodocx.com/examples/turbodocx.pdf",
documentName: "Service Agreement",
senderName: "Acme Corp",
senderEmail: "contracts@acme.com",
recipients: [
{ name: "Alice Smith", email: "alice@example.com", signingOrder: 1 },
{ name: "Bob Johnson", email: "bob@example.com", signingOrder: 2 },
],
fields: [
// Alice's signature
{
type: "signature",
page: 1,
x: 100,
y: 650,
width: 200,
height: 50,
recipientEmail: "alice@example.com",
},
{
type: "date",
page: 1,
x: 320,
y: 650,
width: 100,
height: 30,
recipientEmail: "alice@example.com",
},
// Bob's signature
{
type: "signature",
page: 1,
x: 100,
y: 720,
width: 200,
height: 50,
recipientEmail: "bob@example.com",
},
{
type: "date",
page: 1,
x: 320,
y: 720,
width: 100,
height: 30,
recipientEmail: "bob@example.com",
},
],
});
console.log(JSON.stringify(result, null, 2));
import asyncio
import json
import os
from turbodocx_sdk import TurboSign
TurboSign.configure(
api_key=os.environ["TURBODOCX_API_KEY"],
org_id=os.environ["TURBODOCX_ORG_ID"],
sender_email="contracts@acme.com",
sender_name="Acme Corp",
)
async def send_contract():
result = await TurboSign.send_signature(
recipients=[
{"name": "Alice Smith", "email": "alice@example.com", "signingOrder": 1},
{"name": "Bob Johnson", "email": "bob@example.com", "signingOrder": 2}
],
fields=[
# Alice's signature
{"type": "signature", "page": 1, "x": 100, "y": 650, "width": 200, "height": 50, "recipientEmail": "alice@example.com"},
{"type": "date", "page": 1, "x": 320, "y": 650, "width": 100, "height": 30, "recipientEmail": "alice@example.com"},
# Bob's signature
{"type": "signature", "page": 1, "x": 100, "y": 720, "width": 200, "height": 50, "recipientEmail": "bob@example.com"},
{"type": "date", "page": 1, "x": 320, "y": 720, "width": 100, "height": 30, "recipientEmail": "bob@example.com"}
],
file_link="https://www.turbodocx.com/examples/turbodocx.pdf",
document_name="Service Agreement",
sender_name="Acme Corp",
sender_email="contracts@acme.com",
)
print("Result:", json.dumps(result, indent=2))
asyncio.run(send_contract())
<?php
use TurboDocx\TurboSign;
use TurboDocx\Config\HttpClientConfig;
use TurboDocx\Types\Recipient;
use TurboDocx\Types\Field;
use TurboDocx\Types\SignatureFieldType;
use TurboDocx\Types\Requests\SendSignatureRequest;
TurboSign::configure(HttpClientConfig::fromEnvironment());
// Send document with coordinate-based fields
$result = TurboSign::sendSignature(
new SendSignatureRequest(
recipients: [
new Recipient('Alice Smith', 'alice@example.com', 1),
new Recipient('Bob Johnson', 'bob@example.com', 2)
],
fields: [
// Alice's signature
new Field(
type: SignatureFieldType::SIGNATURE,
recipientEmail: 'alice@example.com',
page: 1,
x: 100,
y: 650,
width: 200,
height: 50
),
new Field(
type: SignatureFieldType::DATE,
recipientEmail: 'alice@example.com',
page: 1,
x: 320,
y: 650,
width: 100,
height: 30,
defaultValue: '12/31/2026' // Pins a fixed date in MM/DD/YYYY; omit to auto-fill the signing date
),
// Bob's signature
new Field(
type: SignatureFieldType::SIGNATURE,
recipientEmail: 'bob@example.com',
page: 1,
x: 100,
y: 720,
width: 200,
height: 50
),
new Field(
type: SignatureFieldType::DATE,
recipientEmail: 'bob@example.com',
page: 1,
x: 320,
y: 720,
width: 100,
height: 30
)
],
fileLink: 'https://www.turbodocx.com/examples/turbodocx.pdf',
documentName: 'Service Agreement',
senderName: 'Acme Corp',
senderEmail: 'contracts@acme.com'
)
);
echo "Document ID: {$result->documentId}\n";
The above examples omit error handling for brevity. In production, wrap all TurboSign calls in try-catch blocks. See Error Handling for complete patterns.
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"os"
turbodocx "github.com/TurboDocx/SDK/packages/go-sdk"
)
func main() {
client, err := turbodocx.NewClient(
os.Getenv("TURBODOCX_API_KEY"),
os.Getenv("TURBODOCX_ORG_ID"),
)
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
result, err := client.TurboSign.SendSignature(ctx, &turbodocx.SendSignatureRequest{
FileLink: "https://www.turbodocx.com/examples/turbodocx.pdf",
DocumentName: "Service Agreement",
SenderName: "Acme Corp",
SenderEmail: "contracts@acme.com",
Recipients: []turbodocx.Recipient{
{Name: "Alice Smith", Email: "alice@example.com", SigningOrder: 1},
{Name: "Bob Johnson", Email: "bob@example.com", SigningOrder: 2},
},
Fields: []turbodocx.Field{
// Alice's signature
{Type: "signature", Page: 1, X: 100, Y: 650, Width: 200, Height: 50, RecipientEmail: "alice@example.com"},
{Type: "date", Page: 1, X: 320, Y: 650, Width: 100, Height: 30, RecipientEmail: "alice@example.com"},
// Bob's signature
{Type: "signature", Page: 1, X: 100, Y: 720, Width: 200, Height: 50, RecipientEmail: "bob@example.com"},
{Type: "date", Page: 1, X: 320, Y: 720, Width: 100, Height: 30, RecipientEmail: "bob@example.com"},
},
})
if err != nil {
log.Fatal(err)
}
b, _ := json.MarshalIndent(result, "", " "); fmt.Println("Result:", string(b))
}
import com.turbodocx.TurboDocxClient;
import com.turbodocx.models.*;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.util.Arrays;
public class Main {
public static void main(String[] args) throws Exception {
TurboDocxClient client = new TurboDocxClient.Builder()
.apiKey(System.getenv("TURBODOCX_API_KEY"))
.orgId(System.getenv("TURBODOCX_ORG_ID"))
.senderEmail(System.getenv("TURBODOCX_SENDER_EMAIL"))
.build();
Gson gson = new GsonBuilder().setPrettyPrinting().create();
SendSignatureResponse result = client.turboSign().sendSignature(
new SendSignatureRequest.Builder()
.fileLink("https://www.turbodocx.com/examples/turbodocx.pdf")
.documentName("Service Agreement")
.senderName("Acme Corp")
.senderEmail("contracts@acme.com")
.recipients(Arrays.asList(
new Recipient("Alice Smith", "alice@example.com", 1),
new Recipient("Bob Johnson", "bob@example.com", 2)
))
.fields(Arrays.asList(
// Alice's signature
new Field("signature", 1, 100, 650, 200, 50, "alice@example.com"),
// defaultValue pins a fixed date in MM/DD/YYYY; omit to auto-fill the signing date
new Field("date", 1, 320, 650, 100, 30, "alice@example.com",
"12/31/2026", null, null, null, null, null, null),
// Bob's signature
new Field("signature", 1, 100, 720, 200, 50, "bob@example.com"),
new Field("date", 1, 320, 720, 100, 30, "bob@example.com")
))
.build()
);
System.out.println("Result: " + gson.toJson(result));
}
}
require "turbodocx_sdk"
TurboDocxSdk::TurboSign.configure(
api_key: ENV["TURBODOCX_API_KEY"],
org_id: ENV["TURBODOCX_ORG_ID"],
sender_email: "contracts@acme.com",
sender_name: "Acme Corp"
)
result = TurboDocxSdk::TurboSign.send_signature(
# Request-hash keys are camelCase — the SDK forwards them to the API verbatim
recipients: [
{ name: "Alice Smith", email: "alice@example.com", signingOrder: 1 },
{ name: "Bob Johnson", email: "bob@example.com", signingOrder: 2 }
],
fields: [
# Alice's signature
{ type: "signature", page: 1, x: 100, y: 650, width: 200, height: 50, recipientEmail: "alice@example.com" },
{ type: "date", page: 1, x: 320, y: 650, width: 100, height: 30, recipientEmail: "alice@example.com" },
# Bob's signature
{ type: "signature", page: 1, x: 100, y: 720, width: 200, height: 50, recipientEmail: "bob@example.com" },
{ type: "date", page: 1, x: 320, y: 720, width: 100, height: 30, recipientEmail: "bob@example.com" }
],
fileLink: "https://www.turbodocx.com/examples/turbodocx.pdf",
documentName: "Service Agreement",
senderName: "Acme Corp",
senderEmail: "contracts@acme.com"
)
puts JSON.pretty_generate(result)
Using Template-Based Fields
- JavaScript / TypeScript
- Python
- PHP
- Go
- Java
- Ruby
- JavaScript
- TypeScript
// Use text anchors instead of coordinates
const result = await TurboSign.sendSignature({
fileLink: "https://www.turbodocx.com/examples/turbodocx.pdf",
recipients: [
{ name: "Alice Smith", email: "alice@example.com", signingOrder: 1 },
],
fields: [
{
type: "signature",
recipientEmail: "alice@example.com",
template: {
anchor: "{SIGNATURE_ALICE}",
placement: "replace",
size: { width: 200, height: 50 },
},
},
{
type: "date",
recipientEmail: "alice@example.com",
template: {
anchor: "{DATE_ALICE}",
placement: "replace",
size: { width: 100, height: 30 },
},
},
],
});
console.log(JSON.stringify(result, null, 2));
// Use text anchors instead of coordinates
const result = await TurboSign.sendSignature({
fileLink: "https://www.turbodocx.com/examples/turbodocx.pdf",
recipients: [
{ name: "Alice Smith", email: "alice@example.com", signingOrder: 1 },
],
fields: [
{
type: "signature",
recipientEmail: "alice@example.com",
template: {
anchor: "{SIGNATURE_ALICE}",
placement: "replace",
size: { width: 200, height: 50 },
},
},
{
type: "date",
recipientEmail: "alice@example.com",
template: {
anchor: "{DATE_ALICE}",
placement: "replace",
size: { width: 100, height: 30 },
},
},
],
});
console.log(JSON.stringify(result, null, 2));
Important: The document file must contain the anchor text (e.g., {SIGNATURE_ALICE}, {DATE_ALICE}) that you reference in your fields. If the anchors don't exist in the document, the API will return an error.
Alternative: Use a TurboDocx template with pre-configured anchors:
const result = await TurboSign.sendSignature({
templateId: "template-uuid-from-turbodocx", // Template already contains anchors
recipients: [
{ name: "Alice Smith", email: "alice@example.com", signingOrder: 1 },
],
fields: [
{
type: "signature",
recipientEmail: "alice@example.com",
template: {
anchor: "{SIGNATURE_ALICE}",
placement: "replace",
size: { width: 200, height: 50 },
},
},
],
});
import asyncio
import json
async def send_with_template():
result = await TurboSign.send_signature(
recipients=[{"name": "Alice Smith", "email": "alice@example.com", "signingOrder": 1}],
fields=[
{
"type": "signature",
"recipientEmail": "alice@example.com",
"template": {
"anchor": "{SIGNATURE_ALICE}",
"placement": "replace",
"size": {"width": 200, "height": 50},
},
},
{
"type": "date",
"recipientEmail": "alice@example.com",
# Pins a fixed date in MM/DD/YYYY; omit to auto-fill the signing date
"defaultValue": "12/31/2026",
"template": {
"anchor": "{DATE_ALICE}",
"placement": "replace",
"size": {"width": 100, "height": 30},
},
},
],
file_link="https://www.turbodocx.com/examples/turbodocx.pdf",
)
print("Result:", json.dumps(result, indent=2))
asyncio.run(send_with_template())
Important: The document file must contain the anchor text (e.g., {SIGNATURE_ALICE}, {DATE_ALICE}) that you reference in your fields. If the anchors don't exist in the document, the API will return an error.
<?php
use TurboDocx\Types\Recipient;
use TurboDocx\Types\Field;
use TurboDocx\Types\SignatureFieldType;
use TurboDocx\Types\Requests\SendSignatureRequest;
use TurboDocx\Types\TemplateConfig;
use TurboDocx\Types\FieldPlacement;
$result = TurboSign::sendSignature(
new SendSignatureRequest(
recipients: [
new Recipient('Alice Smith', 'alice@example.com', 1)
],
fields: [
new Field(
type: SignatureFieldType::SIGNATURE,
recipientEmail: 'alice@example.com',
template: new TemplateConfig(
anchor: '{SIGNATURE_ALICE}',
placement: FieldPlacement::REPLACE,
size: ['width' => 200, 'height' => 50]
)
),
new Field(
type: SignatureFieldType::DATE,
recipientEmail: 'alice@example.com',
template: new TemplateConfig(
anchor: '{DATE_ALICE}',
placement: FieldPlacement::REPLACE,
size: ['width' => 100, 'height' => 30]
)
)
],
fileLink: 'https://www.turbodocx.com/examples/turbodocx.pdf',
senderName: 'Your Company',
senderEmail: 'sender@company.com'
)
);
The above examples omit error handling for brevity. In production, wrap all TurboSign calls in try-catch blocks. See Error Handling for complete patterns.
Important: The document file must contain the anchor text (e.g., {SIGNATURE_ALICE}, {DATE_ALICE}) that you reference in your fields. If the anchors don't exist in the document, the API will return an error.
result, err := client.TurboSign.SendSignature(ctx, &turbodocx.SendSignatureRequest{
FileLink: "https://www.turbodocx.com/examples/turbodocx.pdf",
Recipients: []turbodocx.Recipient{
{Name: "Alice Smith", Email: "alice@example.com", SigningOrder: 1},
},
Fields: []turbodocx.Field{
{
Type: "signature",
RecipientEmail: "alice@example.com",
Template: &turbodocx.TemplateAnchor{
Anchor: "{SIGNATURE_ALICE}",
Placement: "replace",
Size: &turbodocx.Size{Width: 200, Height: 50},
},
},
{
Type: "date",
RecipientEmail: "alice@example.com",
// Pins a fixed date in MM/DD/YYYY; omit to auto-fill the signing date
DefaultValue: "12/31/2026",
Template: &turbodocx.TemplateAnchor{
Anchor: "{DATE_ALICE}",
Placement: "replace",
Size: &turbodocx.Size{Width: 100, Height: 30},
},
},
},
})
Important: The document file must contain the anchor text (e.g., {SIGNATURE_ALICE}, {DATE_ALICE}) that you reference in your fields. If the anchors don't exist in the document, the API will return an error.
// Template-based field using anchor text
Field.TemplateAnchor templateAnchor = new Field.TemplateAnchor(
"{SIGNATURE_ALICE}", // anchor text to find
null, // searchText (alternative to anchor)
"replace", // placement: replace/before/after/above/below
new Field.Size(200, 50), // size
null, // offset
false, // caseSensitive
false // useRegex
);
// Field with template anchor (no page/x/y coordinates needed)
Field templateField = new Field(
"signature", // type
null, // page (null for template-based)
null, // x (null for template-based)
null, // y (null for template-based)
null, // width (null, using template size)
null, // height (null, using template size)
"alice@example.com", // recipientEmail
null, // defaultValue
null, // isMultiline
null, // isReadonly
null, // required
null, // backgroundColor
templateAnchor // template anchor config
);
SendSignatureResponse result = client.turboSign().sendSignature(
new SendSignatureRequest.Builder()
.fileLink("https://www.turbodocx.com/examples/turbodocx.pdf")
.recipients(Arrays.asList(
new Recipient("Alice Smith", "alice@example.com", 1)
))
.fields(Arrays.asList(templateField))
.build()
);
Important: The document file must contain the anchor text (e.g., {SIGNATURE_ALICE}, {DATE_ALICE}) that you reference in your fields. If the anchors don't exist in the document, the API will return an error.
result = TurboDocxSdk::TurboSign.send_signature(
recipients: [{ name: "Alice Smith", email: "alice@example.com", signingOrder: 1 }],
fields: [
{
type: "signature",
recipientEmail: "alice@example.com",
template: {
anchor: "{SIGNATURE_ALICE}",
placement: "replace",
size: { width: 200, height: 50 }
}
},
{
type: "date",
recipientEmail: "alice@example.com",
# Pins a fixed date in MM/DD/YYYY; omit to auto-fill the signing date
defaultValue: "12/31/2026",
template: {
anchor: "{DATE_ALICE}",
placement: "replace",
size: { width: 100, height: 30 }
}
}
],
fileLink: "https://www.turbodocx.com/examples/turbodocx.pdf"
)
puts JSON.pretty_generate(result)
Important: The document file must contain the anchor text (e.g., {SIGNATURE_ALICE}, {DATE_ALICE}) that you reference in your fields. If the anchors don't exist in the document, the API will return an error.
File Input Methods
- JavaScript / TypeScript
- Python
- PHP
- Go
- Java
- Ruby
TurboSign supports four different ways to provide document files:
TurboSign supports four different ways to provide document files:
The examples below use await, so they must run inside an async function (call them with asyncio.run(...)). See the Send a Document for Signature Quick Start for the full runnable form.
TurboSign supports four different ways to provide document files:
The SDK supports multiple ways to provide your document:
The SDK supports multiple ways to provide your document:
TurboSign supports four different ways to provide document files:
The keys inside the request hash (fileLink, deliverableId, templateId, documentName, recipientEmail, signingOrder, …) are camelCase — the SDK passes them to the API verbatim and does not convert snake_case hash keys. Only the method keyword arguments on configure (api_key:, sender_email:, …) are snake_case.
1. File Upload ([]byte) / 1. File Upload (byte[])
- Go
- Java
Upload a document directly from file bytes:
pdfBytes, err := os.ReadFile("/path/to/document.pdf")
if err != nil {
log.Fatal(err)
}
result, err := client.TurboSign.SendSignature(ctx, &turbodocx.SendSignatureRequest{
File: pdfBytes,
Recipients: []turbodocx.Recipient{
{Name: "John Doe", Email: "john@example.com", SigningOrder: 1},
},
Fields: []turbodocx.Field{
{Type: "signature", Page: 1, X: 100, Y: 500, Width: 200, Height: 50, RecipientEmail: "john@example.com"},
},
})
Upload a document directly from file bytes:
import java.nio.file.Files;
import java.nio.file.Paths;
byte[] pdfBytes = Files.readAllBytes(Paths.get("/path/to/document.pdf"));
SendSignatureResponse result = client.turboSign().sendSignature(
new SendSignatureRequest.Builder()
.file(pdfBytes)
.recipients(Arrays.asList(
new Recipient("John Doe", "john@example.com", 1)
))
.fields(Arrays.asList(
new Field("signature", 1, 100, 500, 200, 50, "john@example.com")
))
.build()
);
1. File Upload (Direct)
- PHP
<?php
use TurboDocx\TurboSign;
use TurboDocx\Config\HttpClientConfig;
use TurboDocx\Types\Recipient;
use TurboDocx\Types\Field;
use TurboDocx\Types\SignatureFieldType;
use TurboDocx\Types\Requests\SendSignatureRequest;
TurboSign::configure(HttpClientConfig::fromEnvironment());
$pdfContent = file_get_contents('./contract.pdf');
$result = TurboSign::sendSignature(
new SendSignatureRequest(
file: $pdfContent,
fileName: 'contract.pdf', // Optional
recipients: [
new Recipient('John Doe', 'john@example.com', 1)
],
fields: [
new Field(
type: SignatureFieldType::SIGNATURE,
recipientEmail: 'john@example.com',
page: 1,
x: 100,
y: 500,
width: 200,
height: 50
)
]
)
);
2. File URL
- PHP
- Go
- Java
<?php
use TurboDocx\TurboSign;
use TurboDocx\Types\Recipient;
use TurboDocx\Types\Field;
use TurboDocx\Types\SignatureFieldType;
use TurboDocx\Types\Requests\SendSignatureRequest;
$result = TurboSign::sendSignature(
new SendSignatureRequest(
fileLink: 'https://www.turbodocx.com/examples/turbodocx.pdf',
recipients: [
new Recipient('John Doe', 'john@example.com', 1)
],
fields: [
new Field(
type: SignatureFieldType::SIGNATURE,
recipientEmail: 'john@example.com',
page: 1,
x: 100,
y: 500,
width: 200,
height: 50
)
]
)
);
Use fileLink when your documents are already hosted on cloud storage (S3, Google Cloud Storage, etc.). This is more efficient than downloading and re-uploading files.
Provide a publicly accessible URL to your document:
result, err := client.TurboSign.SendSignature(ctx, &turbodocx.SendSignatureRequest{
FileLink: "https://www.turbodocx.com/examples/turbodocx.pdf",
Recipients: []turbodocx.Recipient{
{Name: "John Doe", Email: "john@example.com", SigningOrder: 1},
},
Fields: []turbodocx.Field{
{Type: "signature", Page: 1, X: 100, Y: 500, Width: 200, Height: 50, RecipientEmail: "john@example.com"},
},
})
Use FileLink when your documents are already hosted on cloud storage (S3, Google Cloud Storage, etc.). This is more efficient than downloading and re-uploading files.
Provide a publicly accessible URL to your document:
SendSignatureResponse result = client.turboSign().sendSignature(
new SendSignatureRequest.Builder()
.fileLink("https://www.turbodocx.com/examples/turbodocx.pdf")
.recipients(Arrays.asList(
new Recipient("John Doe", "john@example.com", 1)
))
.fields(Arrays.asList(
new Field("signature", 1, 100, 500, 200, 50, "john@example.com")
))
.build()
);
Use fileLink when your documents are already hosted on cloud storage (S3, Google Cloud Storage, etc.). This is more efficient than downloading and re-uploading files.
1. File Upload (bytes)
- Python
with open("./contract.pdf", "rb") as f:
pdf_buffer = f.read()
result = await TurboSign.send_signature(
file=pdf_buffer,
recipients=[
{"name": "John Doe", "email": "john@example.com", "signingOrder": 1},
],
fields=[
{
"type": "signature",
"page": 1,
"x": 100,
"y": 650,
"width": 200,
"height": 50,
"recipientEmail": "john@example.com",
},
],
)
1. File Upload (Path, Buffer, or Browser File)
- JavaScript / TypeScript
- JavaScript
- TypeScript
const { readFileSync } = require("fs");
const { TurboSign } = require("@turbodocx/sdk");
const fileBuffer = readFileSync("./contract.pdf");
(async () => {
const result = await TurboSign.sendSignature({
file: fileBuffer,
recipients: [
{ name: "John Doe", email: "john@example.com", signingOrder: 1 },
],
fields: [
{
type: "signature",
page: 1,
x: 100,
y: 650,
width: 200,
height: 50,
recipientEmail: "john@example.com",
},
],
});
})();
import { readFileSync } from "fs";
import { TurboSign } from "@turbodocx/sdk";
const fileBuffer = readFileSync("./contract.pdf");
const result = await TurboSign.sendSignature({
file: fileBuffer,
recipients: [
{ name: "John Doe", email: "john@example.com", signingOrder: 1 },
],
fields: [
{
type: "signature",
page: 1,
x: 100,
y: 650,
width: 200,
height: 50,
recipientEmail: "john@example.com",
},
],
});
file accepts string | File | Buffer. A string is treated as a local file path: the SDK reads it and uses the basename as the document filename, so file: "./contract.pdf" works without readFileSync. A raw Blob is not supported; use a Buffer (Node) or a File (browser).
When file is a Buffer, the filename defaults to document.pdf (extension detected from the content). Pass fileName to control it:
await TurboSign.sendSignature({
file: fileBuffer,
fileName: "acme-msa.pdf",
// ...
});
1. File Upload (path or IO)
- Ruby
Pass a local file path (String) or any IO object (e.g., File.open, StringIO) — the SDK uploads it as multipart form data and detects the file type from magic bytes.
result = TurboDocxSdk::TurboSign.send_signature(
file: "./contract.pdf", # local path — or an IO: File.open("./contract.pdf", "rb")
recipients: [
{ name: "John Doe", email: "john@example.com", signingOrder: 1 }
],
fields: [
{
type: "signature",
page: 1,
x: 100,
y: 650,
width: 200,
height: 50,
recipientEmail: "john@example.com"
}
]
)
2. File URL (fileLink) / 2. File URL (file_link)
- JavaScript / TypeScript
- Python
- Ruby
- JavaScript
- TypeScript
const result = await TurboSign.sendSignature({
fileLink: "https://www.turbodocx.com/examples/turbodocx.pdf",
recipients: [
{ name: "John Doe", email: "john@example.com", signingOrder: 1 },
],
fields: [
{
type: "signature",
page: 1,
x: 100,
y: 650,
width: 200,
height: 50,
recipientEmail: "john@example.com",
},
],
});
const result = await TurboSign.sendSignature({
fileLink: "https://www.turbodocx.com/examples/turbodocx.pdf",
recipients: [
{ name: "John Doe", email: "john@example.com", signingOrder: 1 },
],
fields: [
{
type: "signature",
page: 1,
x: 100,
y: 650,
width: 200,
height: 50,
recipientEmail: "john@example.com",
},
],
});
Use fileLink when your documents are already hosted on cloud storage (S3, Google Cloud Storage, etc.). This is more efficient than downloading and re-uploading files.
result = await TurboSign.send_signature(
file_link="https://www.turbodocx.com/examples/turbodocx.pdf",
recipients=[
{"name": "John Doe", "email": "john@example.com", "signingOrder": 1},
],
fields=[
{
"type": "signature",
"page": 1,
"x": 100,
"y": 650,
"width": 200,
"height": 50,
"recipientEmail": "john@example.com",
},
],
)
Use file_link when your documents are already hosted on cloud storage (S3, Google Cloud Storage, etc.). This is more efficient than downloading and re-uploading files.
result = TurboDocxSdk::TurboSign.send_signature(
fileLink: "https://www.turbodocx.com/examples/turbodocx.pdf",
recipients: [
{ name: "John Doe", email: "john@example.com", signingOrder: 1 }
],
fields: [
{
type: "signature",
page: 1,
x: 100,
y: 650,
width: 200,
height: 50,
recipientEmail: "john@example.com"
}
]
)
Use fileLink when your documents are already hosted on cloud storage (S3, Google Cloud Storage, etc.). This is more efficient than downloading and re-uploading files.
3. TurboDocx Deliverable ID
- JavaScript / TypeScript
- Python
- PHP
- Go
- Java
- Ruby
- JavaScript
- TypeScript
// Use a previously generated TurboDocx document
const result = await TurboSign.sendSignature({
deliverableId: "deliverable-uuid-from-turbodocx",
recipients: [
{ name: "John Doe", email: "john@example.com", signingOrder: 1 },
],
fields: [
{
type: "signature",
page: 1,
x: 100,
y: 650,
width: 200,
height: 50,
recipientEmail: "john@example.com",
},
],
});
// Use a previously generated TurboDocx document
const result = await TurboSign.sendSignature({
deliverableId: "deliverable-uuid-from-turbodocx",
recipients: [
{ name: "John Doe", email: "john@example.com", signingOrder: 1 },
],
fields: [
{
type: "signature",
page: 1,
x: 100,
y: 650,
width: 200,
height: 50,
recipientEmail: "john@example.com",
},
],
});
deliverableId references documents generated using TurboDocx's document generation API. This creates a seamless workflow: generate → sign.
# Use a previously generated TurboDocx document
result = await TurboSign.send_signature(
deliverable_id="deliverable-uuid-from-turbodocx",
recipients=[
{"name": "John Doe", "email": "john@example.com", "signingOrder": 1},
],
fields=[
{
"type": "signature",
"page": 1,
"x": 100,
"y": 650,
"width": 200,
"height": 50,
"recipientEmail": "john@example.com",
},
],
)
deliverable_id references documents generated using TurboDocx's document generation API. This creates a seamless workflow: generate → sign.
<?php
use TurboDocx\TurboSign;
use TurboDocx\Types\Recipient;
use TurboDocx\Types\Field;
use TurboDocx\Types\SignatureFieldType;
use TurboDocx\Types\Requests\SendSignatureRequest;
// Use a previously generated TurboDocx document
$result = TurboSign::sendSignature(
new SendSignatureRequest(
deliverableId: 'deliverable-uuid-from-turbodocx',
recipients: [
new Recipient('John Doe', 'john@example.com', 1)
],
fields: [
new Field(
type: SignatureFieldType::SIGNATURE,
recipientEmail: 'john@example.com',
page: 1,
x: 100,
y: 500,
width: 200,
height: 50
)
]
)
);
deliverableId references documents generated using TurboDocx's document generation API. This creates a seamless workflow: generate → sign.
Use a document generated by TurboDocx document generation:
result, err := client.TurboSign.SendSignature(ctx, &turbodocx.SendSignatureRequest{
DeliverableID: "deliverable-uuid-from-turbodocx",
Recipients: []turbodocx.Recipient{
{Name: "John Doe", Email: "john@example.com", SigningOrder: 1},
},
Fields: []turbodocx.Field{
{Type: "signature", Page: 1, X: 100, Y: 500, Width: 200, Height: 50, RecipientEmail: "john@example.com"},
},
})
DeliverableID references documents generated using TurboDocx's document generation API. This creates a seamless workflow: generate → sign.
Use a document generated by TurboDocx document generation:
SendSignatureResponse result = client.turboSign().sendSignature(
new SendSignatureRequest.Builder()
.deliverableId("deliverable-uuid-from-turbodocx")
.recipients(Arrays.asList(
new Recipient("John Doe", "john@example.com", 1)
))
.fields(Arrays.asList(
new Field("signature", 1, 100, 500, 200, 50, "john@example.com")
))
.build()
);
deliverableId references documents generated using TurboDocx's document generation API. This creates a seamless workflow: generate → sign.
# Use a previously generated TurboDocx document
result = TurboDocxSdk::TurboSign.send_signature(
deliverableId: "deliverable-uuid-from-turbodocx",
recipients: [
{ name: "John Doe", email: "john@example.com", signingOrder: 1 }
],
fields: [
{
type: "signature",
page: 1,
x: 100,
y: 650,
width: 200,
height: 50,
recipientEmail: "john@example.com"
}
]
)
deliverableId references documents generated using TurboDocx's document generation API. This creates a seamless workflow: generate → sign.
4. TurboDocx Template ID
- JavaScript / TypeScript
- Python
- PHP
- Go
- Java
- Ruby
- JavaScript
- TypeScript
// Use a pre-configured TurboSign template
const result = await TurboSign.sendSignature({
templateId: "template-uuid-from-turbodocx", // Template already contains anchors
recipients: [
{ name: "Alice Smith", email: "alice@example.com", signingOrder: 1 },
],
fields: [
{
type: "signature",
recipientEmail: "alice@example.com",
template: {
anchor: "{SIGNATURE_ALICE}",
placement: "replace",
size: { width: 200, height: 50 },
},
},
],
});
// Use a pre-configured TurboSign template
const result = await TurboSign.sendSignature({
templateId: "template-uuid-from-turbodocx", // Template already contains anchors
recipients: [
{ name: "Alice Smith", email: "alice@example.com", signingOrder: 1 },
],
fields: [
{
type: "signature",
recipientEmail: "alice@example.com",
template: {
anchor: "{SIGNATURE_ALICE}",
placement: "replace",
size: { width: 200, height: 50 },
},
},
],
});
templateId references pre-configured TurboSign templates created in the TurboDocx dashboard. These templates come with built-in anchors and field positioning, making it easy to reuse signature workflows across multiple documents.
# Use a pre-configured TurboSign template
result = await TurboSign.send_signature(
template_id="template-uuid-from-turbodocx", # Template already contains anchors
recipients=[
{"name": "Alice Smith", "email": "alice@example.com", "signingOrder": 1},
],
fields=[
{
"type": "signature",
"recipientEmail": "alice@example.com",
"template": {
"anchor": "{SIGNATURE_ALICE}",
"placement": "replace",
"size": {"width": 200, "height": 50},
},
},
],
)
template_id references pre-configured TurboSign templates created in the TurboDocx dashboard. These templates come with built-in anchors and field positioning, making it easy to reuse signature workflows across multiple documents.
<?php
use TurboDocx\TurboSign;
use TurboDocx\Types\Recipient;
use TurboDocx\Types\Field;
use TurboDocx\Types\SignatureFieldType;
use TurboDocx\Types\Requests\SendSignatureRequest;
use TurboDocx\Types\TemplateConfig;
use TurboDocx\Types\FieldPlacement;
// Use a pre-configured TurboSign template
$result = TurboSign::sendSignature(
new SendSignatureRequest(
templateId: 'template-uuid-from-turbodocx',
recipients: [
new Recipient('Alice Smith', 'alice@example.com', 1)
],
fields: [
new Field(
type: SignatureFieldType::SIGNATURE,
recipientEmail: 'alice@example.com',
template: new TemplateConfig(
anchor: '{SIGNATURE_ALICE}',
placement: FieldPlacement::REPLACE,
size: ['width' => 200, 'height' => 50]
)
)
]
)
);
templateId references pre-configured TurboSign templates created in the TurboDocx dashboard. These templates come with built-in anchors and field positioning, making it easy to reuse signature workflows across multiple documents.
Use a pre-configured TurboDocx template:
result, err := client.TurboSign.SendSignature(ctx, &turbodocx.SendSignatureRequest{
TemplateID: "template-uuid-from-turbodocx",
Recipients: []turbodocx.Recipient{
{Name: "John Doe", Email: "john@example.com", SigningOrder: 1},
},
Fields: []turbodocx.Field{
{Type: "signature", Page: 1, X: 100, Y: 500, Width: 200, Height: 50, RecipientEmail: "john@example.com"},
},
})
TemplateID references pre-configured TurboSign templates created in the TurboDocx dashboard. These templates come with built-in anchors and field positioning, making it easy to reuse signature workflows across multiple documents.
Use a pre-configured TurboDocx template:
SendSignatureResponse result = client.turboSign().sendSignature(
new SendSignatureRequest.Builder()
.templateId("template-uuid-from-turbodocx")
.recipients(Arrays.asList(
new Recipient("John Doe", "john@example.com", 1)
))
.fields(Arrays.asList(
new Field("signature", 1, 100, 500, 200, 50, "john@example.com")
))
.build()
);
templateId references pre-configured TurboSign templates created in the TurboDocx dashboard. These templates come with built-in anchors and field positioning, making it easy to reuse signature workflows across multiple documents.
# Use a pre-configured TurboSign template
result = TurboDocxSdk::TurboSign.send_signature(
templateId: "template-uuid-from-turbodocx", # Template already contains anchors
recipients: [
{ name: "Alice Smith", email: "alice@example.com", signingOrder: 1 }
],
fields: [
{
type: "signature",
recipientEmail: "alice@example.com",
template: {
anchor: "{SIGNATURE_ALICE}",
placement: "replace",
size: { width: 200, height: 50 }
}
}
]
)
templateId references pre-configured TurboSign templates created in the TurboDocx dashboard. These templates come with built-in anchors and field positioning, making it easy to reuse signature workflows across multiple documents.
Send a template set up in TurboDocx
Upload a PDF as a template in TurboDocx, add its signers as roles (for example "Client" and "Countersigner"), drag their fields onto the page and save the signature setup. Then send it by naming each recipient's role. The fields saved for that role come with it, so fields can be left out, and a recipient with a role needs no signing order. A role you leave out uses the signer saved on the template. Role keys are shown under Use via API on the template page, or listed with Get template signature setup. Request-body keys stay camelCase in every language.
- JavaScript / TypeScript
- Python
- PHP
- Go
- Java
- Ruby
- JavaScript
- TypeScript
// The role keys come from the template
const { roles } = await TurboSign.getTemplateSignatureSetup("your-template-id");
// [{ key: "client", hasSavedSigner: false, ... }, { key: "countersigner", hasSavedSigner: true, ... }]
const result = await TurboSign.sendSignature({
templateId: "your-template-id",
recipients: [
{ role: "client", name: "Jane Doe", email: "jane@client.com" },
// "countersigner" left out: the signer saved on the template is used
],
});
// The role keys come from the template
const { roles } = await TurboSign.getTemplateSignatureSetup("your-template-id");
// [{ key: "client", hasSavedSigner: false, ... }, { key: "countersigner", hasSavedSigner: true, ... }]
const result = await TurboSign.sendSignature({
templateId: "your-template-id",
recipients: [
{ role: "client", name: "Jane Doe", email: "jane@client.com" },
// "countersigner" left out: the signer saved on the template is used
],
});
role works the same way on createSignatureReviewLink and createEmbeddedSignature.
# The role keys come from the template
setup = await TurboSign.get_template_signature_setup("your-template-id")
# setup["roles"] -> [{"key": "client", "hasSavedSigner": False, ...},
# {"key": "countersigner", "hasSavedSigner": True, ...}]
result = await TurboSign.send_signature(
template_id="your-template-id",
recipients=[
{"role": "client", "name": "Jane Doe", "email": "jane@client.com"},
# "countersigner" left out: the signer saved on the template is used
],
)
role works the same way on create_signature_review_link() and create_embedded_signature().
<?php
use TurboDocx\TurboSign;
use TurboDocx\Types\Recipient;
use TurboDocx\Types\Requests\SendSignatureRequest;
// The role keys come from the template
$setup = TurboSign::getTemplateSignatureSetup('your-template-id');
foreach ($setup->roles as $role) {
echo "{$role->key} (order {$role->order}, saved signer: " . ($role->hasSavedSigner ? 'yes' : 'no') . ")\n";
}
$result = TurboSign::sendSignature(
new SendSignatureRequest(
templateId: 'your-template-id',
recipients: [
new Recipient(name: 'Jane Doe', email: 'jane@client.com', role: 'client'),
// 'countersigner' left out: the signer saved on the template is used
],
)
);
role works the same way on createSignatureReviewLink() (Recipient) and createEmbeddedSignature() (EmbeddedSignatureRecipient). Recipients without a role still need a signingOrder.
// The role keys come from the template
setup, err := client.TurboSign.GetTemplateSignatureSetup(ctx, "your-template-id")
if err != nil {
log.Fatal(err)
}
for _, role := range setup.Roles {
fmt.Printf("%s (order %d, saved signer: %t)\n", role.Key, role.Order, role.HasSavedSigner)
}
result, err := client.TurboSign.SendSignature(ctx, &turbodocx.SendSignatureRequest{
TemplateID: "your-template-id",
Recipients: []turbodocx.Recipient{
{Role: "client", Name: "Jane Doe", Email: "jane@client.com"},
// "countersigner" left out: the signer saved on the template is used
},
})
Role works the same way on CreateSignatureReviewLink and on EmbeddedSignatureRecipient for CreateEmbeddedSignature.
// The role keys come from the template
TemplateSignatureSetup setup = client.turboSign().getTemplateSignatureSetup("your-template-id");
for (TemplateSignatureRole role : setup.getRoles()) {
System.out.println(role.getKey() + " saved signer: " + role.hasSavedSigner());
}
SendSignatureResponse result = client.turboSign().sendSignature(
new SendSignatureRequest.Builder()
.templateId("your-template-id")
.recipients(Arrays.asList(
Recipient.withRole("client", "Jane Doe", "jane@client.com")
// "countersigner" left out: the signer saved on the template is used
))
.build()
);
Use new Recipient.Builder().role("client").name(...).email(...) when you also need a phone or external id. role works the same way on createSignatureReviewLink and, through new EmbeddedSignatureRecipient.Builder().role(...), on createEmbeddedSignature.
Upload a PDF as a template in TurboDocx, add its signers as roles (for example "Client" and "Countersigner"), drag their fields onto the page and save the signature setup. Then send it by naming each recipient's role. The fields saved for that role come with it, so fields can be left out, and a recipient with a role needs no signingOrder.
# The role keys come from the template (also shown under "Use via API" on the template page)
setup = TurboDocxSdk::TurboSign.get_template_signature_setup("your-template-id")
setup["roles"].each { |r| puts "#{r['key']} (saved signer: #{r['hasSavedSigner']})" }
# client (saved signer: false)
# countersigner (saved signer: true)
TurboDocxSdk::TurboSign.send_signature(
templateId: "your-template-id",
recipients: [
{ role: "client", name: "Jane Doe", email: "jane@client.com" }
# "countersigner" left out: the signer saved on the template is used
]
)
- Roles sign in the order saved on the template; recipients without a
rolesign after them. - A role left out uses the template's saved signer. If it has none, the API returns a 400 naming the role.
- An unknown role raises
TurboDocxSdk::ValidationErrorlisting the template's roles (e.code == "UnknownSignerRole"). - Any
fieldsyou pass are added to the template's (for example an extra witness signature). roleworks the same way oncreate_signature_review_linkandcreate_embedded_signature.
See Sending with signer roles for every rule and error.
- Roles sign in the order saved on the template; recipients without a role sign after them.
- A role left out uses the template's saved signer. If it has none, the API returns a 400 (
SignerRoleUnfilled) naming the role. - An unknown role returns a 400 (
UnknownSignerRole) whose message lists the template's roles. The SDKs raise their validation error with that code. - Any fields you pass are added to the template's (for example an extra witness signature).
See Sending with signer roles in the API reference for every rule and error.
API Reference
- Python
- Java
- Ruby
The snippets below use await, so they must run inside an async function. For a standalone script, wrap the call and run it with asyncio.run(...), and add import json if the snippet calls json.dumps:
import asyncio
import json
async def main():
result = await TurboSign.get_status("document-uuid")
print(json.dumps(result, indent=2))
asyncio.run(main())
The snippets below assume a configured client and an available Gson instance, e.g. Gson gson = new GsonBuilder().setPrettyPrinting().create(); (as shown in the Quick Start).
All methods are class-level: call TurboDocxSdk::TurboSign.configure once, then invoke any method directly on the class. Every method is synchronous and returns a plain Hash (or raw bytes for downloads).
Configure
- JavaScript / TypeScript
- Python
- PHP
- Go
- Java
- Ruby
Configure the SDK with your API credentials and organization settings.
TurboSign.configure({
apiKey: string; // Required : Your TurboDocx API key
orgId: string; // Required: Your organization ID
senderEmail: string; // Required: reply-to address for signature request emails
senderName?: string; // Optional: sender name in emails (defaults to your API key's name)
baseUrl?: string; // Optional: API base URL (default: 'https://api.turbodocx.com')
});
Example:
- JavaScript
- TypeScript
const { TurboSign } = require("@turbodocx/sdk");
TurboSign.configure({
apiKey: process.env.TURBODOCX_API_KEY,
orgId: process.env.TURBODOCX_ORG_ID,
senderEmail: process.env.TURBODOCX_SENDER_EMAIL, // Required for TurboSign
// Optional: override for testing
// baseUrl: 'https://api.turbodocx.com'
});
import { TurboSign } from "@turbodocx/sdk";
TurboSign.configure({
apiKey: process.env.TURBODOCX_API_KEY || "",
orgId: process.env.TURBODOCX_ORG_ID || "",
senderEmail: process.env.TURBODOCX_SENDER_EMAIL || "", // Required for TurboSign
// Optional: override for testing
// baseUrl: 'https://api.turbodocx.com'
});
Both apiKey and orgId parameters are required for all API requests. To get your credentials, follow the Get Your Credentials steps from the SDKs main page.
Configure the SDK with your API credentials and organization settings.
TurboSign.configure(
api_key: str, # Required: Your TurboDocx API key
org_id: str, # Required: Your organization ID
sender_email: str, # Required: Reply-to address for signature emails
sender_name: str = None, # Recommended: Sender name shown in emails
base_url: str = "https://api.turbodocx.com" # Optional: API base URL
)
Configure the SDK with your API credentials and organization settings.
use TurboDocx\TurboSign;
use TurboDocx\Config\HttpClientConfig;
// Manual configuration
TurboSign::configure(new HttpClientConfig(
apiKey: 'your-api-key',
orgId: 'your-org-id',
senderEmail: 'you@company.com',
senderName: 'Your Company'
));
// Or from environment
TurboSign::configure(HttpClientConfig::fromEnvironment());
Create a new TurboDocx client.
// Simple initialization
client, err := turbodocx.NewClient(apiKey, orgID string)
// With custom configuration
client, err := turbodocx.NewClientWithConfig(turbodocx.ClientConfig{
APIKey: "your-api-key",
OrgID: "your-org-id",
SenderEmail: "you@example.com", // Required for TurboSign (reply-to address)
BaseURL: "https://api.turbodocx.com", // Optional
})
APIKey (or AccessToken), OrgID, and SenderEmail are required for TurboSign operations. SenderEmail is used as the reply-to address for signature request emails (it can also be supplied via the TURBODOCX_SENDER_EMAIL environment variable). To get your credentials, follow the Get Your Credentials steps from the SDKs main page.
Create a new TurboDocx client using the Builder pattern.
TurboDocxClient client = new TurboDocxClient.Builder()
.apiKey("your-api-key") // Required
.orgId("your-org-id") // Required
.senderEmail("sender@yourcompany.com") // Required for TurboSign
.baseUrl("https://api.turbodocx.com") // Optional
.build();
Configure the SDK with your API credentials and organization settings.
TurboDocxSdk::TurboSign.configure(
api_key: "your-api-key", # Required: Your TurboDocx API key
org_id: "your-org-id", # Required: Your organization ID
sender_email: "you@company.com", # Required: Reply-to address for signature emails
sender_name: "Your Company", # Recommended: Sender name shown in emails
base_url: "https://api.turbodocx.com" # Optional: API base URL
)
Prepare for review
- JavaScript / TypeScript
- Python
- PHP
- Go
- Java
- Ruby
Upload a document for preview without sending signature request emails.
- JavaScript
- TypeScript
const { documentId, previewUrl } = await TurboSign.createSignatureReviewLink({
fileLink: "https://www.turbodocx.com/examples/turbodocx.pdf",
documentName: "Contract Draft",
recipients: [
{ name: "John Doe", email: "john@example.com", signingOrder: 1 },
],
fields: [
{
type: "signature",
page: 1,
x: 100,
y: 500,
width: 200,
height: 50,
recipientEmail: "john@example.com",
},
],
});
const { documentId, previewUrl } = await TurboSign.createSignatureReviewLink({
fileLink: "https://www.turbodocx.com/examples/turbodocx.pdf",
documentName: "Contract Draft",
recipients: [
{ name: "John Doe", email: "john@example.com", signingOrder: 1 },
],
fields: [
{
type: "signature",
page: 1,
x: 100,
y: 500,
width: 200,
height: 50,
recipientEmail: "john@example.com",
},
],
});
Upload a document for preview without sending signature request emails.
result = await TurboSign.create_signature_review_link(
recipients=[{"name": "John Doe", "email": "john@example.com", "signingOrder": 1}],
fields=[{"type": "signature", "page": 1, "x": 100, "y": 500, "width": 200, "height": 50, "recipientEmail": "john@example.com"}],
file_link="https://www.turbodocx.com/examples/turbodocx.pdf",
document_name="Contract Draft",
)
print(result["documentId"])
print(result["previewUrl"])
Upload a document for preview without sending signature request emails.
use TurboDocx\Types\Recipient;
use TurboDocx\Types\Field;
use TurboDocx\Types\SignatureFieldType;
use TurboDocx\Types\Requests\CreateSignatureReviewLinkRequest;
$result = TurboSign::createSignatureReviewLink(
new CreateSignatureReviewLinkRequest(
recipients: [
new Recipient('John Doe', 'john@example.com', 1)
],
fields: [
new Field(
type: SignatureFieldType::SIGNATURE,
recipientEmail: 'john@example.com',
page: 1,
x: 100,
y: 500,
width: 200,
height: 50
)
],
fileLink: 'https://www.turbodocx.com/examples/turbodocx.pdf',
documentName: 'Contract Draft'
)
);
echo "Preview URL: {$result->previewUrl}\n";
echo "Document ID: {$result->documentId}\n";
Upload a document for preview without sending emails.
result, err := client.TurboSign.CreateSignatureReviewLink(ctx, &turbodocx.CreateSignatureReviewLinkRequest{
FileLink: "https://www.turbodocx.com/examples/turbodocx.pdf",
DocumentName: "Contract Draft",
Recipients: []turbodocx.Recipient{
{Name: "John Doe", Email: "john@example.com", SigningOrder: 1},
},
Fields: []turbodocx.Field{
{Type: "signature", Page: 1, X: 100, Y: 500, Width: 200, Height: 50, RecipientEmail: "john@example.com"},
},
})
b, _ := json.MarshalIndent(result, "", " "); fmt.Println("Result:", string(b))
Upload a document for preview without sending emails.
CreateSignatureReviewLinkResponse result = client.turboSign().createSignatureReviewLink(
new CreateSignatureReviewLinkRequest.Builder()
.fileLink("https://www.turbodocx.com/examples/turbodocx.pdf")
.documentName("Contract Draft")
.recipients(Arrays.asList(
new Recipient("John Doe", "john@example.com", 1)
))
.fields(Arrays.asList(
new Field("signature", 1, 100, 500, 200, 50, "john@example.com")
))
.build()
);
System.out.println("Result: " + gson.toJson(result));
Upload a document for preview without sending signature request emails.
result = TurboDocxSdk::TurboSign.create_signature_review_link(
recipients: [{ name: "John Doe", email: "john@example.com", signingOrder: 1 }],
fields: [{ type: "signature", page: 1, x: 100, y: 500, width: 200, height: 50, recipientEmail: "john@example.com" }],
fileLink: "https://www.turbodocx.com/examples/turbodocx.pdf",
documentName: "Contract Draft"
)
puts result["documentId"]
puts result["previewUrl"]
Prepare for signing
- JavaScript / TypeScript
- Python
- PHP
- Go
- Java
- Ruby
Upload a document and immediately send signature requests to all recipients.
- JavaScript
- TypeScript
const { documentId } = await TurboSign.sendSignature({
fileLink: "https://www.turbodocx.com/examples/turbodocx.pdf",
documentName: "Service Agreement",
senderName: "Your Company",
senderEmail: "sender@company.com",
recipients: [
{ name: "Recipient Name", email: "recipient@example.com", signingOrder: 1 },
],
fields: [
{
type: "signature",
page: 1,
x: 100,
y: 500,
width: 200,
height: 50,
recipientEmail: "recipient@example.com",
},
],
});
const { documentId } = await TurboSign.sendSignature({
fileLink: "https://www.turbodocx.com/examples/turbodocx.pdf",
documentName: "Service Agreement",
senderName: "Your Company",
senderEmail: "sender@company.com",
recipients: [
{ name: "Recipient Name", email: "recipient@example.com", signingOrder: 1 },
],
fields: [
{
type: "signature",
page: 1,
x: 100,
y: 500,
width: 200,
height: 50,
recipientEmail: "recipient@example.com",
},
],
});
Upload a document and immediately send signature requests to all recipients.
result = await TurboSign.send_signature(
recipients=[{"name": "Recipient Name", "email": "recipient@example.com", "signingOrder": 1}],
fields=[{"type": "signature", "page": 1, "x": 100, "y": 500, "width": 200, "height": 50, "recipientEmail": "recipient@example.com"}],
file_link="https://www.turbodocx.com/examples/turbodocx.pdf",
document_name="Service Agreement",
sender_name="Your Company",
sender_email="sender@company.com",
)
print(result["documentId"])
Upload a document and immediately send signature requests to all recipients.
use TurboDocx\Types\Recipient;
use TurboDocx\Types\Field;
use TurboDocx\Types\SignatureFieldType;
use TurboDocx\Types\Requests\SendSignatureRequest;
$result = TurboSign::sendSignature(
new SendSignatureRequest(
recipients: [
new Recipient('Recipient Name', 'recipient@example.com', 1)
],
fields: [
new Field(
type: SignatureFieldType::SIGNATURE,
recipientEmail: 'recipient@example.com',
page: 1,
x: 100,
y: 500,
width: 200,
height: 50
)
],
fileLink: 'https://www.turbodocx.com/examples/turbodocx.pdf',
documentName: 'Service Agreement'
)
);
echo "Document ID: {$result->documentId}\n";
Upload a document and immediately send signature requests.
result, err := client.TurboSign.SendSignature(ctx, &turbodocx.SendSignatureRequest{
FileLink: "https://www.turbodocx.com/examples/turbodocx.pdf",
DocumentName: "Service Agreement",
SenderName: "Your Company",
SenderEmail: "sender@company.com",
Recipients: []turbodocx.Recipient{
{Name: "Recipient Name", Email: "recipient@example.com", SigningOrder: 1},
},
Fields: []turbodocx.Field{
{Type: "signature", Page: 1, X: 100, Y: 500, Width: 200, Height: 50, RecipientEmail: "recipient@example.com"},
},
})
Upload a document and immediately send signature requests.
SendSignatureResponse result = client.turboSign().sendSignature(
new SendSignatureRequest.Builder()
.fileLink("https://www.turbodocx.com/examples/turbodocx.pdf")
.documentName("Service Agreement")
.senderName("Your Company")
.senderEmail("sender@company.com")
.recipients(Arrays.asList(
new Recipient("Recipient Name", "recipient@example.com", 1)
))
.fields(Arrays.asList(
new Field("signature", 1, 100, 500, 200, 50, "recipient@example.com")
))
.build()
);
System.out.println("Result: " + gson.toJson(result));
Upload a document and immediately send signature requests to all recipients.
result = TurboDocxSdk::TurboSign.send_signature(
recipients: [{ name: "Recipient Name", email: "recipient@example.com", signingOrder: 1 }],
fields: [{ type: "signature", page: 1, x: 100, y: 500, width: 200, height: 50, recipientEmail: "recipient@example.com" }],
fileLink: "https://www.turbodocx.com/examples/turbodocx.pdf",
documentName: "Service Agreement",
senderName: "Your Company",
senderEmail: "sender@company.com"
)
puts result["documentId"]
Send without emails and sign in your app
Set sendEmail to false to create the signature request without emailing the signers their links. The document still goes out for signing, and passcode and completed-copy emails are still sent. The opt-out also covers the initial CC email, later signers' turn emails and scheduled reminders. Then call createSigningUrl for a signer when they are ready to sign in your app. For a signer with an email/SMS passcode or no verification (as here), the URL is their reusable signing link for the life of the document, so treat it like a password and don't log it. external_idv and override signers get a single-use URL that expires in about five minutes; mint those at the moment of signing.
Embedded signing must be turned on for your organization: in the E-Signature settings, turn on Enable identity verification (see Set up your organization). While it is off, createSigningUrl fails with EmbeddedSigningNotEnabled.
- JavaScript / TypeScript
- Python
- PHP
- Go
- Java
- Ruby
const result = await TurboSign.sendSignature({
fileLink: "https://www.turbodocx.com/examples/turbodocx.pdf",
documentName: "Service Agreement",
recipients: [{ name: "Recipient Name", email: "recipient@example.com", signingOrder: 1 }],
fields: [{ type: "signature", page: 1, x: 100, y: 500, width: 200, height: 50, recipientEmail: "recipient@example.com" }],
sendEmail: false, // no signing-link emails; you hand out the link
});
// When the signer is ready, mint their signing URL
const link = await TurboSign.createSigningUrl(result.documentId, {
recipientId: result.recipients[0].id,
});
console.log(link.url); // open in a new tab or redirect to it
result = await TurboSign.send_signature(
recipients=[{"name": "Recipient Name", "email": "recipient@example.com", "signingOrder": 1}],
fields=[{"type": "signature", "page": 1, "x": 100, "y": 500, "width": 200, "height": 50, "recipientEmail": "recipient@example.com"}],
file_link="https://www.turbodocx.com/examples/turbodocx.pdf",
document_name="Service Agreement",
send_email=False, # no signing-link emails; you hand out the link
)
# When the signer is ready, mint their signing URL
link = await TurboSign.create_signing_url(
result["documentId"],
recipient_id=result["recipients"][0]["id"],
)
print(link["url"]) # open in a new tab or redirect to it
use TurboDocx\TurboSign;
use TurboDocx\Types\Recipient;
use TurboDocx\Types\Field;
use TurboDocx\Types\SignatureFieldType;
use TurboDocx\Types\Requests\SendSignatureRequest;
use TurboDocx\Types\Requests\CreateSigningUrlRequest;
$result = TurboSign::sendSignature(
new SendSignatureRequest(
recipients: [new Recipient('Recipient Name', 'recipient@example.com', 1)],
fields: [
new Field(
type: SignatureFieldType::SIGNATURE,
recipientEmail: 'recipient@example.com',
page: 1,
x: 100,
y: 500,
width: 200,
height: 50
)
],
fileLink: 'https://www.turbodocx.com/examples/turbodocx.pdf',
documentName: 'Service Agreement',
sendEmail: false // no signing-link emails; you hand out the link
)
);
// When the signer is ready, mint their signing URL
$link = TurboSign::createSigningUrl(
$result->documentId,
new CreateSigningUrlRequest(recipientId: $result->recipients[0]['id'])
);
echo $link->url; // open in a new tab or redirect to it
sendEmail := false // no signing-link emails; you hand out the link
result, err := client.TurboSign.SendSignature(ctx, &turbodocx.SendSignatureRequest{
FileLink: "https://www.turbodocx.com/examples/turbodocx.pdf",
DocumentName: "Service Agreement",
Recipients: []turbodocx.Recipient{
{Name: "Recipient Name", Email: "recipient@example.com", SigningOrder: 1},
},
Fields: []turbodocx.Field{
{Type: "signature", Page: 1, X: 100, Y: 500, Width: 200, Height: 50, RecipientEmail: "recipient@example.com"},
},
SendEmail: &sendEmail,
})
if err != nil {
log.Fatal(err)
}
// When the signer is ready, mint their signing URL
link, err := client.TurboSign.CreateSigningURL(ctx, result.DocumentID, &turbodocx.CreateSigningURLRequest{
RecipientID: result.Recipients[0].ID,
})
if err != nil {
log.Fatal(err)
}
fmt.Println(link.URL) // open in a new tab or redirect to it
SendSignatureResponse result = client.turboSign().sendSignature(
new SendSignatureRequest.Builder()
.fileLink("https://www.turbodocx.com/examples/turbodocx.pdf")
.documentName("Service Agreement")
.recipients(Arrays.asList(
new Recipient("Recipient Name", "recipient@example.com", 1)
))
.fields(Arrays.asList(
new Field("signature", 1, 100, 500, 200, 50, "recipient@example.com")
))
.sendEmail(false) // no signing-link emails; you hand out the link
.build()
);
// When the signer is ready, mint their signing URL
CreateSigningUrlResponse link = client.turboSign().createSigningUrl(
result.getDocumentId(),
new CreateSigningUrlRequest.Builder()
.recipientId(result.getRecipients().get(0).getId())
.build()
);
System.out.println(link.getUrl()); // open in a new tab or redirect to it
result = TurboDocxSdk::TurboSign.send_signature(
fileLink: "https://www.turbodocx.com/examples/turbodocx.pdf",
documentName: "Service Agreement",
recipients: [{ name: "Recipient Name", email: "recipient@example.com", signingOrder: 1 }],
fields: [{ type: "signature", page: 1, x: 100, y: 500, width: 200, height: 50, recipientEmail: "recipient@example.com" }],
sendEmail: false # no signing-link emails; you hand out the link
)
# When the signer is ready, mint their signing URL
link = TurboDocxSdk::TurboSign.create_signing_url(
result["documentId"],
recipient_id: result["recipients"][0]["id"]
)
puts link["url"] # open in a new tab or redirect to it
To frame the signing page inside your app instead of opening it, and for identity verification options, see Embedded Signing.
Reminders and expiration
- PHP
- Ruby
sendSignature can also schedule automatic reminder emails and an expiration deadline. All eight
schedule fields are optional and both features are off by default, so omitting them preserves
the original send behavior. The resolved schedule is frozen onto the document when it is sent:
changing your org defaults later never alters a document already out for signature.
use TurboDocx\Types\Requests\SendSignatureRequest;
$result = TurboSign::sendSignature(
new SendSignatureRequest(
// ...recipients, fields, fileLink, documentName
remindersEnabled: true,
reminderDelay: ['value' => 3, 'unit' => 'days'], // time to the FIRST reminder
reminderInterval: ['value' => 3, 'unit' => 'days'], // gap between later reminders
maxReminders: 5, // -1 unlimited, 0 none, max 50 (default 5)
expirationEnabled: true,
expireAfter: ['value' => 30, 'unit' => 'days'], // how long the document stays signable
expirationWarning: ['value' => 3, 'unit' => 'days'], // 0 = never warn
expirationWarningInterval: ['value' => 1, 'unit' => 'days']
)
);
Durations are ['value' => N, 'unit' => 'hours'|'days'] objects. value is a whole number from
1 up to a maximum of 999 days (23976 hours); expirationWarning also accepts 0 to disable
warnings. maxReminders accepts -1 (unlimited), 0 (none), or any value up to 50. The resulting
deadline is readable afterwards via getStatus()->expiresAt.
send_signature accepts an optional per-document schedule. Both reminders and expiration are off by default, so omitting these keys preserves the original send behavior. Keys are camelCase like every other send key, and each duration is a { value:, unit: } hash whose unit is "days" or "hours".
result = TurboDocxSdk::TurboSign.send_signature(
recipients: [{ name: "Recipient Name", email: "recipient@example.com", signingOrder: 1 }],
fields: [{ type: "signature", page: 1, x: 100, y: 500, width: 200, height: 50, recipientEmail: "recipient@example.com" }],
fileLink: "https://www.turbodocx.com/examples/turbodocx.pdf",
documentName: "Service Agreement",
# Reminders — chase signers who haven't signed yet
remindersEnabled: true,
reminderDelay: { value: 3, unit: "days" }, # time to the FIRST reminder
reminderInterval: { value: 3, unit: "days" }, # gap between later reminders
maxReminders: 5, # -1 unlimited, 0 none, max 50 (default 5)
# Expiration — close the signing window
expirationEnabled: true,
expireAfter: { value: 14, unit: "days" }, # how long it stays signable
expirationWarning: { value: 3, unit: "days" }, # lead time before warnings (0 = never warn)
expirationWarningInterval: { value: 1, unit: "days" }
)
| Field | Bounds |
|---|---|
maxReminders | Cap per signer: -1 unlimited, 0 none, max 50 (default 5). |
any duration value | Whole number, min 1, max 999 days (23976 hours). |
expirationWarning | May be 0 to never warn. |
The resolved deadline is frozen onto the document at send time and is then readable via get_status(...)["expiresAt"].
Send a reminder
- PHP
- Ruby
Send a standalone reminder to whoever's turn it is to sign
(POST /turbosign/documents/{documentId}/send-reminder). It is independent of the automatic
cadence: it works even when reminders are disabled or the per-signer cap is already spent, does
not consume that cap, and only emails signers at the current signing order. Pass null (or
omit the argument) to remind everyone eligible; do not pass an empty array, which the API rejects.
// Remind everyone whose turn it is to sign.
$result = TurboSign::sendReminder('document-uuid');
foreach ($result['results'] as $r) {
// 'sent', 'skipped_wrong_order', 'skipped_completed', ...
echo "{$r['recipientId']}: {$r['status']}\n";
}
// Or limit to specific recipients (all-or-nothing): every id must be a current-order pending signer.
TurboSign::sendReminder('document-uuid', ['recipient-uuid-1', 'recipient-uuid-2']);
send_reminder is a standalone nudge, independent of the automatic reminder cadence above: it works even when reminders are disabled or the per-signer cap is spent, does not consume that cap, and only emails signers at the current signing order. (Distinct from Resend above, which re-sends the original invitation — this sends the reminder copy.) Omit recipient_ids to remind everyone eligible; do not pass an empty array, which the API rejects.
result = TurboDocxSdk::TurboSign.send_reminder("document-uuid")
result["results"].each do |r|
# "sent", or a "skipped_*" reason (e.g. "skipped_wrong_order", "skipped_completed")
puts "#{r['recipientId']}: #{r['status']}"
end
# Or limit the reminder to specific recipients:
TurboDocxSdk::TurboSign.send_reminder("document-uuid", ["recipient-uuid-1"])
Calls POST /turbosign/documents/:id/send-reminder.
Schedule reminders and expiration
- Python
- Go
- Java
send_signature() also accepts an optional reminder + expiration schedule. Both features are
off by default, so omitting these kwargs preserves the original send behavior. The resolved
deadline is frozen onto the document at send time and is then readable via
get_status()["expiresAt"].
result = await TurboSign.send_signature(
# ...recipients, fields, file source, etc.
reminders_enabled=True,
reminder_delay={"value": 2, "unit": "days"}, # time to the FIRST reminder
reminder_interval={"value": 3, "unit": "days"}, # gap between later reminders
max_reminders=5, # -1 unlimited, 0 none, max 50
expiration_enabled=True,
expire_after={"value": 14, "unit": "days"}, # how long the document stays signable
expiration_warning={"value": 1, "unit": "days"}, # 0 = never warn
expiration_warning_interval={"value": 1, "unit": "days"},
)
Durations are {"value": N, "unit": "days" | "hours"} objects. value is a whole number from
1 up to a maximum of 999 days / 23976 hours; max_reminders accepts -1 (unlimited),
0 (none), or up to 50, defaulting to 5; expiration_warning may be 0 to disable
warnings. See the full field reference under Request Parameters.
SendSignature accepts an optional SignatureSchedule that turns on automatic reminder emails and a signing deadline. Every field is a pointer, and both features are off by default: omit the schedule entirely to preserve the original send behavior. The resolved schedule is frozen onto the document at send time, so later changes to your org defaults never touch a document already out for signature.
result, err := client.TurboSign.SendSignature(ctx, &turbodocx.SendSignatureRequest{
// ...file, recipients, fields, etc.
SignatureSchedule: turbodocx.SignatureSchedule{
RemindersEnabled: turbodocx.BoolPtr(true),
ReminderDelay: &turbodocx.Duration{Value: 2, Unit: "days"}, // time to the first reminder
ReminderInterval: &turbodocx.Duration{Value: 3, Unit: "days"}, // gap between later reminders
MaxReminders: turbodocx.IntPtr(5), // cap per signer
ExpirationEnabled: turbodocx.BoolPtr(true),
ExpireAfter: &turbodocx.Duration{Value: 14, Unit: "days"}, // how long the document stays signable
ExpirationWarning: &turbodocx.Duration{Value: 3, Unit: "days"}, // how far before expiry warnings start
},
})
| Field | Type | Notes |
|---|---|---|
RemindersEnabled | *bool | Master switch for automatic reminders. Default off. |
ReminderDelay | *Duration | Time to the first reminder, measured from that signer's invitation. |
ReminderInterval | *Duration | Gap between subsequent reminders. |
MaxReminders | *int | Automatic reminders per signer. Valid range -1..50: -1 unlimited, 0 none, default 5. |
ExpirationEnabled | *bool | Master switch for the signing deadline. Default off. |
ExpireAfter | *Duration | How long the document stays signable, counted from sending. |
ExpirationWarning | *Duration | How far before expiry warnings start. 0 = never warn. |
ExpirationWarningInterval | *Duration | Gap between warnings once they start. |
A Duration is a {Value, Unit} pair; Unit is "hours" or "days". Value is a whole number, minimum 1 and at most 999 days (23976 hours). Reminders and expiry warnings run as two independent clocks, so a signer can receive both streams; they are coordinated so a reminder and a warning never land in the same moment.
sendSignature accepts an optional SignatureSchedule that turns on automatic reminder emails and/or a signing deadline. Both features are off by default, so omitting the schedule preserves the original send behavior. The resolved deadline is frozen onto the document at send time and is then readable via getStatus().getExpiresAt().
SignatureSchedule schedule = SignatureSchedule.builder()
.remindersEnabled(true)
.reminderDelay(new SignatureSchedule.Duration(3, "days")) // time to the first reminder
.reminderInterval(new SignatureSchedule.Duration(3, "days")) // gap between later reminders
.maxReminders(5) // -1..50: -1 unlimited, 0 none (default 5)
.expirationEnabled(true)
.expireAfter(new SignatureSchedule.Duration(14, "days")) // how long the document stays signable
.expirationWarning(new SignatureSchedule.Duration(2, "days")) // 0 = never warn
.expirationWarningInterval(new SignatureSchedule.Duration(1, "days"))
.build();
SendSignatureResponse result = client.turboSign().sendSignature(
new SendSignatureRequest.Builder()
// ...file, recipients, fields, etc.
.schedule(schedule)
.build()
);
Each Duration is a {value, unit} pair where unit is "hours" or "days" and value is a whole number from 1 up to 999 days (23976 hours). maxReminders accepts -1 to 50 (-1 unlimited, 0 none, default 5) and caps only automatic reminders, never expiry warnings; expirationWarning may be 0 to disable warnings. Reminders and expiry warnings run as two independent clocks, coordinated so a signer never receives both at the same moment, and a reminder cadence that would outlive the expiry window is rejected with 400.
Reminders & expiration schedule
- JavaScript / TypeScript
sendSignature (and createSignatureReviewLink) accept an optional reminder and expiration schedule. Both features are off by default: omit these fields and the send behaves exactly as before. The resolved schedule is frozen onto the document when it is sent, so later changes to your org defaults never touch a document already out for signature.
- JavaScript
- TypeScript
const result = await TurboSign.sendSignature({
// ...fileLink, recipients, fields, etc.
// Reminders: nudge signers who haven't signed yet
remindersEnabled: true,
reminderDelay: { value: 3, unit: "days" }, // time to the FIRST reminder
reminderInterval: { value: 3, unit: "days" }, // gap between later reminders
maxReminders: 5, // cap per signer
// Expiration: close the signing window
expirationEnabled: true,
expireAfter: { value: 30, unit: "days" }, // how long the document stays signable
expirationWarning: { value: 3, unit: "days" }, // how far before expiry warnings start
expirationWarningInterval: { value: 1, unit: "days" },
});
const result = await TurboSign.sendSignature({
// ...fileLink, recipients, fields, etc.
// Reminders: nudge signers who haven't signed yet
remindersEnabled: true,
reminderDelay: { value: 3, unit: "days" }, // time to the FIRST reminder
reminderInterval: { value: 3, unit: "days" }, // gap between later reminders
maxReminders: 5, // cap per signer
// Expiration: close the signing window
expirationEnabled: true,
expireAfter: { value: 30, unit: "days" }, // how long the document stays signable
expirationWarning: { value: 3, unit: "days" }, // how far before expiry warnings start
expirationWarningInterval: { value: 1, unit: "days" },
});
Durations are { value, unit } objects: unit is "hours" or "days", and value is a whole number from 1 to a maximum of 999 days (23976 hours).
| Field | Type | Default | Notes |
|---|---|---|---|
remindersEnabled | boolean | false | Send reminder emails at all |
reminderDelay | Duration | 3 days | Time to the first reminder, measured from that signer's invitation |
reminderInterval | Duration | 3 days | Gap between subsequent reminders |
maxReminders | number | 5 | Cap per signer, range -1..50 (-1 unlimited, 0 none). Never caps expiry warnings |
expirationEnabled | boolean | false | Expire the document at all |
expireAfter | Duration | 120 days | How long the document stays signable, counted from sending |
expirationWarning | Duration | 3 days | How far before expiry warnings start. 0 = never warn |
expirationWarningInterval | Duration | 1 day | Gap between warnings once they start |
Reminders and expiry warnings run as two independent clocks, so a signer keeps getting reminders even after warnings begin; the two are coordinated so a reminder and a warning never land on the same tick. The API rejects a cadence that can't fit its window (for example a reminder interval that outlives expireAfter) with 400 InvalidSignatureSchedule. See the API validation rules for the full list.
Send reminder
- JavaScript / TypeScript
- Python
- Go
- Java
Send a standalone reminder to whoever's turn it is to sign. Unlike the scheduled reminders above, it ignores the configured cadence, works even when reminders are disabled or the per-signer cap is already spent, and does not consume that cap. Only signers at the current signing order are eligible. It maps to POST /turbosign/documents/{documentId}/send-reminder.
- JavaScript
- TypeScript
// Remind everyone whose turn it is: omit the recipient ids
const { results } = await TurboSign.sendReminder("document-uuid");
results.forEach((r) => {
// Anyone not emailed comes back as a skipped_* status, so you can tell who was reached
console.log(`${r.recipientId}: ${r.status}`); // e.g. "sent", "skipped_wrong_order"
});
// Or limit the reminder to specific recipients
await TurboSign.sendReminder("document-uuid", ["recipient-uuid-1"]);
const { results } = await TurboSign.sendReminder("document-uuid");
results.forEach((r) => {
console.log(`${r.recipientId}: ${r.status}`);
});
await TurboSign.sendReminder("document-uuid", ["recipient-uuid-1"]);
To remind everyone eligible, omit recipientIds entirely. Passing an empty array ([]) is rejected with a 400.
Send a standalone reminder (POST /turbosign/documents/:id/send-reminder) to whoever's turn it
is to sign. It is independent of the automatic reminder cadence: it works even when reminders
are disabled or the per-signer max_reminders cap is already spent, does not consume that
cap, and only emails signers at the current signing order. Omit recipient_ids to remind
everyone eligible; do not pass an empty list, which the API rejects.
# Remind everyone whose turn it is:
result = await TurboSign.send_reminder("document-uuid")
# Or limit to specific recipients:
result = await TurboSign.send_reminder("document-uuid", recipient_ids=["recipient-uuid-1"])
for entry in result["results"]:
# status is e.g. 'sent' or 'skipped_wrong_order'
print(f"{entry['recipientId']}: {entry['status']}")
Send a standalone reminder to whoever's turn it is to sign (POST /turbosign/documents/:id/send-reminder). It is independent of the automatic reminder cadence: it works even when reminders are disabled or the per-signer MaxReminders cap is already spent, does not consume that cap, and only emails signers at the current signing order. Pass nil for recipientIDs to remind everyone eligible; do not pass an empty slice, which the API rejects.
resp, err := client.TurboSign.SendReminder(ctx, "document-uuid", nil)
if err != nil {
log.Fatal(err)
}
for _, r := range resp.Results {
// "sent", "skipped_wrong_order", "skipped_completed", ...
fmt.Printf("%s: %s\n", r.RecipientID, r.Status)
}
This differs from Resend: resend re-sends the original invitation email, while send-reminder sends the reminder copy.
Send a standalone reminder to whoever's turn it is to sign (POST /turbosign/documents/:id/send-reminder). It is independent of the automatic reminder cadence: it works even when reminders are disabled or the maxReminders cap is spent, does not consume that cap, and only emails signers at the current signing order. Use the single-arg overload to remind everyone eligible; pass a list to limit it to specific recipients, but do not pass an empty list, which the API rejects.
// Remind everyone whose turn it is
SendReminderResponse reminder = client.turboSign().sendReminder("document-uuid");
for (SendReminderResponse.ReminderResult r : reminder.getResults()) {
// status is e.g. "sent", "skipped_wrong_order", "skipped_completed"
System.out.println(r.getRecipientId() + ": " + r.getStatus());
}
// Or limit to specific recipients
client.turboSign().sendReminder("document-uuid", Arrays.asList("recipient-uuid-1"));
Get status
- JavaScript / TypeScript
- Python
- PHP
- Go
- Java
- Ruby
Retrieve the document-level status. For per-signer detail, use Get recipients.
- JavaScript
- TypeScript
const result = await TurboSign.getStatus("document-uuid");
console.log(result.status); // 'under_review' | 'completed' | 'voided' | ...
const result = await TurboSign.getStatus("document-uuid");
console.log(result.status); // 'under_review' | 'completed' | 'voided' | ...
The response carries the document-level status (under_review, completed, voided, expired, …) and expiresAt (the ISO 8601 signing-window deadline, or undefined/null when expiration is off). Once that deadline passes the document moves to the terminal expired status and its signing links stop working. The same document.expiresAt is returned by getRecipients() alongside per-recipient detail.
Retrieve the document-level status. For per-signer detail, use Get recipients.
result = await TurboSign.get_status("document-uuid")
print("Result:", json.dumps(result, indent=2))
# status is 'under_review', 'completed', 'voided', or the terminal 'expired'
print("Status:", result["status"])
# expiresAt is the signing-window deadline (ISO 8601), or None when expiration is off.
print("Expires:", result.get("expiresAt"))
Once a document's deadline passes it moves to the terminal expired status and its signing
links stop working. The same expiresAt deadline is also returned on the document object from
get_recipients() (result["document"]["expiresAt"]).
Retrieve the current status of a document. When expiration is enabled, the response also carries the signing-window deadline as expiresAt; once that instant passes the document reaches the terminal expired status. The same expiresAt is available on the document returned by getRecipients(). For per-signer detail, use Get recipients.
$status = TurboSign::getStatus('document-uuid');
echo "Document Status: {$status->status}\n"; // 'under_review', 'completed', 'voided', 'expired'
// expiresAt is the signing-window deadline (ISO 8601), or null when expiration is off.
echo "Expires: " . ($status->expiresAt ?? 'never') . "\n";
Check the status of a document. The response includes ExpiresAt (the signing-window deadline as an ISO 8601 string, or "" when expiration is off) and a Status that can reach the terminal value expired once the deadline passes. For per-signer detail, use Get recipients.
status, err := client.TurboSign.GetStatus(ctx, "document-uuid")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Status: %s\n", status.Status) // "under_review", "completed", "voided", "expired", ...
// ExpiresAt is the signing-window deadline (ISO 8601), or "" when expiration is off.
fmt.Printf("Expires: %s\n", status.ExpiresAt)
Check the document-level status. When an expiration schedule is set, the response also carries getExpiresAt() (the signing-window deadline, ISO 8601, or null when expiration is off). Once that deadline passes, the document moves to the terminal expired status and its signing links stop working. getRecipients() exposes the same deadline on getDocument().getExpiresAt(). For per-signer detail, use Get recipients.
DocumentStatusResponse status = client.turboSign().getStatus("document-uuid");
System.out.println("Status: " + status.getStatus()); // "under_review", "completed", "voided", "expired"
System.out.println("Expires: " + status.getExpiresAt()); // ISO 8601, or null when expiration is off
System.out.println("Result: " + gson.toJson(status));
Retrieve the current status of a document. The status reaches the terminal value "expired" once the signing window closes, and expiresAt carries the deadline (ISO 8601, nil when expiration is off). For per-signer detail, use Get recipients.
result = TurboDocxSdk::TurboSign.get_status("document-uuid")
puts result["status"] # "under_review" | "completed" | "voided" | "expired" | ...
puts result["expiresAt"] # ISO 8601 deadline, or nil when expiration is off
puts JSON.pretty_generate(result)
Once a document's deadline passes it moves to the terminal expired status and its signing
links stop working. The same expiresAt deadline is also returned on the document object from
get_recipients (result["document"]["expiresAt"]).
Get recipients
- JavaScript / TypeScript
- Python
- PHP
- Go
- Java
- Ruby
See who the document went to, who has signed, who you are still waiting on, and who sent it.
- JavaScript
- TypeScript
const { document, recipients, summary } = await TurboSign.getRecipients("document-uuid");
console.log(`Sent by ${document.sentBy.name} on ${document.sentOn ?? "not sent yet"}`);
console.log(`${summary.completed}/${summary.total} signed, waiting on ${summary.waitingOn}`);
recipients.forEach((r) => {
console.log(`${r.name} <${r.email}>: ${r.effectiveStatus}`);
console.log(` emailed ${r.delivery.totalSent}x, last ${r.delivery.lastSentOn ?? "never"}`);
});
const { document, recipients, summary } = await TurboSign.getRecipients("document-uuid");
console.log(`${summary.completed}/${summary.total} signed, waiting on ${summary.waitingOn}`);
const chasing = recipients.filter(
(r) => r.effectiveStatus === "pending" || r.effectiveStatus === "viewed",
);
See who the document went to, who has signed, who you are still waiting on, and who sent it.
result = await TurboSign.get_recipients("document-uuid")
summary = result["summary"]
print(f"{summary['completed']}/{summary['total']} signed, waiting on {summary['waitingOn']}")
for r in result["recipients"]:
print(f"{r['name']} <{r['email']}>: {r['effectiveStatus']}")
print(f" emailed {r['delivery']['totalSent']}x")
See who the document went to, who has signed, who you are still waiting on, and who sent it.
$progress = TurboSign::getRecipients('document-uuid');
echo "{$progress->summary->completed}/{$progress->summary->total} signed, ";
echo "waiting on {$progress->summary->waitingOn}\n";
foreach ($progress->recipients as $r) {
echo " {$r->name} <{$r->email}>: {$r->effectiveStatus}";
echo " (emailed {$r->delivery->totalSent}x)\n";
}
See who the document went to, who has signed, who you are still waiting on, and who sent it.
progress, err := client.TurboSign.GetRecipients(ctx, "document-uuid")
if err != nil {
log.Fatal(err)
}
fmt.Printf("%d/%d signed, waiting on %d\n",
progress.Summary.Completed, progress.Summary.Total, progress.Summary.WaitingOn)
for _, r := range progress.Recipients {
fmt.Printf("%s <%s>: %s (emailed %dx)\n",
r.Name, r.Email, r.EffectiveStatus, r.Delivery.TotalSent)
}
See who the document went to, who has signed, who you are still waiting on, and who sent it.
DocumentRecipientsResponse progress = client.turboSign().getRecipients("document-uuid");
System.out.println(progress.getSummary().getCompleted() + "/"
+ progress.getSummary().getTotal() + " signed, waiting on "
+ progress.getSummary().getWaitingOn());
for (DocumentRecipientsResponse.RecipientSignatureStatus r : progress.getRecipients()) {
System.out.println(r.getName() + " <" + r.getEmail() + ">: " + r.getEffectiveStatus()
+ " (emailed " + r.getDelivery().getTotalSent() + "x)");
}
See who the document went to, who has signed, who you are still waiting on, and who sent it.
result = TurboDocxSdk::TurboSign.get_recipients("document-uuid")
summary = result["summary"]
puts "#{summary['completed']}/#{summary['total']} signed, waiting on #{summary['waitingOn']}"
result["recipients"].each do |r|
puts "#{r['name']} <#{r['email']}>: #{r['effectiveStatus']} " \
"(emailed #{r['delivery']['totalSent']}x)"
end
status is the raw database value and is only ever pending, viewed or completed.
effectiveStatus layers the document's outcome on top, adding voided and expired: that
is the one to display.
On a voided or expired document an unsigned signer still reads pending in status, so
branching on it would show someone as "still to sign" when their signing link is already dead.
A completed signature is never revoked: someone who signed before the document was voided
still reads completed.
summary counts by effectiveStatus, and waitingOn (pending + viewed) drops to zero once
the document is terminal.
Each recipient also carries a delivery block: firstSentOn, lastSentOn, totalSent,
reminderCount, lastRemindedAt, warningCount, lastWarningAt. It counts the signature
request, resends, reminders, expiry warnings and terminal notices; CC notifications are
excluded, since a CC address is not a signer.
reminderCount and lastRemindedAt do not mean what their names suggestreminderCount counts automatic (scheduled) reminders only: the counter maxReminders
caps. A manual "remind now" is a standalone nudge that must not consume the cap budget, so it
does not increment this, even though the email it sends does appear in totalSent.
lastRemindedAt is a cadence clock, not a record of a reminder: the initial
signature-request send, each scheduled reminder, each manual "remind now" and each expiry
warning all stamp it. Only scheduled reminders bump reminderCount.
So a freshly-sent document returns a non-null lastRemindedAt equal to the invitation
timestamp alongside reminderCount: 0: nobody has been reminded. To answer "have we actually
chased this person", read totalSent, not reminderCount.
warningCount / lastWarningAt have no such caveat.
Download document
- JavaScript / TypeScript
- Python
- PHP
- Go
- Java
- Ruby
Download the completed signed document as a PDF Blob.
- JavaScript
- TypeScript
const { writeFileSync } = require("fs");
(async () => {
const result = await TurboSign.download("document-uuid");
// Node.js: Save to file
const buffer = Buffer.from(await result.arrayBuffer());
writeFileSync("signed-contract.pdf", buffer);
})();
const result = await TurboSign.download("document-uuid");
// Node.js: Save to file
import { writeFileSync } from "fs";
const buffer = Buffer.from(await result.arrayBuffer());
writeFileSync("signed-contract.pdf", buffer);
Download the completed signed document as PDF bytes.
pdf_bytes = await TurboSign.download("document-uuid")
# Save to file
with open("signed-contract.pdf", "wb") as f:
f.write(pdf_bytes)
Download the completed signed document as PDF content.
$pdfContent = TurboSign::download('document-uuid');
// Save to file
file_put_contents('signed-contract.pdf', $pdfContent);
// Or send as HTTP response
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="signed.pdf"');
echo $pdfContent;
Download the completed signed document.
pdfData, err := client.TurboSign.Download(ctx, "document-uuid")
if err != nil {
log.Fatal(err)
}
// Save to file
err = os.WriteFile("signed-contract.pdf", pdfData, 0644)
if err != nil {
log.Fatal(err)
}
Download the completed signed document.
byte[] pdfData = client.turboSign().download("document-uuid");
// Save to file
Files.write(Paths.get("signed-contract.pdf"), pdfData);
Download the completed signed document as PDF bytes.
pdf_bytes = TurboDocxSdk::TurboSign.download("document-uuid")
# Save to file
File.binwrite("signed-contract.pdf", pdf_bytes)
Void
- JavaScript / TypeScript
- Python
- PHP
- Go
- Java
- Ruby
Cancel/void a signature request.
- JavaScript
- TypeScript
await TurboSign.void("document-uuid", "Contract terms changed");
await TurboSign.void("document-uuid", "Contract terms changed");
Cancel/void a signature request.
result = await TurboSign.void_document("document-uuid", reason="Contract terms changed")
Cancel/void a signature request that hasn't been completed.
use TurboDocx\Types\Responses\VoidDocumentResponse;
$result = TurboSign::void('document-uuid', 'Document needs to be revised');
echo "Document ID: {$result->id}\n";
echo "Status: {$result->status}\n";
echo "Void Reason: {$result->voidReason}\n";
Cancel/void a signature request.
result, err := client.TurboSign.VoidDocument(ctx, "document-uuid", "Contract terms changed")
Cancel/void a signature request.
VoidDocumentResponse result = client.turboSign().voidDocument("document-uuid", "Contract terms changed");
Cancel/void a signature request.
result = TurboDocxSdk::TurboSign.void_document("document-uuid", "Contract terms changed")
Resend
- JavaScript / TypeScript
- Python
- PHP
- Go
- Java
- Ruby
Resend signature request emails to specific recipients.
- JavaScript
- TypeScript
// Resend to specific recipients
await TurboSign.resend("document-uuid", [
"recipient-uuid-1",
"recipient-uuid-2",
]);
// Resend to specific recipients
await TurboSign.resend("document-uuid", [
"recipient-uuid-1",
"recipient-uuid-2",
]);
Resend signature request emails to specific recipients.
result = await TurboSign.resend_email("document-uuid", recipient_ids=["recipient-uuid-1", "recipient-uuid-2"])
Resend signature request emails to specific recipients.
// Resend to specific recipients
$result = TurboSign::resend('document-uuid', ['recipient-id-1', 'recipient-id-2']);
// Resend to all recipients
$result = TurboSign::resend('document-uuid', []);
echo "Recipients notified: {$result->recipientCount}\n";
Resend signature request emails.
// Resend to specific recipients
result, err := client.TurboSign.ResendEmail(ctx, "document-uuid", []string{"recipient-uuid-1", "recipient-uuid-2"})
Resend signature request emails.
// Resend to specific recipients
ResendEmailResponse result = client.turboSign().resendEmail(
"document-uuid",
Arrays.asList("recipient-uuid-1", "recipient-uuid-2")
);
Resend signature request emails to specific recipients.
result = TurboDocxSdk::TurboSign.resend_email("document-uuid", ["recipient-uuid-1", "recipient-uuid-2"])
Get audit trail
- JavaScript / TypeScript
- Python
- PHP
- Go
- Java
- Ruby
Retrieve the complete audit trail for a document, including all events and actions.
- JavaScript
- TypeScript
const result = await TurboSign.getAuditTrail("document-uuid");
console.log(JSON.stringify(result, null, 2));
const result = await TurboSign.getAuditTrail("document-uuid");
console.log(JSON.stringify(result, null, 2));
Retrieve the complete audit trail for a document, including all events and actions.
result = await TurboSign.get_audit_trail("document-uuid")
print("Result:", json.dumps(result, indent=2))
Retrieve the complete audit trail for a document, including all events and actions.
$audit = TurboSign::getAuditTrail('document-uuid');
echo "Audit Trail:\n";
foreach ($audit->auditTrail as $entry) {
echo " {$entry->timestamp} - {$entry->actionType}";
if ($entry->user) {
echo " by {$entry->user->name}";
}
echo "\n";
}
Retrieve the audit trail for a document.
auditTrail, err := client.TurboSign.GetAuditTrail(ctx, "document-uuid")
if err != nil {
log.Fatal(err)
}
b, _ := json.MarshalIndent(auditTrail, "", " "); fmt.Println("Result:", string(b))
Retrieve the audit trail for a document.
AuditTrailResponse auditTrail = client.turboSign().getAuditTrail("document-uuid");
System.out.println("Result: " + gson.toJson(auditTrail));
Retrieve the complete audit trail for a document, including all events and actions.
result = TurboDocxSdk::TurboSign.get_audit_trail("document-uuid")
result["auditTrail"].each do |entry|
puts "#{entry['actionType']} at #{entry['timestamp']}"
end
Get template signature setup
- JavaScript / TypeScript
- Python
- PHP
- Go
- Java
- Ruby
List a template's signer roles, in signing order, to send it with signer roles. Each role carries key (the value for a recipient's role), label, order, hasSavedSigner, fieldCount, and defaultName / defaultEmail when it has a saved signer.
- JavaScript
- TypeScript
const setup = await TurboSign.getTemplateSignatureSetup("template-uuid");
setup.roles.forEach((role) => console.log(role.key, role.hasSavedSigner));
const setup = await TurboSign.getTemplateSignatureSetup("template-uuid");
setup.roles.forEach((role) => console.log(role.key, role.hasSavedSigner));
List a template's signer roles, in signing order, to send it with signer roles. Each role carries key (the value for a recipient's role), label, order, hasSavedSigner, fieldCount, and defaultName / defaultEmail when it has a saved signer.
setup = await TurboSign.get_template_signature_setup("template-uuid")
for role in setup["roles"]:
print(role["key"], role["hasSavedSigner"])
List a template's signer roles, in signing order, to send it with signer roles. Each TemplateSignatureRole carries key (the value for a recipient's role), label, order, hasSavedSigner, fieldCount, and defaultName / defaultEmail when it has a saved signer.
$setup = TurboSign::getTemplateSignatureSetup('template-uuid');
foreach ($setup->roles as $role) {
echo "{$role->key}: " . ($role->hasSavedSigner ? 'saved signer' : 'slot') . "\n";
}
List a template's signer roles, in signing order, to send it with signer roles. Each TemplateSignatureRole carries Key (the value for Recipient.Role), Label, Order, HasSavedSigner, FieldCount, and DefaultName / DefaultEmail when it has a saved signer.
setup, err := client.TurboSign.GetTemplateSignatureSetup(ctx, "template-uuid")
if err != nil {
log.Fatal(err)
}
for _, role := range setup.Roles {
fmt.Printf("%s saved signer: %t\n", role.Key, role.HasSavedSigner)
}
List a template's signer roles, in signing order, to send it with signer roles. Each TemplateSignatureRole exposes getKey() (the value for a recipient's role), getLabel(), getOrder(), hasSavedSigner(), getFieldCount(), and getDefaultName() / getDefaultEmail() when it has a saved signer.
TemplateSignatureSetup setup = client.turboSign().getTemplateSignatureSetup("template-uuid");
for (TemplateSignatureRole role : setup.getRoles()) {
System.out.println(role.getKey() + " saved signer: " + role.hasSavedSigner());
}
List a template's signer roles, in signing order, to send it with signer roles. Each role carries key (the value for a recipient's role), label, order, hasSavedSigner, fieldCount, and defaultName / defaultEmail when it has a saved signer.
setup = TurboDocxSdk::TurboSign.get_template_signature_setup("template-uuid")
setup["roles"].each do |role|
puts "#{role['key']} saved signer: #{role['hasSavedSigner']}"
end
Calls GET /turbosign/templates/:templateId/signature-setup. A template with no saved signature setup returns a 404 (SignatureSetupNotFound).
A template that doesn't exist in your organization, or has no saved signature setup, returns a 404 (SignatureSetupNotFound). Calls GET /turbosign/templates/{templateId}/signature-setup; see Get Template Signature Setup.
Field Types
- PHP
TurboSign supports 11 different field types using PHP enums:
use TurboDocx\Types\SignatureFieldType;
SignatureFieldType::SIGNATURE // Signature field
SignatureFieldType::INITIAL // Initial field
SignatureFieldType::DATE // Date stamp (auto-filled when signed)
SignatureFieldType::TEXT // Free text input
SignatureFieldType::FULL_NAME // Full name (auto-filled from recipient)
SignatureFieldType::FIRST_NAME // First name
SignatureFieldType::LAST_NAME // Last name
SignatureFieldType::EMAIL // Email address
SignatureFieldType::TITLE // Job title
SignatureFieldType::COMPANY // Company name
SignatureFieldType::CHECKBOX // Checkbox field
Field Positioning
- PHP
TurboSign supports two ways to position fields:
1. Coordinate-Based (Pixel Perfect)
- PHP
new Field(
type: SignatureFieldType::SIGNATURE,
recipientEmail: 'john@example.com',
page: 1, // Page number (1-indexed)
x: 100, // X coordinate
y: 500, // Y coordinate
width: 200, // Width in pixels
height: 50 // Height in pixels
)
2. Template Anchors (Dynamic)
- PHP
use TurboDocx\Types\TemplateConfig;
use TurboDocx\Types\FieldPlacement;
new Field(
type: SignatureFieldType::SIGNATURE,
recipientEmail: 'john@example.com',
template: new TemplateConfig(
anchor: '{signature1}', // Text to find in PDF
placement: FieldPlacement::REPLACE, // How to place the field
size: ['width' => 100, 'height' => 30]
)
)
Placement Options:
FieldPlacement::REPLACE- Replace the anchor textFieldPlacement::BEFORE- Place before the anchorFieldPlacement::AFTER- Place after the anchorFieldPlacement::ABOVE- Place above the anchorFieldPlacement::BELOW- Place below the anchor
Advanced Field Options
- PHP
// Checkbox (pre-checked, readonly)
new Field(
type: SignatureFieldType::CHECKBOX,
recipientEmail: 'john@example.com',
page: 1,
x: 100,
y: 600,
width: 20,
height: 20,
defaultValue: 'true', // Pre-checked
isReadonly: true // Cannot be unchecked
)
// Multiline text field
new Field(
type: SignatureFieldType::TEXT,
recipientEmail: 'john@example.com',
page: 1,
x: 100,
y: 200,
width: 400,
height: 100,
isMultiline: true, // Allow multiple lines
required: true, // Field is required
backgroundColor: '#f0f0f0' // Background color
)
// Readonly text (pre-filled, non-editable)
new Field(
type: SignatureFieldType::TEXT,
recipientEmail: 'john@example.com',
page: 1,
x: 100,
y: 300,
width: 300,
height: 30,
defaultValue: 'This text is pre-filled',
isReadonly: true
)
Conditional (IF/THEN) Fields
- PHP
The optional metadata builds IF/THEN relationships between fields. Put a fieldKey on a
controlling checkbox, then point each dependent field's controllingFieldKey back at it.
use TurboDocx\Types\FieldMetadata;
use TurboDocx\Types\FieldConditional;
use TurboDocx\Types\ConditionalOperator;
use TurboDocx\Types\ConditionalAction;
// Controlling checkbox, carries a stable fieldKey
new Field(
type: SignatureFieldType::CHECKBOX,
recipientEmail: 'reviewer@company.com',
page: 1,
x: 100,
y: 400,
width: 20,
height: 20,
metadata: new FieldMetadata(fieldKey: 'request_changes')
);
// Dependent text field, hidden until the checkbox is checked
new Field(
type: SignatureFieldType::TEXT,
recipientEmail: 'reviewer@company.com',
page: 1,
x: 130,
y: 400,
width: 300,
height: 60,
metadata: new FieldMetadata(
conditional: new FieldConditional(
controllingFieldKey: 'request_changes', // = the checkbox's fieldKey
operator: ConditionalOperator::IS_CHECKED, // ::IS_CHECKED | ::IS_NOT_CHECKED
action: ConditionalAction::SHOW // ::SHOW | ::UNLOCK
)
)
);
Use action: 'unlock' to keep a field visible but read-only until the box is checked. A
malformed rule returns 400 InvalidConditionalRule; a well-formed rule whose
controllingFieldKey matches no checkbox fails open (the field stays visible/editable). See
Conditional (IF/THEN) Fields.
Error Handling
- JavaScript / TypeScript
- Python
- PHP
- Go
- Java
- Ruby
All errors are real Error subclasses (instanceof works) that extend the base TurboDocxError. code is a plain string, not an enum member: the HTTP client passes the API's own code through when the response includes one (for example QUOTE_NOT_FOUND), and only falls back to the class default below when it doesn't, so you can branch on err.code for the precise reason instead of just the HTTP category.
Every error is a plain Exception subclass; catch the most specific one first, since except TurboDocxError also matches every subclass below it. Each of the 7 named subclasses sets its own DEFAULT_CODE class attribute, so e.code is populated for those even when the API response itself carries none; the base TurboDocxError raised for an unmapped status (e.g. an unexpected 5xx) has DEFAULT_CODE = None, so e.code can be None there.
Every typed exception extends TurboDocxException, itself a plain Exception subclass with two extra readonly properties: statusCode (int, HTTP status) and errorCode (string, e.g. 'VALIDATION_ERROR'). Because the constructor hardcodes PHP's built-in Exception::getCode() to 0, read $e->errorCode, not $e->getCode(), for the machine-readable reason:
use TurboDocx\Exceptions\AuthenticationException;
use TurboDocx\Exceptions\AuthorizationException;
use TurboDocx\Exceptions\ValidationException;
use TurboDocx\Exceptions\NotFoundException;
use TurboDocx\Exceptions\ConflictException;
use TurboDocx\Exceptions\RateLimitException;
use TurboDocx\Exceptions\NetworkException;
try {
$result = TurboSign::sendSignature(/* ... */);
} catch (AuthenticationException $e) {
// 401 - Invalid API key or access token
echo "Authentication failed: {$e->getMessage()}\n";
} catch (AuthorizationException $e) {
// 403 - Valid credentials without permission for this operation
echo "Authorization error: {$e->getMessage()}\n";
} catch (ValidationException $e) {
// 400 - Invalid request data
echo "Validation error: {$e->getMessage()}\n";
} catch (NotFoundException $e) {
// 404 - Document not found
echo "Not found: {$e->getMessage()}\n";
} catch (ConflictException $e) {
// 409 - Conflicts with the current resource state
echo "Conflict: {$e->getMessage()}\n";
} catch (RateLimitException $e) {
// 429 - Rate limit exceeded
echo "Rate limit: {$e->getMessage()}\n";
} catch (NetworkException $e) {
// Network/connection error
echo "Network error: {$e->getMessage()}\n";
} catch (TurboDocxException $e) {
// Catch-all: read the machine-readable reason from errorCode, not getCode()
echo "Error {$e->errorCode}: {$e->getMessage()} (status {$e->statusCode})\n";
}
Every typed error embeds TurboDocxError by value, which promotes its Message string, StatusCode int, and Code string fields onto the typed error, so read them directly off the matched variable (authErr.Message, not a getter). Match with errors.As, as the example below does:
Every typed exception is a nested static class of TurboDocxException (TurboDocxException.ValidationException, not a separate top-level import) and extends RuntimeException, so the compiler never forces a catch:
Every error is a StandardError subclass; rescue the most specific one first, since rescue TurboDocxSdk::TurboDocxError also matches every subclass below it. Each of the 7 named subclasses has its own default code (for example "NOT_FOUND"), so e.code is populated for those even when the API response itself carries none; the base TurboDocxError raised for an unmapped status (e.g. an unexpected 5xx) has no default, so e.code can be nil there.
Error Types
- Go
- Java
| Error Type | Status Code | Description |
|---|---|---|
TurboDocxError | varies | Base error type for all API errors |
AuthenticationError | 401 | Invalid or missing API key |
AuthorizationError | 403 | Authenticated but lacks required permissions |
ValidationError | 400 | Invalid request parameters |
NotFoundError | 404 | Resource not found |
ConflictError | 409 | Request conflicts with current resource state; most common on the webhook routes (creating or renaming to a name that already exists) |
RateLimitError | 429 | Too many requests |
NetworkError | - | Network connectivity issues |
| Error Type | Status Code | Description |
|---|---|---|
TurboDocxException | varies | Base exception for all API errors |
TurboDocxException.AuthenticationException | 401 | Invalid or missing API credentials |
TurboDocxException.ValidationException | 400 | Invalid request parameters |
TurboDocxException.AuthorizationException | 403 | Authenticated but lacks permissions for the route |
TurboDocxException.NotFoundException | 404 | Document or resource not found |
TurboDocxException.ConflictException | 409 | Request conflicts with current state of resource (e.g., webhook name already exists) |
TurboDocxException.RateLimitException | 429 | Too many requests |
TurboDocxException.NetworkException | - | Network connectivity issues |
Error Classes
- JavaScript / TypeScript
- Python
- PHP
- Ruby
| Error Class | Status Code | Code | Description |
|---|---|---|---|
TurboDocxError | varies | varies | Base error class for all SDK errors |
AuthenticationError | 401 | AUTHENTICATION_ERROR | Invalid or missing API credentials |
AuthorizationError | 403 | AUTHORIZATION_ERROR | API key lacks required permissions |
ValidationError | 400 | VALIDATION_ERROR | Invalid request parameters |
NotFoundError | 404 | NOT_FOUND | Document or resource not found |
ConflictError | 409 | CONFLICT | Resource conflict |
RateLimitError | 429 | RATE_LIMIT_EXCEEDED | Too many requests |
NetworkError | - | NETWORK_ERROR | Network connectivity issues |
| Error Class | Status Code | Description |
|---|---|---|
TurboDocxError | varies | Base error class for all SDK errors |
AuthenticationError | 401 | Invalid or missing API credentials |
AuthorizationError | 403 | Authenticated but lacks required permissions |
ValidationError | 400 | Invalid request parameters |
NotFoundError | 404 | Document or resource not found |
ConflictError | 409 | Request conflicts with current resource state |
RateLimitError | 429 | Too many requests |
NetworkError | - | Network connectivity issues |
| Error Class | Status Code | Description |
|---|---|---|
TurboDocxException | varies | Base exception for all SDK errors |
AuthenticationException | 401 | Invalid or missing API credentials |
AuthorizationException | 403 | Valid credentials without permission for this operation |
ValidationException | 400 | Invalid request parameters |
NotFoundException | 404 | Document or resource not found |
ConflictException | 409 | Request conflicts with current resource state |
RateLimitException | 429 | Too many requests |
NetworkException | - | Network connectivity issues |
All exceptions extend TurboDocxException and include:
getMessage()- Human-readable error messagestatusCode- HTTP status code, a public readonly?int(null forNetworkException)errorCode- Error code string (e.g.,'AUTHENTICATION_ERROR'), a public readonly?string
| Error Class | Status Code | Description |
|---|---|---|
TurboDocxError | varies | Base error class for all SDK errors |
AuthenticationError | 401 | Invalid or missing API credentials |
AuthorizationError | 403 | Authenticated but lacks required permissions |
ValidationError | 400 | Invalid request parameters |
NotFoundError | 404 | Document or resource not found |
ConflictError | 409 | Request conflicts with current resource state |
RateLimitError | 429 | Too many requests |
NetworkError | - | Network connectivity issues |
Handling Errors
- JavaScript / TypeScript
- Python
- Ruby
- JavaScript
- TypeScript
const {
TurboSign,
TurboDocxError,
AuthenticationError,
ValidationError,
NotFoundError,
RateLimitError,
NetworkError,
} = require("@turbodocx/sdk");
(async () => {
try {
const result = await TurboSign.sendSignature({
fileLink: "https://www.turbodocx.com/examples/turbodocx.pdf",
recipients: [
{ name: "John Doe", email: "john@example.com", signingOrder: 1 },
],
fields: [
{
type: "signature",
page: 1,
x: 100,
y: 650,
width: 200,
height: 50,
recipientEmail: "john@example.com",
},
],
});
} catch (error) {
if (error instanceof AuthenticationError) {
console.error("Authentication failed:", error.message);
// Check your API key and org ID
} else if (error instanceof ValidationError) {
console.error("Validation error:", error.message);
// Check request parameters
} else if (error instanceof NotFoundError) {
console.error("Resource not found:", error.message);
// Document or recipient doesn't exist
} else if (error instanceof RateLimitError) {
console.error("Rate limited:", error.message);
// Wait and retry
} else if (error instanceof NetworkError) {
console.error("Network error:", error.message);
// Check connectivity
} else if (error instanceof TurboDocxError) {
console.error("SDK error:", error.message, error.statusCode, error.code);
}
}
})();
import {
TurboSign,
TurboDocxError,
AuthenticationError,
ValidationError,
NotFoundError,
RateLimitError,
NetworkError,
} from "@turbodocx/sdk";
try {
const result = await TurboSign.sendSignature({
fileLink: "https://www.turbodocx.com/examples/turbodocx.pdf",
recipients: [
{ name: "John Doe", email: "john@example.com", signingOrder: 1 },
],
fields: [
{
type: "signature",
page: 1,
x: 100,
y: 650,
width: 200,
height: 50,
recipientEmail: "john@example.com",
},
],
});
} catch (error) {
if (error instanceof AuthenticationError) {
console.error("Authentication failed:", error.message);
// Check your API key and org ID
} else if (error instanceof ValidationError) {
console.error("Validation error:", error.message);
// Check request parameters
} else if (error instanceof NotFoundError) {
console.error("Resource not found:", error.message);
// Document or recipient doesn't exist
} else if (error instanceof RateLimitError) {
console.error("Rate limited:", error.message);
// Wait and retry
} else if (error instanceof NetworkError) {
console.error("Network error:", error.message);
// Check connectivity
} else if (error instanceof TurboDocxError) {
console.error("SDK error:", error.message, error.statusCode, error.code);
}
}
import asyncio
from turbodocx_sdk import (
TurboSign,
TurboDocxError,
AuthenticationError,
AuthorizationError,
ValidationError,
NotFoundError,
ConflictError,
RateLimitError,
NetworkError,
)
async def send_with_error_handling():
try:
result = await TurboSign.send_signature(
recipients=[{"name": "John Doe", "email": "john@example.com", "signingOrder": 1}],
fields=[{
"type": "signature",
"page": 1,
"x": 100,
"y": 650,
"width": 200,
"height": 50,
"recipientEmail": "john@example.com",
}],
file_link="https://www.turbodocx.com/examples/turbodocx.pdf",
)
except AuthenticationError as e:
print(f"Authentication failed: {e}")
# Check your API key and org ID
except AuthorizationError as e:
print(f"Not authorized: {e}")
# Authenticated, but lacks permission for this operation
except ValidationError as e:
print(f"Validation error: {e}")
# Check request parameters
except NotFoundError as e:
print(f"Resource not found: {e}")
# Document or recipient doesn't exist
except ConflictError as e:
print(f"Conflict: {e}")
# Request conflicts with the current resource state
except RateLimitError as e:
print(f"Rate limited: {e}")
# Wait and retry
except NetworkError as e:
print(f"Network error: {e}")
# Check connectivity
except TurboDocxError as e:
print(f"SDK error: {e}, status_code={e.status_code}, code={e.code}")
asyncio.run(send_with_error_handling())
require "turbodocx_sdk"
begin
result = TurboDocxSdk::TurboSign.send_signature(
recipients: [{ name: "John Doe", email: "john@example.com", signingOrder: 1 }],
fields: [{
type: "signature",
page: 1,
x: 100,
y: 650,
width: 200,
height: 50,
recipientEmail: "john@example.com"
}],
fileLink: "https://www.turbodocx.com/examples/turbodocx.pdf"
)
rescue TurboDocxSdk::AuthenticationError => e
puts "Authentication failed: #{e.message}"
# Check your API key and org ID
rescue TurboDocxSdk::AuthorizationError => e
puts "Not authorized: #{e.message}"
# Authenticated, but lacks permission for this operation
rescue TurboDocxSdk::ValidationError => e
puts "Validation error: #{e.message}"
# Check request parameters
rescue TurboDocxSdk::NotFoundError => e
puts "Resource not found: #{e.message}"
# Document or recipient doesn't exist
rescue TurboDocxSdk::ConflictError => e
puts "Conflict: #{e.message}"
# Request conflicts with the current resource state
rescue TurboDocxSdk::RateLimitError => e
puts "Rate limited: #{e.message}"
# Wait and retry
rescue TurboDocxSdk::NetworkError => e
puts "Network error: #{e.message}"
# Check connectivity
rescue TurboDocxSdk::TurboDocxError => e
puts "SDK error: #{e.message}, status_code=#{e.status_code}, code=#{e.code}"
end
Error Properties
- JavaScript / TypeScript
- Python
- Go
- Java
- Ruby
All errors include these readonly properties:
| Property | Type | Description |
|---|---|---|
message | string | Human-readable error description |
statusCode | number | undefined | HTTP status code (if applicable) |
code | string | undefined | Machine-readable error code |
All errors include these instance attributes:
| Property | Type | Description |
|---|---|---|
message | str | Human-readable error description (via str(error)) |
status_code | int | None | HTTP status code (if applicable) |
code | str | None | Machine-readable error code; the API's code wins when present, otherwise the class's DEFAULT_CODE |
| Property | Type | Description |
|---|---|---|
Message | string | Human-readable error message. Error() does not return this bare string: it returns TurboDocx API error [CODE]: MESSAGE (status N), omitting the [CODE] segment when Code is empty. Compare against .Message directly, not err.Error() |
StatusCode | int | HTTP status code |
Code | string | Machine-readable code; the API's code wins when present, otherwise the SDK fills in a per-status default for each of the 7 named types above. The bare TurboDocxError returned for an unmapped status (e.g. an unexpected 5xx) can have an empty Code if the API didn't supply one |
| Property | Type | Description |
|---|---|---|
getMessage() | String | Human-readable error message |
getStatusCode() | int | HTTP status code |
getCode() | String | Machine-readable code; each of the 7 named subclasses falls back to its own default (e.g. AuthenticationException's AUTHENTICATION_ERROR) whenever the API response carries none. The bare TurboDocxException thrown for an unmapped status (e.g. an unexpected 5xx) can return null |
All errors expose these readers:
| Property | Type | Description |
|---|---|---|
message | String | Human-readable error description |
status_code | Integer | nil | HTTP status code (if applicable) |
code | String | nil | Machine-readable error code; the API's code wins when present, otherwise the class's default |
Example
- Go
- Java
import (
"errors"
turbodocx "github.com/TurboDocx/SDK/packages/go-sdk"
)
result, err := client.TurboSign.SendSignature(ctx, request)
if err != nil {
// Check for specific error types
var authErr *turbodocx.AuthenticationError
var authzErr *turbodocx.AuthorizationError
var validationErr *turbodocx.ValidationError
var notFoundErr *turbodocx.NotFoundError
var conflictErr *turbodocx.ConflictError
var rateLimitErr *turbodocx.RateLimitError
var networkErr *turbodocx.NetworkError
switch {
case errors.As(err, &authErr):
log.Printf("Authentication failed: %s", authErr.Message)
case errors.As(err, &authzErr):
log.Printf("Authorization failed: %s", authzErr.Message)
case errors.As(err, &validationErr):
log.Printf("Validation error: %s", validationErr.Message)
case errors.As(err, ¬FoundErr):
log.Printf("Not found: %s", notFoundErr.Message)
case errors.As(err, &conflictErr):
log.Printf("Conflict: %s", conflictErr.Message)
case errors.As(err, &rateLimitErr):
log.Printf("Rate limited: %s", rateLimitErr.Message)
case errors.As(err, &networkErr):
log.Printf("Network error: %s", networkErr.Message)
default:
// Base TurboDocxError or unexpected error
var turboErr *turbodocx.TurboDocxError
if errors.As(err, &turboErr) {
log.Printf("API error [%d]: %s", turboErr.StatusCode, turboErr.Message)
} else {
log.Fatal(err)
}
}
}
import com.turbodocx.TurboDocxException;
try {
SendSignatureResponse result = client.turboSign().sendSignature(request);
} catch (TurboDocxException.AuthenticationException e) {
System.err.println("Authentication failed: " + e.getMessage());
// Check your API key and org ID
} catch (TurboDocxException.ValidationException e) {
System.err.println("Validation error: " + e.getMessage());
// Check request parameters
} catch (TurboDocxException.NotFoundException e) {
System.err.println("Not found: " + e.getMessage());
// Document or recipient doesn't exist
} catch (TurboDocxException.RateLimitException e) {
System.err.println("Rate limited: " + e.getMessage());
// Wait and retry
} catch (TurboDocxException.NetworkException e) {
System.err.println("Network error: " + e.getMessage());
// Check connectivity
} catch (TurboDocxException e) {
// Base exception for other API errors
System.err.println("API error [" + e.getStatusCode() + "]: " + e.getMessage());
}
TypeScript Types / Python Types / PHP Types / Types
- JavaScript / TypeScript
- Python
- Ruby
The SDK exports TypeScript types for full type safety. Import them directly from the package.
The SDK uses Python type hints with Dict[str, Any] for flexible JSON-like structures.
The SDK uses plain Ruby Hash objects for flexible JSON-like structures. Symbol and string keys are both accepted; the key spelling stays camelCase because the SDK forwards the hash to the API verbatim.
Signature Field Types
- Go
- Java
The Type field accepts the following string values:
| Type | Description |
|---|---|
"signature" | Signature field |
"initials" | Initials field |
"text" | Text input field |
"date" | Date field |
"checkbox" | Checkbox field |
"full_name" | Full name field |
"first_name" | First name field |
"last_name" | Last name field |
"email" | Email field |
"title" | Title field |
"company" | Company field |
The type field accepts the following string values:
| Type | Description |
|---|---|
"signature" | Signature field |
"initial" | Initials field |
"text" | Text input field |
"date" | Date field |
"checkbox" | Checkbox field |
"full_name" | Full name field |
"first_name" | First name field |
"last_name" | Last name field |
"email" | Email field |
"title" | Title field |
"company" | Company field |
Enums
- PHP
The SDK uses PHP 8.1+ enums for type safety:
// Field types
enum SignatureFieldType: string {
case SIGNATURE = 'signature';
case INITIAL = 'initial';
case DATE = 'date';
case TEXT = 'text';
case FULL_NAME = 'full_name';
case FIRST_NAME = 'first_name';
case LAST_NAME = 'last_name';
case EMAIL = 'email';
case TITLE = 'title';
case COMPANY = 'company';
case CHECKBOX = 'checkbox';
}
// Field placement
enum FieldPlacement: string {
case REPLACE = 'replace';
case BEFORE = 'before';
case AFTER = 'after';
case ABOVE = 'above';
case BELOW = 'below';
}
// Document status
enum DocumentStatus: string {
case DRAFT = 'draft';
case SETUP_COMPLETE = 'setup_complete';
case REVIEW_READY = 'review_ready';
case UNDER_REVIEW = 'under_review';
case COMPLETED = 'completed';
case VOIDED = 'voided';
}
// Conditional (IF/THEN) operator, the condition evaluated against the controlling checkbox
enum ConditionalOperator: string {
case IS_CHECKED = 'is_checked';
case IS_NOT_CHECKED = 'is_not_checked';
}
// Conditional (IF/THEN) action, what happens to the dependent field until the condition is met
enum ConditionalAction: string {
case SHOW = 'show'; // hidden until met
case UNLOCK = 'unlock'; // visible but read-only until met
}
Typed Classes
- PHP
The SDK uses final classes with typed, constructor-promoted properties:
final class Recipient {
public function __construct(
public string $name,
public string $email,
public ?int $signingOrder = null, // Required unless the recipient has a role
public ?string $phone = null, // E.164; required for SMS passcodes
public ?string $externalId = null, // Your own key, unique in the document
public ?IdentityVerification $identityVerification = null,
public ?string $role = null // Template signer role this recipient fills
) {}
}
final class Field {
public function __construct(
public SignatureFieldType $type,
public string $recipientEmail,
public ?int $page = null,
public ?int $x = null,
public ?int $y = null,
public ?int $width = null,
public ?int $height = null,
public ?TemplateConfig $template = null,
public ?string $defaultValue = null, // checkbox: 'true'/'false'; date: a fixed MM/DD/YYYY (omit to auto-fill the signing date)
public bool $isMultiline = false,
public bool $isReadonly = false,
public ?bool $required = null, // null (default) = required; false = optional for the signer
public ?string $backgroundColor = null,
public ?FieldMetadata $metadata = null // Conditional (IF/THEN) metadata
) {}
}
// Conditional (IF/THEN) metadata
final class FieldMetadata {
public function __construct(
public ?string $fieldKey = null, // On a controlling checkbox
public ?FieldConditional $conditional = null // On a dependent field
) {}
}
final class FieldConditional {
public function __construct(
public string $controllingFieldKey, // = the checkbox's fieldKey (non-empty)
public ConditionalOperator $operator, // ConditionalOperator::IS_CHECKED | ::IS_NOT_CHECKED
public ConditionalAction $action // ConditionalAction::SHOW | ::UNLOCK
) {}
}
Request Objects
- PHP
final class SendSignatureRequest {
public function __construct(
public array $recipients, // Recipient[]
public array $fields, // Field[]
public ?string $file = null,
public ?string $fileName = null,
public ?string $fileLink = null,
public ?string $deliverableId = null,
public ?string $templateId = null,
public ?string $documentName = null,
public ?string $documentDescription = null,
public ?string $senderName = null,
public ?string $senderEmail = null,
public ?array $ccEmails = null
) {}
}
Exactly one file source is required: file, fileLink, deliverableId, or templateId.
Importing Types
- JavaScript / TypeScript
- Python
import type {
// Field types
SignatureFieldType,
Field,
Recipient,
// Request types
CreateSignatureReviewLinkRequest,
SendSignatureRequest,
} from "@turbodocx/sdk";
from typing import Dict, List, Any, Optional
SignatureFieldType
- JavaScript / TypeScript
- Python
- Ruby
Union type for all available field types:
type SignatureFieldType =
| "signature"
| "initial"
| "date"
| "text"
| "full_name"
| "title"
| "company"
| "first_name"
| "last_name"
| "email"
| "checkbox";
String literal values for field types:
# Available field type values
field_types = [
"signature",
"initial",
"date",
"text",
"full_name",
"title",
"company",
"first_name",
"last_name",
"email",
"checkbox",
]
String values for field types:
# Available field type values
field_types = [
"signature",
"initial",
"date",
"text",
"full_name",
"title",
"company",
"first_name",
"last_name",
"email",
"checkbox"
]
Recipient
- JavaScript / TypeScript
- Python
- Go
- Java
- Ruby
Recipient configuration for signature requests:
| Property | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Recipient's full name |
email | string | Yes | Recipient's email address |
signingOrder | number | Conditional | Signing order (1-indexed). Not needed when role is set |
role | string | No | Template signer role to fill (for example "client"). Requires templateId. See Send a template set up in TurboDocx |
Recipient configuration for signature requests:
| Property | Type | Required | Description |
|---|---|---|---|
name | str | Yes | Recipient's full name |
email | str | Yes | Recipient's email address |
signingOrder | int | Conditional | Signing order (1-indexed). Not needed when role is set |
role | str | No | Template signer role to fill (for example "client"). Requires template_id. See Send a template set up in TurboDocx |
recipient: Dict[str, Any] = {
"name": "John Doe",
"email": "john@example.com",
"signingOrder": 1
}
| Property | Type | Required | Description |
|---|---|---|---|
Name | string | Yes | Recipient's full name |
Email | string | Yes | Recipient's email address |
SigningOrder | int | Conditional | Order in which recipient should sign (1, 2, 3...). Not needed when Role is set |
Role | string | No | Template signer role to fill (for example "client"). Requires TemplateID. See Send a template set up in TurboDocx |
| Property | Type | Required | Description |
|---|---|---|---|
name | String | Yes | Recipient's full name |
email | String | Yes | Recipient's email address |
signingOrder | int | Conditional | Order in which recipient should sign (1, 2, 3...). Not needed when role is set |
role | String | No | Template signer role to fill (for example "client"), set with Recipient.withRole(...) or the builder. Requires templateId. See Send a template set up in TurboDocx |
Recipient configuration for signature requests:
| Property | Type | Required | Description |
|---|---|---|---|
name | String | Yes | Recipient's full name |
email | String | Yes | Recipient's email address |
signingOrder | Integer | Conditional | Signing order (1-indexed). Not needed when role is set |
role | String | No | Template signer role to fill (for example "client"). Requires templateId |
recipient = {
name: "John Doe",
email: "john@example.com",
signingOrder: 1
}
Field
- JavaScript / TypeScript
- Python
- Go
- Java
- Ruby
Field configuration supporting both coordinate-based and template-based positioning:
| Property | Type | Required | Description |
|---|---|---|---|
type | SignatureFieldType | Yes | Field type |
recipientEmail | string | Yes | Which recipient fills this field |
page | number | No* | Page number (1-indexed) |
x | number | No* | X coordinate in pixels |
y | number | No* | Y coordinate in pixels |
width | number | No* | Field width in pixels |
height | number | No* | Field height in pixels |
defaultValue | string | No | Default value (checkbox: "true"/"false"; date: a fixed MM/DD/YYYY, omit to auto-fill the signing date) |
isMultiline | boolean | No | Enable multiline text |
isReadonly | boolean | No | Make field read-only (pre-filled) |
required | boolean | No | Whether field is required |
backgroundColor | string | No | Background color (hex, rgb, or named) |
template | object | No | Template anchor configuration |
metadata | object | No | Conditional (IF/THEN) metadata, see below |
*Required when not using template anchors
Metadata Configuration (Conditional Fields):
The optional metadata object builds IF/THEN relationships between fields. Put a fieldKey on a
controlling checkbox, then point each dependent field's conditional.controllingFieldKey back
at it.
| Property | Type | Required | Description |
|---|---|---|---|
fieldKey | string | No | Stable id on a controlling checkbox (type: "checkbox"). |
conditional | object | No | Rule on a dependent field (see below). |
conditional.controllingFieldKey | string | Yes | The controlling checkbox's fieldKey. Must be non-empty. |
conditional.operator | string | Yes | "is_checked" | "is_not_checked". |
conditional.action | string | Yes | "show" (hidden until met) | "unlock" (locked until met). |
// Checkbox reveals a text field when checked
const fields: Field[] = [
{
type: "checkbox",
recipientEmail: "reviewer@company.com",
page: 1, x: 100, y: 400, width: 20, height: 20,
metadata: { fieldKey: "request_changes" },
},
{
type: "text",
recipientEmail: "reviewer@company.com",
page: 1, x: 130, y: 400, width: 300, height: 60,
metadata: {
conditional: {
controllingFieldKey: "request_changes",
operator: "is_checked",
action: "show",
},
},
},
];
A malformed rule returns 400 InvalidConditionalRule; a well-formed rule whose
controllingFieldKey matches no checkbox fails open (the field stays visible/editable). See
Conditional (IF/THEN) Fields.
Template Configuration:
| Property | Type | Required | Description |
|---|---|---|---|
anchor | string | No† | Text anchor pattern like {TagName} |
searchText | string | No† | Alternative to anchor: search for any text in the document |
placement | string | No | "replace" | "before" | "after" | "above" | "below" |
size | object | No | { width: number; height: number } |
offset | object | No | { x: number; y: number } |
caseSensitive | boolean | No | Case sensitive search (default: false) |
useRegex | boolean | No | Use regex for anchor/searchText (default: false) |
†All template properties are optional in the type definition, but at least one of anchor or searchText must be provided for anchor-based positioning to work.
Field configuration supporting both coordinate-based and template-based positioning:
| Property | Type | Required | Description |
|---|---|---|---|
type | str | Yes | Field type (see SignatureFieldType) |
recipientEmail | str | Yes | Which recipient fills this field |
page | int | No* | Page number (1-indexed) |
x | int | No* | X coordinate in pixels |
y | int | No* | Y coordinate in pixels |
width | int | No* | Field width in pixels |
height | int | No* | Field height in pixels |
defaultValue | str | No | Default value (checkbox: "true"/"false"; date: a fixed MM/DD/YYYY, omit to auto-fill the signing date) |
isMultiline | bool | No | Enable multiline text |
isReadonly | bool | No | Make field read-only (pre-filled) |
required | bool | No | Whether field is required |
backgroundColor | str | No | Background color (hex, rgb, or named) |
template | Dict | No | Template anchor configuration |
metadata | Dict | No | Conditional (IF/THEN) metadata, see below |
*Required when not using template anchors
Metadata Configuration (Conditional Fields):
The optional metadata dict builds IF/THEN relationships between fields. Put a fieldKey on a
controlling checkbox, then point each dependent field's conditional.controllingFieldKey back
at it.
| Property | Type | Required | Description |
|---|---|---|---|
fieldKey | str | No | Stable id on a controlling checkbox (type: "checkbox"). |
conditional | Dict | No | Rule on a dependent field (see below). |
conditional.controllingFieldKey | str | Yes | The controlling checkbox's fieldKey. Must be non-empty. |
conditional.operator | str | Yes | "is_checked" | "is_not_checked". |
conditional.action | str | Yes | "show" (hidden until met) | "unlock" (locked until met). |
# Checkbox reveals a text field when checked
fields = [
{
"type": "checkbox",
"recipientEmail": "reviewer@company.com",
"page": 1, "x": 100, "y": 400, "width": 20, "height": 20,
"metadata": {"fieldKey": "request_changes"},
},
{
"type": "text",
"recipientEmail": "reviewer@company.com",
"page": 1, "x": 130, "y": 400, "width": 300, "height": 60,
"metadata": {
"conditional": {
"controllingFieldKey": "request_changes",
"operator": "is_checked",
"action": "show",
}
},
},
]
A malformed rule returns 400 InvalidConditionalRule; a well-formed rule whose
controllingFieldKey matches no checkbox fails open (the field stays visible/editable). See
Conditional (IF/THEN) Fields.
Template Configuration:
| Property | Type | Required | Description |
|---|---|---|---|
anchor | str | Yes | Text anchor pattern like {TagName} |
placement | str | Yes | "replace" | "before" | "after" | "above" | "below" |
size | Dict | Yes | { "width": int, "height": int } |
offset | Dict | No | { "x": int, "y": int } |
caseSensitive | bool | No | Case sensitive search (default: False) |
useRegex | bool | No | Use regex for anchor/searchText (default: False) |
field: Dict[str, Any] = {
"type": "signature",
"page": 1,
"x": 100,
"y": 500,
"width": 200,
"height": 50,
"recipientEmail": "john@example.com"
}
| Property | Type | Required | Description |
|---|---|---|---|
Type | string | Yes | Field type (see table above) |
RecipientEmail | string | Yes | Email of the recipient who fills this field |
Page | int | No* | Page number (1-indexed) |
X | int | No* | X coordinate in pixels |
Y | int | No* | Y coordinate in pixels |
Width | int | No* | Field width in pixels |
Height | int | No* | Field height in pixels |
DefaultValue | string | No | Pre-filled value (checkbox: "true"/"false"; date: a fixed MM/DD/YYYY, omit to auto-fill the signing date) |
IsMultiline | bool | No | Enable multiline for text fields |
IsReadonly | bool | No | Make field read-only |
Required | bool | No | Make field required |
BackgroundColor | string | No | Background color |
Template | *TemplateAnchor | No | Template anchor configuration |
Metadata | *FieldMetadata | No | Conditional (IF/THEN) metadata, see below |
*Required when not using template anchors
The coordinate-based constructor takes positional arguments in this order: new Field(type, page, x, y, width, height, recipientEmail). For template-based fields, use the extended constructor shown in Using Template-Based Fields.
| Property | Type | Required | Description |
|---|---|---|---|
type | String | Yes | Field type (see table above) |
recipientEmail | String | Yes | Email of the recipient who fills this field |
page | Integer | No* | Page number (1-indexed) |
x | Integer | No* | X coordinate in pixels |
y | Integer | No* | Y coordinate in pixels |
width | Integer | No* | Field width in pixels |
height | Integer | No* | Field height in pixels |
defaultValue | String | No | Pre-filled value (checkbox: "true"/"false"; date: a fixed MM/DD/YYYY, omit to auto-fill the signing date) |
isMultiline | Boolean | No | Enable multiline for text fields |
isReadonly | Boolean | No | Make field read-only |
required | Boolean | No | Make field required |
backgroundColor | String | No | Background color |
template | TemplateAnchor | No | Template anchor configuration |
*Required when not using template anchors
Field configuration supporting both coordinate-based and template-based positioning:
| Property | Type | Required | Description |
|---|---|---|---|
type | String | Yes | Field type (see SignatureFieldType) |
recipientEmail | String | Yes | Which recipient fills this field |
page | Integer | No* | Page number (1-indexed) |
x | Integer | No* | X coordinate in pixels |
y | Integer | No* | Y coordinate in pixels |
width | Integer | No* | Field width in pixels |
height | Integer | No* | Field height in pixels |
defaultValue | String | No | Default value (checkbox: "true"/"false"; date: a fixed MM/DD/YYYY, omit to auto-fill the signing date) |
isMultiline | Boolean | No | Enable multiline text |
isReadonly | Boolean | No | Make field read-only (pre-filled) |
required | Boolean | No | Whether field is required |
backgroundColor | String | No | Background color (hex, rgb, or named) |
template | Hash | No | Template anchor configuration |
metadata | Hash | No | Conditional (IF/THEN) metadata, see below |
*Required when not using template anchors
Metadata Configuration (Conditional Fields):
The optional metadata hash builds IF/THEN relationships between fields. Put a fieldKey on a
controlling checkbox, then point each dependent field's conditional.controllingFieldKey back
at it.
| Property | Type | Required | Description |
|---|---|---|---|
fieldKey | String | No | Stable id on a controlling checkbox (type: "checkbox"). |
conditional | Hash | No | Rule on a dependent field (see below). |
conditional.controllingFieldKey | String | Yes | The controlling checkbox's fieldKey. Must be non-empty. |
conditional.operator | String | Yes | "is_checked" | "is_not_checked". |
conditional.action | String | Yes | "show" (hidden until met) | "unlock" (locked until met). |
# Checkbox reveals a text field when checked
fields = [
{
type: "checkbox",
recipientEmail: "reviewer@company.com",
page: 1, x: 100, y: 400, width: 20, height: 20,
metadata: { fieldKey: "request_changes" }
},
{
type: "text",
recipientEmail: "reviewer@company.com",
page: 1, x: 130, y: 400, width: 300, height: 60,
metadata: {
conditional: {
controllingFieldKey: "request_changes",
operator: "is_checked",
action: "show"
}
}
}
]
A malformed rule returns 400 InvalidConditionalRule; a well-formed rule whose
controllingFieldKey matches no checkbox fails open (the field stays visible/editable). See
Conditional (IF/THEN) Fields.
Template Configuration:
| Property | Type | Required | Description |
|---|---|---|---|
anchor | String | Yes | Text anchor pattern like {TagName} |
placement | String | Yes | "replace" | "before" | "after" | "above" | "below" |
size | Hash | Yes | { width: Integer, height: Integer } |
offset | Hash | No | { x: Integer, y: Integer } |
caseSensitive | Boolean | No | Case sensitive search (default: false) |
useRegex | Boolean | No | Use regex for anchor/searchText (default: false) |
field = {
type: "signature",
page: 1,
x: 100,
y: 500,
width: 200,
height: 50,
recipientEmail: "john@example.com"
}
Metadata Configuration (Conditional Fields)
- Go
The optional Metadata builds IF/THEN relationships between fields. Put a FieldKey on a
controlling checkbox, then point each dependent field's Conditional.ControllingFieldKey back
at it.
| Property | Type | Required | Description |
|---|---|---|---|
FieldKey | string | No | Stable id on a controlling checkbox (Type: "checkbox"). |
Conditional | *FieldConditional | No | Rule on a dependent field (see below). |
Conditional.ControllingFieldKey | string | Yes | The controlling checkbox's FieldKey. Must be non-empty. |
Conditional.Operator | string | Yes | "is_checked" or "is_not_checked". |
Conditional.Action | string | Yes | "show" (hidden until met) or "unlock" (locked until met). |
// Checkbox reveals a text field when checked
fields := []turbodocx.Field{
{
Type: "checkbox",
RecipientEmail: "reviewer@company.com",
Page: 1, X: 100, Y: 400, Width: 20, Height: 20,
Metadata: &turbodocx.FieldMetadata{
FieldKey: "request_changes",
},
},
{
Type: "text",
RecipientEmail: "reviewer@company.com",
Page: 1, X: 130, Y: 400, Width: 300, Height: 60,
Metadata: &turbodocx.FieldMetadata{
Conditional: &turbodocx.FieldConditional{
ControllingFieldKey: "request_changes",
Operator: "is_checked",
Action: "show",
},
},
},
}
A malformed rule returns 400 InvalidConditionalRule; a well-formed rule whose
ControllingFieldKey matches no checkbox fails open (the field stays visible/editable). See
Conditional (IF/THEN) Fields.
Template Configuration
- Go
- Java
When using Template instead of coordinates:
| Property | Type | Required | Description |
|---|---|---|---|
Anchor | string | Yes | Text to find in document (e.g., "{SIGNATURE}") |
Placement | string | Yes | Position relative to anchor: "replace", "before", "after", "above", "below" |
Size | *Size | Yes | Size with Width and Height |
Offset | *Point | No | Offset with X and Y |
CaseSensitive | bool | No | Case-sensitive anchor search |
UseRegex | bool | No | Use regex for anchor search |
When using template instead of coordinates:
| Property | Type | Required | Description |
| --------------- | --------- | -------- | ------------------------------------------------------------------------------------- | --- |
| anchor | String | Yes | Text to find in document (e.g., "{SIGNATURE}") | |
| placement | String | Yes | Position relative to anchor: "replace", "before", "after", "above", "below" |
| size | Size | Yes | Size with width and height |
| offset | Offset | No | Offset with x and y |
| caseSensitive | Boolean | No | Case-sensitive anchor search |
| useRegex | Boolean | No | Use regex for anchor search |
Request Parameters
- Python
- Go
- Java
- Ruby
Request configuration for create_signature_review_link and send_signature methods:
| Parameter | Type | Required | Description |
|---|---|---|---|
recipients | List[Dict] | Yes | Recipients who will sign |
fields | List[Dict] | Conditional | Signature fields configuration. Optional when a recipient has a role |
file | bytes | Conditional | PDF file content as bytes |
file_name | str | No | Original filename (used with file bytes) |
file_link | str | Conditional | URL to document file |
deliverable_id | str | Conditional | TurboDocx deliverable ID |
template_id | str | Conditional | TurboDocx template ID |
document_name | str | No | Document name |
document_description | str | No | Document description |
sender_name | str | No | Sender name (overrides the configured value) |
sender_email | str | No** | Sender / reply-to email (overrides the configured value) |
cc_emails | List[str] | No | Array of CC email addresses |
reminders_enabled | bool | No | Send reminder emails to signers who haven't signed. Off by default |
reminder_delay | Dict | No | {"value": N, "unit": "days"|"hours"}, time to the FIRST reminder |
reminder_interval | Dict | No | {"value": N, "unit": ...}, gap between later reminders |
max_reminders | int | No | Cap per signer. -1 unlimited, 0 none, max 50. Default 5 |
expiration_enabled | bool | No | Close the signing window after expire_after. Off by default |
expire_after | Dict | No | {"value": N, "unit": ...}, how long the document stays signable |
expiration_warning | Dict | No | {"value": N, "unit": ...}, how far before expiry warnings start. 0 = never warn |
expiration_warning_interval | Dict | No | {"value": N, "unit": ...}, gap between warnings once they start |
Each duration {"value", "unit"} uses "days" or "hours"; value is a whole number from 1
up to 999 days / 23976 hours. Reminder and expiration are independent and both off by
default. See Schedule reminders and expiration.
Exactly one file source is required: file, file_link, deliverable_id, or template_id.
** sender_email is optional per call but required at the SDK level for TurboSign: it must be supplied via configure(), the TURBODOCX_SENDER_EMAIL environment variable, or this per-call parameter, otherwise the SDK raises a ValidationError.
Both CreateSignatureReviewLinkRequest and SendSignatureRequest accept:
| Property | Type | Required | Description |
|---|---|---|---|
File | []byte | Conditional | File content as bytes |
FileLink | string | Conditional | URL to document |
DeliverableID | string | Conditional | TurboDocx deliverable ID |
TemplateID | string | Conditional | TurboDocx template ID |
Recipients | []Recipient | Yes | List of recipients |
Fields | []Field | Conditional | List of fields. Optional when a recipient has a Role |
DocumentName | string | No | Document display name |
DocumentDescription | string | No | Document description |
SenderName | string | No | Sender's name |
SenderEmail | string | No | Sender's email |
CCEmails | []string | No | CC email addresses |
Exactly one file source is required: File, FileLink, DeliverableID, or TemplateID.
Both CreateSignatureReviewLinkRequest and SendSignatureRequest accept:
| Property | Type | Required | Description |
|---|---|---|---|
file | byte[] | Conditional | File content as bytes |
fileLink | String | Conditional | URL to document |
deliverableId | String | Conditional | TurboDocx deliverable ID |
templateId | String | Conditional | TurboDocx template ID |
recipients | List<Recipient> | Yes | List of recipients |
fields | List<Field> | Conditional | List of fields. Optional when a recipient has a role |
documentName | String | No | Document display name |
documentDescription | String | No | Document description |
senderName | String | No | Sender's name |
senderEmail | String | No | Sender's email |
ccEmails | List<String> | No | CC email addresses |
schedule | SignatureSchedule | No | Reminder + expiration schedule (see Schedule reminders and expiration) |
Exactly one file source is required: file, fileLink, deliverableId, or templateId.
Request configuration for create_signature_review_link and send_signature methods:
| Parameter | Type | Required | Description |
|---|---|---|---|
recipients | Array<Hash> | Yes | Recipients who will sign |
fields | Array<Hash> | Conditional | Signature fields configuration. Optional when a recipient has a role |
file | String | IO | Conditional | Local file path or an IO object (multipart upload) |
fileName | String | No | File name sent with file (a path upload uses the file's own name by default) |
fileLink | String | Conditional | URL to document file |
deliverableId | String | Conditional | TurboDocx deliverable ID |
templateId | String | Conditional | TurboDocx template ID |
documentName | String | No | Document name |
documentDescription | String | No | Document description |
senderName | String | No | Sender name (overrides the configured value) |
senderEmail | String | No** | Sender / reply-to email (overrides the configured value) |
ccEmails | Array<String> | No | Array of CC email addresses |
remindersEnabled | Boolean | No | Send reminder emails to signers who haven't signed. Off by default |
reminderDelay | Hash | No | { "value" => N, "unit" => "days"|"hours" }, time to the FIRST reminder |
reminderInterval | Hash | No | { "value" => N, "unit" => ... }, gap between later reminders |
maxReminders | Integer | No | Cap per signer. -1 unlimited, 0 none, max 50. Default 5 |
expirationEnabled | Boolean | No | Close the signing window after expireAfter. Off by default |
expireAfter | Hash | No | { "value" => N, "unit" => ... }, how long the document stays signable |
expirationWarning | Hash | No | { "value" => N, "unit" => ... }, how far before expiry warnings start. 0 = never warn |
expirationWarningInterval | Hash | No | { "value" => N, "unit" => ... }, gap between warnings once they start |
Each duration { "value", "unit" } uses "days" or "hours"; value is a whole number from 1
up to 999 days / 23976 hours. Reminder and expiration are independent and both off by
default. See Reminders and expiration.
Exactly one file source is required: file, fileLink, deliverableId, or templateId.
** senderEmail is optional per call but required at the SDK level for TurboSign: it must be supplied via configure, the TURBODOCX_SENDER_EMAIL environment variable, or this per-call parameter, otherwise the SDK raises a TurboDocxSdk::ValidationError.
CreateSignatureReviewLinkRequest / SendSignatureRequest
- JavaScript / TypeScript
Request configuration for createSignatureReviewLink and sendSignature methods:
| Property | Type | Required | Description |
|---|---|---|---|
file | string | File | Buffer | Conditional | Document as a local file path, Buffer, or browser File |
fileName | string | No | Original filename, used when file is a Buffer (defaults to document.<ext>) |
fileLink | string | Conditional | URL to document file |
deliverableId | string | Conditional | TurboDocx deliverable ID |
templateId | string | Conditional | TurboDocx template ID |
recipients | Recipient[] | Yes | Recipients who will sign |
fields | Field[] | Conditional | Signature fields configuration. Optional when a recipient has a role |
documentName | string | No | Document name |
documentDescription | string | No | Document description |
senderName | string | No | Sender name, falls back to senderName in the SDK config, then your API key's name |
senderEmail | string | Conditional | Sender email, required on the request unless supplied via TurboSign.configure({ senderEmail }) or TURBODOCX_SENDER_EMAIL |
ccEmails | string[] | No | Array of CC email addresses |
remindersEnabled | boolean | No | Send reminder emails to signers who haven't signed (default false) |
reminderDelay | Duration | No | { value, unit }, time to the first reminder |
reminderInterval | Duration | No | { value, unit }, gap between later reminders |
maxReminders | number | No | Cap per signer, range -1..50 (-1 unlimited, 0 none, default 5) |
expirationEnabled | boolean | No | Close the signing window after expireAfter (default false) |
expireAfter | Duration | No | { value, unit }, how long the document stays signable |
expirationWarning | Duration | No | { value, unit }, how far before expiry warnings start (0 = never warn) |
expirationWarningInterval | Duration | No | { value, unit }, gap between warnings once they start |
A Duration is { value: number, unit: "hours" | "days" }. value is a whole number from 1 to 999 days (23976 hours).
Exactly one file source is required: file, fileLink, deliverableId, or templateId.
SenderEmailRequired/SenderNameRequired API errorUnlike TurboQuote (where the sender comes from the org quote template and there is no per-request
field), TurboSign expects the sender to come from the request body, TurboSign.configure({ senderEmail }),
or the TURBODOCX_SENDER_EMAIL environment variable. The SDK enforces this itself: TurboSign.configure()
throws a ValidationError if no senderEmail is configured (client-side, before any request is sent).
The API itself does not reject a send that omits a sender: if no sender email or name can be resolved,
it falls back to a generic TurboDocx sender identity rather than returning 400 SenderEmailRequired or
400 SenderNameRequired.
Additional Documentation
For detailed information about advanced configuration and API concepts, see:
Core API References
- Request Body Reference - Complete request body parameters, file sources, and multipart/form-data structure
- Recipients Reference - Recipient properties, signing order, metadata, and configuration options
- Field Types Reference - All available field types (signature, date, text, checkbox, etc.) with properties and behaviors
- Field Positioning Methods - Template-based vs coordinate-based positioning, anchor configuration, and best practices
Resources
- JavaScript / TypeScript
- Python
- PHP
- Go
- Java
- Ruby