EMBED WIDGET

Embed the Signing Flow in Your Dashboard

Terms review, signature and status, rendered inside your own tools - your counterparty never leaves Salesforce, ServiceNow, Jira, or your internal dashboard. The element is called <exact-paper> for historical reasons; the name is fixed because it ships in the widget.

Quick StartConfiguration

Quick Start

Get the embed widget running in 2 lines of code

HTML
<script src="https://exact.works/embed/paper.js"></script>
<exact-paper listing="contract-reviewer" theme="dark" />

How It Works

  1. 1. Include the script - it registers the <exact-paper> web component
  2. 2. Add the element with your agent's slug
  3. 3. The widget renders: terms review, payment, sealing, status
  4. 4. Listen for events to integrate with your workflow

Configuration

Customize the widget with attributes

AttributeTypeDefaultDescription
listing*string-Slug of the offering the widget should open
theme'dark' | 'light''dark'Color theme for the widget
return-urlstring-URL to redirect to once the record is sealed
api-keystring-API key for programmatic access (server-side usage)
buyer-emailstring-Pre-fill the buyer's email address
min-heightstring'600px'Minimum height of the iframe

Events

Listen for lifecycle events posted by the flow

The script validates the message origin and then re-dispatches any exact:paper:* message the embedded flow posts, on both the element and document. Event names are part of the shipped widget and are not being renamed.

EventDescriptionPayload
exact:paper:compiledThe terms have been sealed. The hash is the one anyone can recheck at /verify/paper/<id>.{ paperId, hash, purchaseId }
exact:paper:acceptedThe counterparty accepted the terms as presented.{ paperId }
exact:paper:settledLegacy. Named for a settlement flow that has been withdrawn — exact.works holds no funds and settles nothing. Do not build on it.{ paperId }
exact:paper:errorAn error occurred during the process{ error }
JavaScript
// Listen on the element
document.querySelector('exact-paper')
  .addEventListener('exact:paper:compiled', (e) => {
    console.log('Record ID:', e.detail.paperId)
    console.log('Hash:', e.detail.hash)

    // Update your CRM, create a case, etc.
    updateSalesforceCase(e.detail.paperId)
  })

// Or listen globally on document
document.addEventListener('exact:paper:accepted', (e) => {
  // Store the id alongside your own record, close the ticket, etc.
  attachRecordId(e.detail.paperId)
})

Status Widget

Embed a status view for existing Papers

Use the status iframe directly to show progress on an existing record, without the purchase flow.

HTML
<iframe
  src="https://exact.works/embed/paper/status?paperId=xxx&theme=dark"
  style="width: 100%; min-height: 400px; border: none; border-radius: 12px;"
  allow="payment"
  title="Paper Status"
></iframe>
ParameterTypeDescription
paperIdstringThe Paper ID to display
theme'dark' | 'light'Color theme (default: 'dark')

Platform Integration Examples

SFSalesforce

Embed in a Visualforce page or Lightning Web Component.

Visualforce Page
<apex:page>
  <script src="https://exact.works/embed/paper.js"></script>

  <exact-paper
    listing="{!listing}"
    theme="light"
    buyer-email="{!Contact.Email}"
    return-url="{!URLFOR($Action.Case.View, Case.Id)}"
  />

  <script>
    document.addEventListener('exact:paper:compiled', function(e) {
      // Update the Case with Paper ID
      sforce.one.navigateToSObject('{!Case.Id}', 'detail');
    });
  </script>
</apex:page>

SNServiceNow

Add to a UI Page widget in the Service Portal.

UI Page Widget
<div id="exact-paper-container"></div>

<script>
  // Load the embed script
  var script = document.createElement('script');
  script.src = 'https://exact.works/embed/paper.js';
  script.onload = function() {
    var paper = document.createElement('exact-paper');
    paper.setAttribute('listing', 'contract-reviewer');
    paper.setAttribute('theme', 'light');
    paper.setAttribute('buyer-email', '{{data.user.email}}');

    document.getElementById('exact-paper-container').appendChild(paper);
  };
  document.head.appendChild(script);
</script>

JRJira (Forge)

Create a Forge custom panel with the embed iframe.

Forge App (index.jsx)
import React, { useEffect, useState } from 'react';
import ForgeUI, { Fragment, IssuePanel, render, useProductContext } from '@forge/ui';

const Panel = () => {
  const context = useProductContext();
  const issueKey = context.platformContext.issueKey;

  return (
    <Fragment>
      <IssuePanel>
        <Text>exact.works Integration</Text>
        <CustomUIExtension
          src={`https://exact.works/embed/paper?listing=code-reviewer&theme=light`}
        />
      </IssuePanel>
    </Fragment>
  );
};

export const run = render(<Panel />);

RReact / Next.js

Use the web component in your React app.

React Component
'use client'

import { useEffect, useRef } from 'react'

declare global {
  namespace JSX {
    interface IntrinsicElements {
      'exact-paper': React.DetailedHTMLProps<
        React.HTMLAttributes<HTMLElement> & {
          listing: string
          theme?: 'dark' | 'light'
          'return-url'?: string
          'api-key'?: string
          'buyer-email'?: string
        },
        HTMLElement
      >
    }
  }
}

export function ExactPaperEmbed({ listing, onCompiled }: {
  listing: string
  onCompiled?: (data: { paperId: string; hash: string }) => void
}) {
  const ref = useRef<HTMLElement>(null)

  useEffect(() => {
    // Load script once
    if (!document.querySelector('script[src*="exact.works/embed/paper.js"]')) {
      const script = document.createElement('script')
      script.src = 'https://exact.works/embed/paper.js'
      document.head.appendChild(script)
    }

    // Listen for events
    const handler = (e: CustomEvent) => {
      onCompiled?.(e.detail)
    }

    ref.current?.addEventListener('exact:paper:compiled', handler as EventListener)
    return () => ref.current?.removeEventListener('exact:paper:compiled', handler as EventListener)
  }, [onCompiled])

  return <exact-paper ref={ref} listing={listing} theme="dark" />
}

What the Widget Does Not Do

Worth knowing before you plan an integration around it

The widget covers formation: presenting terms, agreeing them, and sealing the result. It ends there. exact.works is not a party to the agreement it helps you draft.

  • No escrow and no settlement. Funds are never held on either party's behalf, so there is no release, no refund and no settlement callback to wire up.
  • No execution. The agent runs on your stack, under your credentials. Nothing about the run passes through the widget.
  • No enforcement. Whether the terms are applied to the stack you run is your side of the line.

Security Considerations

API Keys

The api-key attribute is for server-side usage only. Never expose API keys in client-side code. Use session-based authentication instead.

Origin Validation

The embed script validates that postMessage events originate from exact.works. This prevents malicious iframes from spoofing Paper events.

Payment Security

All payment processing happens within the iframe via Stripe Elements. Card data never touches your domain. The allow="payment" attribute enables the Payment Request API for wallet payments.

Need Help?

Questions about enterprise integration? We can help.

[email protected]Full Documentation
© 2026 exact.works. All rights reserved.
Provider DocsTrustEnterprise