Back to Articles
Stripe & Revenue Ops
•15 min read•Next.js 15 & Stripe Webhooks Blueprint

Stripe Checkout + Signbee: Trigger Contract Signing After Payment (2026)

Closing a high-ticket B2B deal via Stripe Checkout or Payment Links takes seconds, but chasing the signed Master Services Agreement, Order Form, or SOW afterward stalls onboarding for days. Learn how to wire Stripe webhooks directly to Signbee's REST API so every paid session dispatches a dynamic, cryptographically verifiable contract in under 250ms.

Quick Answer (TL;DR)

Stripe Checkout + Signbee Post-Payment Architecture • Production Standard

To trigger automatic contract signing immediately after payment in Stripe Checkout, configure a Next.js App Router webhook handler listening for checkout.session.completed and invoice.paid. Verify the event signature with stripe.webhooks.constructEvent, expand the line items and customer billing details, render a CommonMark agreement with dynamic pricing tables, and send a single JSON POST request to https://signb.ee/api/v1/send. Store the returned document_id on your Stripe Customer or Subscription metadata, and automatically attach the signed SHA-256 PDF certificate to Stripe Disputes if a chargeback ever occurs.

Sub-250ms webhook dispatch
Dynamic CommonMark line items
Automated Stripe chargeback defense

1. The Post-Payment Compliance Gap in Modern B2B Checkout Flows

Over the last three years, how B2B software companies, productized agencies, AI infrastructure providers, and fractional consultancies collect revenue has shifted dramatically. Rather than forcing every $5,000 to $50,000 annual deal through a slow, multi-week procurement cycle before issuing an invoice, high-velocity revenue teams increasingly close transactions on live sales calls using Stripe Checkout, Stripe Payment Links, or self-serve upgrade modals backed by ACH Direct Debit, SEPA, and corporate cards.

However, moving payment collection to the front of the funnel creates a dangerous operational blind spot: the post-payment contract execution gap. Once a prospect enters their corporate Amex or authorizes an ACH debit on a Stripe Checkout page, the sales rep marks the deal as Closed-Won in the CRM—yet no counter-signed Master Services Agreement (MSA), Statement of Work (SOW), Data Processing Agreement (DPA), or Intellectual Property assignment has actually been executed.

Relying on Stripe Checkout's generic consent_collection.terms_of_service checkbox fails B2B revenue operations across three critical dimensions:

  • No Dynamic Commercial Schedule: A static URL link to a generic Terms of Service page on your marketing site does not bind customer-specific deliverables, seat tiers, implementation milestones, custom SLA uptime credits, or bespoke renewal notice periods.
  • Corporate Audit & Procurement Rejection: Enterprise finance controllers and SOC 2 / ISO 27001 auditors require a downloadable, dual-signed PDF artifact bearing named signatory titles, execution timestamps, and tamper-evident cryptographic hashes—not a boolean terms_of_service: "accepted" property buried inside a Stripe JSON log.
  • Vulnerability to Friendly Fraud & Chargebacks: In B2B SaaS and agency retainers, disputed charges of $3,000 to $25,000 are frequently decided by card network arbitrators inspecting whether the merchant can produce a signed contract (customer_signature) matching the exact line items, billing interval, and cardholder identity.

Previously, engineering teams avoided automating this step because legacy platforms examined in our DocuSign API pricing breakdown require clunky OAuth 2.0 JWT grant flows, rigid PDF coordinate anchor tags, and $2.50–$4.80 per-envelope tolls. When you evaluate the best e-signature APIs for developers in 2026, markdown-native execution engines like Signbee allow you to bridge Stripe webhooks and legal execution in a single stateless HTTP call.

2. End-to-End Event Architecture: From Stripe Webhook to Signed SHA-256 PDF

Designing a resilient post-payment contract pipeline requires coordinating two asynchronous webhook streams: the inbound payment confirmation from Stripe and the inbound signature completion callback from Signbee. Because Stripe expects your webhook endpoint to return an HTTP 200 OK response quickly, your contract generation and API dispatch must be deterministic, low-latency, and idempotent.

01

Stripe Fires checkout.session.completed or invoice.paid

When the customer completes payment, Stripe dispatches a signed webhook event to your Next.js route (/api/webhooks/stripe). Your handler validates the Stripe-Signature header against the raw request body buffer using your endpoint secret (whsec_...).

02

Hydrate Line Items, Custom Fields & Idempotency Check

Because Stripe webhook payloads omit expanded line items by default, your server calls stripe.checkout.sessions.retrieve(session.id, { expand: ['line_items', 'customer', 'subscription'] }). It checks whether metadata.signbee_document_id already exists to guarantee zero duplicate dispatches on webhook retries.

03

CommonMark Contract Synthesis & Signbee POST /api/v1/send

Your server injects the customer's legal entity name, tax ID, purchased SKUs, unit quantities, currency, billing interval, and Stripe receipt reference into a clean CommonMark markdown template and posts it to https://signb.ee/api/v1/send. Party A (your company) is cryptographically bound immediately via your API key, and Party B receives an instant signing link.

04

Metadata Back-Linking & Provisioning Gate

The returned document_id and signing_url are written back onto the Stripe Customer and Subscription metadata. When the customer completes the 15-second signing ceremony, Signbee fires document.signed, unlocking full enterprise workspace provisioning and archiving the SHA-256 hash.

Stripe Event Selection Matrix for Automated Contract Triggering

Choosing the right Stripe webhook event depends on whether your customers buy via self-serve Checkout links, B2B Accounts Receivable invoices, or delayed bank debits (ACH / SEPA). The matrix below outlines how each Stripe event maps to contract execution:

Stripe Webhook EventPrimary Commercial ScenarioPayment Settlement StateRecommended Signbee Contract Action
checkout.session.completedCard payment or subscription started via Stripe Checkout / Payment LinkPaid immediately (cards) or pending (async bank debit)Dispatch MSA + Order Form immediately if payment_status === 'paid'
checkout.session.async_payment_succeededACH Direct Debit, SEPA, or Bacs transfer clears 3–5 days post-checkoutCleared & settled in Stripe balanceRelease countersigned SOW or trigger phase-two onboarding addendum
invoice.paidB2B Stripe Invoicing (Net-15/Net-30) or mid-term subscription seat expansionInvoice settled in fullCheck billing_reason === 'subscription_create' or 'manual' and send Order Form
customer.subscription.updatedEnterprise plan upgrade or annual commitment conversionProrated invoice generatedDispatch Amendment Schedule detailing new seat count and annual term
charge.dispute.createdCardholder initiates chargeback (fraudulent, unrecognized, or subscription canceled)Funds debited & held by StripeFetch signed PDF from Signbee and upload to Stripe Dispute Evidence API

3. Configuring Stripe Checkout to Capture Legal Entity & Signatory Metadata

Before a contract can be generated, your checkout session needs two pieces of identity data that a bare credit card form sometimes lacks: the customer's registered corporate legal name (e.g., Acme Cloud Holdings Inc. rather than just the cardholder's personal name) and the signatory's official job title.

Stripe Checkout natively supports up to three custom_fields alongside tax_id_collection and billing_address_collection: 'required'. By passing these parameters when creating the Checkout Session, you capture everything required to populate a legally robust B2B Order Form without adding an extra pre-checkout onboarding screen.

src/app/api/checkout/create-session/route.tsTypeScript / Next.js 15
import { NextResponse } from "next/server";
import Stripe from "stripe";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
  apiVersion: "2025-02-24.acacia",
});

export async function POST(req: Request) {
  const { priceId, seats, customerEmail, planTier } = await req.json();

  const session = await stripe.checkout.sessions.create({
    mode: "subscription",
    customer_email: customerEmail,
    billing_address_collection: "required",
    tax_id_collection: { enabled: true },
    line_items: [
      {
        price: priceId,
        quantity: seats,
      },
    ],
    // Collect legal entity & signatory title directly on Stripe Checkout
    custom_fields: [
      {
        key: "legal_company_name",
        label: { type: "custom", custom: "Registered Legal Entity Name" },
        type: "text",
        optional: false,
      },
      {
        key: "signatory_title",
        label: { type: "custom", custom: "Signatory Job Title (e.g. VP Engineering)" },
        type: "text",
        optional: false,
      },
    ],
    metadata: {
      contract_template: "b2b_enterprise_msa_v2026",
      sla_uptime_target: planTier === "enterprise" ? "99.99%" : "99.9%",
      governing_law: "State of Delaware, USA",
      payment_terms: "Annual Pre-Paid via Stripe",
    },
    // Redirect customer to an onboarding status page that surfaces the signing link
    success_url: `${process.env.NEXT_PUBLIC_APP_URL}/onboarding/contract?session_id={CHECKOUT_SESSION_ID}`,
    cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/pricing`,
  });

  return NextResponse.json({ url: session.url });
}

Notice how the metadata object on the Checkout Session carries domain-specific legal variables (sla_uptime_target, governing_law, and contract_template). When the session completes, your webhook handler receives both the user-entered custom_fields and your server-signed metadata, ensuring a client can never tamper with the SLA or governing law parameters in transit.

4. Production Webhook Handler: Generating & Dispatching the Contract via Signbee

Unlike traditional PDF template systems where adding a third line item overflows a fixed table box and breaks signature field coordinates, Signbee compiles CommonMark Markdown dynamically into paginated, typeset A4 PDF documents. Whether a customer buys 1 product SKU or 14 add-on modules in Stripe Checkout, your code simply maps session.line_items.data into markdown table rows.

Below is the complete, production-ready Next.js App Router webhook handler that verifies the Stripe event, extracts the expanded line items and custom fields, synthesizes the Master Services Agreement and Order Schedule, dispatches it via POST https://signb.ee/api/v1/send, and writes the resulting document_id back to Stripe metadata:

src/app/api/webhooks/stripe/route.tsStripe + Signbee Integration
import { NextResponse } from "next/server";
import Stripe from "stripe";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
  apiVersion: "2025-02-24.acacia",
});

export async function POST(req: Request) {
  const rawBody = await req.text();
  const signature = req.headers.get("stripe-signature");

  if (!signature) {
    return NextResponse.json({ error: "Missing stripe-signature" }, { status: 400 });
  }

  let event: Stripe.Event;
  try {
    event = stripe.webhooks.constructEvent(
      rawBody,
      signature,
      process.env.STRIPE_WEBHOOK_SECRET!
    );
  } catch (err) {
    const message = err instanceof Error ? err.message : "Invalid signature";
    return NextResponse.json({ error: `Webhook Error: ${message}` }, { status: 400 });
  }

  if (event.type === "checkout.session.completed") {
    const rawSession = event.data.object as Stripe.Checkout.Session;

    // 1. Expand line items, customer, and subscription for full commercial context
    const session = await stripe.checkout.sessions.retrieve(rawSession.id, {
      expand: ["line_items.data.price.product", "customer", "subscription"],
    });

    // 2. Guard against duplicate webhook deliveries (idempotency check)
    const subscription = session.subscription as Stripe.Subscription | null;
    if (subscription?.metadata?.signbee_document_id) {
      return NextResponse.json({
        received: true,
        status: "already_dispatched",
        document_id: subscription.metadata.signbee_document_id,
      });
    }

    // 3. Extract customer legal details & custom Checkout fields
    const legalEntityField = session.custom_fields?.find(
      (f) => f.key === "legal_company_name"
    );
    const titleField = session.custom_fields?.find(
      (f) => f.key === "signatory_title"
    );

    const companyName =
      legalEntityField?.text?.value ||
      session.customer_details?.name ||
      "Client Organization";
    const signatoryTitle = titleField?.text?.value || "Authorized Representative";
    const recipientName = session.customer_details?.name || companyName;
    const recipientEmail = session.customer_details?.email;

    if (!recipientEmail) {
      return NextResponse.json({ error: "No customer email on session" }, { status: 422 });
    }

    // 4. Build dynamic CommonMark Order Schedule table from Stripe line items
    const currency = (session.currency || "usd").toUpperCase();
    const lineItemRows = (session.line_items?.data || [])
      .map((item) => {
        const unitAmount = ((item.price?.unit_amount || 0) / 100).toFixed(2);
        const subtotal = ((item.amount_total || 0) / 100).toFixed(2);
        const interval = item.price?.recurring?.interval || "one-time";
        return `| ${item.description} | ${item.quantity} | ${currency} ${unitAmount} (${interval}) | ${currency} ${subtotal} |`;
      })
      .join("\n");

    const totalPaid = ((session.amount_total || 0) / 100).toFixed(2);
    const effectiveDate = new Date().toISOString().split("T")[0];
    const slaTarget = session.metadata?.sla_uptime_target || "99.9%";
    const governingLaw = session.metadata?.governing_law || "State of Delaware, USA";

    const contractMarkdown = `# Master SaaS Subscription & Order Form

**Effective Date:** ${effectiveDate}  
**Stripe Payment Reference:** ${session.id}  
**Provider:** Acme Cloud Infrastructure Inc. ("Provider")  
**Customer Legal Entity:** ${companyName} ("Customer")  
**Authorized Signatory:** ${recipientName}, ${signatoryTitle} (${recipientEmail})

---

## 1. Commercial Order Schedule

Customer has remitted payment via Stripe Checkout for the following subscription licenses and implementation services:

| Description / SKU | Quantity | Unit Rate & Billing Cycle | Total Paid |
|---|---|---|---|
${lineItemRows}
| **Total Settled at Checkout** | — | — | **${currency} ${totalPaid}** |

## 2. Service Level Agreement & Uptime Commitment

Provider guarantees a Monthly Uptime Percentage of at least **${slaTarget}** across all production API endpoints. If Provider falls below this threshold in any calendar month, Customer shall be eligible for service credits equal to 10% of the monthly pro-rata subscription fee for each 30 minutes of unscheduled downtime.

## 3. Data Processing, Confidentiality & Intellectual Property

1. **Customer Data Ownership:** Customer retains all right, title, and interest in and to all data transmitted through the platform.
2. **Subprocessors & Security:** Provider maintains SOC 2 Type II controls and encrypts all customer payloads at rest (AES-256) and in transit (TLS 1.3).
3. **No-Chargeback Covenant:** Customer acknowledges that the above commercial schedule matches the authorized Stripe transaction (${session.payment_intent || session.id}) and agrees that any billing dispute shall be resolved under Section 4 prior to initiating a card network dispute.

## 4. Term, Renewal & Governing Law

This Agreement commences on **${effectiveDate}** and renews automatically according to the billing interval specified in Section 1 unless canceled with at least thirty (30) days written notice. This Agreement is governed by the laws of the **${governingLaw}**.`;

    // 5. Dispatch to Signbee REST API (Party A auto-signed via Bearer token)
    const signbeeRes = await fetch("https://signb.ee/api/v1/send", {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${process.env.SIGNBEE_API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        title: `MSA & Order Form — ${companyName} (${effectiveDate})`,
        markdown: contractMarkdown,
        sender_name: "Michael Beckett, CEO — Acme Cloud Infrastructure Inc.",
        sender_email: "contracts@acmecloud.io",
        recipient_name: `${recipientName} (${signatoryTitle})`,
        recipient_email: recipientEmail,
        expires_in_days: 14,
        webhook_url: `${process.env.NEXT_PUBLIC_APP_URL}/api/webhooks/signbee`,
      }),
    });

    if (!signbeeRes.ok) {
      const errText = await signbeeRes.text();
      throw new Error(`Signbee dispatch failed: ${errText}`);
    }

    const signbeeData = await signbeeRes.json();

    // 6. Persist Signbee document_id and signing_url back onto Stripe metadata
    if (subscription) {
      await stripe.subscriptions.update(subscription.id, {
        metadata: {
          ...subscription.metadata,
          signbee_document_id: signbeeData.document_id,
          signbee_signing_url: signbeeData.signing_url || "",
          contract_status: "awaiting_counterparty_signature",
        },
      });
    }

    if (typeof session.customer === "object" && session.customer && !session.customer.deleted) {
      await stripe.customers.update(session.customer.id, {
        metadata: {
          latest_signbee_document_id: signbeeData.document_id,
          legal_company_name: companyName,
        },
      });
    }
  }

  return NextResponse.json({ received: true });
}

This architecture is remarkably clean. You do not need to spin up a headless Chromium container, maintain brittle Word-to-PDF conversion workers, or pay for a separate CPQ middleware platform. If you are migrating away from heavy enterprise wrappers, our affordable e-signature API integration guide and open-source e-signature API comparison illustrate why stateless JSON-to-PDF compilation reduces infrastructure memory usage by over 85%.

5. Automated Chargeback Defense: Attaching Signbee SHA-256 Certificates to Stripe Disputes

One of the highest-ROI advantages of triggering a Signbee contract on every Stripe Checkout payment over $1,000 is autonomous chargeback dispute defense. Under updated 2026 Visa Compelling Evidence 3.0 (CE3.0) and Mastercard merchant dispute standards, B2B merchants lose over 74% of “Service Not Provided” or “Unrecognized Transaction” chargebacks when they only submit a Stripe invoice receipt.

When a customer signs the post-checkout agreement on Signbee, the resulting PDF includes an appended A4 Cryptographic Signing Certificate containing:

  • The exact Stripe Checkout Session ID and Payment Intent ID embedded inside Section 1 of the agreement prose.
  • Party B's verified email address, browser User-Agent, and originating IPv4/IPv6 network address at the moment of execution.
  • An immutable SHA-256 cryptographic digest computed across the entire PDF byte stream, publicly verifiable at https://signb.ee/verify/[document_id].

When Stripe fires charge.dispute.created, your backend can automatically fetch the signed PDF from Signbee, upload it to Stripe's File Upload API (purpose: 'dispute_evidence'), and bind it to the dispute's customer_signature and service_documentation evidence slots without human intervention:

src/lib/stripe-dispute-evidence.tsAutomated Chargeback Defense
import Stripe from "stripe";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
  apiVersion: "2025-02-24.acacia",
});

export async function attachSignbeeCertificateToDispute(dispute: Stripe.Dispute) {
  // 1. Retrieve the charge & customer metadata to locate the Signbee document_id
  const chargeId = typeof dispute.charge === "string" ? dispute.charge : dispute.charge.id;
  const charge = await stripe.charges.retrieve(chargeId, { expand: ["customer"] });
  const customer = charge.customer as Stripe.Customer | null;

  const documentId =
    charge.metadata?.signbee_document_id ||
    customer?.metadata?.latest_signbee_document_id;

  if (!documentId) {
    console.warn(`No Signbee document found for disputed charge ${chargeId}`);
    return;
  }

  // 2. Fetch document metadata & signed PDF bytes from Signbee REST API
  const docRes = await fetch(`https://signb.ee/api/v1/documents/${documentId}`, {
    headers: { Authorization: `Bearer ${process.env.SIGNBEE_API_KEY}` },
  });
  const docMeta = await docRes.json();

  if (docMeta.status !== "signed" || !docMeta.signed_pdf_url) {
    console.warn(`Document ${documentId} is not yet signed (status: ${docMeta.status})`);
    return;
  }

  const pdfResponse = await fetch(docMeta.signed_pdf_url);
  const pdfBuffer = Buffer.from(await pdfResponse.arrayBuffer());

  // 3. Upload the cryptographically sealed PDF to Stripe Files API
  const stripeFile = await stripe.files.create({
    purpose: "dispute_evidence",
    file: {
      data: pdfBuffer,
      name: `signed-contract-${documentId}.pdf`,
      type: "application/pdf",
    },
  });

  // 4. Attach file ID and SHA-256 audit metadata directly to the Stripe Dispute
  await stripe.disputes.update(dispute.id, {
    evidence: {
      customer_signature: stripeFile.id,
      service_documentation: stripeFile.id,
      customer_Email_address: docMeta.recipient_email,
      customer_name: docMeta.recipient_name,
      uncategorized_text: `Customer executed binding Master Services Agreement (Signbee ID: ${documentId}) on ${docMeta.signed_at} from IP ${docMeta.signer_ip}. Cryptographic SHA-256 Certificate Hash: ${docMeta.sha256_hash}. Public verification URL: https://signb.ee/verify/${documentId}`,
    },
    submit: false, // Stage evidence for final review or set true for auto-submission
  });
}

6. Seamless UX: Surfacing the Signing Link on the Post-Checkout Success Page

Even though Signbee immediately emails the recipient a mobile-friendly signing link when POST /api/v1/send executes, highest completion rates occur when you also present the signing ceremony CTA right on your /onboarding/contract?session_id=cs_live_... redirect screen while the buyer still has their browser tab open.

Teams that want a branded experience can pair this redirect flow with the patterns in our white-label e-signature API guide. When the buyer signs the agreement, Signbee posts a document.signed webhook back to your application, which updates the Stripe Subscription metadata to contract_status: "signed" and unlocks production API keys or seat provisioning.

7. Cost & Latency Benchmark: Signbee vs. Legacy CPQ & E-Sign Stacks

When automating contract execution across hundreds or thousands of Stripe Checkout transactions per month, both webhook processing latency and per-envelope unit economics matter. For a broader market breakdown, review our 2026 e-signature API pricing comparison and learn how to prototype on a free e-signature API tier.

Architectural DimensionStripe + Signbee REST APIStripe + DocuSign eSignature APIZapier + PandaDoc Middleware
Dynamic Line-Item TablesNative CommonMark table rowsRequires HTML-to-PDF worker + anchor coordinatesRequires paid CPQ pricing table add-on
Median Webhook-to-Send Latency195 ms – 260 ms1,450 ms – 2,800 ms4,500 ms – 15,000 ms (polling/tasks)
Authentication ProtocolStateless Bearer API KeyOAuth 2.0 RSA JWT Grant + Token RefreshMulti-hop OAuth + Webhook Secret
Cost per 500 Checkout ContractsFlat developer plan (Zero envelope tax)$1,250 – $2,400 / mo$650+ / mo (Seat + Zapier tasks + API)
Tamper VerificationPublic SHA-256 URL + A4 CertificateProprietary Envelope Summary PDFAudit Trail Footer Page

8. Production Edge Cases: Multi-Currency, Tax IDs & Async Bank Debits

Before shipping your Stripe-to-Signbee integration to production, ensure your webhook handler accounts for three real-world B2B billing edge cases:

  1. Zero-Decimal Currencies (JPY, KRW, CLP): Stripe stores USD, EUR, and GBP in cents (dividing amount_total by 100), but stores Japanese Yen (jpy) and Korean Won (krw) as whole integers. Always normalize currency amounts with Intl.NumberFormat before injecting them into your CommonMark table so a ¥500,000 contract does not render as ¥5,000.00.
  2. Asynchronous Payment Methods (ACH Direct Debit, SEPA, Bacs): When an enterprise customer pays via US ACH Direct Debit on Stripe Checkout, checkout.session.completed fires immediately with payment_status: "unpaid" while the bank transfer clears over 3–5 business days. You should still dispatch the Signbee MSA immediately upon checkout.session.completed so legal execution runs in parallel with ACH clearing, and require both checkout.session.async_payment_succeeded and Signbee's document.signed before unlocking high-cost infrastructure resources.
  3. Customer Tax IDs (VAT / EIN / ABN): When tax_id_collection.enabled is true, Stripe attaches the customer's verified VAT or EIN number inside session.customer_details.tax_ids. Including this tax identifier directly in the preamble of your generated CommonMark contract ensures the executed agreement satisfies EU cross-border VAT reverse-charge documentation rules.

Frequently Asked Questions: Stripe Checkout & Signbee Contract Automation

How do I automatically trigger a contract after a Stripe Checkout payment?

You can automatically trigger a legally binding contract after a Stripe payment by listening for the checkout.session.completed or invoice.paid webhook events in your Next.js or Node.js backend. After verifying the Stripe-Signature header using stripe.webhooks.constructEvent, expand the session line items and customer object, interpolate the commercial terms into a CommonMark markdown string, and dispatch a single HTTP POST request to https://signb.ee/api/v1/send. Signbee compiles the markdown into an A4 PDF, sends the recipient a signing ceremony link, and returns a document_id that you persist directly inside Stripe customer or subscription metadata.

Why is a standard Stripe Checkout terms checkbox often insufficient for high-ticket B2B transactions?

While Stripe Checkout offers a generic terms-of-service checkbox for self-serve consumer transactions, B2B transactions over $2,500 frequently require counter-signed Master Services Agreements (MSAs), Data Processing Agreements (DPAs), custom SLA schedules, and named signatory attribution for corporate procurement and audit compliance. Furthermore, in the event of a fraudulent credit card chargeback under Visa Compelling Evidence 3.0 or Mastercard dispute rules, submitting a cryptographically sealed PDF bearing the customer's IP address, timestamp, email verification, and SHA-256 certificate yields significantly higher dispute win rates than a checkbox boolean flag.

How do I prevent duplicate contracts when Stripe retries checkout.session.completed webhooks?

Stripe guarantees at-least-once webhook delivery and will retry events for up to 72 hours if your endpoint times out or returns a non-2xx status code. To prevent sending duplicate contracts to your customer, inspect the Stripe subscription or payment intent metadata for an existing signbee_document_id before calling the Signbee API, or store the Stripe event.id in a database or Redis idempotency table. Immediately after Signbee returns the created document_id, call stripe.subscriptions.update or stripe.paymentIntents.update to write that identifier onto the Stripe object.

Can I attach a Signbee signed PDF directly to a Stripe Dispute as chargeback evidence?

Yes. When a customer signs their post-payment agreement on Signbee, your webhook endpoint receives a document.signed payload containing the final PDF download URL and the immutable SHA-256 hash. If a customer later initiates an unrecognized or fraudulent chargeback in Stripe (charge.dispute.created), your backend can download the sealed PDF from Signbee, upload it to the Stripe Files API with purpose set to dispute_evidence, and attach the resulting file ID to stripe.disputes.update under the customer_signature and service_documentation fields.

Should I trigger contract generation on checkout.session.completed or invoice.paid?

Use checkout.session.completed for initial onboarding agreements, one-time implementation Statements of Work (SOWs), and first-time annual SaaS subscriptions initiated through a Stripe Checkout link. Use invoice.paid when customers purchase through Stripe Invoicing (B2B accounts receivable workflows where an invoice is emailed with Net-30 terms or paid via ACH bank transfer) or when an enterprise subscription upgrades mid-cycle and requires an amended Order Form reflecting the new seat count.

How much does it cost to automate post-checkout Stripe contracts with Signbee compared to DocuSign?

Legacy e-signature vendors like DocuSign and Adobe Sign charge per-envelope metering ($1.50 to $4.80 per API envelope) alongside mandatory annual seat minimums, which quickly erodes SaaS gross margins when triggered automatically on every Stripe Checkout session. Signbee provides a generous free tier for developers and flat, predictable usage pricing without per-envelope tax or forced enterprise sales calls, allowing engineering teams to automate post-payment MSAs and order forms at less than one-tenth the cost of legacy platforms.

Automate Your Post-Stripe Contract Workflow in 5 Minutes

Connect your Stripe webhooks to Signbee's REST API and turn every completed checkout session into a cryptographically sealed MSA or Order Form.