Skip to content

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}/remind reminds everyone whose turn it is and who wasn’t reminded in the last hour.
  • Void: POST /v1/envelopes/{id}/void with a reason stops a sent, in-progress or declined envelope; its signing links stop working. Needs envelopes:void.
  • Download: once completed, GET /v1/envelopes/{id}/signed_pdf (sealed, with the certificate of completion as its last pages) and /certificate on its own.
  • Delete: DELETE /v1/envelopes/{id} moves a draft or declined envelope to Trash for 30 days.
  • Labels: PATCH /v1/envelopes/{id} with labels (up to 10, each up to 40 characters) at any status; filter lists with ?label=.