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

# a0.dev

> Add legally binding e-signatures to any a0.dev mobile app using a backend endpoint and an in-app WebView.

Firma lets you add legally binding e-signatures to any React Native / Expo app generated with a0.dev. Mobile apps need signing constantly: service agreements, parental consent, gig-worker contracts, rental and delivery confirmations. Because a0.dev generates Expo apps, you can present the Firma signing experience in a native WebView while keeping your API key safe on a backend you control.

This guide covers two integration paths:

1. **Backend endpoint + WebView (recommended)** - Send signing requests from a server-side endpoint and present the signing experience in a native WebView. Best for production mobile apps.
2. **Deep link (lightweight)** - Open the Firma signing URL in the system browser. Best for very simple flows.

## Prerequisites

* A [Firma account](https://app.firma.dev) with an API key
* An a0.dev project (uses Expo + React Native)
* `react-native-webview` installed in the project (add it via a0.dev's AI chat or the import panel)
* At least one Firma template with signing fields configured
* A backend service for your API calls (e.g., Express, Fastify, or a Supabase Edge Function)

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

## Getting started

<Tabs>
  <Tab title="Backend endpoint + WebView">
    This is the recommended approach. Your app calls a server-side endpoint to create the signing request, then opens the signing URL in a WebView that lives inside your app. Because a0.dev apps are frontend-only, you need an external backend to keep your API key secure.

    ### Step 1: Set up a backend endpoint

    Create a backend endpoint that calls the Firma API. This can be an Express server, a Supabase Edge Function, or any server-side environment where you can store secrets. Here is an example using Express:

    ```typescript theme={null}
    import express from "express";

    const FIRMA_API = "https://api.firma.dev/functions/v1/signing-request-api";
    const app = express();
    app.use(express.json());

    app.post("/api/signing-requests", async (req, res) => {
      const { name, template_id, signer_email, signer_first_name, signer_last_name } =
        req.body;

      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,
            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 res.status(response.status).json({ error: data });
      res.json({
        signing_request_id: data.id,
        signing_request_user_id: data.first_signer.id,
        signing_link: data.first_signer.signing_link,
      });
    });

    app.listen(3000);
    ```

    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.

    <Warning>
      Never expose your API key in client-side React Native code. Always call the Firma API from a backend where secrets are kept secure.
    </Warning>

    ### Step 2: Store your backend URL in the app

    In your a0.dev project, create a `.env` file and add your backend URL:

    ```text theme={null}
    BACKEND_URL=https://your-backend.example.com
    ```

    Make sure `.env` is in `.gitignore`.

    ### Step 3: Call the endpoint and open the WebView

    From any React Native screen, call the backend endpoint and store the returned `signing_request_user_id`. Then render the Firma signing experience inside a WebView:

    ```typescript theme={null}
    import { useState } from "react";
    import { View, Pressable, Text } from "react-native";
    import { WebView } from "react-native-webview";

    export default function SignContractScreen() {
      const [signingUserId, setSigningUserId] = useState<string | null>(null);

      async function sendForSigning() {
        const res = await fetch(`${process.env.BACKEND_URL}/api/signing-requests`, {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            name: "Consulting Agreement",
            template_id: "your-template-id",
            signer_email: "alice@example.com",
            signer_first_name: "Alice",
            signer_last_name: "Johnson",
          }),
        });
        const data = await res.json();
        setSigningUserId(data.signing_request_user_id);
      }

      if (signingUserId) {
        return (
          <WebView
            source={{ uri: `https://app.firma.dev/signing/${signingUserId}` }}
            style={{ flex: 1 }}
            allowsInlineMediaPlayback
            mediaPlaybackRequiresUserAction={false}
          />
        );
      }

      return (
        <View style={{ flex: 1, justifyContent: "center", padding: 24 }}>
          <Pressable onPress={sendForSigning}>
            <Text>Send contract for signature</Text>
          </Pressable>
        </View>
      );
    }
    ```

    You can also ask the a0.dev AI chat to wire this up: "When the user taps Send Contract on the contract screen, call my backend at /api/signing-requests with the template ID and the user's details, then show the Firma signing WebView."
  </Tab>

  <Tab title="Deep link">
    If you do not want to host a WebView, open the signing URL in the system browser using `expo-linking`. The signer completes the document in the browser and your app handles the return via webhook.

    ```typescript theme={null}
    import * as Linking from "expo-linking";

    async function sendForSigning() {
      const res = await fetch(`${process.env.BACKEND_URL}/api/signing-requests`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          name: "Consulting Agreement",
          template_id: "your-template-id",
          signer_email: "alice@example.com",
          signer_first_name: "Alice",
          signer_last_name: "Johnson",
        }),
      });
      const data = await res.json();
      await Linking.openURL(data.signing_link);
    }
    ```

    This is fine for low-stakes flows but loses the in-app experience. The WebView path is preferred for production.
  </Tab>
</Tabs>

## Webhook integration

To track when documents are signed, add a webhook endpoint to your backend:

```typescript theme={null}
app.post("/api/firma-webhook", async (req, res) => {
  const { type, data } = req.body;

  if (type === "signing_request.completed") {
    const signingRequestId = data.signing_request.id;
    // Update your database or trigger the next workflow step.
  }

  res.json({ received: true });
});
```

In the Firma dashboard under **Settings > Webhooks**, register a webhook pointing to your endpoint URL. Firma sends events for key state changes. See the [webhooks guide](/guides/webhooks) for all event types and signature verification.

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

## Embedded signing

The WebView shown in the getting started section is the mobile equivalent of Firma's embedded signing experience. Once you have the recipient's `signing_request_user_id` from the API (returned as `first_signer.id` in the `create-and-send` response), load it in a WebView:

```typescript theme={null}
<WebView
  source={{ uri: `https://app.firma.dev/signing/${signingRequestUserId}` }}
  style={{ flex: 1 }}
  allowsInlineMediaPlayback
  mediaPlaybackRequiresUserAction={false}
/>
```

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

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