Skip to main content

TurboSign Go SDK

Agent Skill

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.

bash — turbodocx
$npx skills add TurboDocx/quickstart
# then, inside your agent:/turbodocx-sdk turbosign

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.

Installation

go get github.com/TurboDocx/SDK/packages/go-sdk

Requirements

  • Go 1.21+

Configuration

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
}

Environment Variables

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)

Quick Start

Send a Document for Signature

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

Using Template-Based Fields

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},
},
},
},
})
Template Anchors Required

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

The SDK supports multiple ways to provide your document:

1. File Upload ([]byte)

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"},
},
})

2. File URL

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"},
},
})
When to use FileLink

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

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"},
},
})
Integration with TurboDocx

DeliverableID references documents generated using TurboDocx's document generation API. This creates a seamless workflow: generate → sign.

4. TurboDocx Template ID

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"},
},
})
Integration with TurboDocx

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.


API Reference

Configure

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
})
API Credentials Required

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.

Prepare for review

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

Prepare for signing

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"},
},
})

Schedule reminders and expiration

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
},
})
FieldTypeNotes
RemindersEnabled*boolMaster switch for automatic reminders. Default off.
ReminderDelay*DurationTime to the first reminder, measured from that signer's invitation.
ReminderInterval*DurationGap between subsequent reminders.
MaxReminders*intAutomatic reminders per signer. Valid range -1..50-1 unlimited, 0 none, default 5.
ExpirationEnabled*boolMaster switch for the signing deadline. Default off.
ExpireAfter*DurationHow long the document stays signable, counted from sending.
ExpirationWarning*DurationHow far before expiry warnings start. 0 = never warn.
ExpirationWarningInterval*DurationGap 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.

Get status

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)

Get recipients

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)
}
Two status fields, and they differ on purpose

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 suggest

reminderCount 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

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

Get audit trail

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

Void

Cancel/void a signature request.

result, err := client.TurboSign.VoidDocument(ctx, "document-uuid", "Contract terms changed")

Resend

Resend signature request emails.

// Resend to specific recipients
result, err := client.TurboSign.ResendEmail(ctx, "document-uuid", []string{"recipient-uuid-1", "recipient-uuid-2"})

Send reminder

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.


Error Handling

The SDK provides typed errors for different error scenarios:

Error Types

Error TypeStatus CodeDescription
TurboDocxErrorvariesBase error type for all API errors
AuthenticationError401Invalid or missing API key
AuthorizationError403Authenticated but lacks required permissions
ValidationError400Invalid request parameters
NotFoundError404Resource not found
RateLimitError429Too many requests
NetworkError-Network connectivity issues

Error Properties

PropertyTypeDescription
MessagestringHuman-readable error message
StatusCodeintHTTP status code
CodestringError code (if available)

Example

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 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, &notFoundErr):
log.Printf("Not found: %s", notFoundErr.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)
}
}
}

Types

Signature Field Types

The Type field accepts the following string values:

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

Recipient

PropertyTypeRequiredDescription
NamestringYesRecipient's full name
EmailstringYesRecipient's email address
SigningOrderintYesOrder in which recipient should sign (1, 2, 3...)

Field

PropertyTypeRequiredDescription
TypestringYesField type (see table above)
RecipientEmailstringYesEmail of the recipient who fills this field
PageintNo*Page number (1-indexed)
XintNo*X coordinate in pixels
YintNo*Y coordinate in pixels
WidthintNo*Field width in pixels
HeightintNo*Field height in pixels
DefaultValuestringNoPre-filled value (checkbox: "true"/"false"; date: a fixed MM/DD/YYYY, omit to auto-fill the signing date)
IsMultilineboolNoEnable multiline for text fields
IsReadonlyboolNoMake field read-only
RequiredboolNoMake field required
BackgroundColorstringNoBackground color
Template*TemplateAnchorNoTemplate anchor configuration
Metadata*FieldMetadataNoConditional (IF/THEN) metadata — see below

*Required when not using template anchors

Metadata Configuration (Conditional Fields)

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.

PropertyTypeRequiredDescription
FieldKeystringNoStable id on a controlling checkbox (Type: "checkbox").
Conditional*FieldConditionalNoRule on a dependent field (see below).
Conditional.ControllingFieldKeystringYesThe controlling checkbox's FieldKey. Must be non-empty.
Conditional.OperatorstringYes"is_checked" or "is_not_checked".
Conditional.ActionstringYes"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

When using Template instead of coordinates:

PropertyTypeRequiredDescription
AnchorstringYesText to find in document (e.g., "{SIGNATURE}")
PlacementstringYesPosition relative to anchor: "replace", "before", "after", "above", "below"
Size*SizeYesSize with Width and Height
Offset*PointNoOffset with X and Y
CaseSensitiveboolNoCase-sensitive anchor search
UseRegexboolNoUse regex for anchor search

Request Parameters

Both CreateSignatureReviewLinkRequest and SendSignatureRequest accept:

PropertyTypeRequiredDescription
File[]byteConditionalFile content as bytes
FileLinkstringConditionalURL to document
DeliverableIDstringConditionalTurboDocx deliverable ID
TemplateIDstringConditionalTurboDocx template ID
Recipients[]RecipientYesList of recipients
Fields[]FieldYesList of fields
DocumentNamestringNoDocument display name
DocumentDescriptionstringNoDocument description
SenderNamestringNoSender's name
SenderEmailstringNoSender's email
CCEmails[]stringNoCC email addresses
File Source (Conditional)

Exactly one file source is required: File, FileLink, DeliverableID, or TemplateID.


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