Passer au contenu principal
Firma vous permet d’ajouter des signatures électroniques juridiquement contraignantes à tout ce qui est construit sur Gemini. Utilisez le function calling de l’API Gemini pour permettre à vos agents IA d’envoyer des demandes de signature à la demande, connectez les serveurs MCP de Firma au Gemini CLI pour que l’IA génère du code d’intégration précis, ou configurez des webhooks pour suivre les finalisations. Ce guide couvre les trois approches.

Prérequis

  • Un compte Firma avec une clé API
  • Un compte Google AI Studio avec une clé API Gemini, ou Gemini Code Assist installé dans votre IDE
  • Au moins un modèle Firma avec des champs de signature configurés
Firma utilise la clé API brute comme valeur de l’en-tête Authorization - ne la préfixez pas avec Bearer. Cela diffère de l’API Gemini elle-même, qui utilise x-goog-api-key.

Prise en main

Le function calling permet à un modèle Gemini de décider quand appeler votre code. Vous déclarez une fonction que le modèle peut appeler, et lorsque l’utilisateur lui demande d’envoyer un contrat, Gemini renvoie un appel d’outil structuré que votre application exécute contre l’API Firma.Cette approche convient parfaitement aux agents IA, aux chatbots et à toute application où une demande utilisateur en langage naturel doit se transformer en demande de signature.

Étape 1 : Stockez vos clés API

Conservez votre clé API Gemini et votre clé API Firma dans des variables d’environnement. Ne les placez jamais dans du code frontend.
GEMINI_API_KEY=your_gemini_key
FIRMA_API_KEY=your_firma_key

Étape 2 : Déclarez une fonction et gérez les appels d’outils

Déclarez la fonction dans le format attendu par Gemini, puis exécutez une boucle de chat qui gère l’appel d’outil en appelant le point de terminaison create-and-send de Firma.
import { GoogleGenAI, Type } from "@google/genai";

const FIRMA_API = "https://api.firma.dev/functions/v1/signing-request-api";

const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });

const sendSigningRequestDeclaration = {
  name: "send_signing_request",
  description:
    "Send a document for e-signature via Firma. Uses a pre-configured template and one signer.",
  parameters: {
    type: Type.OBJECT,
    properties: {
      name: {
        type: Type.STRING,
        description: "A descriptive name for the signing request.",
      },
      template_id: {
        type: Type.STRING,
        description: "The ID of the Firma template to send.",
      },
      signer_email: { type: Type.STRING },
      signer_first_name: { type: Type.STRING },
      signer_last_name: { type: Type.STRING },
    },
    required: [
      "name",
      "template_id",
      "signer_email",
      "signer_first_name",
      "signer_last_name",
    ],
  },
};

async function sendSigningRequest(args) {
  const response = await fetch(
    `${FIRMA_API}/signing-requests/create-and-send`,
    {
      method: "POST",
      headers: {
        Authorization: process.env.FIRMA_API_KEY,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        name: args.name,
        template_id: args.template_id,
        recipients: [
          {
            first_name: args.signer_first_name,
            last_name: args.signer_last_name,
            email: args.signer_email,
            designation: "Signer",
            order: 1,
          },
        ],
      }),
    }
  );

  const data = await response.json();
  if (!response.ok) {
    return { error: data };
  }

  return {
    signing_request_id: data.id,
    status: "sent",
    signing_link: data.first_signer?.signing_link,
  };
}

export async function handleUserMessage(userText) {
  const result = await ai.models.generateContent({
    model: "gemini-2.5-flash",
    contents: userText,
    config: {
      tools: [{ functionDeclarations: [sendSigningRequestDeclaration] }],
    },
  });

  const call = result.functionCalls?.[0];
  if (!call) {
    return { text: result.text };
  }

  const toolResult = await sendSigningRequest(call.args);

  const followUp = await ai.models.generateContent({
    model: "gemini-2.5-flash",
    contents: [
      { role: "user", parts: [{ text: userText }] },
      { role: "model", parts: [{ functionCall: call }] },
      {
        role: "user",
        parts: [
          {
            functionResponse: {
              name: call.name,
              response: toolResult,
            },
          },
        ],
      },
    ],
  });

  return { text: followUp.text, result: toolResult };
}
Le point de terminaison create-and-send crée la demande de signature et l’envoie aux destinataires en un seul appel API. Si vous avez besoin que le modèle rédige une demande à valider avant l’envoi, utilisez POST /signing-requests pour créer un brouillon, puis POST /signing-requests/{id}/send une fois celui-ci approuvé.

Étape 3 : Intégrez-la à votre application

Appelez handleUserMessage depuis votre backend chaque fois que l’utilisateur envoie un message de chat. Gemini décide seul du moment où la demande justifie un flux de signature :
// e.g. an Express handler
app.post("/chat", async (req, res) => {
  const reply = await handleUserMessage(req.body.message);
  res.json(reply);
});
Un message utilisateur du type « Envoie le contrat de conseil à alice@acme.com » déclenchera l’appel de fonction. Le modèle renvoie le functionCall structuré, votre code l’exécute contre Firma, et le tour de suivi permet à Gemini de résumer le résultat à l’utilisateur.

Alternative Python

Le même flux avec le SDK Python :
import os
import requests
from google import genai
from google.genai import types

FIRMA_API = "https://api.firma.dev/functions/v1/signing-request-api"
client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])

send_signing_request = {
    "name": "send_signing_request",
    "description": "Send a document for e-signature via Firma.",
    "parameters": {
        "type": "object",
        "properties": {
            "name": {"type": "string", "description": "A descriptive name for the signing request."},
            "template_id": {"type": "string"},
            "signer_email": {"type": "string"},
            "signer_first_name": {"type": "string"},
            "signer_last_name": {"type": "string"},
        },
        "required": [
            "name",
            "template_id",
            "signer_email",
            "signer_first_name",
            "signer_last_name",
        ],
    },
}

def execute(args):
    r = requests.post(
        f"{FIRMA_API}/signing-requests/create-and-send",
        headers={
            "Authorization": os.environ["FIRMA_API_KEY"],
            "Content-Type": "application/json",
        },
        json={
            "name": args["name"],
            "template_id": args["template_id"],
            "recipients": [
                {
                    "first_name": args["signer_first_name"],
                    "last_name": args["signer_last_name"],
                    "email": args["signer_email"],
                    "designation": "Signer",
                    "order": 1,
                }
            ],
        },
    )
    return r.json()

result = client.models.generate_content(
    model="gemini-2.5-flash",
    contents="Send the NDA template to bob@example.com (Bob Smith).",
    config=types.GenerateContentConfig(
        tools=[types.Tool(function_declarations=[send_signing_request])]
    ),
)

call = result.function_calls[0]
print(execute(call.args))

Intégration de webhooks

Pour suivre les événements de signature en temps réel, enregistrez un webhook Firma pointant vers un point de terminaison de votre application. Voici un gestionnaire minimal pour signing_request.completed :
// e.g. app/api/firma-webhook/route.ts
import { NextRequest, NextResponse } from "next/server";

export async function POST(req: NextRequest) {
  const { type, data } = await req.json();

  if (type === "signing_request.completed") {
    const signingRequestId = data.signing_request.id;
    // Update your DB, trigger downstream automation, etc.
  }

  return NextResponse.json({ received: true });
}
Dans le tableau de bord Firma, sous Paramètres > Webhooks, enregistrez l’URL de votre point de terminaison. Firma envoie des événements pour tous les changements d’état majeurs. Consultez le guide des webhooks pour la liste complète des événements et la vérification de signature.
Vérifiez toujours la signature du webhook à l’aide de votre secret de signature de webhook Firma en production. Consultez le guide des webhooks pour les détails d’implémentation.

Signature intégrée

Pour les applications où les signataires complètent les documents directement dans votre interface plutôt que d’ouvrir une page hébergée par Firma, la réponse de create-and-send inclut first_signer.id (le signing_request_user_id) ainsi qu’un first_signer.signing_link prêt à l’emploi. Chargez l’URL du signataire dans un iframe :
<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>
Consultez le guide de la signature intégrée pour la configuration complète, y compris les bonnes pratiques de sécurité.

Astuces

  • Utilisez le function calling pour les agents en exécution, et MCP pour la phase de construction. Le function calling est ce que votre application déployée utilise pour réellement envoyer des documents. Les serveurs MCP sont ce que vous et le Gemini CLI utilisez pour construire cette intégration.
  • Passez template_id comme argument d’outil, pas comme chaîne libre. Les modèles constituent le moyen le plus sûr de limiter ce que le modèle peut envoyer. Laissez le modèle choisir un ID de modèle dans une liste courte et nommée que vous contrôlez.
  • Validez avant l’envoi. Pour les documents à plus fort enjeu, prévoyez une étape de confirmation. Utilisez POST /signing-requests pour créer un brouillon, présentez-le à l’utilisateur, puis appelez POST /signing-requests/{id}/send uniquement après sa confirmation.
  • Espaces de travail pour les applications multi-tenant. Si vous construisez un produit SaaS sur Gemini, attribuez à chaque client final son propre espace de travail Firma afin que les modèles et l’utilisation restent isolés.

Étapes suivantes