Contents

Packages

Send documents to be signed, and follow them until everyone has signed.

The SigPackage object

Attributes

  • id required Type: string
  • title required Type: string
  • message optional Type: string null
  • reference optional Type: string null
  • status required Type: SigPackageStatus
  • require_location optional Type: boolean
  • require_camera optional Type: boolean
  • file_to_folio optional Type: boolean
  • check_with_nib optional Type: boolean
  • folio optional Type: SigFolioFiling
  • expires_at required Type: string
  • created_by optional Type: string
  • created_at required Type: string
  • sent_at optional Type: string null
  • completed_at optional Type: string null
  • sealed_at optional Type: string null

    When the signed copies and certificate were ready.

  • voided_at optional Type: string null
  • void_reason optional Type: string null
  • return_url optional Type: string null

    Where its signers go once they've finished, if it has its own link.

  • certificate optional Type: SigCertificate
  • documents required Type: SigDocument[]
  • signers required Type: SigSigner[]
  • fields required Type: SigField[]
  • events optional Type: SigEvent[]

SigPackage on its own page

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

  • 200 Type: SigCounts
    The counts
  • 401 Type: Problem
    unauthorized
  • 403 Type: Problem
    permission_denied, org_suspended, or address_not_allowed (your organisation only accepts requests from the addresses it has listed)
  • 404 Type: Problem
    not_found
  • 421 Type: Problem
    wrong_region
  • 429 Type: Problem
    rate_limited or quota_exceeded
  • default Type: Problem
    internal
GET /sig/counts · curl
curl "https://api.unforged.sh/sig/counts" \
  -H "Authorization: Bearer $UNFORGED_KEY" \
  -H "Unforged-Version: 2026-10-14"
GET /sig/counts · Python
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())
GET /sig/counts · Node.js
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

  • status queryoptional Type: string

    One status, or several separated by commas, such as sent,partly_signed: draft, sent, partly_signed, completed, declined, voided or expired.

  • q queryoptional Type: string

    Words 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_by queryoptional Type: string

    Only the packages this person sent, by the created_by the list gives.

  • review queryoptional Type: "waiting"

    waiting lists only the packages with a signature waiting for someone in your organisation to review it.

  • cursor queryoptional Type: string
  • limit queryoptional Type: integer

Returns

  • 200 Type: SigPackagePage
    A page of packages
  • 400 Type: Problem
    invalid_request, invalid_version, idempotency_key_required or invalid_signature
  • 401 Type: Problem
    unauthorized
  • 403 Type: Problem
    permission_denied, org_suspended, or address_not_allowed (your organisation only accepts requests from the addresses it has listed)
  • 404 Type: Problem
    not_found
  • 421 Type: Problem
    wrong_region
  • 429 Type: Problem
    rate_limited or quota_exceeded
  • default Type: Problem
    internal
GET /sig/packages · curl
curl "https://api.unforged.sh/sig/packages" \
  -H "Authorization: Bearer $UNFORGED_KEY" \
  -H "Unforged-Version: 2026-10-14"
GET /sig/packages · Python
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())
GET /sig/packages · Node.js
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-Key headerrequired Type: string

    1–255 printable ASCII characters, kept 24 hours.

Returns

  • 201 Type: SigPackage
    Created (and sent, unless send was false)
  • 400 Type: Problem
    invalid_request, invalid_version, idempotency_key_required or invalid_signature
  • 401 Type: Problem
    unauthorized
  • 403 Type: Problem
    permission_denied, org_suspended, or address_not_allowed (your organisation only accepts requests from the addresses it has listed)
  • 404 Type: Problem
    not_found
  • 409 Type: Problem
    conflict, idempotency_conflict, idempotency_in_progress or idempotency_not_replayable
  • 413 Type: Problem
    too_large
  • 415 Type: Problem
    unsupported_media
  • 421 Type: Problem
    wrong_region
  • 429 Type: Problem
    rate_limited or quota_exceeded
  • default Type: Problem
    internal
POST /sig/packages · curl
curl -X POST "https://api.unforged.sh/sig/packages" \
  -H "Authorization: Bearer $UNFORGED_KEY" \
  -H "Unforged-Version: 2026-10-14"
POST /sig/packages · Python
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())
POST /sig/packages · Node.js
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_id pathrequired Type: string

    The package's id.

Returns

  • 200 Type: SigPackage
    The package
  • 400 Type: Problem
    invalid_request, invalid_version, idempotency_key_required or invalid_signature
  • 401 Type: Problem
    unauthorized
  • 403 Type: Problem
    permission_denied, org_suspended, or address_not_allowed (your organisation only accepts requests from the addresses it has listed)
  • 404 Type: Problem
    not_found
  • 421 Type: Problem
    wrong_region
  • 429 Type: Problem
    rate_limited or quota_exceeded
  • default Type: Problem
    internal
GET /sig/packages/{package_id} · curl
curl "https://api.unforged.sh/sig/packages/{package_id}" \
  -H "Authorization: Bearer $UNFORGED_KEY" \
  -H "Unforged-Version: 2026-10-14"
GET /sig/packages/{package_id} · Python
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())
GET /sig/packages/{package_id} · Node.js
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-Key headerrequired Type: string

    1–255 printable ASCII characters, kept 24 hours.

  • package_id pathrequired Type: string

    The package's id.

Request body

SigSendRequest

  • sender optional Type: string

    The 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

  • 200 Type: SigPackage
    The package
  • 400 Type: Problem
    invalid_request, invalid_version, idempotency_key_required or invalid_signature
  • 401 Type: Problem
    unauthorized
  • 402 Type: Problem
    billing_required
  • 403 Type: Problem
    permission_denied, org_suspended, or address_not_allowed (your organisation only accepts requests from the addresses it has listed)
  • 404 Type: Problem
    not_found
  • 409 Type: Problem
    conflict, idempotency_conflict, idempotency_in_progress or idempotency_not_replayable
  • 421 Type: Problem
    wrong_region
  • 429 Type: Problem
    rate_limited or quota_exceeded
  • default Type: Problem
    internal
POST /sig/packages/{package_id}/send · curl
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 '{}'
POST /sig/packages/{package_id}/send · Python
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())
POST /sig/packages/{package_id}/send · Node.js
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-Key headerrequired Type: string

    1–255 printable ASCII characters, kept 24 hours.

  • package_id pathrequired Type: string

    The package's id.

Returns

  • 200 Type: SigPackage
    The package
  • 400 Type: Problem
    invalid_request, invalid_version, idempotency_key_required or invalid_signature
  • 401 Type: Problem
    unauthorized
  • 403 Type: Problem
    permission_denied, org_suspended, or address_not_allowed (your organisation only accepts requests from the addresses it has listed)
  • 404 Type: Problem
    not_found
  • 409 Type: Problem
    conflict, idempotency_conflict, idempotency_in_progress or idempotency_not_replayable
  • 421 Type: Problem
    wrong_region
  • 429 Type: Problem
    rate_limited or quota_exceeded
  • default Type: Problem
    internal
POST /sig/packages/{package_id}/remind · curl
curl -X POST "https://api.unforged.sh/sig/packages/{package_id}/remind" \
  -H "Authorization: Bearer $UNFORGED_KEY" \
  -H "Unforged-Version: 2026-10-14"
POST /sig/packages/{package_id}/remind · Python
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())
POST /sig/packages/{package_id}/remind · Node.js
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-Key headerrequired Type: string

    1–255 printable ASCII characters, kept 24 hours.

  • package_id pathrequired Type: string

    The package to start from. It must have been sent.

Returns

  • 201 Type: SigPackage
    The new draft
  • 400 Type: Problem
    invalid_request, invalid_version, idempotency_key_required or invalid_signature
  • 401 Type: Problem
    unauthorized
  • 403 Type: Problem
    permission_denied, org_suspended, or address_not_allowed (your organisation only accepts requests from the addresses it has listed)
  • 404 Type: Problem
    not_found
  • 409 Type: Problem
    conflict, idempotency_conflict, idempotency_in_progress or idempotency_not_replayable
  • 421 Type: Problem
    wrong_region
  • 429 Type: Problem
    rate_limited or quota_exceeded
  • default Type: Problem
    internal
POST /sig/packages/{package_id}/copy · curl
curl -X POST "https://api.unforged.sh/sig/packages/{package_id}/copy" \
  -H "Authorization: Bearer $UNFORGED_KEY" \
  -H "Unforged-Version: 2026-10-14"
POST /sig/packages/{package_id}/copy · Python
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())
POST /sig/packages/{package_id}/copy · Node.js
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-Key headerrequired Type: string

    1–255 printable ASCII characters, kept 24 hours.

  • package_id pathrequired Type: string

    The package's id.

Request body

SigVoidRequest

  • reason optional Type: string

Returns

  • 200 Type: SigPackage
    The package
  • 400 Type: Problem
    invalid_request, invalid_version, idempotency_key_required or invalid_signature
  • 401 Type: Problem
    unauthorized
  • 403 Type: Problem
    permission_denied, org_suspended, or address_not_allowed (your organisation only accepts requests from the addresses it has listed)
  • 404 Type: Problem
    not_found
  • 409 Type: Problem
    conflict, idempotency_conflict, idempotency_in_progress or idempotency_not_replayable
  • 421 Type: Problem
    wrong_region
  • 429 Type: Problem
    rate_limited or quota_exceeded
  • default Type: Problem
    internal
POST /sig/packages/{package_id}/void · curl
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 '{}'
POST /sig/packages/{package_id}/void · Python
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())
POST /sig/packages/{package_id}/void · Node.js
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_id pathrequired Type: string

    The package's id.

Request body

SigFieldsRequest

Returns

  • 200 Type: SigPackage
    The package
  • 400 Type: Problem
    invalid_request, invalid_version, idempotency_key_required or invalid_signature
  • 401 Type: Problem
    unauthorized
  • 403 Type: Problem
    permission_denied, org_suspended, or address_not_allowed (your organisation only accepts requests from the addresses it has listed)
  • 404 Type: Problem
    not_found
  • 409 Type: Problem
    conflict, idempotency_conflict, idempotency_in_progress or idempotency_not_replayable
  • 421 Type: Problem
    wrong_region
  • 429 Type: Problem
    rate_limited or quota_exceeded
  • default Type: Problem
    internal
PUT /sig/packages/{package_id}/fields · curl
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 '{}'
PUT /sig/packages/{package_id}/fields · Python
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())
PUT /sig/packages/{package_id}/fields · Node.js
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_id pathrequired Type: string

    The package's id.

Request body

SigDraftRequest

  • title optional Type: string
  • message optional Type: string
  • reference optional Type: string
  • signers optional Type: SigDraftSigner[]
  • fields optional Type: SigFieldRequest[]

    Name each field's document by its id (document_id) and its signer by their place in signers (signer).

  • require_location optional Type: boolean
  • require_camera optional Type: boolean
  • file_to_folio optional Type: boolean
  • check_with_nib optional Type: boolean
  • folio_folder optional Type: string
  • folio_class optional Type: string
  • expires_in_days optional Type: integer
  • send optional Type: boolean
  • return_url optional Type: string null

    Where signers go once they've finished; leave it out or null for your settings' default.

Returns

  • 200 Type: SigPackage
    The package
  • 400 Type: Problem
    invalid_request, invalid_version, idempotency_key_required or invalid_signature
  • 401 Type: Problem
    unauthorized
  • 403 Type: Problem
    permission_denied, org_suspended, or address_not_allowed (your organisation only accepts requests from the addresses it has listed)
  • 404 Type: Problem
    not_found
  • 409 Type: Problem
    conflict, idempotency_conflict, idempotency_in_progress or idempotency_not_replayable
  • 421 Type: Problem
    wrong_region
  • 429 Type: Problem
    rate_limited or quota_exceeded
  • default Type: Problem
    internal
PUT /sig/packages/{package_id}/draft · curl
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 '{}'
PUT /sig/packages/{package_id}/draft · Python
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())
PUT /sig/packages/{package_id}/draft · Node.js
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_id pathrequired Type: string

    The package's id.

  • signer_id pathrequired Type: string

    The signer's id.

Request body

SigSignerCorrection

  • name optional Type: string
  • email optional Type: string
  • mobile optional Type: string null

    null removes it.

Returns

  • 200 Type: SigPackage
    The package
  • 400 Type: Problem
    invalid_request, invalid_version, idempotency_key_required or invalid_signature
  • 401 Type: Problem
    unauthorized
  • 403 Type: Problem
    permission_denied, org_suspended, or address_not_allowed (your organisation only accepts requests from the addresses it has listed)
  • 404 Type: Problem
    not_found
  • 409 Type: Problem
    conflict, idempotency_conflict, idempotency_in_progress or idempotency_not_replayable
  • 421 Type: Problem
    wrong_region
  • 429 Type: Problem
    rate_limited or quota_exceeded
  • default Type: Problem
    internal
PATCH /sig/packages/{package_id}/signers/{signer_id} · curl
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 '{}'
PATCH /sig/packages/{package_id}/signers/{signer_id} · Python
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())
PATCH /sig/packages/{package_id}/signers/{signer_id} · Node.js
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());