npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@16it/autosignly

v0.1.5

Published

Node.js client for the Autosignly API — send documents for eIDAS signature and verify webhook deliveries.

Readme

@16it/autosignly

Node.js client for the Autosignly API: send documents for eIDAS electronic signature, follow their status, download the sealed PDF, and verify webhook deliveries.

No runtime dependencies — it uses the fetch and crypto built into Node.

npm install @16it/autosignly

Requires Node 20 or newer.

Credentials

Create an API key and secret in the Autosignly application. Every environment, production and each sandbox, has its own pair, and the pair decides which environment a call operates on — a sandbox key can never touch production data.

The secret must never reach a browser or a mobile app. This client belongs on your own server.

import { AutosignlyClient } from "@16it/autosignly";

const client = new AutosignlyClient(
  process.env.AUTOSIGNLY_API_KEY!,
  process.env.AUTOSIGNLY_API_SECRET!,
);

const credentials = await client.describeCredentials();
console.log(credentials.environmentType); // "PROD" or "SANDBOX"

Checking this before the first call is worth the round trip: it is the only way to be sure a key points where you think it does.

Sending a document for signature

import { readFile } from "node:fs/promises";

const documentId = await client.uploadAndSign({
  pdf: await readFile("contract.pdf"),
  documentName: "Rental agreement 2026",
  fileName: "contract.pdf",
  signers: [
    { firstName: "Anna", lastName: "Nowak", email: "[email protected]", country: "PL", order: 1 },
    { firstName: "Jan", lastName: "Kowalski", email: "[email protected]", country: "PL", order: 2 },
  ],
});

Signing links are e-mailed to the signers by Autosignly. order is not cosmetic: signers are notified one after another, and the next person receives their link only once the previous one has signed.

Signing options default to a simple electronic signature (SES) with a visual stamp. Pass signatureType and signatureMode to change that — for example signatureMode: "SIGNATURES_CARD" collects signatures on a card appended to the document instead of stamping its pages.

Following a document and downloading the result

const document = await client.getDocument(documentId);
if (document.status === "SIGNED") {
  await writeFile("contract-signed.pdf", await client.downloadDocument(documentId));
}

fileUrl on the document is a short-lived link — fetch the document again for a fresh one rather than storing it. downloadDocument does that for you.

A document can be downloaded while signing is still in progress; it then carries only the signatures collected so far. Wait for SIGNED if you want the final, sealed file.

What a signer may be asked for

The rules differ by country, and a signer sent with a combination their country does not allow is rejected when the document goes out. Read them first:

const policy = await client.getSignaturePolicy(signer.country);
for (const allowed of policy.signatureTypes) {
  console.log(allowed.type, allowed.verificationMethods);
}

A country without its own rules answers with the fallback policy rather than an error.

Verifying by SMS also needs a reachable phone number:

const countries = await client.listSmsCountries();

A number outside that list is refused when the code is requested — which happens after the document has already gone out, so check it while preparing the signer.

Attachments

Files attached to a document are converted to PDF and merged into it when it is sent for signing, behind an index page listing each one with its checksum — so a single signature covers the document and everything attached to it.

Attachments can only be added before the document is sent, so upload it first and send it afterwards instead of using uploadAndSign:

const documentId = await client.uploadPdf({
  pdf: await readFile("protocol.pdf"),
  documentName: "Handover protocol",
});

const attachment = await client.addAttachment(documentId, {
  content: await readFile("site-photo.jpg"),
  fileName: "site-photo.jpg",
});

for (const existing of await client.listAttachments(documentId)) {
  console.log(existing.fileName, existing.orderIndex, existing.sha256);
}

await client.sendForSigning(documentId, { signers });

An attachment can be dropped again while the document is still unsent:

await client.deleteAttachment(documentId, attachment.id);

PDF, JPEG and PNG are accepted, recognised from the content rather than the file name. Attachments merge in the order they were added, and can only be changed before the document is sent for signing.

Listing

const page = await client.listDocuments({ status: "SIGNED", size: 50 });

for await (const document of client.iterDocuments({ status: "SIGNED" })) {
  console.log(document.id, document.name);
}

iterDocuments fetches pages as the iterator advances, so a large environment never has to be held in memory at once.

Both calls take tagId as well. Several tags narrow the result — a document has to carry all of them — and a tag that does not exist gives an empty page rather than an error:

const tagged = await client.listDocuments({ tagId: ["contracts", "2026"], status: "SIGNED" });

Parties

A party is the other side of a document — a business or a natural person the company signs with.

import { PartyType } from "@16it/autosignly";

const acme = await client.createParty({
  type: PartyType.COMPANY,
  name: "Acme Sp. z o.o.",
  taxId: "5842831253",
  email: "[email protected]",
  address: { street: "Marszalkowska", number: "12/34", postalCode: "00-001", city: "Warszawa", countryCode: "PL" },
});

const page = await client.listParties({ name: "acme", type: PartyType.COMPANY });

await client.updateParty(acme.id!, { type: PartyType.COMPANY, name: "Acme Renamed", taxId: "5842831253" });
await client.deleteParty(acme.id!);

A COMPANY needs a taxId and an address; a PERSON needs a firstname and an email. A Polish address makes the tax id subject to the NIP checksum.

updateParty replaces the whole party, so send every field you want to keep. Creating a party that already exists — same tax id for a COMPANY, same e-mail for a PERSON — is rejected rather than deduplicated, so look the party up before retrying a failed create.

Parties belong to the environment of the key that created them: a sandbox key never sees a production party. Listing has no sort — the searchable fields are stored encrypted, so the server cannot order by them.

Webhooks

Autosignly signs every delivery with X-Webhook-Signature and X-Webhook-Timestamp. Verify it before trusting the body:

import express from "express";
import { webhooks } from "@16it/autosignly";

app.post("/webhooks/autosignly", express.raw({ type: "application/json" }), (req, res) => {
  const ok = webhooks.isValid(
    req.body,
    req.header("X-Webhook-Signature") ?? "",
    process.env.AUTOSIGNLY_WEBHOOK_KEY!,
    req.header("X-Webhook-Timestamp") ?? "",
  );
  if (!ok) return res.sendStatus(401);

  const event = JSON.parse(req.body.toString("utf8"));
  // ...
  res.sendStatus(200);
});

Two things decide whether this works:

Verify the raw body. The signature covers the exact bytes that were sent. Parsing the JSON and re-serialising it changes them — hence express.raw() rather than express.json().

Deliveries expire. Anything older than five minutes is rejected even when the signature matches, so a captured request cannot be replayed later. Pass { tolerance: 0 } to opt out, for example when replaying a stored delivery in a test.

During a key rotation Autosignly signs with both the new and the previous key and sends both signatures in one header. isValid accepts either, so you can swap your stored secret without dropping deliveries.

Errors

Every failure is an AutosignlyError subclass carrying the API's errorType and errorId — quote the latter when reporting a problem.

| class | when | |---|---| | AuthenticationError | the key or secret was rejected (401) | | PermissionDeniedError | valid credentials, no access to this resource (403) | | NotFoundError | no such document, tag or file (404) | | ValidationError | the request was rejected as invalid (4xx) | | RateLimitError | too many requests (429); retryAfter holds the delay asked for | | ServerError | the API failed to process the request (5xx) | | ConnectionError | the API could not be reached at all | | InvalidSignatureError | a webhook signature did not match |

Transient failures — 429 and 5xx — are retried twice by default with exponential backoff and jitter, honouring Retry-After. A rate limit asking for longer than a minute is reported to you instead of blocking. Set maxRetries: 0 to handle retries yourself.

Writes carry an Idempotency-Key header. The API does not act on it yet, so a retried upload can still create a second document — until it does, treat a timed-out uploadAndSign as "unknown" and check the document list before sending again.

License

Apache-2.0