Developers

The document surface is one HTML tag.

<signitri-viewer> and <signitri-prepare> are custom elements built on web standards. No build step, no framework, no dependencies — they run inside your own application rather than beside it.

See the embed codeAsk about API access
Shadow DOM, so neither stylesheet reaches the other Themed through custom properties Zero dependencies
The embeddable viewer

Load the module, write the tag.

Importing the module defines the element. After that it behaves like any other tag your framework does not recognize: give it a page source, tell it who is signing, and listen for what happened.

There is no toolchain in this path at all. The files are plain ES modules and they carry their own stylesheet and fonts, so the element looks right on a page that knows nothing about Signitri.

index.html
<script type="module" src="/signitri/viewer.js"></script>

<signitri-viewer id="lease"></signitri-viewer>

<script type="module">
  import Signitri from '/signitri/signitri.js';

  const doc = await Signitri.open('/lease.pdf');
  const el = document.getElementById('lease');

  // open() first: signAs paints fields that do not
  // exist until the document has been read.
  await el.open(doc.source());

  el.signerName = 'Nkosi Dlamini';
  el.setTerms({ mode: 'text', text: terms });
  el.signAs('rcpt_2', recipients);

  el.addEventListener('signitri-complete', () => {
    send(el.values());
  });
</script>
Viewer.jsx
export function Viewer({
  source,
  actor,
  recipients,
  terms,
  signatureStore,
}) {
  const ref = useRef(null);

  useEffect(() => {
    const el = ref.current;
    if (!el || !source) return;

    (async () => {
      await el.open(source);
      el.signatureStore = signatureStore ?? null;
      el.setTerms(terms ?? { mode: 'none' });
      el.signAs(actor ?? null, recipients ?? []);
    })();
  }, [source, actor, recipients, terms, signatureStore]);

  return <signitri-viewer ref={ref} />;
}
Inside your framework

React, Vue, Rails, or a bare script tag.

One thing is worth knowing before you write the wrapper. The element takes objects — a page source, a recipient list, a terms definition — and JSX props on an unknown tag become HTML attributes, which are strings. So objects are set as properties, while the methods setTerms() and signAs() are called through a ref.

The order is not arbitrary either. open() is async and has to resolve before signAs(), because signAs paints fields that do not exist until the document has been read. Get it wrong and you see an empty document and no error.

That is the whole wrapper. Vue sets properties on custom elements by default, so its version is shorter still.

Two seams

You supply the pages. You supply the palette.

Rendering comes from a page source

The viewer is not wired to one engine. It consumes an interface, and Signitri’s WASM engine returns one from doc.source(). A server-side renderer or a test double satisfies the same four members, and the element does not know the difference.

PageSource
interface PageSource {
  pageCount: number
  size(i): { w, h }                  // points
  render(i, scale): Promise<ImageBitmap|HTMLCanvasElement>
  text?(i): Promise<TextLayer>
}

Styling crosses in one direction only

The element’s styles live in a shadow root. Your CSS does not reach in and its CSS does not leak out. The custom properties on the host are the contract, and they are the only thing that crosses. It follows the operating system’s color scheme, and a data-theme attribute forces either one.

your-styles.css
/* Your stylesheet. The element reads these off the host. */
signitri-viewer {
  height: 80vh;

  --sg-bg: var(--app-desk);      /* the desk */
  --sg-paper: var(--app-paper);  /* the sheet itself */
  --sg-accent: var(--app-accent);
  --sg-line: var(--app-border);
}
Element reference

The whole surface, in one list.

<signitri-viewer>

open(source)
Attach a page source and render it. Async, and everything else waits on it.
signAs(recipientId, recipients)
Only that recipient’s fields respond. Everyone else’s stay visible and inert, so the signer can see the whole document is accounted for.
setTerms(terms)
Nothing can be filled until the signer accepts. Four shapes, because senders have all four: none, text, link, document.
download(name)
Hands the signer the document as it stands, at any point, not only at the end. The control hides itself when there is nothing to hand over.
values()
Field names, kinds and values, ready for the writer.
toJSON()
Annotations as plain data.
signatureStore
Any object with list, save, remove and rename. Leave it unset and saved signatures are off for that document.
signerName · downloadName
Prefills the typed-signature tab, and names the file the signer downloads.

Events it dispatches

signitri-complete
Every required field this signer owns is filled.
signitri-field-signed
One field now carries a signature. The detail is the field.
signitri-terms-accepted
Who accepted, a digest of the text they saw, and when.

<signitri-prepare>

open(source)
The same page source the viewer takes.
enableAI(client)
Field suggestions read from the document’s own text layer.
toJSON()
Recipients, fields, and where on the page each one sits.
signitri-package
The prepared document, ready to send for signature.
signitri-ai-fields
Suggested fields, for your own review step before they are placed.
signitri-notice
A message to show in your own chrome. The element never draws over your interface.

Custom events do not bubble into React’s synthetic system. Subscribe on the element.

The API

The REST API is not published yet.

There is no endpoint reference on this page, because publishing one before the paths are final would cost you a rewrite. Here is the shape it will take, so you can judge whether it fits.

What it will cover

  • Create a document from a PDF you upload.
  • Place fields and assign each one to a recipient.
  • Send for signature, and set the signing window.
  • Read a document’s status and each signer’s verification result.
  • Download the signed file and the evidence pack.
  • Cancel a document that is still live.

Outbound webhooks

One delivery per state change, signed so you can tell it came from Signitri, retried until you acknowledge it.

  • Document sent
  • Signer opened the document
  • Signer verified their identity
  • Verification sent to manual review
  • Signer signed
  • Document completed
  • Document cancelled or expired
Not published

Endpoint paths, payload shapes, authentication and rate limits are not final, and nothing above should be built against. Say what you need the API to do and you will get a straight answer on timing.

Ask for API access
Where this stands

What you can build against today.

Working now

  • Both elements, loaded as ES modules from files you serve yourself.
  • Signature capture — drawn, typed, or reused from a saved set. Drawn signatures carry pressure and timing wherever the hardware reports it.
  • Fields read from the PDF’s own AcroForm widgets, so a document prepared in Acrobat presents the same way here.
  • Terms acceptance recorded alongside the signature, including a digest of the text the signer actually saw.
  • Light and dark, through the --sg-* custom properties on the host.

Not yet

  • A published package and a versioned URL to load the elements from. For now you serve the files.
  • The REST API, API keys, and a sandbox environment.
  • Outbound webhooks.
  • Reference documentation beyond this page.

Put the signing surface in your own application.

The elements are the same ones Signitri runs on. While the package is unpublished, you get the files by arrangement — ask, and say what you are building.

Ask for the element filesRead the security overview