'use client'; import { useState } from 'react'; import { CheckCircle2, HelpCircle, Loader2, XCircle } from 'lucide-react'; import { toast } from 'sonner'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, } from '@/components/ui/sheet'; import { SettingsFormCard, type SettingFieldDef, } from '@/components/admin/shared/settings-form-card'; import { apiFetch } from '@/lib/api/client'; import { toastError } from '@/lib/api/toast-error'; interface TestResult { ok: boolean; host?: string; checks?: Array<{ path: string; status?: number; ok: boolean; error?: string }>; error?: string; at: Date; } const EMBED_FIELDS: SettingFieldDef[] = [ { key: 'embedded_signing_host', label: 'Embedded signing host', description: "Origin of the public site that hosts the embedded Documenso signing pages. Outbound emails wrap raw Documenso signing URLs into {host}/sign// so clients sign on your branded page rather than Documenso's domain. Leave blank to fall back to the app URL. Marketing-website pattern: https://portnimara.com", type: 'string', placeholder: 'https://portnimara.com', defaultValue: '', }, ]; /** * Admin card for the embedded-signing host setting. Provides: * - The setting field itself (via SettingsFormCard) * - A Test connection button that probes the host's `/` and * `/sign/success` paths to verify the marketing-site cutover is * ready BEFORE signers get sent there from outbound emails. * - A Help button that opens a Sheet with the setup instructions — * what routes the marketing site needs, what URL parameters to * handle, and the Documenso webhook config that pairs with it. */ export function EmbeddedSigningCard() { const [testing, setTesting] = useState(false); const [result, setResult] = useState(null); const [helpOpen, setHelpOpen] = useState(false); const handleTest = async () => { setTesting(true); setResult(null); try { const res = (await apiFetch('/api/v1/admin/embedded-signing/test', { method: 'POST', body: {}, })) as { data: { ok: boolean; host?: string; error?: string; checks?: Array<{ path: string; status?: number; ok: boolean; error?: string }>; }; }; setResult({ ...res.data, at: new Date() }); if (res.data.ok) toast.success('Embedded signing host reachable.'); else toast.error('Embedded signing host probe failed - see card.'); } catch (err) { toastError(err); setResult({ ok: false, error: err instanceof Error ? err.message : String(err), at: new Date(), }); } finally { setTesting(false); } }; return ( <>
Embedded signing Where the public-facing branded signing pages live. The CRM rewrites Documenso signing URLs to point here when sending invitation and reminder emails.
{/* Renders inside our outer Card with its own micro-header. Title kept terse (empty string would look broken) so the user still has a visual anchor for the field. */}

Probes / and /sign/success on the configured host.

{result ? (
{result.ok ? ( ) : ( )}

{result.ok ? 'Connection ok' : 'Connection failed'}

{result.host ? (

Host: {result.host}

) : null} {result.error ?

{result.error}

: null} {result.checks ? (
    {result.checks.map((c) => (
  • {c.path} →{' '} {c.ok ? ( {c.status ?? 'ok'} ) : ( {c.status ? `${c.status} fail` : (c.error ?? 'fail')} )}
  • ))}
) : null}

{result.at.toLocaleTimeString()}

) : null}
Set up embedded signing How the marketing site has to be wired up so the branded signing flow works.

1. Choose the host

Pick a public host (e.g. https://portnimara.com) and enter it in the Embedded signing host field above. The CRM rewrites raw Documenso signing URLs into{' '} {'{host}/sign//'} for every outbound invitation + reminder email.

2. Implement the signing route

The marketing site needs to handle /sign/[role]/[token] by forwarding to the underlying Documenso signing URL (or embedding it in an iframe). Role is one of client / developer / approver - useful for tracking which slot the signer is in.

Minimum Next.js example:

                {`// app/sign/[role]/[token]/page.tsx
export default function SignPage({ params }) {
  const documenseUrl = \`\${env.DOCUMENSO_URL}/sign/\${params.token}\`;
  return