Skip to content

Latest commit

 

History

143 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

esign

Unit E2E Coverage License: MIT

E2E covers backend, web, Android and the iOS simulator suite, see CI/CD.

Your React Native or React web app renders one ESignature component. A SigningSource picks one of three modes: public URL (no backend), Web Forms instance (one backend endpoint), or envelope (one backend endpoint, or a GraphQL backend for orchestration). The backend-backed modes talk to DocuSign either from your own Node API with @blinkbitcoin/esign-node or from the deployable @blinkbitcoin/esign-service, which mints without a database.

Embedded e-signing for React Native and React web apps. One ESignature component, three integration modes - you only need the parts for your mode, and for two of the three that is a single small package:

Mode What it is What your app installs Backend required
1. Public URL A published public form
URL embedded directly
One package via the
Apollo-free /webform
entry - no Apollo,
no GraphQL
None
2. Web Forms
instances
Prefilled per-signer forms
(read-only fields locked);
your backend mints an
instance URL with one
API call
Same minimal /webform
entry
One mint endpoint,
either tier: in-process
with @blinkbitcoin/esign-node,
or deploy
@blinkbitcoin/esign-service
(no database)
3. Envelope The signer opens the agreement
itself, minted from your
template(s) with your values
locked on the document; with a
database, full orchestration:
per-recipient sessions, restart
on expiry, webhook status sync
The mint: the same
minimal entry (your app
calls one endpoint).
Orchestration: the
package + @apollo/client
+ graphql
The mint: @blinkbitcoin/esign-service
under ESIGN_MINT_MODE=envelope
(no database), or the envelope
preset of @blinkbitcoin/esign-node
in your own Node API.
Orchestration: the service with
DATABASE_URL, or the envelope
domain of esign-node

The GraphQL API and the Apollo wiring exist for mode 3's orchestration only. If you need modes 1 or 2, or mode 3's envelope mint, none of that ships with you: the mint runs either inside your own Node API or in this repo's service, with no database at all (Backend options). The Integration section walks each mode from simplest up.

Which mode? Nothing to lock and no per-signer data: mode 1. Values the signer must not change (amounts, rates, dates set by you): mode 2 when a form should ask the signer the rest first, mode 3's envelope mint when the signer should land on the agreement itself - both lock fields, both need one call on your backend and no database. A document workflow with per-recipient sessions, restarts and status tracking: mode 3 with a database. Reading path for locked terms: docs/integration/locked-prefill.md (the Web Forms recipe, backend + app) or docs/integration/locked-prefill-envelopes.md (the envelope recipe) → docs/integration/docusign-lessons.md (the rules, one page) → docs/integration/webforms.md (the details) → examples/mint-only-demo (the API side, runnable).

Who are you? Three paths through this repository:

You are Your path
App developer /
integrator
Integration - the three modes, same component
consuming.md - registry setup and the minimal install
locked-prefill.md - the mode 2 recipe, backend + app
Backend
developer
The in-process mint preset - routes in your own API
examples/mint-only-demo - a runnable API that mints
Runbook: backend developer - what to build once
DevOps
engineer
Backend options - the two tiers, side by side
Deploy table - the copy-paste per target
Runbook: DevOps - env, keys, boot guard, health

Integration

Every mode drives the same component with the same callbacks - the only thing that changes is the SigningSource you pass in:

<ESignature source={source} onComplete={…} onError={…} onCancel={…} />

The modes below go from simplest to most capable. Start with the first one that covers your needs.

1. Public URL - the simplest (no backend, no credentials)

Use when: every signer gets the same form and you don't need per-signer prefill - you just publish the form in the DocuSign Web Forms builder and embed its public URL.

Install the package and the two WebView peers - nothing else:

npm i @blinkbitcoin/esign-react-native react-native-webview @react-native-community/netinfo
# note: no @apollo/client, no graphql - not needed for modes 1 and 2
import { ESignature, createPublicUrlSource } from '@blinkbitcoin/esign-react-native/webform';

const source = createPublicUrlSource({ url: 'https://your-published-form-url' });

That's the whole integration: the component embeds the URL in a WebView and your callbacks fire on completion/cancel/error.

2. Web Forms instances - adds per-signer prefill (one backend endpoint)

Use when: you want each signer's data prefilled into the form, need values the signer cannot change (the fields marked read-only in the builder show the minted values locked), or need to know which signer completed it. DocuSign requires minting a short-lived instance URL per signer, and that API call carries your DocuSign credentials - so it belongs on a backend, not in the app. One rule from the live runs: locked fields must be Text (or Dropdown) fields fed strings - a read-only Number or Date field makes DocuSign refuse the submission (lessons).

  1. Add one authenticated endpoint to your own backend that calls DocuSign's createInstance with the signer's clientUserId + prefill values and returns { url } - one call from @blinkbitcoin/esign-node (createWebFormInstance) in any Node backend, or this repo's service, which exposes exactly that as POST /webform/instance.
  2. Install exactly as in mode 1 (same minimal packages, still no Apollo).
  3. Point the source at your endpoint:
import { ESignature, createWebFormsSource } from '@blinkbitcoin/esign-react-native/webform';

const source = createWebFormsSource({
  // your endpoint + the app's own session token; the backend mints with
  // @blinkbitcoin/esign-node, so read-only fields come back locked
  mint: { url: 'https://your-backend.example.com/webform/instance', getAuthToken },
  prefill: { number_of_units: '1000', settlement_amount_btc: '0.01268231' }, // locked fields: strings
});
// A GraphQL mutation instead of a POST endpoint: createWebFormsSource({ createInstance })

Completion reaches the app through the instance's return URL: your backend serves the small bridge page (renderSigningReturnBridge) that the component listens to - no DocuSign.js, works in a plain WebView. The whole recipe, backend and app: docs/integration/locked-prefill.md.

Modes 1 and 2 import from the /webform subpath, which is Apollo-free by construction (a guard test walks the import graph to keep it that way). When installing from GitHub Packages add --omit=peer, otherwise npm also drops the unused Apollo peers into node_modules - the registry omits peerDependenciesMeta (details in docs/integration/consuming.md). Web Forms specifics - event model, real-DocuSign caveats: docs/integration/webforms.md.

3. Proxy envelope - full orchestration (this repo's backend service)

Use when: you need real envelope workflows: creation from DocuSign templates, a distinct session per recipient, session restart after expiry, and webhook-driven status tracking in a database. This is the mode the rest of this repo exists for - packages/esign-service (GraphQL service, provider adapters, webhooks) plus the Apollo client wiring.

  1. Deploy this repo's backend (packages/esign-service).
  2. Install the package plus the Apollo peers:
npm i @blinkbitcoin/esign-react-native react-native-webview @react-native-community/netinfo \
      @apollo/client graphql
  1. Wire the client and source from the package root (not /webform):
import {
  ESignature,
  createESignApolloClient,
  createProxySigningSource,
} from '@blinkbitcoin/esign-react-native';
import { ApolloProvider } from '@apollo/client/react';

const client = createESignApolloClient({
  uri: 'https://your-backend.example.com/graphql',
  getAuthToken: () => readTokenFromSecureStorage(),
});
const source = createProxySigningSource({
  client,
  contractType: 'loan_agreement',
  recipient: { name, email },
});

<ApolloProvider client={client}>
  <ESignature source={source} onComplete={…} onError={…} onCancel={…} />
</ApolloProvider>;

Web apps

The web package (@blinkbitcoin/esign-react) mirrors all of the above for React DOM apps (iframe instead of WebView), and adds a DocuSign.js source for real Web Forms embedding on web - see its README.

Packages publish to GitHub Packages under the blinkbitcoin org - registry setup: docs/integration/consuming.md.

The UI: component or hook

Independent of the signing mode above, pick how much of the screen the library draws. Same state machine in every column; the host takes over more from left to right.

Default Themed Headless
Drop in the component Recolor and relabel it Bring your own UI
<ESignature source={source} … /> theme · styles · labels on ESignature useESignature + your own WebView / iframe

Details and code for each path: the package READMEs (RN, web).

Backend options

Modes 2 and 3 need a mint on a backend you control, and there are exactly two tiers to choose between. The app code is identical for both - the same SigningSource calls one endpoint and embeds the URL it gets back.

Tier What you run What your API must provide Capabilities Copy-paste
In-process
@blinkbitcoin/esign-node
The package inside
your own Node API
(router or Fetch
handler)
Your own session check
(authenticate), and the
locked terms from the
prefill hook - which can
reject a mint by throwing
Errors.validationError
Mint; the envelope
domain too, over
your own store
The mint-only preset
Deployable
@blinkbitcoin/esign-service
The package or the
ghcr.io/blinkbitcoin/esign-service
image, as a function
or a container
ESIGN_SESSION_JWKS_URL or
ESIGN_SESSION_SECRET
(who the caller is), plus
ESIGN_PREFILL_URL when the
locked terms come from
your data
Mint always on;
envelopes, webhooks
and GraphQL with
DATABASE_URL
Deploy table

Mode 2 needs no database with the service: the mint is always on, and DATABASE_URL only adds the envelope half. Deploy targets are a Node container, Vercel, a Cloudflare Worker (mint only), Kubernetes, or Lambda via the same image - one row each, with the commands, in the service's Deploy table.

Taking either tier live - DocuSign go-live, the environment, the private key per platform, the boot guard and the verification checklist - is the runbook: docs/operations/production.md.

Repository Layout

Ordered by how likely you are to need each part:

Path What lives there
packages/esign-react-native/ The React Native library you install:
the ESignature component and the signing sources.
packages/esign-react/ The React web library: the same component and sources for
browser apps, embedding with an iframe instead of a WebView.
packages/esign-core/ The shared core both libraries build on: the SigningSource
abstraction and event interpreters, plus the GraphQL client
pieces used by mode 3. It arrives automatically as a
dependency - you never install it directly.
packages/esign-node/ The Node-only server half for your own backend: mint Web Forms
instances with locked prefill in one call, or run the whole
envelope domain (mode 3) over your own store, as a Fetch
handler or an Express router. The service below is built on it.
examples/mint-only-demo/ Server shape for most hosts: your existing API adds one
mutation that mints a locked Web Forms instance.
examples/serverless-handler-demo/ Server shape for route handlers and edge functions: the
package's mint and webhook handlers, Request → Response.
packages/esign-service/ The whole service as one deployable: it always mints, and
adds the GraphQL API, the provider webhook and the
PostgreSQL store when DATABASE_URL is set. Needed only
when you deploy a backend rather than mint from your own.
examples/react-native-demo/ A complete React Native app hosting the component. Used for
manual testing, and the mobile end-to-end suites drive it.
examples/react-demo/ The same for the browser: a small React app hosting the web
component, driven by the browser end-to-end suites.
docs/ Documentation of how everything currently works -
start at docs/index.md; upgrading notes in
docs/upgrading.md.
scripts/ The CI / E2E / release shell and node the Makefile and the
workflows run; its logic is a tested tooling workspace.

Development

Only needed if you're working on the packages or the backend themselves - consuming the packages requires none of this (see Integration above).

One-time setup:

make install                             # npm ci across all workspaces (also installs git hooks)
direnv allow . && direnv allow packages/esign-service  # once per machine (loads env + nix flake dev shell)

Working on the libraries requires nothing else - no backend, no database. The unit suites, lint, and typecheck run standalone:

make test

Running the demo apps is where the backend comes in: the demos sign against a locally running service and its Postgres. By default it uses the mock provider, so no DocuSign account or credentials are needed:

make db-up migrate backend              # dev Postgres + migrations + server (:4100 in the main clone;
                                        # a worktree gets its own port block - `make ports` shows it)

# in a new terminal:
make start                              # Metro
make ios                                # or: make android
make web                                # or the web demo (Vite)

Real DocuSign stays opt-in: configure credentials per docs/integration/docusign-proxy.md, then make test-live verifies the API contracts against a demo account (it skips itself when no credentials are set).

make help lists all targets; the table below is the everyday subset. Every CI job that can run on a laptop has one, and the workflows call it rather than the script underneath - make check-ci fails if those two ever disagree (see Running CI Locally):

Target Purpose
make test Unit suites + lint + typecheck + format check
make unit
make coverage
Test suites (100% coverage on packages + backend + scripts/lib)
make coverage-badge Coverage badge + HTML report from the last make coverage run
make check-code Lint + typecheck + format check only
make build Build the packages (bob for RN, tsup for core/node/web,
tsc for the service)
make e2e-backend
make e2e-web
Backend / browser E2E: test DB up → migrate → tests → teardown (e2e-web
builds the libraries first and bundles the demo against their dist)
make e2e-ios
make e2e-android
Maestro E2E against a running stack
make e2e-ios-local
make e2e-android-local
The whole mobile stack in one command (DB, backend, .app or APK,
Metro, Maestro, teardown); iOS boots a simulator, Android needs a
running emulator. The steps: e2e-backend-up, ios-build /
android-build, e2e-metro-up, e2e-ios / e2e-android
make check-ci Lint the CI itself: actionlint on the workflows, shellcheck on
scripts/**, the dependency audit, and check-parity - the
workflows must call the make targets, not repeat their commands
make check-packages Package shape of the built packages: publint +
arethetypeswrong + the pack/install smoke
make docker-smoke Build the service image and boot it in both modes (mint
only, then with Postgres), as CI's Docker job does
make e2e-server-demos Boot the mint-only and serverless examples on the mock
provider and call their routes
make ports
make ports-free
This worktree's port block (every service, both databases, Metro)
and who holds each port; stop what this worktree left on them
make test-live Opt-in live DocuSign API verification (skips without credentials)
make e2e-live
make e2e-ios-live
Live journeys against real DocuSign (needs make docusign-env): the
locked Web Form submitted and signed inside the web component + a
proxy-mode signature; the same Web Form journey in the React Native
demo's WebView (booted simulator)
make live-web
make live-ios
make live-android
The web demo / the RN demo on the attached phone against real
DocuSign, interactive: .env, a Tailscale Funnel public URL for
Connect webhooks, the service, the demo; waits for the manual rows
of docs/integration/docusign-proxy.md section 5, Ctrl-C tears down
make pods iOS CocoaPods install

Coverage is 100% everywhere, the demo apps included. The HTML report lands in coverage/report/index.html; CI publishes the badge per branch to gh-pages/badges/<branch>/ and uploads the report as the coverage-report artifact of every run.

See docs/development-guide.md for full setup, environment variables, and troubleshooting.

Documentation and contributing

About

Embedded e-signing for React and React Native apps.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages