> ## Documentation Index
> Fetch the complete documentation index at: https://docs.firma.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Signature intégrable

> Intégrez l'expérience de signature dans votre produit afin que les signataires puissent compléter les documents sans quitter votre application.

# 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

```html theme={null}
<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

```js theme={null}
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

```jsx theme={null}
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)

```js theme={null}
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 :

```js theme={null}
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é

<Warning>
  **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é.
</Warning>

### ✅ À 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](rate-limits).

## 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 :

```js theme={null}
// 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](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

* [Envoyer des demandes de signature](sending-signing-request) avec des modèles personnalisés
* [Configurer des webhooks](webhooks) pour des notifications d'événements en temps réel (60 req/min)
* [Paramètres de l'espace de travail](workspace-settings) pour personnaliser l'image de marque des e-mails (100-200 req/min)
