Astro 5 Actions & Server Islands: Lightweight E-Signature Integration (2026)
How to build zero-bloat contract generation, instant client proposal signing, and real-time audit status widgets in Astro 5 using defineAction, server:defer Server Islands, astro:env, and Signbee webhooks.
Quick Answer (TL;DR)
Astro 5 Zero-JS E-Signature Pattern • Actions + Server Islands
To integrate legally binding e-signatures into Astro 5 without sacrificing static CDN performance, declare SIGNBEE_API_KEY in astro:env/server and create a type-safe mutation in src/actions/index.ts using defineAction. Your action validates form inputs with Zod, compiles CommonMark markdown or base64 PDF agreements, and calls POST https://signb.ee/api/v1/send. Render live contract status inside an Astro Server Island (server:defer) so your surrounding proposal or portal page remains cached at the edge, and verify document.signed webhooks in src/pages/api/webhooks/signbee.ts.
1. Why Astro 5 Changes the Game for Client Portals & Interactive Proposals
Astro began as a static site generator for content-heavy marketing websites and documentation hubs, but the arrival of Astro 5 has turned it into a formidable full-stack application framework for B2B SaaS onboarding flows, interactive agency proposals, wealth-management portals, and self-serve legal agreement workflows. Three foundational primitives in Astro 5 drive this shift: Astro Actions (astro:actions), Server Islands (server:defer), and type-safe Environment Variables (astro:env).
Historically, embedding an electronic signature workflow into an agency proposal or SaaS pricing checkout forced developers into an unpleasant architectural compromise. If you embedded a legacy iframe widget from DocuSign, HelloSign, or PandaDoc, the browser had to download 1.8 MB to 3.4 MB of third-party scripts, trackers, and polyfills—destroying Core Web Vitals, adding 2.5 seconds of Total Blocking Time (TBT), and breaking your dark-mode design language. Alternatively, if you converted your entire Astro site into an uncached SSR application just to query contract status on page load, you lost the sub-20ms CDN edge caching that made you choose Astro in the first place.
Combining Astro 5 with Signbee's API solves both problems simultaneously. Because Signbee compiles CommonMark markdown directly into A4 legal PDFs on the backend via POST https://signb.ee/api/v1/send, your client bundle ships 0 KB of e-signature SDK JavaScript. And by isolating dynamic contract state inside an Astro 5 server:defer Server Island, 98% of your proposal or onboarding page is served instantaneously from the CDN edge while the live signature status streams in asynchronously.
2. Architectural Benchmark: Signbee + Astro 5 vs. Legacy Embedded Iframes
When comparing the best e-signature APIs for developers in 2026, frontend performance and server execution cost are critical differentiators. As we analyzed in our DocuSign API pricing breakdown and our open-source e-signature comparison, heavy client-side embeds penalize both your conversion rate and your cloud hosting bill:
| Performance & DX Metric | Astro 5 Actions + Signbee API | Legacy Embedded Iframe SDKs | Full-Page Uncached SSR Wrapper |
|---|---|---|---|
| Client JS Payload Added | 0 KB (Server Action Dispatch) | 1,850 KB – 3,400 KB (Iframe + Vendor JS) | 240 KB – 650 KB (Hydrated SPA Runtime) |
| Page Time to First Byte (TTFB) | 14 ms (CDN Static Shell + Server Island) | 480 ms (Third-Party Handshake) | 620 ms (Blocking Upstream API Call) |
| Contract Template Storage | Astro Content Collections (Markdown) | Vendor Cloud Portal PDF Upload | External S3 Bucket + Coordinate JSON |
| Environment Secret Safety | Compile-Time Enforced (astro:env) | Manual process.env Checks | Runtime Env Inspection |
| Monthly API Cost at 500 Docs | $20/mo Flat (Unlimited Pro) | $1,250+/mo (Envelope Overages) | $480+/mo + Compute Costs |
3. Configuring `astro:env` for Compile-Time Secret Isolation
In hybrid Astro 5 projects where static pages coexist with Server Islands, UI islands (React, Preact, Vue, or Svelte), and Server Actions, accidentally importing a server secret inside a client island is a common security pitfall. Astro 5 eliminates this risk through the built-in env.schema configuration in astro.config.mjs.
By declaring SIGNBEE_API_KEY and SIGNBEE_WEBHOOK_SECRET with context: "server" and access: "secret", Astro validates that the keys exist before starting the server and throws an immediate build-time error if any client script attempts to import astro:env/server:
// astro.config.mjs
import { defineConfig, envField } from "astro/config";
import vercel from "@astrojs/vercel";
export default defineConfig({
// In Astro 5, static output automatically supports per-route SSR,
// Astro Actions, and server:defer Server Islands via your adapter
output: "static",
adapter: vercel(),
env: {
schema: {
SIGNBEE_API_KEY: envField.string({
context: "server",
access: "secret",
}),
SIGNBEE_WEBHOOK_SECRET: envField.string({
context: "server",
access: "secret",
}),
SIGNBEE_SENDER_NAME: envField.string({
context: "server",
access: "public",
default: "Vanguard Digital Studio",
}),
SIGNBEE_SENDER_EMAIL: envField.string({
context: "server",
access: "public",
default: "legal@vanguardstudio.io",
}),
PUBLIC_PORTAL_URL: envField.string({
context: "client",
access: "public",
default: "https://proposals.vanguardstudio.io",
}),
},
},
});4. Defining a Type-Safe Contract Action in `src/actions/index.ts`
With astro:env configured, we can create our backend mutation inside src/actions/index.ts. Astro Actions provide built-in input validation powered by Zod (astro:schema), automatic CSRF protection, and first-class support for both standard HTML accept: "form" submissions and programmatic JSON RPC calls.
In the production handler below, our dispatchProposalContract action validates the client's scope selection, dynamically builds a CommonMark Statement of Work with milestone pricing tables, and sends it to POST https://signb.ee/api/v1/send. If you want an affordable e-signature API integration that works with Astro Content Collections, you can also load base Markdown clauses via getEntry("contracts", "msa-2026") and append the dynamic pricing table right inside the action:
// src/actions/index.ts
import { defineAction, ActionError } from "astro:actions";
import { z } from "astro:schema";
import {
SIGNBEE_API_KEY,
SIGNBEE_SENDER_NAME,
SIGNBEE_SENDER_EMAIL,
PUBLIC_PORTAL_URL,
} from "astro:env/server";
export const server = {
dispatchProposalContract: defineAction({
accept: "form",
input: z.object({
proposalId: z.string().min(3),
clientOrganization: z.string().min(2, "Organization name is required"),
signerFullName: z.string().min(2, "Signer full name is required"),
signerEmail: z.string().email("Valid corporate email is required"),
projectRetainerUsd: z.coerce.number().min(500).max(500000),
includeDedicatedSla: z.coerce.boolean().optional().default(false),
}),
handler: async (input) => {
const slaFee = input.includeDedicatedSla ? 2500 : 0;
const totalValue = input.projectRetainerUsd + slaFee;
const issueDate = new Date().toISOString().split("T")[0];
const markdownDocument = [
`# Statement of Work & Master Retainer — ${input.clientOrganization}`,
"",
`**Proposal Reference:** ${input.proposalId} | **Effective Date:** ${issueDate}`,
"",
`This Statement of Work ("SOW") is entered into between **${SIGNBEE_SENDER_NAME}** ("Agency") and **${input.clientOrganization}** ("Client").`,
"",
"## 1. Deliverable & Fee Schedule",
"",
"| Workstream | Scope Description | Fee (USD) |",
"| :--- | :--- | :--- |",
`| Core Engineering Retainer | Astro 5 Architecture & Edge Deployment | $${input.projectRetainerUsd.toLocaleString()} |`,
input.includeDedicatedSla
? "| Priority 24/7 SLA Addendum | 15-Minute Incident Response & Dedicated Slack | $2,500 |"
: "| Standard Support | Business Hours Engineering Support | Included |",
`| **Total Contract Value** | **Net-15 Electronic Invoicing** | **$${totalValue.toLocaleString()}** |`,
"",
"## 2. Intellectual Property & Confidentiality",
"Upon full settlement of invoices, Agency assigns all right, title, and interest in custom deliverable source code to Client. Both parties agree to maintain strict confidentiality regarding proprietary data.",
"",
"## 3. Cryptographic Execution",
"Executed electronically via Signbee with an immutable SHA-256 audit certificate compliant with ESIGN, UETA, and eIDAS.",
].join("\n");
const response = await fetch("https://signb.ee/api/v1/send", {
method: "POST",
headers: {
Authorization: `Bearer ${SIGNBEE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
title: `Statement of Work (${input.proposalId}) — ${input.clientOrganization}`,
markdown: markdownDocument,
sender_name: SIGNBEE_SENDER_NAME,
sender_email: SIGNBEE_SENDER_EMAIL,
recipient_name: input.signerFullName,
recipient_email: input.signerEmail,
expires_in_days: 14,
webhook_url: `${PUBLIC_PORTAL_URL}/api/webhooks/signbee`,
}),
});
if (!response.ok) {
const errorBody = await response.json().catch(() => ({}));
throw new ActionError({
code: "BAD_REQUEST",
message:
errorBody.error ||
"Signbee API rejected the contract dispatch request.",
});
}
const data = await response.json();
return {
documentId: data.id as string,
status: data.status as string,
verifyUrl: data.verify_url as string,
};
},
}),
};5. Building a Live Contract Status Server Island (`server:defer`)
Once a client proposal page is published (for example, at /proposals/acme-q4-architecture), you want the executive summary, architectural diagrams, and pricing tables to load from the global CDN edge in under 15 milliseconds. At the same time, the signature banner at the bottom of the proposal must always show the real-time legal execution state from Signbee (sent, viewed, or signed with its SHA-256 verification badge).
Astro 5's server:defer directive makes this trivial. We create an Astro component at src/components/ContractStatusIsland.astro that fetches live metadata from GET https://signb.ee/api/v1/documents/{documentId} on the server, and embed it inside our static proposal page with a slot="fallback" skeleton:
---
// src/components/ContractStatusIsland.astro
import { SIGNBEE_API_KEY } from "astro:env/server";
interface Props {
documentId: string;
}
const { documentId } = Astro.props;
const res = await fetch(`https://signb.ee/api/v1/documents/${documentId}`, {
headers: {
Authorization: `Bearer ${SIGNBEE_API_KEY}`,
},
});
const doc = res.ok ? await res.json() : null;
---
{doc ? (
<div class="p-5 rounded-xl bg-zinc-900 border border-zinc-800 flex items-center justify-between">
<div>
<p class="text-xs font-mono text-zinc-400">
Document ID: {doc.id} • Recipient: {doc.recipient_email}
</p>
<p class="text-base font-bold text-zinc-100 mt-1">
Status: <span class={doc.status === "signed" ? "text-emerald-400" : "text-amber-400"}>
{doc.status.toUpperCase()}
</span>
</p>
</div>
{doc.status === "signed" && (
<a
href={doc.verify_url}
target="_blank"
rel="noopener noreferrer"
class="px-4 py-2 rounded-lg bg-emerald-500/10 text-emerald-400 border border-emerald-500/30 text-xs font-mono"
>
Verify SHA-256 Certificate
</a>
)}
</div>
) : (
<div class="p-4 rounded-xl bg-zinc-900/60 border border-zinc-800 text-xs text-zinc-400">
Awaiting contract dispatch...
</div>
)}
<!-- Usage inside src/pages/proposals/[slug].astro (Statically Cached Page) -->
<!--
<ContractStatusIsland server:defer documentId={proposal.signbeeDocumentId}>
<div slot="fallback" class="p-5 rounded-xl bg-zinc-900/40 border border-zinc-800 animate-pulse text-xs text-zinc-500">
Checking live cryptographic signature status...
</div>
</ContractStatusIsland>
-->Notice how Astro encrypts the documentId prop passed to the Server Island URL parameter automatically so external visitors cannot tamper with island props or enumerate private resources. And because the island runs on the server, your SIGNBEE_API_KEY remains 100% isolated from the browser. Teams building custom branded client portals can combine Server Islands with our white-label e-signature API architecture to deliver a cohesive agency experience.
6. Verifying `document.signed` Webhooks in Astro API Endpoints
When the client clicks the signing link in their email and executes the contract, Signbee seals the PDF with its A4 cryptographic certificate and dispatches a webhook POST request to your Astro backend. To handle this in an Astro 5 project using hybrid/static output, create an API route at src/pages/api/webhooks/signbee.ts and explicitly declare export const prerender = false at the top of the file so Astro compiles it as an on-demand serverless endpoint.
Inside the POST handler, call the standard Web Fetch API await request.text() to capture the raw unparsed payload bytes and verify the X-Signbee-Signature HMAC-SHA256 digest before parsing the JSON:
// src/pages/api/webhooks/signbee.ts
export const prerender = false;
import type { APIRoute } from "astro";
import { createHmac, timingSafeEqual } from "node:crypto";
import { SIGNBEE_WEBHOOK_SECRET } from "astro:env/server";
export const POST: APIRoute = async ({ request }) => {
const signature = request.headers.get("x-signbee-signature");
if (!signature) {
return new Response(
JSON.stringify({ error: "Missing X-Signbee-Signature header" }),
{ status: 401, headers: { "Content-Type": "application/json" } }
);
}
// Read exact raw request body text before JSON parsing
const rawBody = await request.text();
const expectedDigest = createHmac("sha256", SIGNBEE_WEBHOOK_SECRET)
.update(rawBody, "utf8")
.digest("hex");
const cleanProvided = signature.replace(/^sha256=/, "");
const providedBuf = Buffer.from(cleanProvided, "hex");
const expectedBuf = Buffer.from(expectedDigest, "hex");
if (
providedBuf.length !== expectedBuf.length ||
!timingSafeEqual(providedBuf, expectedBuf)
) {
return new Response(
JSON.stringify({ error: "Invalid cryptographic webhook signature" }),
{ status: 401, headers: { "Content-Type": "application/json" } }
);
}
const eventPayload = JSON.parse(rawBody);
if (eventPayload.event === "document.signed") {
// Trigger Astro DB / Turso update, unlock client portal, or notify Slack
console.info(
`[Astro Signbee Webhook] Contract ${eventPayload.document_id} signed. SHA-256: ${eventPayload.sha256_hash}`
);
}
return new Response(
JSON.stringify({ ok: true, documentId: eventPayload.document_id }),
{ status: 200, headers: { "Content-Type": "application/json" } }
);
};7. Choosing the Right Astro 5 Pattern for Your E-Signature Workflow
Astro 5 offers four distinct server and content primitives. Depending on whether you are building a high-converting SaaS self-serve checkout, an agency proposal hub, or an automated back-office webhook pipeline, here is how each Astro 5 feature maps to Signbee's API capabilities:
| Astro 5 Primitive | Primary E-Signature Role | Signbee Endpoint Used | Caching Strategy |
|---|---|---|---|
| Astro Actions (defineAction) | Validate form inputs & dispatch new contracts | POST /api/v1/send | On-Demand Server RPC (No Cache) |
| Server Islands (server:defer) | Stream live status & SHA-256 verification badge | GET /api/v1/documents/{id} | Static Page Shell + Dynamic Island |
| Content Layer (src/content) | Version-controlled MSA & SOW Markdown templates | Compiled into markdown payload | Build-Time Validated & Indexed |
| API Routes (APIRoute POST) | Ingest & verify HMAC completion webhooks | Inbound X-Signbee-Signature | prerender = false (Serverless) |
8. Astro Content Collections as Version-Controlled Legal Clauses
One of the most underrated advantages of pairing Astro 5 with Signbee is using Astro's Content Layer API as a Git-backed contract management system. In traditional legal tech stacks, when your counsel updates an indemnification clause or data processing addendum, a developer has to log into a vendor dashboard, export a Word doc to PDF, re-upload it, and manually drag signature and initial boxes back into place.
In Astro 5, you can store all of your standard legal templates inside src/content/contracts/*.md with typed Zod frontmatter (jurisdiction, version, effectiveDate, governingLaw). Every change to a legal clause goes through a standard GitHub Pull Request with diff review. When an Astro Action runs, it loads the approved Markdown body, interpolates the customer's order schedule table, and posts the combined CommonMark string to Signbee.
You can test this entire workflow today using our free e-signature API tier (3 free legal documents per month with full API and webhook access). When you are ready to launch your portal to unlimited clients, check our 2026 e-signature API pricing comparison to see why agencies and SaaS founders are switching to Signbee's flat $20/month unlimited Pro plan.
9. Frequently Asked Questions: Astro 5 E-Signature Integration
How do Astro 5 Actions simplify sending e-signature agreements with Signbee?
Astro 5 Actions allow you to define type-safe backend functions inside src/actions/index.ts using defineAction and astro:schema (Zod). Instead of writing manual REST boilerplate, parsing multipart form data, and handling serialization errors on the client, your Astro Action accepts either FormData or JSON input, validates every field on the server, loads SIGNBEE_API_KEY via astro:env/server, and dispatches a CommonMark or PDF contract to POST https://signb.ee/api/v1/send. Client islands or HTML forms call actions.dispatchProposalContract() with full TypeScript autocomplete.
What is the advantage of using Astro 5 Server Islands (server:defer) for live contract status?
In traditional SSR frameworks, fetching live e-signature status from an external API forces the entire page to opt out of CDN edge caching, degrading Time to First Byte (TTFB) from 15ms to 400ms+. Astro 5 Server Islands solve this by allowing the outer marketing page, client proposal, or onboarding shell to be cached statically at the CDN edge while deferring the personalized <ContractStatusIsland server:defer /> component. Astro renders a lightweight fallback skeleton immediately and streams the live Signbee document status and SHA-256 verification badge asynchronously in parallel.
How does astro:env/server protect Signbee API keys in Astro 5?
Astro 5 introduces the built-in astro:env module in astro.config.mjs, which enforces strict schema validation and runtime access boundaries for environment variables. By declaring SIGNBEE_API_KEY and SIGNBEE_WEBHOOK_SECRET with envField.string({ context: 'server', access: 'secret' }), Astro guarantees at build time that neither secret can ever be imported into client-side island components or leaked into public HTML bundles. Server Actions and API endpoints import the typed variables directly from astro:env/server across Node, Cloudflare, and Vercel adapters.
How do you verify Signbee webhooks in an Astro 5 API endpoint?
To verify Signbee webhooks in Astro 5, create an on-demand server endpoint at src/pages/api/webhooks/signbee.ts with export const prerender = false. Inside the POST APIRoute handler, read the raw request body string via await request.text() rather than request.json() so the exact byte sequence is preserved. Compute an HMAC-SHA256 hex digest using your SIGNBEE_WEBHOOK_SECRET and compare it against the X-Signbee-Signature request header using crypto.timingSafeEqual. Once verified, parse the JSON event and update your database when event === 'document.signed'.
Does integrating Signbee with Astro 5 add any client-side JavaScript bundle weight?
No. Because Signbee executes contract compilation, PDF typography layout, email delivery, and SHA-256 cryptographic sealing entirely on the server via POST https://signb.ee/api/v1/send, your Astro 5 pages ship 0 KB of third-party e-signature JavaScript to the browser. Even if you invoke Astro Actions from a progressive HTML form without a React, Vue, or Svelte island, the entire contract dispatch workflow runs server-side while preserving a 100/100 Google Lighthouse performance score.
Can digital agencies use Astro 5 Content Collections as reusable legal contract templates?
Yes. Astro 5's Content Layer API and Content Collections pair naturally with Signbee's CommonMark markdown engine. Agencies and SaaS teams can store version-controlled master services agreements, statements of work, and mutual NDAs as Markdown files in src/content/contracts/, validate their frontmatter with Zod, inject client-specific pricing tables at runtime inside an Astro Action, and send the compiled markdown directly to Signbee for legally binding execution.
Add Zero-JS E-Signatures to Your Astro 5 Project
Create a free Signbee account in seconds, grab your Bearer API key, and start dispatching CommonMark and PDF contracts from Astro Actions.