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 application running on Cloudflare. Call the Firma API from Workers or Pages Functions to create templates, send signing requests, and track completions via webhooks. This guide covers two integration paths:
  1. Workers — Standalone serverless functions that call the Firma REST API. Best for APIs, microservices, or apps that don’t use Cloudflare Pages.
  2. Pages Functions — File-based serverless functions that deploy alongside your Cloudflare Pages site. Best if you already have a Pages project and want to keep everything in one repo.
Both paths use the same Firma API endpoints. The difference is how you structure and deploy your code.

Prerequisites

  • A Firma account with an API key
  • The Wrangler CLI installed (npm install -g wrangler)
  • At least one Firma template with signing fields configured
Firma uses the raw API key as the Authorization header value. Do not prefix it with Bearer. This differs from many other APIs.

Choose your path

Step 1: Create a Worker project

If you don’t have a Workers project yet, scaffold one:
npm create cloudflare@latest -- firma-signing
Select “Hello World” Worker when prompted, then choose JavaScript or TypeScript. Once scaffolding completes:
cd firma-signing

Step 2: Store your API key as a secret

Add your Firma API key as an encrypted secret. Wrangler will prompt you to paste the value:
wrangler secret put FIRMA_API_KEY
Never hardcode your API key in source files. Secrets are encrypted at rest and injected into your Worker’s env binding at runtime.

Step 3: Create a Worker to send signing requests

Replace the contents of src/index.js (or src/index.ts if you chose TypeScript) with the following. This Worker accepts a POST request with signer details, calls the Firma create-and-send endpoint, and returns the signing request ID.
const FIRMA_API = "https://api.firma.dev/functions/v1/signing-request-api";

export default {
  async fetch(request, env) {
    if (request.method === "OPTIONS") {
      return new Response(null, {
        headers: {
          "Access-Control-Allow-Origin": "*",
          "Access-Control-Allow-Methods": "POST, OPTIONS",
          "Access-Control-Allow-Headers": "Content-Type",
        },
      });
    }

    if (request.method !== "POST") {
      return Response.json({ error: "Method not allowed" }, { status: 405 });
    }

    const { name, template_id, signer_email, signer_first_name, signer_last_name } =
      await request.json();

    const response = await fetch(
      `${FIRMA_API}/signing-requests/create-and-send`,
      {
        method: "POST",
        headers: {
          Authorization: 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 Response.json({ error: data }, { status: response.status });
    }

    return Response.json({
      signing_request_id: data.id,
      status: "sent",
    });
  },
};
The create-and-send endpoint creates the signing request and sends it to recipients in a single call. 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.

Step 4: Deploy

wrangler deploy
Wrangler will output your Worker’s URL (something like https://firma-signing.<your-subdomain>.workers.dev). Your frontend can now POST signer details to that URL.

Webhook handling

To track when documents are signed, create a second Worker that receives Firma webhook events. In the Firma dashboard under Settings → Webhooks, register a webhook pointing to this Worker’s URL.
export default {
  async fetch(request, env) {
    if (request.method !== "POST") {
      return new Response("Method not allowed", { status: 405 });
    }

    const payload = await request.json();
    const { type, data } = payload;

    if (type === "signing_request.completed") {
      const signingRequestId = data.signing_request.id;
      console.log(`Signing request ${signingRequestId} completed`);
    }

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

    return Response.json({ received: true });
  },
};
For production use, always verify the webhook signature using your Firma webhook signing secret. See the webhooks guide for implementation details.

Embedded signing

For apps where signers complete documents directly in your UI, 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. Load it in an 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>
See the embedded signing guide for full setup instructions including security best practices.

Bonus: MCP connection for AI-assisted building

Firma offers a Docs MCP server that AI coding tools can connect to. If you use an AI assistant while building your Cloudflare integration, connecting the MCP server lets it search Firma documentation and generate accurate API calls. Add the MCP server URL to your tool’s configuration:
https://docs.firma.dev/mcp
This is for the development experience only and does not affect your deployed app.

Next steps