Packages
Send documents to be signed, and follow them until everyone has signed.
The SigPackage object
Attributes
idrequired Type:stringtitlerequired Type:stringmessageoptional Type:stringnullreferenceoptional Type:stringnullstatusrequired Type:SigPackageStatusrequire_locationoptional Type:booleanrequire_cameraoptional Type:booleanfile_to_foliooptional Type:booleancheck_with_niboptional Type:booleanfoliooptional Type:SigFolioFilingexpires_atrequired Type:stringcreated_byoptional Type:stringcreated_atrequired Type:stringsent_atoptional Type:stringnullcompleted_atoptional Type:stringnullsealed_atoptional Type:stringnullWhen the signed copies and certificate were ready.
voided_atoptional Type:stringnullvoid_reasonoptional Type:stringnullreturn_urloptional Type:stringnullWhere its signers go once they've finished, if it has its own link.
certificateoptional Type:SigCertificatedocumentsrequired Type:SigDocument[]signersrequired Type:SigSigner[]fieldsrequired Type:SigField[]eventsoptional Type:SigEvent[]
How many packages are where
GET /sig/counts
Needs sig:packages:read: See packages, their signers and signed files
The packages with signers now, the signatures waiting for someone in your organisation to review them, the drafts, and the packages fully signed this UTC month.
Returns
200Type:SigCountsThe counts401Type:Problemunauthorized403Type:Problempermission_denied,org_suspended, oraddress_not_allowed(your organisation only accepts requests from the addresses it has listed)404Type:Problemnot_found421Type:Problemwrong_region429Type:Problemrate_limitedorquota_exceededdefaultType:Probleminternal
curl "https://api.unforged.sh/sig/counts" \
-H "Authorization: Bearer $UNFORGED_KEY" \
-H "Unforged-Version: 2026-10-14" import requests
r = requests.get(
"https://api.unforged.sh/sig/counts",
headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
)
print(r.json()) const res = await fetch('https://api.unforged.sh/sig/counts', {
method: 'GET',
headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14' },
});
console.log(await res.json()); List packages
GET /sig/packages
Needs sig:packages:read: See packages, their signers and signed files
Newest first, 50 a page (limit up to 200); next_cursor continues the list. A package past its expiry shows as expired.
Parameters
statusqueryoptional Type:stringOne status, or several separated by commas, such as
sent,partly_signed: draft, sent, partly_signed, completed, declined, voided or expired.qqueryoptional Type:stringWords to look for in the package's title or reference, or in a signer's name or email. A title or name close to them is found too, so a small typo still finds it.
sent_byqueryoptional Type:stringOnly the packages this person sent, by the
created_bythe list gives.reviewqueryoptional Type:"waiting"waitinglists only the packages with a signature waiting for someone in your organisation to review it.cursorqueryoptional Type:stringlimitqueryoptional Type:integer
Returns
200Type:SigPackagePageA page of packages400Type:Probleminvalid_request,invalid_version,idempotency_key_requiredorinvalid_signature401Type:Problemunauthorized403Type:Problempermission_denied,org_suspended, oraddress_not_allowed(your organisation only accepts requests from the addresses it has listed)404Type:Problemnot_found421Type:Problemwrong_region429Type:Problemrate_limitedorquota_exceededdefaultType:Probleminternal
curl "https://api.unforged.sh/sig/packages" \
-H "Authorization: Bearer $UNFORGED_KEY" \
-H "Unforged-Version: 2026-10-14" import requests
r = requests.get(
"https://api.unforged.sh/sig/packages",
headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
)
print(r.json()) const res = await fetch('https://api.unforged.sh/sig/packages', {
method: 'GET',
headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14' },
});
console.log(await res.json()); Create a package and send it
POST /sig/packages
Needs sig:packages:send: Create, send, correct and void packages
One call: each document as a file part (PDF, PNG or JPEG; up to 20, 25 MB each and 50 MB together) and the package as a package part (JSON). It is sent at once unless send is false, and each signer whose turn it is gets an email with their own link; the response carries those links, once. Signers with a lower order sign first; signers sharing an order sign in any order. We find where each signer signs, initials and dates from the documents themselves, and give each place to the signer it names (a role helps); a scan takes a little longer, while we read it. Pass fields to place them yourself.
Parameters
Idempotency-Keyheaderrequired Type:string1–255 printable ASCII characters, kept 24 hours.
Returns
201Type:SigPackageCreated (and sent, unlesssendwas false)400Type:Probleminvalid_request,invalid_version,idempotency_key_requiredorinvalid_signature401Type:Problemunauthorized403Type:Problempermission_denied,org_suspended, oraddress_not_allowed(your organisation only accepts requests from the addresses it has listed)404Type:Problemnot_found409Type:Problemconflict,idempotency_conflict,idempotency_in_progressoridempotency_not_replayable413Type:Problemtoo_large415Type:Problemunsupported_media421Type:Problemwrong_region429Type:Problemrate_limitedorquota_exceededdefaultType:Probleminternal
curl -X POST "https://api.unforged.sh/sig/packages" \
-H "Authorization: Bearer $UNFORGED_KEY" \
-H "Unforged-Version: 2026-10-14" import requests
r = requests.post(
"https://api.unforged.sh/sig/packages",
headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
)
print(r.json()) const res = await fetch('https://api.unforged.sh/sig/packages', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14' },
});
console.log(await res.json()); Read a package
GET /sig/packages/{package_id}
Needs sig:packages:read: See packages, their signers and signed files
With its documents, signers and every event (who opened it, from which address and browser, and when).
Parameters
package_idpathrequired Type:stringThe package's id.
Returns
200Type:SigPackageThe package400Type:Probleminvalid_request,invalid_version,idempotency_key_requiredorinvalid_signature401Type:Problemunauthorized403Type:Problempermission_denied,org_suspended, oraddress_not_allowed(your organisation only accepts requests from the addresses it has listed)404Type:Problemnot_found421Type:Problemwrong_region429Type:Problemrate_limitedorquota_exceededdefaultType:Probleminternal
curl "https://api.unforged.sh/sig/packages/{package_id}" \
-H "Authorization: Bearer $UNFORGED_KEY" \
-H "Unforged-Version: 2026-10-14" import requests
r = requests.get(
"https://api.unforged.sh/sig/packages/{package_id}",
headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
)
print(r.json()) const res = await fetch('https://api.unforged.sh/sig/packages/{package_id}', {
method: 'GET',
headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14' },
});
console.log(await res.json()); Send a draft package
POST /sig/packages/{package_id}/send
Needs sig:packages:send: Create, send, correct and void packages
Emails each signer whose turn it is; the response carries their links, once. 409 once it has been sent.
Parameters
Idempotency-Keyheaderrequired Type:string1–255 printable ASCII characters, kept 24 hours.
package_idpathrequired Type:stringThe package's id.
Request body
senderoptional Type:stringThe email address of the person in your organisation it comes from. The signers' emails name them, and their replies go to them. Defaults to whoever made the API key.
Returns
200Type:SigPackageThe package400Type:Probleminvalid_request,invalid_version,idempotency_key_requiredorinvalid_signature401Type:Problemunauthorized402Type:Problembilling_required403Type:Problempermission_denied,org_suspended, oraddress_not_allowed(your organisation only accepts requests from the addresses it has listed)404Type:Problemnot_found409Type:Problemconflict,idempotency_conflict,idempotency_in_progressoridempotency_not_replayable421Type:Problemwrong_region429Type:Problemrate_limitedorquota_exceededdefaultType:Probleminternal
curl -X POST "https://api.unforged.sh/sig/packages/{package_id}/send" \
-H "Authorization: Bearer $UNFORGED_KEY" \
-H "Unforged-Version: 2026-10-14" \
-H "Content-Type: application/json" \
-d '{}' import requests
r = requests.post(
"https://api.unforged.sh/sig/packages/{package_id}/send",
headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
json={},
)
print(r.json()) const res = await fetch('https://api.unforged.sh/sig/packages/{package_id}/send', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14', 'Content-Type': 'application/json' },
body: JSON.stringify({}),
});
console.log(await res.json()); Remind the signers
POST /sig/packages/{package_id}/remind
Needs sig:packages:send: Create, send, correct and void packages
Emails everyone who has a link and hasn't signed, at most once an hour each. Their earlier links keep working. A reminder never counts as a new signed document.
Parameters
Idempotency-Keyheaderrequired Type:string1–255 printable ASCII characters, kept 24 hours.
package_idpathrequired Type:stringThe package's id.
Returns
200Type:SigPackageThe package400Type:Probleminvalid_request,invalid_version,idempotency_key_requiredorinvalid_signature401Type:Problemunauthorized403Type:Problempermission_denied,org_suspended, oraddress_not_allowed(your organisation only accepts requests from the addresses it has listed)404Type:Problemnot_found409Type:Problemconflict,idempotency_conflict,idempotency_in_progressoridempotency_not_replayable421Type:Problemwrong_region429Type:Problemrate_limitedorquota_exceededdefaultType:Probleminternal
curl -X POST "https://api.unforged.sh/sig/packages/{package_id}/remind" \
-H "Authorization: Bearer $UNFORGED_KEY" \
-H "Unforged-Version: 2026-10-14" import requests
r = requests.post(
"https://api.unforged.sh/sig/packages/{package_id}/remind",
headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
)
print(r.json()) const res = await fetch('https://api.unforged.sh/sig/packages/{package_id}/remind', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14' },
});
console.log(await res.json()); Start again from a package
POST /sig/packages/{package_id}/copy
Needs sig:packages:send: Create, send, correct and void packages
Makes a new draft with the same documents, people and places to sign, so you can fix what needs fixing and send it again. Use it after someone declines, or when a package was voided or ran out of time. The original stays as it was.
Parameters
Idempotency-Keyheaderrequired Type:string1–255 printable ASCII characters, kept 24 hours.
package_idpathrequired Type:stringThe package to start from. It must have been sent.
Returns
201Type:SigPackageThe new draft400Type:Probleminvalid_request,invalid_version,idempotency_key_requiredorinvalid_signature401Type:Problemunauthorized403Type:Problempermission_denied,org_suspended, oraddress_not_allowed(your organisation only accepts requests from the addresses it has listed)404Type:Problemnot_found409Type:Problemconflict,idempotency_conflict,idempotency_in_progressoridempotency_not_replayable421Type:Problemwrong_region429Type:Problemrate_limitedorquota_exceededdefaultType:Probleminternal
curl -X POST "https://api.unforged.sh/sig/packages/{package_id}/copy" \
-H "Authorization: Bearer $UNFORGED_KEY" \
-H "Unforged-Version: 2026-10-14" import requests
r = requests.post(
"https://api.unforged.sh/sig/packages/{package_id}/copy",
headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
)
print(r.json()) const res = await fetch('https://api.unforged.sh/sig/packages/{package_id}/copy', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14' },
});
console.log(await res.json()); Void a package
POST /sig/packages/{package_id}/void
Needs sig:packages:send: Create, send, correct and void packages
Stops a draft or open package for good, with an optional reason. Signers who had a link are told there's nothing to sign.
Parameters
Idempotency-Keyheaderrequired Type:string1–255 printable ASCII characters, kept 24 hours.
package_idpathrequired Type:stringThe package's id.
Request body
reasonoptional Type:string
Returns
200Type:SigPackageThe package400Type:Probleminvalid_request,invalid_version,idempotency_key_requiredorinvalid_signature401Type:Problemunauthorized403Type:Problempermission_denied,org_suspended, oraddress_not_allowed(your organisation only accepts requests from the addresses it has listed)404Type:Problemnot_found409Type:Problemconflict,idempotency_conflict,idempotency_in_progressoridempotency_not_replayable421Type:Problemwrong_region429Type:Problemrate_limitedorquota_exceededdefaultType:Probleminternal
curl -X POST "https://api.unforged.sh/sig/packages/{package_id}/void" \
-H "Authorization: Bearer $UNFORGED_KEY" \
-H "Unforged-Version: 2026-10-14" \
-H "Content-Type: application/json" \
-d '{}' import requests
r = requests.post(
"https://api.unforged.sh/sig/packages/{package_id}/void",
headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
json={},
)
print(r.json()) const res = await fetch('https://api.unforged.sh/sig/packages/{package_id}/void', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14', 'Content-Type': 'application/json' },
body: JSON.stringify({}),
});
console.log(await res.json()); Change where signers sign
PUT /sig/packages/{package_id}/fields
Needs sig:packages:send: Create, send, correct and void packages
Replaces the package's fields with these, until someone has signed. Send them all: a field you leave out is removed.
Parameters
package_idpathrequired Type:stringThe package's id.
Request body
fieldsrequired Type:SigFieldRequest[]
Returns
200Type:SigPackageThe package400Type:Probleminvalid_request,invalid_version,idempotency_key_requiredorinvalid_signature401Type:Problemunauthorized403Type:Problempermission_denied,org_suspended, oraddress_not_allowed(your organisation only accepts requests from the addresses it has listed)404Type:Problemnot_found409Type:Problemconflict,idempotency_conflict,idempotency_in_progressoridempotency_not_replayable421Type:Problemwrong_region429Type:Problemrate_limitedorquota_exceededdefaultType:Probleminternal
curl -X PUT "https://api.unforged.sh/sig/packages/{package_id}/fields" \
-H "Authorization: Bearer $UNFORGED_KEY" \
-H "Unforged-Version: 2026-10-14" \
-H "Content-Type: application/json" \
-d '{}' import requests
r = requests.put(
"https://api.unforged.sh/sig/packages/{package_id}/fields",
headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
json={},
)
print(r.json()) const res = await fetch('https://api.unforged.sh/sig/packages/{package_id}/fields', {
method: 'PUT',
headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14', 'Content-Type': 'application/json' },
body: JSON.stringify({}),
});
console.log(await res.json()); Save a draft, and send it when ready
PUT /sig/packages/{package_id}/draft
Needs sig:packages:send: Create, send, correct and void packages
Saves a draft package as you have it now: its details, everyone on it and the places on its documents. Send them all, as with fields: a person or place you leave out is removed. Keep someone already on the draft by giving their id. Until you send it, people may be missing a name or email address. Set send to true to send it, once everyone has both (a witness their signer names as they sign can wait).
Parameters
package_idpathrequired Type:stringThe package's id.
Request body
titleoptional Type:stringmessageoptional Type:stringreferenceoptional Type:stringsignersoptional Type:SigDraftSigner[]fieldsoptional Type:SigFieldRequest[]Name each field's document by its id (
document_id) and its signer by their place insigners(signer).require_locationoptional Type:booleanrequire_cameraoptional Type:booleanfile_to_foliooptional Type:booleancheck_with_niboptional Type:booleanfolio_folderoptional Type:stringfolio_classoptional Type:stringexpires_in_daysoptional Type:integersendoptional Type:booleanreturn_urloptional Type:stringnullWhere signers go once they've finished; leave it out or null for your settings' default.
Returns
200Type:SigPackageThe package400Type:Probleminvalid_request,invalid_version,idempotency_key_requiredorinvalid_signature401Type:Problemunauthorized403Type:Problempermission_denied,org_suspended, oraddress_not_allowed(your organisation only accepts requests from the addresses it has listed)404Type:Problemnot_found409Type:Problemconflict,idempotency_conflict,idempotency_in_progressoridempotency_not_replayable421Type:Problemwrong_region429Type:Problemrate_limitedorquota_exceededdefaultType:Probleminternal
curl -X PUT "https://api.unforged.sh/sig/packages/{package_id}/draft" \
-H "Authorization: Bearer $UNFORGED_KEY" \
-H "Unforged-Version: 2026-10-14" \
-H "Content-Type: application/json" \
-d '{}' import requests
r = requests.put(
"https://api.unforged.sh/sig/packages/{package_id}/draft",
headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
json={},
)
print(r.json()) const res = await fetch('https://api.unforged.sh/sig/packages/{package_id}/draft', {
method: 'PUT',
headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14', 'Content-Type': 'application/json' },
body: JSON.stringify({}),
});
console.log(await res.json()); Correct a signer
PATCH /sig/packages/{package_id}/signers/{signer_id}
Needs sig:packages:send: Create, send, correct and void packages
Change the name, email address or mobile number of a signer who hasn't signed. A new address stops their earlier links and, if it is their turn, sends a new one, returned once.
Parameters
package_idpathrequired Type:stringThe package's id.
signer_idpathrequired Type:stringThe signer's id.
Request body
nameoptional Type:stringemailoptional Type:stringmobileoptional Type:stringnullnull removes it.
Returns
200Type:SigPackageThe package400Type:Probleminvalid_request,invalid_version,idempotency_key_requiredorinvalid_signature401Type:Problemunauthorized403Type:Problempermission_denied,org_suspended, oraddress_not_allowed(your organisation only accepts requests from the addresses it has listed)404Type:Problemnot_found409Type:Problemconflict,idempotency_conflict,idempotency_in_progressoridempotency_not_replayable421Type:Problemwrong_region429Type:Problemrate_limitedorquota_exceededdefaultType:Probleminternal
curl -X PATCH "https://api.unforged.sh/sig/packages/{package_id}/signers/{signer_id}" \
-H "Authorization: Bearer $UNFORGED_KEY" \
-H "Unforged-Version: 2026-10-14" \
-H "Content-Type: application/json" \
-d '{}' import requests
r = requests.patch(
"https://api.unforged.sh/sig/packages/{package_id}/signers/{signer_id}",
headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
json={},
)
print(r.json()) const res = await fetch('https://api.unforged.sh/sig/packages/{package_id}/signers/{signer_id}', {
method: 'PATCH',
headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14', 'Content-Type': 'application/json' },
body: JSON.stringify({}),
});
console.log(await res.json());