Embeddable signing
Embed the signing flow directly in your application so recipients can sign documents without leaving your product. This creates a seamless, white-label signing experience.
Signing URL pattern
The signing interface is available at:
https://app.firma.dev/signing/{signing_request_user_id}
Basic iframe embed
Required iframe permissions
camera - For identity verification (if enabled)
microphone - For video verification (if enabled)
clipboard-write - For copying/pasting content
Getting the signing URL
The signing_request_user_id is returned when you fetch signing request users via the API.
Example: Get recipient signing URLs
Complete implementation example
React component
Server-side endpoint (Node.js)
postMessage events
The signing iframe emits postMessage events for tracking signing progress:
Security best practices
Never expose API keys in frontend code. Always fetch signing user IDs through a secure backend endpoint.
✅ Do’s
- ✅ Fetch signing user IDs through your backend
- ✅ Validate postMessage origins (
https://app.firma.dev)
- ✅ Use HTTPS for all API requests
- ✅ Monitor signing events via webhooks (60 req/min)
❌ Don’ts
- ❌ Don’t expose API keys in client code
- ❌ Don’t trust postMessage data without origin validation
Rate Limits
See the guide on Rate Limits.
Webhook integration
Use webhooks to track signing events in real-time instead of polling:
See the Webhooks guide for complete implementation details.
Troubleshooting
Iframe not loading
Possible causes:
- Invalid
signing_request_user_id
- Recipient already completed signing
- Signing request was cancelled or expired
Solution: Verify signing request status via API
postMessage events not received
Possible causes:
- Origin validation blocking messages
- Event listener not attached before iframe loads
Solution:
- Check origin is exactly
https://app.firma.dev
- Attach listener before creating iframe
403 Forbidden when fetching signing request
Possible causes:
- User doesn’t have access to the signing request
- API key lacks required permissions
Solution: Verify authorization logic and API key permissions
Next steps