Create and send your first envelope
This tutorial walks you through creating and sending an envelope via the API. You’ll learn how to:
- Upload a document and create an envelope
- Add recipients
- Add blocks (signatures, text, images)
- Send the envelope
Prerequisites
Section titled “Prerequisites”- A Subnoto workspace with API credentials (access key and secret key)
- A programming language with HTTP client support (examples shown in Node.js)
Note: This tutorial presents examples in Node.js, but the Subnoto API can be used from any programming language. The API is language-agnostic and uses standard HTTP requests and JSON payloads. You can adapt these examples to Python, Go, Ruby, PHP, or any other language that supports HTTP requests.
-
Upload Document and Create Envelope
Upload your document using the
create-from-fileendpoint. This endpoint accepts multipart/form-data and handles encryption, upload, and document processing automatically.Using curl:
Terminal window curl -X POST https://enclave.subnoto.com/public/envelope/create-from-file \-H "Authorization: Bearer $ACCESS_KEY:$SECRET_KEY" \-F "workspaceUuid=your-workspace-uuid" \-F "envelopeTitle=My First Envelope" \-F "file=@/path/to/document.pdf"Using Node.js with fetch:
import FormData from "form-data";import { createReadStream } from "fs";const API_BASE_URL = "https://enclave.subnoto.com";const ACCESS_KEY = "your-access-key";const SECRET_KEY = "your-secret-key";const WORKSPACE_UUID = "your-workspace-uuid";// Create FormData with file and metadataconst formData = new FormData();formData.append("workspaceUuid", WORKSPACE_UUID);formData.append("envelopeTitle", "My First Envelope");formData.append("file", createReadStream("path/to/document.pdf"));// Create envelope and upload documentconst createResponse = await fetch(`${API_BASE_URL}/public/envelope/create-from-file`, {method: "POST",headers: {Authorization: `Bearer ${ACCESS_KEY}:${SECRET_KEY}`,...formData.getHeaders()},body: formData});if (!createResponse.ok) {throw new Error(`Failed to create envelope: ${createResponse.status}`);}const createResult = await createResponse.json();const { envelopeUuid, documentUuid } = createResult;console.log("Envelope UUID:", envelopeUuid);console.log("Document UUID:", documentUuid);Using Node.js with the
@subnoto/api-clientSDK:import { SubnotoClient } from "@subnoto/api-client";import FormData from "form-data";import { createReadStream } from "fs";const client = new SubnotoClient({apiBaseUrl: "https://enclave.subnoto.com",accessKey: process.env.API_ACCESS_KEY,secretKey: process.env.API_SECRET_KEY});const formData = new FormData();formData.append("workspaceUuid", "your-workspace-uuid");formData.append("envelopeTitle", "My First Envelope");formData.append("file", createReadStream("path/to/document.pdf"));const { data, error } = await client.POST("/public/envelope/create-from-file", {body: formData,headers: {...formData.getHeaders()}});if (error) {throw new Error(`Failed to create envelope: ${error}`);}const { envelopeUuid, documentUuid } = data;console.log("Envelope UUID:", envelopeUuid);console.log("Document UUID:", documentUuid);Response:
The endpoint returns a JSON object with:
envelopeUuid(string): The unique identifier for your envelopedocumentUuid(string): The unique identifier for the document
What happens automatically:
- Document encryption
- File upload to storage
- PDF validation and preview generation
- Setting envelope status to
draft
Supported file types:
- PDF files (
.pdf) - MIME type:application/pdf - Word documents (
.docx,.doc) - MIME types:application/vnd.openxmlformats-officedocument.wordprocessingml.documentorapplication/msword- automatically converted to PDF
File size limits:
- Maximum file size: 50 MB
Required dependencies:
For Node.js examples, install the required dependency for multipart form data:
Terminal window npm install form-data -
Add Recipients
Add recipients to your envelope. Recipients can be added as manual entries, contacts, or workspace members:
const addRecipientsResponse = await fetch(`${API_BASE_URL}/public/envelope/add-recipients`, {method: "POST",headers: {Authorization: `Bearer ${ACCESS_KEY}:${SECRET_KEY}`,"Content-Type": "application/json"},body: JSON.stringify({workspaceUuid: WORKSPACE_UUID,envelopeUuid,recipients: [{type: "manual",firstname: "John",lastname: "Doe"}]})});const recipientsResult = await addRecipientsResponse.json();You can add multiple recipients (up to 50) at once. Each recipient can be:
type: 'manual'- Manual entry with email, firstname, lastnametype: 'contact'- Existing contact usingcontactUuidtype: 'member'- Workspace member usinguserUuid
-
Add Blocks
Add blocks to your document. Blocks can be signatures, text, or images:
Tip: Use the Block Preview Tool to visually place blocks on your PDF and get the exact coordinates (page, x, y, width, height) before making API calls. This tool helps you preview block placement and copy the JSON format directly for your API requests.
const addBlocksResponse = await fetch(`${API_BASE_URL}/public/envelope/add-blocks`, {method: "POST",headers: {Authorization: `Bearer ${ACCESS_KEY}:${SECRET_KEY}`,"Content-Type": "application/json"},body: JSON.stringify({workspaceUuid: WORKSPACE_UUID,envelopeUuid,documentUuid,blocks: [{type: "signature",page: "1",x: 100,y: 200,},{type: "text",page: "1",x: 100,y: 250,text: "Please sign here",width: 200,height: 30}]})});const blocksResult = await addBlocksResponse.json();Block Types
{type: 'signature',page: '1', // Page number as stringx: 100, // X position in pointsy: 200, // Y position in points}{type: 'text',page: '1',x: 100,y: 200,text: 'Your text here',width: 200, // Optionalheight: 30, // OptionaltemplatedText: 'email' // Optional: 'email', 'fullname', or 'phone'}{type: 'image',page: '1',x: 100,y: 200,src: 'data:image/png;base64,iVBORw0KGgo...', // Base64 encoded (max 256KB)fileType: 'image/png',width: 100, // Optionalheight: 100 // Optional} -
Send the Envelope
Finally, send the envelope to all recipients:
const sendResponse = await fetch(`${API_BASE_URL}/public/envelope/send`, {method: "POST",headers: {Authorization: `Bearer ${ACCESS_KEY}:${SECRET_KEY}`,"Content-Type": "application/json"},body: JSON.stringify({workspaceUuid: WORKSPACE_UUID,envelopeUuid,useUserAsSenderName: false, // Optional: use user name instead of company namecustomInvitationMessage: "Please review and sign this document" // Optional: custom message})});if (!sendResponse.ok) {throw new Error(`Failed to send envelope: ${sendResponse.status}`);} -
Track Envelope Events with Webhooks
After sending an envelope, you can track signing events and completion using webhooks. Webhooks allow you to receive real-time notifications when recipients sign documents or when envelopes are completed.
Setting Up Webhooks
- Log into your Subnoto workspace at app.subnoto.com
- Navigate to Settings → Webhooks
- Create a webhook with your endpoint URL
- Configure the webhook to receive
ENVELOPE_SIGNEDandENVELOPE_COMPLETEDevents
For detailed webhook configuration, see the Webhooks Setup Guide.
Available Events
- ENVELOPE_SIGNED: Triggered each time a recipient signs the document. Use this to track individual signing events.
- ENVELOPE_COMPLETED: Triggered when the envelope is fully completed and all required signatures have been collected. Use this to know when the envelope process is complete.
Webhook Payload Example
When a recipient signs the document, you’ll receive an
ENVELOPE_SIGNEDevent:{"eventUuid": "0f29a0a2-3a6f-44a4-b2a2-5d7b3f1b1f9b","eventType": "ENVELOPE_SIGNED","eventTime": 1704067200000,"webhookUuid": "webhook-uuid","teamUuid": "team-uuid","data": {"workspaceUuid": "workspace-uuid","envelopeUuid": "envelope-uuid","recipientName": "John Doe"}}When the envelope is completed, you’ll receive an
ENVELOPE_COMPLETEDevent:{"eventUuid": "f5a6c2d3-2e1b-4c7a-92ab-5e8f2d1c0b9a","eventType": "ENVELOPE_COMPLETED","eventTime": 1704067200000,"webhookUuid": "webhook-uuid","teamUuid": "team-uuid","data": {"workspaceUuid": "workspace-uuid","envelopeUuid": "envelope-uuid","documentUuid": "document-uuid","finalRevisionVersion": 1}}
Important Notes
Section titled “Important Notes”- Envelope Status: Envelopes must be in
draftstatus to add recipients, blocks, or make modifications. Once sent, the status changes and modifications are restricted. - Block Positioning: Block coordinates (x, y) are in PDF points (1/72 inch). Make sure to position blocks correctly on your pages.
- Signature Blocks: Signature blocks always require a
recipientEmailthat must match one of the recipients added to the envelope. - Recipient Emails: When adding blocks with
recipientEmail, the email must match one of the recipients added to the envelope.
Error Handling
Section titled “Error Handling”The API may return various error codes. Common ones include:
When creating envelopes:
WORKSPACE_NOT_FOUND: Invalid workspace UUIDINVALID_FILE_DATA: The uploaded file data is invalid or corruptedFILE_SIZE_EXCEEDED: File size exceeds the 50 MB limitINVALID_PDF: The uploaded file is not a valid PDFINVALID_REQUEST: The request format is invalid (e.g., missing required fields)DOCUMENT_INVALID: The document failed validationDOCX_CONVERSION_UNAVAILABLE: DOCX conversion service is unavailableDOCUMENT_CONVERSION_FAILED: Failed to convert Word document to PDF
When managing envelopes:
ENVELOPE_NOT_FOUND: Invalid envelope UUIDENVELOPE_NOT_IN_DRAFT: Envelope has already been sentNO_RECIPIENTS: Attempting to send without recipientsDOCUMENT_NOT_FOUND: Invalid document UUIDINVALID_RECIPIENT_EMAIL: Recipient email doesn’t match any added recipients
Always check response status codes and handle errors appropriately in your application.