SignSecureSignSecure Docs

Complete Workflow

End-to-end API integration covering every action from setup to signed document retrieval.

This guide covers the complete SignSecure API workflow with production-ready examples. Each section is self-contained -- jump to the action you need.

Overview

The typical signing flow follows these steps:

Create API Key → Create Envelope → Upload PDF → Add Recipients
    → (Optional) Add Form Fields → Send for Signing
    → Track Progress → Download Signed PDF

For all examples below, set these variables first:

const API_KEY = process.env.API_KEY;
const BASE = "https://api.signpad.signsecure.in/api/v1";

async function api(path, options = {}) {
  const response = await fetch(`${BASE}${path}`, {
    ...options,
    headers: {
      Authorization: `Bearer ${API_KEY}`,
      "Content-Type": "application/json",
      ...options.headers,
    },
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(`${error.code}: ${error.message}`);
  }

  return response.json();
}
import os
import requests

API_KEY = os.environ["API_KEY"]
BASE = "https://api.signpad.signsecure.in/api/v1"
HEADERS = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
}


def api(method, path, **kwargs):
    response = requests.request(method, f"{BASE}{path}", headers=HEADERS, **kwargs)
    response.raise_for_status()
    return response.json()
$apiKey = getenv('API_KEY');
$base = 'https://api.signpad.signsecure.in/api/v1';

function api(string $method, string $path, ?array $body = null): array {
    global $apiKey, $base;

    $ch = curl_init("$base$path");
    curl_setopt_array($ch, [
        CURLOPT_CUSTOMREQUEST  => $method,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER     => [
            "Authorization: Bearer $apiKey",
            'Content-Type: application/json',
        ],
        CURLOPT_POSTFIELDS => $body ? json_encode($body) : null,
    ]);

    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($httpCode >= 400) {
        $error = json_decode($response, true);
        throw new Exception("{$error['code']}: {$error['message']}");
    }

    return json_decode($response, true);
}
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "os"
)

var (
    apiKey = os.Getenv("API_KEY")
    base   = "https://api.signpad.signsecure.in/api/v1"
)

func api(method, path string, body interface{}) (map[string]interface{}, error) {
    var reqBody io.Reader
    if body != nil {
        b, _ := json.Marshal(body)
        reqBody = bytes.NewBuffer(b)
    }

    req, _ := http.NewRequest(method, base+path, reqBody)
    req.Header.Set("Authorization", "Bearer "+apiKey)
    req.Header.Set("Content-Type", "application/json")

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    var result map[string]interface{}
    json.NewDecoder(resp.Body).Decode(&result)

    if resp.StatusCode >= 400 {
        return nil, fmt.Errorf("%s: %s", result["code"], result["message"])
    }

    return result, nil
}

Envelopes

Create an Envelope

Creates a new envelope in draft status and returns a presigned upload URL.

const envelope = await api("/envelopes", {
  method: "POST",
  body: JSON.stringify({
    fileName: "contract.pdf",
    fileType: "application/pdf",
    fileSize: 52400, // bytes
    title: "Service Agreement",
  }),
});

// envelope.id        → "env_abc123"
// envelope.uploadUrl → presigned S3 URL
// envelope.status    → "draft"
envelope = api("POST", "/envelopes", json={
    "fileName": "contract.pdf",
    "fileType": "application/pdf",
    "fileSize": 52400,
    "title": "Service Agreement",
})

# envelope["id"]        → "env_abc123"
# envelope["uploadUrl"] → presigned S3 URL
# envelope["status"]    → "draft"
$envelope = api('POST', '/envelopes', [
    'fileName' => 'contract.pdf',
    'fileType' => 'application/pdf',
    'fileSize' => 52400,
    'title'    => 'Service Agreement',
]);

// $envelope['id']        → "env_abc123"
// $envelope['uploadUrl'] → presigned S3 URL
envelope, _ := api("POST", "/envelopes", map[string]interface{}{
    "fileName": "contract.pdf",
    "fileType": "application/pdf",
    "fileSize": 52400,
    "title":    "Service Agreement",
})

envelopeID := envelope["id"].(string)
uploadURL := envelope["uploadUrl"].(string)

Upload the PDF

Upload the PDF to the presigned URL. Only application/pdf files up to 10 MB are accepted.

import { readFileSync } from "node:fs";

await fetch(envelope.uploadUrl, {
  method: "PUT",
  headers: { "Content-Type": "application/pdf" },
  body: readFileSync("contract.pdf"),
});
with open("contract.pdf", "rb") as f:
    requests.put(
        envelope["uploadUrl"],
        headers={"Content-Type": "application/pdf"},
        data=f.read(),
    )
$ch = curl_init($envelope['uploadUrl']);
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/pdf'],
    CURLOPT_POSTFIELDS     => file_get_contents('contract.pdf'),
]);
curl_exec($ch);
curl_close($ch);
fileData, _ := os.ReadFile("contract.pdf")
req, _ := http.NewRequest("PUT", uploadURL, bytes.NewReader(fileData))
req.Header.Set("Content-Type", "application/pdf")
http.DefaultClient.Do(req)

List Envelopes

Retrieve all envelopes for the current workspace.

const envelopes = await api("/envelopes");
// envelopes → array of envelope objects
envelopes = api("GET", "/envelopes")
$envelopes = api('GET', '/envelopes');
envelopes, _ := api("GET", "/envelopes", nil)

Get an Envelope

Retrieve a single envelope by ID.

const detail = await api(`/envelopes/${envelopeId}`);
detail = api("GET", f"/envelopes/{envelope_id}")
$detail = api('GET', "/envelopes/$envelopeId");
detail, _ := api("GET", "/envelopes/"+envelopeID, nil)

Update an Envelope

Update the title of a draft envelope.

const updated = await api(`/envelopes/${envelopeId}`, {
  method: "PATCH",
  body: JSON.stringify({ title: "Updated Agreement" }),
});
updated = api("PATCH", f"/envelopes/{envelope_id}", json={
    "title": "Updated Agreement",
})
$updated = api('PATCH', "/envelopes/$envelopeId", [
    'title' => 'Updated Agreement',
]);
updated, _ := api("PATCH", "/envelopes/"+envelopeID, map[string]interface{}{
    "title": "Updated Agreement",
})

Delete an Envelope

Delete a draft envelope. Only envelopes in draft status can be deleted.

await api(`/envelopes/${envelopeId}`, { method: "DELETE" });
api("DELETE", f"/envelopes/{envelope_id}")
api('DELETE', "/envelopes/$envelopeId");
api("DELETE", "/envelopes/"+envelopeID, nil)

Get Download URL

Get a temporary download URL for the envelope's PDF (original or signed).

const download = await api(`/envelopes/${envelopeId}/file`);
// download.url       → temporary S3 URL (valid 1 hour)
// download.fileName  → "contract.pdf"
download = api("GET", f"/envelopes/{envelope_id}/file")
# download["url"] → temporary S3 URL (valid 1 hour)
$download = api('GET', "/envelopes/$envelopeId/file");
// $download['url'] → temporary S3 URL (valid 1 hour)
download, _ := api("GET", "/envelopes/"+envelopeID+"/file", nil)
// download["url"] → temporary S3 URL (valid 1 hour)

Recipients

Add Recipients

Add one or more recipients to a draft envelope.

const result = await api(`/envelopes/${envelopeId}/recipients`, {
  method: "POST",
  body: JSON.stringify({
    recipients: [
      {
        name: "Jane Doe",
        email: "jane@example.com",
        role: "signer",
        order: 1,
        signatureMethod: "electronic",
      },
      {
        name: "Bob Smith",
        email: "bob@example.com",
        role: "approver",
        order: 2,
      },
      {
        name: "Alice CC",
        email: "alice@example.com",
        role: "cc",
      },
    ],
  }),
});
result = api("POST", f"/envelopes/{envelope_id}/recipients", json={
    "recipients": [
        {
            "name": "Jane Doe",
            "email": "jane@example.com",
            "role": "signer",
            "order": 1,
            "signatureMethod": "electronic",
        },
        {
            "name": "Bob Smith",
            "email": "bob@example.com",
            "role": "approver",
            "order": 2,
        },
        {
            "name": "Alice CC",
            "email": "alice@example.com",
            "role": "cc",
        },
    ]
})
$result = api('POST', "/envelopes/$envelopeId/recipients", [
    'recipients' => [
        [
            'name'  => 'Jane Doe',
            'email' => 'jane@example.com',
            'role'  => 'signer',
            'order' => 1,
            'signatureMethod' => 'electronic',
        ],
        [
            'name'  => 'Bob Smith',
            'email' => 'bob@example.com',
            'role'  => 'approver',
            'order' => 2,
        ],
        [
            'name'  => 'Alice CC',
            'email' => 'alice@example.com',
            'role'  => 'cc',
        ],
    ],
]);
result, _ := api("POST", "/envelopes/"+envelopeID+"/recipients", map[string]interface{}{
    "recipients": []map[string]interface{}{
        {"name": "Jane Doe", "email": "jane@example.com", "role": "signer", "order": 1, "signatureMethod": "electronic"},
        {"name": "Bob Smith", "email": "bob@example.com", "role": "approver", "order": 2},
        {"name": "Alice CC", "email": "alice@example.com", "role": "cc"},
    },
})

Roles and Signature Methods

RoleDescription
signerMust sign the document
approverMust approve (no signature)
ccReceives a copy only
MethodCostDescriptionText Placement
electronicFreeDraw or type a signatureSupported
aadhaar_otpCreditsAadhaar eSign with OTPSupported (auto-resolved to coordinates)
dsc_usbFreeUSB digital certificateNot supported (coordinates only)
allVariesRecipient chooses at signingSupported (coordinates recommended)

List Recipients

const recipients = await api(`/envelopes/${envelopeId}/recipients`);
recipients = api("GET", f"/envelopes/{envelope_id}/recipients")
$recipients = api('GET', "/envelopes/$envelopeId/recipients");
recipients, _ := api("GET", "/envelopes/"+envelopeID+"/recipients", nil)

Remove a Recipient

await api(`/envelopes/${envelopeId}/recipients/${recipientId}`, {
  method: "DELETE",
});
api("DELETE", f"/envelopes/{envelope_id}/recipients/{recipient_id}")
api('DELETE', "/envelopes/$envelopeId/recipients/$recipientId");
api("DELETE", "/envelopes/"+envelopeID+"/recipients/"+recipientID, nil)

Form Fields

Place interactive fields on the PDF for recipients to fill out during signing.

Save Form Fields

const fields = await api(`/envelopes/${envelopeId}/fields`, {
  method: "POST",
  body: JSON.stringify({
    fields: [
      {
        recipientId: "rec_abc123",
        type: "signature",
        page: 1,
        x: 100,
        y: 600,
        width: 200,
        height: 50,
        required: true,
      },
      {
        recipientId: "rec_abc123",
        type: "date",
        page: 1,
        x: 100,
        y: 660,
        width: 150,
        height: 30,
        required: true,
      },
      {
        recipientId: "rec_def456",
        type: "signature",
        page: 2,
        x: 100,
        y: 600,
        width: 200,
        height: 50,
        required: true,
      },
    ],
  }),
});
fields = api("POST", f"/envelopes/{envelope_id}/fields", json={
    "fields": [
        {
            "recipientId": "rec_abc123",
            "type": "signature",
            "page": 1,
            "x": 100, "y": 600,
            "width": 200, "height": 50,
            "required": True,
        },
        {
            "recipientId": "rec_abc123",
            "type": "date",
            "page": 1,
            "x": 100, "y": 660,
            "width": 150, "height": 30,
            "required": True,
        },
        {
            "recipientId": "rec_def456",
            "type": "signature",
            "page": 2,
            "x": 100, "y": 600,
            "width": 200, "height": 50,
            "required": True,
        },
    ]
})
$fields = api('POST', "/envelopes/$envelopeId/fields", [
    'fields' => [
        [
            'recipientId' => 'rec_abc123',
            'type' => 'signature',
            'page' => 1,
            'x' => 100, 'y' => 600,
            'width' => 200, 'height' => 50,
            'required' => true,
        ],
        [
            'recipientId' => 'rec_abc123',
            'type' => 'date',
            'page' => 1,
            'x' => 100, 'y' => 660,
            'width' => 150, 'height' => 30,
            'required' => true,
        ],
        [
            'recipientId' => 'rec_def456',
            'type' => 'signature',
            'page' => 2,
            'x' => 100, 'y' => 600,
            'width' => 200, 'height' => 50,
            'required' => true,
        ],
    ],
]);
fields, _ := api("POST", "/envelopes/"+envelopeID+"/fields", map[string]interface{}{
    "fields": []map[string]interface{}{
        {"recipientId": "rec_abc123", "type": "signature", "page": 1, "x": 100, "y": 600, "width": 200, "height": 50, "required": true},
        {"recipientId": "rec_abc123", "type": "date", "page": 1, "x": 100, "y": 660, "width": 150, "height": 30, "required": true},
        {"recipientId": "rec_def456", "type": "signature", "page": 2, "x": 100, "y": 600, "width": 200, "height": 50, "required": true},
    },
})

Field Types

TypeDescription
signatureSignature capture area
textSingle-line text input
textareaMulti-line text input
emailEmail address field
numberNumeric input
dateDate picker
checkboxCheckbox
radioRadio button group
dropdownDropdown selection

List Form Fields

const fields = await api(`/envelopes/${envelopeId}/fields`);
fields = api("GET", f"/envelopes/{envelope_id}/fields")
$fields = api('GET', "/envelopes/$envelopeId/fields");
fields, _ := api("GET", "/envelopes/"+envelopeID+"/fields", nil)

Get Filled Values

Retrieve the values recipients have filled in after signing.

const values = await api(`/envelopes/${envelopeId}/fields/values`);
// values → array of { fieldId, recipientId, value, filledAt }
values = api("GET", f"/envelopes/{envelope_id}/fields/values")
$values = api('GET', "/envelopes/$envelopeId/fields/values");
values, _ := api("GET", "/envelopes/"+envelopeID+"/fields/values", nil)

Signing Workflow

Send Envelope

Send the envelope to all recipients to begin the signing process. No request body is needed -- workflow settings are configured during envelope creation via POST /envelopes.

const result = await api(`/envelopes/${envelopeId}/send`, {
  method: "POST",
});
result = api("POST", f"/envelopes/{envelope_id}/send")
$result = api('POST', "/envelopes/$envelopeId/send");
result, _ := api("POST", "/envelopes/"+envelopeID+"/send", nil)

Check Signing Progress

const status = await api(`/envelopes/${envelopeId}/status`);

console.log(status.status);         // "pending" | "completed"
console.log(status.percentage);     // 50
console.log(status.completedCount); // 1
console.log(status.totalRecipients); // 2

for (const r of status.recipients) {
  console.log(`${r.name}: ${r.status}`); // "signed" | "pending"
}
status = api("GET", f"/envelopes/{envelope_id}/status")

print(status["status"])          # "pending" | "completed"
print(status["percentage"])      # 50
print(status["completedCount"])  # 1

for r in status["recipients"]:
    print(f"{r['name']}: {r['status']}")
$status = api('GET', "/envelopes/$envelopeId/status");

echo $status['status'];         // "pending" | "completed"
echo $status['percentage'];     // 50

foreach ($status['recipients'] as $r) {
    echo "{$r['name']}: {$r['status']}\n";
}
status, _ := api("GET", "/envelopes/"+envelopeID+"/status", nil)

fmt.Println(status["status"])     // "pending" | "completed"
fmt.Println(status["percentage"]) // 50

Send a Reminder

Send a reminder email to recipients who haven't signed yet.

await api(`/envelopes/${envelopeId}/remind`, { method: "POST" });
api("POST", f"/envelopes/{envelope_id}/remind")
api('POST', "/envelopes/$envelopeId/remind");
api("POST", "/envelopes/"+envelopeID+"/remind", nil)

Cancel Signing

Cancel a pending envelope. Recipients will no longer be able to sign.

await api(`/envelopes/${envelopeId}/cancel`, { method: "POST" });
api("POST", f"/envelopes/{envelope_id}/cancel")
api('POST', "/envelopes/$envelopeId/cancel");
api("POST", "/envelopes/"+envelopeID+"/cancel", nil)

Templates

Templates let you save envelope configurations (recipients, fields, settings) for reuse.

Create a Template

const template = await api("/templates", {
  method: "POST",
  body: JSON.stringify({
    name: "NDA Template",
    description: "Standard non-disclosure agreement",
  }),
});
template = api("POST", "/templates", json={
    "name": "NDA Template",
    "description": "Standard non-disclosure agreement",
})
$template = api('POST', '/templates', [
    'name' => 'NDA Template',
    'description' => 'Standard non-disclosure agreement',
]);
template, _ := api("POST", "/templates", map[string]interface{}{
    "name":        "NDA Template",
    "description": "Standard non-disclosure agreement",
})

Create Envelope from Template

Create a new envelope pre-configured with the template's settings.

const envelope = await api(`/templates/${templateId}/create-envelope`, {
  method: "POST",
  body: JSON.stringify({
    title: "NDA - Acme Corp",
    fileName: "nda-acme.pdf",
    fileType: "application/pdf",
    fileSize: 34200,
  }),
});

// Continue with upload → add recipients → send
envelope = api("POST", f"/templates/{template_id}/create-envelope", json={
    "title": "NDA - Acme Corp",
    "fileName": "nda-acme.pdf",
    "fileType": "application/pdf",
    "fileSize": 34200,
})
$envelope = api('POST', "/templates/$templateId/create-envelope", [
    'title'    => 'NDA - Acme Corp',
    'fileName' => 'nda-acme.pdf',
    'fileType' => 'application/pdf',
    'fileSize' => 34200,
]);
envelope, _ := api("POST", "/templates/"+templateID+"/create-envelope", map[string]interface{}{
    "title":    "NDA - Acme Corp",
    "fileName": "nda-acme.pdf",
    "fileType": "application/pdf",
    "fileSize": 34200,
})

Other Template Operations

ActionMethodEndpoint
List templatesGET/templates
Get templateGET/templates/{id}
Update templatePATCH/templates/{id}
Delete templateDELETE/templates/{id}
Duplicate templatePOST/templates/{id}/duplicate

Credits

Check Balance

const balance = await api("/credits/balance");
console.log(`${balance.available} credits available`);
balance = api("GET", "/credits/balance")
print(f"{balance['available']} credits available")
$balance = api('GET', '/credits/balance');
echo "{$balance['available']} credits available";
balance, _ := api("GET", "/credits/balance", nil)
fmt.Printf("%.0f credits available\n", balance["available"])

Get Usage Stats

const usage = await api("/credits/usage");
usage = api("GET", "/credits/usage")
$usage = api('GET', '/credits/usage');
usage, _ := api("GET", "/credits/usage", nil)

List Transactions

const transactions = await api("/credits/transactions");
transactions = api("GET", "/credits/transactions")
$transactions = api('GET', '/credits/transactions');
transactions, _ := api("GET", "/credits/transactions", nil)

Full Example: End-to-End Signing

Here is the complete flow in a single script.

import { readFileSync } from "node:fs";

const API_KEY = process.env.API_KEY;
const BASE = "https://api.signpad.signsecure.in/api/v1";

async function api(path, options = {}) {
  const res = await fetch(`${BASE}${path}`, {
    ...options,
    headers: {
      Authorization: `Bearer ${API_KEY}`,
      "Content-Type": "application/json",
      ...options.headers,
    },
  });
  if (!res.ok) throw new Error(`API error: ${res.status}`);
  return res.json();
}

async function main() {
  // 1. Create envelope with workflow settings
  const envelope = await api("/envelopes", {
    method: "POST",
    body: JSON.stringify({
      fileName: "contract.pdf",
      fileType: "application/pdf",
      fileSize: readFileSync("contract.pdf").length,
      title: "Service Agreement",
      workflow: {
        mode: "parallel",
        verificationMethod: "email_verification",
        message: "Please sign this agreement.",
        emailNotifications: "all",
      },
    }),
  });
  console.log("Created:", envelope.id);

  // 2. Upload PDF
  await fetch(envelope.uploadUrl, {
    method: "PUT",
    headers: { "Content-Type": "application/pdf" },
    body: readFileSync("contract.pdf"),
  });
  console.log("Uploaded PDF");

  // 3. Add recipients
  await api(`/envelopes/${envelope.id}/recipients`, {
    method: "POST",
    body: JSON.stringify({
      recipients: [
        {
          name: "Jane Doe",
          email: "jane@example.com",
          role: "signer",
          order: 1,
          signatureMethod: "electronic",
        },
      ],
    }),
  });
  console.log("Added recipients");

  // 4. Send for signing (no body needed -- workflow was set during creation)
  await api(`/envelopes/${envelope.id}/send`, {
    method: "POST",
  });
  console.log("Sent for signing");

  // 5. Poll until complete
  let status;
  do {
    await new Promise((r) => setTimeout(r, 5000));
    status = await api(`/envelopes/${envelope.id}/status`);
    console.log(`Progress: ${status.percentage}%`);
  } while (status.status !== "completed");

  // 6. Download signed PDF
  const download = await api(`/envelopes/${envelope.id}/file`);
  console.log("Download URL:", download.url);
}

main().catch(console.error);
import os
import time
import requests

API_KEY = os.environ["API_KEY"]
BASE = "https://api.signpad.signsecure.in/api/v1"
HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}


def api(method, path, **kwargs):
    r = requests.request(method, f"{BASE}{path}", headers=HEADERS, **kwargs)
    r.raise_for_status()
    return r.json()


def main():
    # 1. Create envelope with workflow settings
    file_data = open("contract.pdf", "rb").read()
    envelope = api("POST", "/envelopes", json={
        "fileName": "contract.pdf",
        "fileType": "application/pdf",
        "fileSize": len(file_data),
        "title": "Service Agreement",
        "workflow": {
            "mode": "parallel",
            "verificationMethod": "email_verification",
            "message": "Please sign this agreement.",
            "emailNotifications": "all",
        },
    })
    print(f"Created: {envelope['id']}")

    # 2. Upload PDF
    requests.put(
        envelope["uploadUrl"],
        headers={"Content-Type": "application/pdf"},
        data=file_data,
    )
    print("Uploaded PDF")

    # 3. Add recipients
    api("POST", f"/envelopes/{envelope['id']}/recipients", json={
        "recipients": [
            {
                "name": "Jane Doe",
                "email": "jane@example.com",
                "role": "signer",
                "order": 1,
                "signatureMethod": "electronic",
            },
        ]
    })
    print("Added recipients")

    # 4. Send for signing (no body needed -- workflow was set during creation)
    api("POST", f"/envelopes/{envelope['id']}/send")
    print("Sent for signing")

    # 5. Poll until complete
    while True:
        time.sleep(5)
        status = api("GET", f"/envelopes/{envelope['id']}/status")
        print(f"Progress: {status['percentage']}%")
        if status["status"] == "completed":
            break

    # 6. Download signed PDF
    download = api("GET", f"/envelopes/{envelope['id']}/file")
    print(f"Download URL: {download['url']}")


if __name__ == "__main__":
    main()

For production, replace polling with Webhooks to get notified instantly when signing events occur.

Next Steps

  • API Introduction -- authentication, rate limits, error handling
  • Webhooks -- real-time event notifications
  • Getting Started -- step-by-step first envelope tutorial
  • Browse the endpoint reference in the sidebar for request/response schemas

On this page