Signature intégrable
Intégrez le flux de signature directement dans votre application afin que les destinataires puissent signer des documents sans quitter votre produit. Cela crée une expérience de signature fluide et en marque blanche.
Modèle d’URL de signature
L’interface de signature est disponible à l’adresse :
https://app.firma.dev/signing/{signing_request_user_id}
Intégration iframe de base
<iframe
src="https://app.firma.dev/signing/{signing_request_user_id}"
style="width:100%;height:900px;border:0;"
allow="camera;microphone;clipboard-write"
title="Document Signing"
></iframe>
Permissions iframe requises
camera - Pour la vérification d’identité (si activée)
microphone - Pour la vérification vidéo (si activée)
clipboard-write - Pour copier/coller du contenu
Obtenir l’URL de signature
Le signing_request_user_id est renvoyé lorsque vous récupérez les utilisateurs de la demande de signature via l’API.
Exemple : obtenir les URL de signature des destinataires
const response = await fetch(
`https://api.firma.dev/functions/v1/signing-request-api/signing-requests/${signingRequestId}/users`,
{
headers: {
'Authorization': `Bearer ${API_KEY}`
}
}
)
const users = await response.json()
// Each user has their own signing URL
users.forEach(user => {
const signingUrl = `https://app.firma.dev/signing/${user.id}`
console.log(`${user.email}: ${signingUrl}`)
})
Exemple d’implémentation complète
Composant React
import { useState, useEffect } from 'react'
function SigningView({ signingRequestId, recipientEmail }) {
const [signingUrl, setSigningUrl] = useState(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
useEffect(() => {
async function loadSigningUrl() {
try {
// Fetch signing request users from your backend
const response = await fetch(
`/api/signing-requests/${signingRequestId}/users`,
{ credentials: 'include' }
)
if (!response.ok) {
throw new Error('Failed to load signing users')
}
const users = await response.json()
// Find user by email
const user = users.find(
u => u.email === recipientEmail
)
if (!user) {
throw new Error('User not found')
}
const url = `https://app.firma.dev/signing/${user.id}`
setSigningUrl(url)
setLoading(false)
} catch (err) {
setError(err.message)
setLoading(false)
}
}
loadSigningUrl()
}, [signingRequestId, recipientEmail])
if (loading) return <div>Loading signing interface...</div>
if (error) return <div>Error: {error}</div>
return (
<iframe
src={signingUrl}
style={{
width: '100%',
height: '900px',
border: 0
}}
allow="camera;microphone;clipboard-write"
title="Sign Document"
/>
)
}
Point de terminaison côté serveur (Node.js)
import express from 'express'
import fetch from 'node-fetch'
const app = express()
const API_KEY = process.env.FIRMA_API_KEY
app.get('/api/signing-requests/:id/users', async (req, res) => {
try {
const response = await fetch(
`https://api.firma.dev/functions/v1/signing-request-api/signing-requests/${req.params.id}/users`,
{
headers: {
'Authorization': `Bearer ${API_KEY}`
}
}
)
if (!response.ok) {
const error = await response.text()
return res.status(response.status).json({ error })
}
const users = await response.json()
res.json(users)
} catch (error) {
console.error('Failed to fetch signing users:', error)
res.status(500).json({ error: 'Internal server error' })
}
})
app.listen(3000)
Événements postMessage
L’iframe de signature émet des événements postMessage pour suivre la progression de la signature :
window.addEventListener('message', (event) => {
// Validate origin
if (event.origin !== 'https://app.firma.dev') return
const data = event.data
switch (data.type) {
case 'signing.started':
console.log('User started signing')
break
case 'signing.completed':
console.log('Document signed successfully')
// Redirect or show success message
break
case 'signing.declined':
console.log('User declined to sign')
break
case 'signing.error':
console.error('Signing error:', data.error)
break
}
})
Bonnes pratiques de sécurité
N’exposez jamais de clés API dans le code frontend. Récupérez toujours les ID d’utilisateur de signature via un point de terminaison backend sécurisé.
✅ À faire
- ✅ Récupérez les ID d’utilisateur de signature via votre backend
- ✅ Validez les origines des postMessage (
https://app.firma.dev)
- ✅ Utilisez HTTPS pour toutes les requêtes API
- ✅ Surveillez les événements de signature via des webhooks (60 req/min)
❌ À ne pas faire
- ❌ N’exposez pas de clés API dans le code client
- ❌ Ne faites pas confiance aux données postMessage sans validation de l’origine
Limites de débit
Consultez le guide sur les Limites de débit.
Intégration des webhooks
Utilisez des webhooks pour suivre les événements de signature en temps réel plutôt que de faire du polling :
// Webhook handler
app.post('/webhooks/firma', async (req, res) => {
const event = req.body
switch (event.event_type) {
case 'signing_request.viewed':
await notifyUser(event.data.signing_request_id, 'viewed')
break
case 'signing_request.recipient.signed':
await notifyUser(event.data.signing_request_id, 'signed')
break
case 'signing_request.completed':
await markComplete(event.data.signing_request_id)
break
}
res.json({ received: true })
})
Consultez le guide des Webhooks pour tous les détails d’implémentation.
Dépannage
L’iframe ne se charge pas
Causes possibles :
signing_request_user_id invalide
- Le destinataire a déjà terminé la signature
- La demande de signature a été annulée ou a expiré
Solution : vérifiez le statut de la demande de signature via l’API
Les événements postMessage ne sont pas reçus
Causes possibles :
- La validation de l’origine bloque les messages
- L’écouteur d’événement n’est pas attaché avant le chargement de l’iframe
Solution :
- Vérifiez que l’origine est exactement
https://app.firma.dev
- Attachez l’écouteur avant de créer l’iframe
403 Forbidden lors de la récupération de la demande de signature
Causes possibles :
- L’utilisateur n’a pas accès à la demande de signature
- La clé API ne dispose pas des permissions requises
Solution : vérifiez la logique d’autorisation et les permissions de la clé API
Étapes suivantes