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

# v0

> Add legally binding e-signatures to any v0 app using Next.js route handlers and environment variables.

Firma lets you add legally binding e-signatures to any v0 application. Use Next.js route handlers to call the Firma REST API, send signing requests, and receive webhook events when documents are signed. This guide walks through the full flow: storing your API key, sending a signing request, wiring it to your UI, and handling completion events.

## Prerequisites

* A [Firma account](https://app.firma.dev) with an API key
* A [v0](https://v0.dev) account with an active project (free tier works)
* At least one Firma template with signing fields configured

<Note>
  Firma uses the raw API key as the `Authorization` header value — do not prefix it with `Bearer`. This differs from many other APIs.
</Note>

## Step 1: Store your API key as an environment variable

v0 manages environment variables through the Vars panel in the chat sidebar. Values are synced with the connected Vercel project, so the same secret works in previews and in production.

1. Open your v0 project and click the **Vars** tab in the sidebar
2. Click **Add Variable**
3. Set the key to `FIRMA_API_KEY` and paste your Firma API key as the value
4. Save

The variable is now available as `process.env.FIRMA_API_KEY` in any server-side code v0 generates.

<Warning>
  Never expose your API key in client components. Always call the Firma API from route handlers or server actions, not from `"use client"` files.
</Warning>

## Step 2: Create a route handler to send signing requests

Ask v0 to create a route handler, or add the file directly. v0 generates Next.js App Router code by default, so route handlers live in `app/api/<route-name>/route.ts`.

This example creates and sends a signing request from a template in a single call using the `create-and-send` endpoint:

```typescript theme={null}
// app/api/send-signing-request/route.ts
import { NextRequest, NextResponse } from "next/server";

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

export async function POST(req: NextRequest) {
  const { name, template_id, signer_email, signer_first_name, signer_last_name } =
    await req.json();

  const apiKey = process.env.FIRMA_API_KEY;
  if (!apiKey) {
    return NextResponse.json(
      { error: "FIRMA_API_KEY not configured" },
      { status: 500 }
    );
  }

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

  const data = await response.json();

  if (!response.ok) {
    return NextResponse.json({ error: data }, { status: response.status });
  }

  return NextResponse.json({
    signing_request_id: data.id,
    status: "sent",
  });
}
```

<Note>
  The `create-and-send` endpoint creates the signing request and sends it to recipients atomically. If you need to review or modify the request before sending, use `POST /signing-requests` to create a draft, then `POST /signing-requests/{id}/send` separately.
</Note>

## Step 3: Call the route handler from your UI

From a client component, POST to your new route when the user completes the form:

```typescript theme={null}
// components/send-contract-button.tsx
"use client";

import { useState } from "react";

export function SendContractButton({ templateId }: { templateId: string }) {
  const [loading, setLoading] = useState(false);

  async function handleSend() {
    setLoading(true);
    const res = await fetch("/api/send-signing-request", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        name: "NDA for Alice Johnson",
        template_id: templateId,
        signer_email: "alice@example.com",
        signer_first_name: "Alice",
        signer_last_name: "Johnson",
      }),
    });
    const data = await res.json();
    setLoading(false);
    console.log(data);
  }

  return (
    <button onClick={handleSend} disabled={loading}>
      {loading ? "Sending..." : "Send contract"}
    </button>
  );
}
```

You can also ask v0's chat to wire this up: *"When the user submits the contract form, call `/api/send-signing-request` with the template ID, signer email, first name, and last name from the form."* v0 will generate the form, the button, and the fetch call together.

## Webhook integration

To track when documents are signed, add a webhook route handler and register it in the Firma dashboard.

```typescript theme={null}
// app/api/webhooks/firma/route.ts
import { NextRequest, NextResponse } from "next/server";

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

  if (type === "signing_request.completed") {
    const signingRequestId = data.signing_request.id;

    // Update your database, send a notification, or trigger
    // the next step in your workflow
    console.log(`Signing request ${signingRequestId} completed`);
  }

  if (type === "signing_request.recipient.signed") {
    const recipientEmail = data.recipient?.email;
    console.log(`${recipientEmail} signed the document`);
  }

  return NextResponse.json({ received: true });
}
```

Then register the webhook:

1. Publish your app (see below) so the route is publicly reachable
2. In the Firma dashboard under **Settings → Webhooks**, add a webhook pointing to `https://<your-app-domain>/api/webhooks/firma`
3. Select the events you want to receive. See the [webhooks guide](/guides/webhooks) for all event types and signature verification

<Warning>
  For production use, always verify the webhook signature using your Firma webhook signing secret. See the [webhooks guide](/guides/webhooks) for implementation details.
</Warning>

## Publishing to Vercel

v0 projects connect to a Vercel project automatically. To push your app to production:

1. Click **Publish** in the top right of the v0 interface, or connect an existing Vercel project from **Project settings**
2. Environment variables set in the Vars panel sync to Vercel automatically. To scope values per environment (Production, Preview, Development), edit them under **Project → Settings → Environment Variables** in the Vercel dashboard
3. Once published, your webhook URL will be `https://<project-name>.vercel.app/api/webhooks/firma`. Use that when registering the webhook in Firma

<Note>
  If you need a different Firma API key for preview vs. production, set `FIRMA_API_KEY` per environment in the Vercel dashboard. v0's Vars panel writes to all environments by default.
</Note>

## Embedded signing

For apps where signers complete documents inside your UI instead of jumping to email, Firma provides an embeddable signing experience. The `create-and-send` response includes a `first_signer.id` (the `signing_request_user_id`) and a ready-made `first_signer.signing_link`. Render it in an iframe:

```typescript theme={null}
// components/embedded-signing.tsx
export function EmbeddedSigning({
  signingRequestUserId,
}: {
  signingRequestUserId: string;
}) {
  return (
    <iframe
      src={`https://app.firma.dev/signing/${signingRequestUserId}`}
      style={{ width: "100%", height: "900px", border: 0 }}
      allow="camera; microphone; clipboard-write"
      title="Document Signing"
    />
  );
}
```

See the [embedded signing guide](/guides/embeddable-signing) for full setup including security best practices.

## Bonus: MCP connection for AI-assisted building

Firma offers a [Docs MCP server](/guides/mcp) that v0's chat can connect to directly. When connected, v0 searches Firma documentation while generating code so it uses accurate endpoints, field names, and patterns.

To set it up:

1. In v0, click **Add MCP** on the prompt form or go to **Settings → MCP Connections**
2. Add a custom MCP server with the URL: `https://docs.firma.dev/mcp`
3. Save

From that point, prompts like *"Add a Firma route handler that sends a signing request from template `abc123` using the email in the form"* will generate code that matches the current Firma API. This is a build-time aid and does not affect your deployed app.

## Next steps

* [API authentication](/guides/authentication) — API keys and workspace scoping
* [Webhooks guide](/guides/webhooks) — event types, payloads, and signature verification
* [Embedded signing](/guides/embeddable-signing) — in-app signing experience
* [Creating workspaces](/guides/creating-workspaces) — multi-tenant setups for SaaS apps
* [Complete setup guide](/guides/complete-setup-guide) — end-to-end Firma integration walkthrough
* [API reference](/api-reference) — full endpoint documentation
