Already implemented on main across a series of commits referencing this
intake (88c3f2b5, ad234b51, 63936419, 566c49ee, b1b1bc77, and the
sponsorContracts/contractSigning backend modules). Verified live in prod on
2026-11-05. The 2026-10-31 handoff snapshot was stale: no provider purchase or
process switch is needed — DataOps runs its own eIDAS-SES signing flow.
- Record model
backend/src/sponsorContracts/types.ts: statusesdraft → sent → viewed → signed | declined; per-signersignedAt/viewedAt/declinedAt/tokenUsedAt;draftPdf+signedPdf(withsha256); counter-signer and signing-order support. The code comment at line 7 records the legal analysis: "eIDAS leaves these B2B contracts at a Simple Electronic Signature" — which is the level eIDAS requires for B2B service contracts (no QES needed). - Signing flow
backend/src/routes/sponsorContracts.ts:handleSigningRequest(line 374) implements anonymous, token-gated, single-use, expiring signer links with rate limiting (IP-hashed), signer-email binding, in-order signing (sponsor → counter-signer), viewed/signed/declined states with IP + user-agent captured per event, and signing lead-time validation (rejects signing after terms lapse — see also commit053e0669for the backdating guard). - Final signed document: on the last signature the backend renders a flattened signed PDF with an audit page, stores it, and records its SHA-256 for tamper checks.
- Staff surface:
POST /send|resendemails each signer their unique link in signing order;GET /pdf?which=draft|signed;GET /auditper-event trail;POST /verifyrecomputes the stored SHA-256 and recordsverified/tamper-detectedin the audit log. Portal contract dialog on the deal page + standalone signing page (frontend/src/contract-sign-page.js,surfaces/operations/contract-sign.js). - CLI:
dataops sponsors contract create|send|resend|status|audit|verify(cli/src/commands/sponsors.mjs:99-110, 680+). - Gating deal agreed: contract records hang off CRM deals
(
dealIdrequired, 422 on invalid deal reference).
backend/tests/sponsor-contracts.test.ts— 15 tests.frontend/test/sponsor-contract-dialog.test.mjs(deal-page dialog).- Backend typecheck clean on current main.
dataops api GET /api/sponsor-crm/contracts(authenticated) →{"items":[],"nextCursor":null}— endpoint live in prod, no contracts created yet.- Signing page asset served:
GET /src/contract-sign-page.js→ HTTP 200. - Unauthenticated
POST /api/sponsor-crm/contracts/test/sign→{"error":"Unauthorized"}— token/auth gates hold.
Google Workspace eSignature exists and is included in Business Standard and higher at no extra cost; it satisfies eIDAS SES — the same legal level this system already provides. Switching would be a net loss:
- Workspace eSignature has no API/webhooks: status, signed-PDF retrieval and the deal gating/audit chain would become manual, while the built flow already automates send → view → sign → countersign → signed PDF → tamper check → CRM gating.
- The signed document lives in Drive instead of next to the deal with its audit trail.
Google eSignature remains a sensible fallback for one-off documents that are not sponsor deals (no integration needed). For the sponsor contract process, keep the built-in flow; the only genuine follow-up is running the first real contract through it end-to-end.
None in code. First live end-to-end run (real sponsor contract) is an operational milestone, not a code change.