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: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 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. Step 1: Set up your Pages project
If you already have a Cloudflare Pages project, skip to the next step. Otherwise, create one:mkdir firma-app && cd firma-app
npm init -y
mkdir public functions
echo "<h1>Firma App</h1>" > public/index.html
Pages Functions live in the /functions directory. Each file maps to a URL path automatically, so functions/api/send-signing-request.js becomes /api/send-signing-request.Step 2: Store your API key as a secret
Add your Firma API key as an encrypted Pages secret:wrangler pages secret put FIRMA_API_KEY --project-name your-project-name
Alternatively, go to Cloudflare Dashboard → Pages → your project → Settings → Environment variables and add FIRMA_API_KEY as an encrypted variable.Step 3: Create a Pages Function to send signing requests
Create a new file at functions/api/send-signing-request.js:const FIRMA_API = "https://api.firma.dev/functions/v1/signing-request-api";
export async function onRequestPost(context) {
const { name, template_id, signer_email, signer_first_name, signer_last_name } =
await context.request.json();
const response = await fetch(
`${FIRMA_API}/signing-requests/create-and-send`,
{
method: "POST",
headers: {
Authorization: context.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",
});
}
Pages Functions export named handlers per HTTP method: onRequestPost, onRequestGet, etc. If you need to handle multiple methods, export onRequest and check context.request.method inside.
Step 4: Deploy
If your Pages project is connected to a Git repo, push your changes and Cloudflare deploys automatically. For manual deploys:wrangler pages deploy public --project-name your-project-name
Your function is now live at https://your-project.pages.dev/api/send-signing-request.Webhook handling
Create a second Pages Function at functions/api/firma-webhook.js to handle incoming Firma webhook events. Register this URL in the Firma dashboard under Settings → Webhooks.export async function onRequestPost(context) {
const payload = await context.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.