GuidesWorking with the API
Envelopes and recipients
An envelope holds the documents, the people who sign them and the fields they fill in. Build one step by step as a draft, then send it — or make it from a template in one call.
How an envelope moves
status goes draft → sent → in_progress (someone has signed) → completed, or ends as declined, voided or expired. Only drafts can be changed; labels can change at any time.
Each recipient has a recipient_type (signer, approver, in_person_signer, certified_delivery or cc) and an order_group: groups sign one after another, people in the same group at the same time. Their status is pending (not their turn yet), sent, viewed, signed, declined, auto_responded, reassigned or removed.
Ids carry their type: env_, rcp_, doc_, fld_ and tpl_, each followed by 32 hex characters.
1. Create a draft
Give it a subject and message, and the recipients if you know them. external_ref is yours — use it to find the envelope again (GET /v1/envelopes?external_ref=…).
curl https://api.skanything.io/v1/envelopes \
-H "Authorization: Bearer $SA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: nda-2207" \
-d '{
"subject": "Mutual NDA",
"message": "Please review and sign.",
"external_ref": "nda-2207",
"recipients": [
{ "name": "Sam Rivera", "email": "sam@example.com", "recipient_type": "signer", "order_group": 1 },
{ "name": "Jordan Ellis", "email": "jordan@example.com", "recipient_type": "signer", "order_group": 2 }
]
}'const res = await fetch("https://api.skanything.io/v1/envelopes", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SA_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": "nda-2207",
},
body: JSON.stringify({
subject: "Mutual NDA",
message: "Please review and sign.",
external_ref: "nda-2207",
recipients: [
{ name: "Sam Rivera", email: "sam@example.com", recipient_type: "signer", order_group: 1 },
{ name: "Jordan Ellis", email: "jordan@example.com", recipient_type: "signer", order_group: 2 },
],
}),
});
const envelope = await res.json();
if (!res.ok) throw new Error(`${envelope.code}: ${envelope.detail}`);import os
import requests
res = requests.post(
"https://api.skanything.io/v1/envelopes",
headers={
"Authorization": f"Bearer {os.environ['SA_API_KEY']}",
"Idempotency-Key": "nda-2207",
},
json={
"subject": "Mutual NDA",
"message": "Please review and sign.",
"external_ref": "nda-2207",
"recipients": [
{"name": "Sam Rivera", "email": "sam@example.com", "recipient_type": "signer", "order_group": 1},
{"name": "Jordan Ellis", "email": "jordan@example.com", "recipient_type": "signer", "order_group": 2},
],
},
)
envelope = res.json()
if not res.ok:
raise RuntimeError(f"{envelope['code']}: {envelope['detail']}")using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
using var http = new HttpClient { BaseAddress = new Uri("https://api.skanything.io/v1/") };
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("SA_API_KEY"));
using var req = new HttpRequestMessage(HttpMethod.Post, "envelopes")
{
Content = JsonContent.Create(new
{
subject = "Mutual NDA",
message = "Please review and sign.",
external_ref = "nda-2207",
recipients = new[]
{
new { name = "Sam Rivera", email = "sam@example.com", recipient_type = "signer", order_group = 1 },
new { name = "Jordan Ellis", email = "jordan@example.com", recipient_type = "signer", order_group = 2 },
},
}),
};
req.Headers.Add("Idempotency-Key", "nda-2207");
using var res = await http.SendAsync(req);
var envelope = await res.Content.ReadFromJsonAsync<JsonElement>();
if (!res.IsSuccessStatusCode)
throw new HttpRequestException($"{envelope.GetProperty("code")}: {envelope.GetProperty("detail")}");Recipients can also carry phone, delivery (email, sms or both), require_text_code or access_pin (4–8 digits, never returned — only access_pin_set), language, signing_capacity and allow_reassign. Text delivery, text codes and PINs depend on your plan (402 plan_feature_required). Add, change and remove recipients on a draft with POST, PATCH and DELETE /v1/envelopes/{id}/recipients/{rid}.
2. Add documents
Upload each document as multipart/form-data in a part called file: PDF, Word (.docx, .doc) or Excel (.xlsx, .xls). Word and Excel are converted to PDF. Up to 50 MB and 2,000 pages a document, 100 documents and 200 MB an envelope. Every document is virus-checked: its scan_status must be clean (or unscannable / too_large) before it can be sent.
curl https://api.skanything.io/v1/envelopes/env_4f9c2d7e1b8a4c6f9e2d3b7a1c5e8f02/documents \ -H "Authorization: Bearer $SA_API_KEY" \ -F "file=@nda.pdf"
import { readFile } from "node:fs/promises";
const form = new FormData();
form.append("file", new Blob([await readFile("nda.pdf")], { type: "application/pdf" }), "nda.pdf");
const res = await fetch("https://api.skanything.io/v1/envelopes/env_4f9c2d7e1b8a4c6f9e2d3b7a1c5e8f02/documents", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.SA_API_KEY}` },
body: form,
});
const doc = await res.json();
if (!res.ok) throw new Error(`${doc.code}: ${doc.detail}`);
console.log(doc.id, doc.page_count, doc.scan_status);import os
import requests
with open("nda.pdf", "rb") as f:
res = requests.post(
"https://api.skanything.io/v1/envelopes/env_4f9c2d7e1b8a4c6f9e2d3b7a1c5e8f02/documents",
headers={"Authorization": f"Bearer {os.environ['SA_API_KEY']}"},
files={"file": ("nda.pdf", f, "application/pdf")},
)
doc = res.json()
if not res.ok:
raise RuntimeError(f"{doc['code']}: {doc['detail']}")
print(doc["id"], doc["page_count"], doc["scan_status"])using System.Net.Http.Headers;
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("SA_API_KEY"));
using var form = new MultipartFormDataContent();
var pdf = new ByteArrayContent(await File.ReadAllBytesAsync("nda.pdf"));
pdf.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
form.Add(pdf, "file", "nda.pdf");
using var res = await http.PostAsync(
"https://api.skanything.io/v1/envelopes/env_4f9c2d7e1b8a4c6f9e2d3b7a1c5e8f02/documents", form);
Console.WriteLine(await res.Content.ReadAsStringAsync());3. Place fields
Each field belongs to one recipient and one page. Positions and sizes are in PDF points (1/72 inch) from the page’s top-left corner. field_type is one of signature, initials, date_signed, name, email, phone, address, company, title, text, checkbox, dropdown, radio, calculated, approve, decline or attachment; options holds its settings (see Place a field).
curl https://api.skanything.io/v1/envelopes/env_4f9c2d7e1b8a4c6f9e2d3b7a1c5e8f02/fields \
-H "Authorization: Bearer $SA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"recipient_id": "rcp_1a7c3e9b5d2f4a8c6e0b9d1f3a5c7e2b",
"document_id": "doc_6c0e4a8d2b9f4c1e7a3d5b8f0c2e6a4d",
"field_type": "signature",
"page": 3,
"x": 72, "y": 640, "width": 180, "height": 40
}'4. Send
POST /v1/envelopes/{id}/send checks the draft (a clean document, at least one signer, ready fields) and your plan, then sends it to the first signing group. Your account’s default expiry and reminders apply.
curl -X POST https://api.skanything.io/v1/envelopes/env_4f9c2d7e1b8a4c6f9e2d3b7a1c5e8f02/send \ -H "Authorization: Bearer $SA_API_KEY"
After sending
- Follow it with webhooks rather than polling
GET /v1/envelopes/{id}. - Remind:
POST /v1/envelopes/{id}/remindreminds everyone whose turn it is and who wasn’t reminded in the last hour. - Void:
POST /v1/envelopes/{id}/voidwith areasonstops a sent, in-progress or declined envelope; its signing links stop working. Needsenvelopes:void. - Download: once completed,
GET /v1/envelopes/{id}/signed_pdf(sealed, with the certificate of completion as its last pages) and/certificateon its own. - Delete:
DELETE /v1/envelopes/{id}moves a draft or declined envelope to Trash for 30 days. - Labels:
PATCH /v1/envelopes/{id}withlabels(up to 10, each up to 40 characters) at any status; filter lists with?label=.