Guides
Bulk sending and campaigns
Send the same PDF to many people, each with their own document and audit trail: an annual NDA refresh, a policy update, a consent form.
Bulk drafts over the API#
POST /documents/bulk (documents:write) is a multipart request with:
| Part | Description |
|---|---|
pdf | The PDF (file, application/pdf). |
recipients_csv | CSV file with a header row. Columns: email (or e-mail), name (or imiÄ™ i nazwisko, imie), optional phone (or telefon). Rows without email or name are skipped. Up to 500 rows. |
title_template | Document title; {name} and {email} are replaced per row. |
verification_type | Optional: NONE (default), EMAIL_OTP, SMS_OTP, BOTH_OTP. |
It creates one draft per row with that row's person as the only signer, and returns:
{"created": 2, "skipped": 0, "errors": [], "document_ids": ["3f1c2a9e-5b7d-4c11-9a3e-0d4b8e2f6a71", "8d0b1c2e-3f4a-4b5c-9d6e-7f8a9b0c1d2e"]}Rows that fail while creating their document are counted in skipped and described in errors ("<email>: <reason>", first 20).
Nothing is sent and no fields are placed: place a signature field on each draft, then send it. The same PDF, CSV, title and verification within 24 hours return the original result instead of a second batch. The documents count against your monthly document and storage quotas.
email,name,phone
[email protected],Anna Nowak,+48500100200
[email protected],Jan Kowalski,curl -sS -X POST "https://api.sign.yt/v1/documents/bulk" \
-H "Authorization: Bearer $SIGNYT_API_KEY" \
-F "title_template=NDA 2027 - {name}" \
-F "verification_type=EMAIL_OTP" \
-F "[email protected];type=application/pdf" \
-F "[email protected];type=text/csv" | tee bulk.json
for id in $(jq -r '.document_ids[]' bulk.json); do
rid=$(curl -sS "https://api.sign.yt/v1/documents/$id" -H "Authorization: Bearer $SIGNYT_API_KEY" | jq -r '.recipients[0].id')
curl -sS -X PUT "https://api.sign.yt/v1/documents/$id/fields" \
-H "Authorization: Bearer $SIGNYT_API_KEY" -H "Content-Type: application/json" \
-d '{"fields":[{"type":"SIGNATURE","page":1,"position_x":12,"position_y":80,"width":28,"height":7,"recipient_id":"'"$rid"'"}]}' > /dev/null
curl -sS -X POST "https://api.sign.yt/v1/documents/$id/send" \
-H "Authorization: Bearer $SIGNYT_API_KEY" -H "Idempotency-Key: send-$id" > /dev/null
doneimport os
import httpx
from signyt import Signyt
key = os.environ["SIGNYT_API_KEY"]
with open("nda.pdf", "rb") as pdf, open("people.csv", "rb") as csv_file:
result = httpx.post(
"https://api.sign.yt/v1/documents/bulk",
headers={"Authorization": f"Bearer {key}"},
data={"title_template": "NDA 2027 - {name}", "verification_type": "EMAIL_OTP"},
files={"pdf": ("nda.pdf", pdf, "application/pdf"),
"recipients_csv": ("people.csv", csv_file, "text/csv")},
timeout=120,
).raise_for_status().json()
client = Signyt(key)
for doc_id in result["document_ids"]:
doc = client.documents.get(doc_id)
client.documents.set_fields(doc_id, [{
"type": "SIGNATURE", "page": 1, "position_x": 12, "position_y": 80,
"width": 28, "height": 7, "recipient_id": doc.recipients[0].id,
}])
client.documents.send(doc_id)import { readFile } from "node:fs/promises";
import { Signyt } from "@signyt/sdk";
const key = process.env.SIGNYT_API_KEY!;
const form = new FormData();
form.set("title_template", "NDA 2027 - {name}");
form.set("verification_type", "EMAIL_OTP");
form.set("pdf", new Blob([await readFile("nda.pdf")], { type: "application/pdf" }), "nda.pdf");
form.set("recipients_csv", new Blob([await readFile("people.csv")], { type: "text/csv" }), "people.csv");
const res = await fetch("https://api.sign.yt/v1/documents/bulk", {
method: "POST",
headers: { Authorization: `Bearer ${key}` },
body: form,
});
const result = (await res.json()) as { document_ids: string[] };
const client = new Signyt(key);
for (const id of result.document_ids) {
const doc = await client.documents.get(id);
await client.documents.setFields(id, [
{ type: "SIGNATURE", page: 1, position_x: 12, position_y: 80, width: 28, height: 7, recipient_id: doc.recipients[0].id },
]);
await client.documents.send(id);
}Each draft costs three more requests (read, fields, send), so 500 rows stay well inside the 100 requests per second limit if you send sequentially. If the document is the same layout every time, consider creating each document from a template (fields included) instead.
Reminders#
POST /documents/{id}/remindre-emails the recipients whose turn it is (in sequential order only the current one) on aPENDINGdocument and returns{"reminded": 1, "recipients": [...]}.- sign.yt also reminds automatically: once a day, pending recipients who have not been contacted for 3 days get a reminder.
Campaigns (in the app)#
For larger or ongoing sends, organization members can run campaigns from the sign.yt app. A campaign is built on a template and is either a throttled bulk send (CSV up to 50,000 rows, extra CSV columns become variables) or a public signing link with access rules (open, or restricted with allow/deny patterns), a QR code, start time, business-hours sending, automatic reminders, expiry and a signature cap. Sending is throttled per hour according to your email provider.
Campaigns are managed in the app only; they are not part of the API-key surface. Documents they create emit the usual webhooks.