9.3 KiB
Website Implementation Guide for Embedded Documenso Signing
Task Summary
Implement embedded Documenso signing on the Port Nimara website to provide a seamless, branded signing experience for Expression of Interest (EOI) documents.
Context
The Port Nimara client portal (separate repository) generates EOI documents via a self-hosted Documenso instance and now creates embedded signing URLs that should be handled by the main website at portnimara.com.
Technical Specifications
Website Stack
- Framework: Nuxt 3 (v3.15.4) with Vue 3
- Styling: Tailwind CSS
- TypeScript: Enabled
- Package to Install:
@documenso/embed-vue
Documenso Configuration
- Host:
https://signatures.portnimara.dev - Self-hosted community edition (full embedding features available)
- Authentication: Handled via tokens (no API keys needed on website)
Brand Assets
- Logos:
/images/logo-full.png,/images/logo-no-text.png,/images/logo-no-text-white.png - Primary Color:
#0056b3 - Background:
#ffffff,#f8f9fa - Fonts: Custom fonts in
/public/fonts/(Bill Corporate, Garamond, Runalto)
URL Structure to Implement
Route Pattern
/sign/[type]/[token]
URL Examples
Client signing: https://portnimara.com/sign/client/ABC123
CC approval: https://portnimara.com/sign/cc/DEF456
Developer signing: https://portnimara.com/sign/developer/GHI789
Route Parameters
- type:
'client' | 'cc' | 'developer' - token: Alphanumeric string from Documenso (e.g.,
ABC123,mK9N2pL5vX8)
Required Implementation
1. Install Dependencies
npm install @documenso/embed-vue
2. Create Main Signing Page
File: pages/sign/[type]/[token].vue
Key Requirements:
- Validate route parameters (type and token)
- Extract token and signer type from route
- Display appropriate messaging per signer type
- Embed Documenso signing interface
- Handle successful signing with redirect
- Apply Port Nimara branding throughout
Signer Type Messages:
const signerMessages = {
client: {
title: 'Sign Your Expression of Interest',
subtitle: 'Please review and sign your Port Nimara berth EOI'
},
cc: {
title: 'Approve Expression of Interest',
subtitle: 'Please review and approve this Port Nimara berth EOI'
},
developer: {
title: 'Review and Sign Expression of Interest',
subtitle: 'Please review and sign this Port Nimara berth EOI'
}
};
Embed Configuration:
// Use these exact values
const documensoHost = 'https://signatures.portnimara.dev';
// CSS variables for brand consistency
const cssVars = {
primary: '#0056b3',
primaryForeground: '#ffffff',
background: '#ffffff',
foreground: '#1a1a1a',
secondary: '#f8f9fa',
secondaryForeground: '#1a1a1a',
accent: '#e9ecef',
accentForeground: '#1a1a1a',
destructive: '#dc3545',
destructiveForeground: '#ffffff',
border: '#dee2e6',
input: '#ffffff',
ring: '#0056b3',
radius: '0.5rem'
};
3. Create Success Page
File: pages/sign/success.vue
Requirements:
- Thank you message
- Confirmation that document was signed
- Link back to main Port Nimara website
- Professional, clean design
4. Create Error Page
File: pages/sign/error.vue
Requirements:
- Handle invalid/expired tokens
- Provide contact information
- Link to support or main website
5. Add Environment Configuration
Add to your .env file:
WEBHOOK_SECRET_SIGNING=96BQQRiKkTIN2w0rHbqo7yHggV/sT8702HtHih3uNSY=
Implementation Template
Main Signing Page Structure
<template>
<div class="min-h-screen bg-gray-50">
<!-- Header with Port Nimara branding -->
<header class="bg-white shadow-sm border-b">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
<div class="flex items-center justify-between">
<img src="/images/logo-full.png" alt="Port Nimara" class="h-12" />
<div class="text-right">
<h1 class="text-2xl font-bold text-gray-900">{{ pageTitle }}</h1>
<p class="text-sm text-gray-600">{{ pageSubtitle }}</p>
</div>
</div>
</div>
</header>
<!-- Main signing interface -->
<main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div class="bg-white rounded-lg shadow-lg overflow-hidden">
<!-- Loading state -->
<div v-if="loading" class="flex items-center justify-center h-96">
<div class="text-center">
<div class="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto"></div>
<p class="mt-4 text-gray-600">Loading document...</p>
</div>
</div>
<!-- Embedded Documenso interface -->
<EmbedSignDocument
v-else
:token="token"
:host="documensoHost"
:on-document-signed="handleDocumentSigned"
:on-document-viewed="handleDocumentViewed"
:dark-mode-disabled="false"
class="w-full h-[800px]"
:css-vars="cssVars"
/>
</div>
</main>
</div>
</template>
<script setup>
import { EmbedSignDocument } from '@documenso/embed-vue';
// ... implementation details in full guide
</script>
Validation Requirements
Route Parameter Validation
// Validate signer type
const validSignerTypes = ['client', 'cc', 'developer'];
if (!validSignerTypes.includes(signerType.value)) {
router.push('/sign/error');
}
// Validate token format (basic)
const tokenPattern = /^[a-zA-Z0-9_-]+$/;
if (!tokenPattern.test(token.value)) {
router.push('/sign/error');
}
Event Handling
Document Signed Handler
const handleDocumentSigned = async () => {
console.log('Document signed successfully');
// Optional: Webhook notification (Phase 2)
// [Webhook code - initially commented out]
// Always redirect to success
router.push('/sign/success');
};
Document Viewed Handler
const handleDocumentViewed = () => {
console.log('Document viewed:', { token: token.value, type: signerType.value });
// Optional: Analytics tracking
};
Mobile Responsiveness
Ensure the implementation works well on mobile devices:
/* Example responsive adjustments */
@media (max-width: 768px) {
.signing-header h1 {
font-size: 1.25rem;
}
.documenso-embed {
height: 600px; /* Reduced height for mobile */
}
}
Testing Approach
1. Manual Testing
Once implemented, you can test with these example URLs:
http://localhost:3000/sign/client/test123http://localhost:3000/sign/cc/test456http://localhost:3000/sign/developer/test789
2. Real Token Testing
After the client portal generates a real EOI, you'll receive actual tokens to test with.
CORS Requirements
The Documenso instance needs CORS headers to allow embedding. This is handled manually via nginx configuration on the server.
Expected CORS headers from signatures.portnimara.dev:
Access-Control-Allow-Origin: https://portnimara.com
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Credentials: true
Error Handling
Common Scenarios
- Invalid token: Redirect to error page
- Invalid signer type: Redirect to error page
- Expired token: Documenso will handle this within the embed
- Network issues: Show loading state, let Documenso handle retries
Success Criteria
✅ Functionality:
- Signing interface loads correctly
- Different messaging for each signer type
- Successful signing redirects to success page
- Invalid URLs redirect to error page
✅ User Experience:
- Port Nimara branding throughout
- Mobile responsive design
- Fast loading times
- Clear error messages
✅ Technical:
- Clean URL structure
- Proper error handling
- Console logging for debugging
- Ready for webhook integration
Webhook Integration (Phase 2)
Initially implement with webhook code commented out
// Future webhook endpoint:
// https://client-portal.portnimara.com/api/webhook/document-signed
const notifyPortal = async () => {
try {
await $fetch('https://client-portal.portnimara.com/api/webhook/document-signed', {
method: 'POST',
headers: {
'x-webhook-secret': process.env.WEBHOOK_SECRET_SIGNING
},
body: {
token: token.value,
signerType: signerType.value,
signedAt: new Date().toISOString()
}
});
} catch (error) {
console.error('Webhook failed:', error);
// Don't block user flow
}
};
Questions & Support
If you encounter any issues during implementation:
- CORS errors: The server-side CORS configuration may need adjustment
- Token format issues: Check that tokens are being extracted correctly
- Embedding not loading: Verify the Documenso host URL and package installation
- Styling issues: Refer to the CSS variables provided for brand consistency
Next Steps After Implementation
- Test with real tokens from generated EOIs
- Verify mobile experience on actual devices
- Coordinate with client portal for email template updates
- Monitor for any CORS issues in production
- Prepare for webhook integration in Phase 2
This guide provides everything needed to implement embedded signing on the Port Nimara website. The client portal changes are already complete and ready to generate the embedded URLs this implementation will handle.