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

# Mistral

> Agrega firmas electrónicas legalmente vinculantes a cualquier agente, conversación o flujo de trabajo Vibe de Mistral usando el Conector MCP de Firma.

Agrega firmas electrónicas legalmente vinculantes a cualquier agente, conversación o flujo de trabajo Vibe de Mistral registrando el servidor MCP de Firma como un Conector.

Firma expone su API como un servidor MCP, lo que significa que un agente de Mistral puede enviar solicitudes de firma, listar plantillas y consultar el estado de firma como llamadas nativas a herramientas. Esta guía cubre tres rutas de integración:

1. **Registrar Firma como Conector** - El patrón nativo de Mistral. Registra el servidor MCP de Firma una vez y luego úsalo en conversaciones, agentes y Vibe. Ideal para la mayoría de los casos de uso.
2. **Function calling con la Chat Completions API** - Define las operaciones de Firma como herramientas manualmente. Ideal cuando quieres control total sobre el esquema de la herramienta o aún no usas Conectores.
3. **Conector personalizado (autenticación inline)** - Pasa la URL del servidor MCP de Firma y la clave API en el momento de la conversación sin registrarla previamente. Útil para configuraciones multi-tenant donde cada usuario tiene su propia clave de Firma.

## Requisitos previos

* Una [cuenta de Firma](https://app.firma.dev) con una clave API
* Una cuenta de Mistral con acceso API a Studio (`https://console.mistral.ai`)
* Al menos una plantilla de Firma con campos de firma configurados
* El SDK de Python `mistralai`: `pip install mistralai`
* La librería `requests` (para la Ruta 2): `pip install requests`

<Note>
  Firma usa la clave API sin procesar como valor del encabezado `Authorization`. No la antepongas con `Bearer`. Esto difiere de muchas otras API.
</Note>

## Ruta 1: Registrar Firma como Conector

Esta es la ruta más limpia. Registras el servidor MCP de Firma una vez, y todas las superficies de Mistral - la Conversations API, la Agents API y Vibe - pueden usar las herramientas de Firma de inmediato.

### Paso 1: Registrar el Conector

```python theme={null}
import asyncio
import os
from mistralai.client import Mistral

client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])


async def main() -> None:
    connector = await client.beta.connectors.create_async(
        name="firma",
        description="Send and manage e-signature requests via Firma",
        server="https://mcp.firma.dev/mcp",
        auth_type="api-key",
        auth_value=os.environ["FIRMA_API_KEY"],
    )
    print(f"Connector ID: {connector.id}")


asyncio.run(main())
```

El Conector queda registrado de forma centralizada en tu espacio de trabajo de Mistral. La clave API de Firma se almacena cifrada y nunca llega al modelo ni al usuario final.

<Warning>
  Nunca coloques `FIRMA_API_KEY` en código del lado del cliente ni la subas a un repositorio. Guárdala en el gestor de secretos de tu plataforma de despliegue y cárgala en tiempo de ejecución.
</Warning>

### Paso 2: Usar el Conector en una conversación

Pasa el ID del Conector en el array `tools` y deja que el modelo decida qué herramienta de Firma llamar:

```python theme={null}
import asyncio
import os
from mistralai.client import Mistral

client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])


async def main() -> None:
    response = await client.beta.conversations.start_async(
        model="mistral-large-latest",
        inputs=[
            {"role": "user", "content": "Send a signing request for template tmpl_abc123 to Alice Johnson at alice@example.com."}
        ],
        tools=[{"type": "connector", "connector_id": "<firma_connector_id>"}],
    )
    for output in response.outputs:
        if output.type == "message.output":
            print(output.content)


asyncio.run(main())
```

El modelo inspecciona las herramientas expuestas por el Conector, elige la correcta (p. ej., `signing_requests_create_and_send`), construye los argumentos a partir del mensaje del usuario y la ejecuta.

### Paso 3: Construir un agente de Firma persistente

Para un agente reutilizable que siempre tenga disponibles las herramientas de Firma, créalo una vez y conversa con él más tarde:

```python theme={null}
import asyncio
import os
from mistralai.client import Mistral

client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])


async def main() -> None:
    agent = await client.beta.agents.create_async(
        name="signing_agent",
        description="Sends and tracks e-signature requests with Firma.",
        model="mistral-large-latest",
        instructions=(
            "You help users send and track signing requests with Firma. "
            "Always confirm signer name and email before calling create_and_send. "
            "After sending, return the signing_request_id so the user can reference it later."
        ),
        tools=[{"type": "connector", "connector_id": "<firma_connector_id>"}],
    )
    print(f"Agent ID: {agent.id}")


asyncio.run(main())
```

Este mismo agente se puede usar desde Vibe y desde tu propio producto a través de la Conversations API.

## Ruta 2: Function calling con la Chat Completions API

Si prefieres definir las herramientas de Firma tú mismo, expónlas como funciones en el endpoint de Chat Completions. Esto funciona en cualquier modelo de Mistral que admita tool calling.

```python theme={null}
import json
import os
import requests
from mistralai.client import Mistral

FIRMA_API = "https://api.firma.dev/functions/v1/signing-request-api"
mistral = Mistral(api_key=os.environ["MISTRAL_API_KEY"])

tools = [
    {
        "type": "function",
        "function": {
            "name": "create_and_send_signing_request",
            "description": "Create a signing request from a Firma template and send it to a recipient.",
            "parameters": {
                "type": "object",
                "properties": {
                    "template_id": {"type": "string"},
                    "first_name": {"type": "string"},
                    "email": {"type": "string"},
                    "last_name": {"type": "string"},
                },
                "required": ["template_id", "first_name", "email"],
            },
        },
    }
]


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


messages = [
    {"role": "user", "content": "Send template tmpl_abc123 to Alice Johnson, alice@example.com."}
]

completion = mistral.chat.complete(
    model="mistral-large-latest",
    messages=messages,
    tools=tools,
    tool_choice="auto",
)

tool_call = completion.choices[0].message.tool_calls[0]
args = json.loads(tool_call.function.arguments)
result = create_and_send_signing_request(**args)
print(result)
```

<Note>
  El endpoint `create-and-send` crea la solicitud de firma y la envía por correo a los destinatarios en una sola llamada. Solo se requieren `first_name` y `email` para cada destinatario. Si quieres un paso de revisión mediado por el modelo antes de enviar, usa `POST /signing-requests` para crear un borrador, y luego `POST /signing-requests/{id}/send` por separado.
</Note>

## Ruta 3: Conector personalizado (autenticación inline)

Cuando no quieras pre-registrar un Conector (p. ej., cada usuario tiene su propia clave API de Firma), pasa la URL del servidor MCP y las credenciales de forma inline:

```python theme={null}
import asyncio
import os
from mistralai.client import Mistral

client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])


async def main() -> None:
    response = await client.beta.conversations.start_async(
        model="mistral-large-latest",
        inputs=[
            {"role": "user", "content": "Send template tmpl_abc123 to Alice at alice@example.com."}
        ],
        tools=[
            {
                "type": "custom-connector",
                "connector_id": "firma",
                "authorization": {
                    "type": "api-key",
                    "value": os.environ["FIRMA_API_KEY"],
                },
                "tool_configuration": {
                    "type": "custom",
                    "url": "https://mcp.firma.dev/mcp",
                },
            }
        ],
    )
    for output in response.outputs:
        if output.type == "message.output":
            print(output.content)


asyncio.run(main())
```

Esto es útil para configuraciones multi-tenant donde cada cliente tiene un espacio de trabajo de Firma y una clave API independientes.

## Integración de webhooks: reaccionar cuando se firman documentos

Firma envía webhooks para cada cambio de estado en una solicitud de firma. El patrón típico con agentes de Mistral es recibir el webhook en tu propio backend y luego actualizar tu base de datos directamente o devolver el evento a un agente para acciones de seguimiento.

Handler mínimo en FastAPI:

```python theme={null}
import os
from fastapi import FastAPI, Request
from mistralai.client import Mistral

app = FastAPI()
mistral = Mistral(api_key=os.environ["MISTRAL_API_KEY"])


@app.post("/webhooks/firma")
async def firma_webhook(request: Request):
    payload = await request.json()
    event_type = payload.get("type")
    data = payload.get("data", {})

    if event_type == "signing_request.completed":
        signing_request_id = data["signing_request"]["id"]
        # Option A: update your database and move on.
        # Option B: hand the event to an agent for follow-up.
        await mistral.beta.conversations.start_async(
            agent_id="<signing_agent_id>",
            inputs=[
                {
                    "role": "user",
                    "content": (
                        f"Signing request {signing_request_id} was just completed. "
                        "Update the deal in our CRM and email the account owner."
                    ),
                }
            ],
        )

    return {"received": True}
```

Registra el endpoint en Firma en **Configuración > Webhooks**.

<Warning>
  Para uso en producción, verifica las firmas de los webhooks usando el secreto de webhook de tu Firma y HMAC-SHA256. Agrega esta verificación al inicio de tu handler:
</Warning>

```python theme={null}
import hashlib
import hmac

def verify_signature(payload: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(), payload, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)


@app.post("/webhooks/firma")
async def firma_webhook(request: Request):
    body = await request.body()
    signature = request.headers.get("x-firma-signature", "")
    if not verify_signature(body, signature, os.environ["FIRMA_WEBHOOK_SECRET"]):
        return JSONResponse(status_code=401, content={"error": "invalid signature"})
    payload = json.loads(body)
    # ... handle event
```

Consulta la [guía de webhooks](/es/guides/webhooks) para conocer todos los tipos de eventos y formatos de payload.

## Vibe: flujos de trabajo de firma electrónica para usuarios finales

Una vez que el Conector de Firma esté registrado en tu espacio de trabajo de Mistral, los usuarios de Vibe pueden invocar Firma directamente desde el chat. Prompts típicos:

* "Envía el NDA estándar a [alice@example.com](mailto:alice@example.com)."
* "¿Cuál es el estado del contrato que envié a Bob ayer?"
* "Lista todas las solicitudes de firma que sigan pendientes."

<Warning>
  Usa el flujo de aprobación human-in-the-loop de Mistral si quieres que los usuarios finales confirmen antes de que el agente realmente envíe la solicitud. Las solicitudes de firma salientes llegan a terceros; un paso de confirmación vale el turno adicional.
</Warning>

## Firma embebida

Si quieres que los firmantes completen documentos dentro de tu propio producto en lugar de abrir la página alojada por Firma, lee el ID del firmante de la respuesta de create-and-send e incrusta la UI de firma en un iframe:

```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>
```

Consulta la [guía de firma embebida](/es/guides/embeddable-signing) para la configuración completa, incluidas las mejores prácticas de seguridad.

## Docs MCP para asistencia en tiempo de desarrollo

Para el desarrollo dentro de Vibe o cualquier herramienta de codificación compatible con MCP, registra el [servidor Docs MCP](/es/guides/mcp) de Firma en `https://docs.firma.dev/mcp` como un Conector separado. El Docs MCP expone la búsqueda sobre la documentación completa de Firma, de modo que el modelo pueda responder con precisión preguntas sobre la API mientras escribes código de integración. Esto es para la experiencia de desarrollo y no afecta a tus agentes en ejecución.

## Próximos pasos

* [Autenticación de API](/es/guides/authentication) - claves API y alcance por espacio de trabajo
* [Guía de webhooks](/es/guides/webhooks) - tipos de eventos, payloads y verificación de firmas
* [Firma embebida](/es/guides/embeddable-signing) - experiencia de firma dentro de la app
* [Creación de espacios de trabajo](/es/guides/creating-workspaces) - configuraciones multi-tenant para aplicaciones SaaS
* [Guía de configuración completa](/es/guides/complete-setup-guide) - recorrido de integración de Firma de principio a fin
* [Referencia de API](/api-reference) - documentación completa de los endpoints
