Skip to main content

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.

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 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)
Firma uses the raw API key as the Authorization header value - do not prefix it with Bearer. This differs from many other APIs.

Getting started

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:
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.
Never expose your API key in client-side React Native code. Always call the Firma API from a backend where secrets are kept secure.

Step 2: Store your backend URL in the app

In your a0.dev project, create a .env file and add your backend URL:
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:
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.”

Webhook integration

To track when documents are signed, add a webhook endpoint to your backend:
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 for all event types and signature verification.
Always verify the webhook signature using your Firma webhook signing secret in production. See the webhooks guide for implementation details.

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:
<WebView
  source={{ uri: `https://app.firma.dev/signing/${signingRequestUserId}` }}
  style={{ flex: 1 }}
  allowsInlineMediaPlayback
  mediaPlaybackRequiresUserAction={false}
/>
See the embedded signing guide for full setup instructions including security best practices.

Next steps