Services

Backend Engineering, Product

Industry

B2B Marketplace

Year

2022-2024

Three days to two seconds. Same contract. Zero errors.

A B2B marketplace needed contracts for every new listing agreement. The existing process: an account manager fills out a Word template, emails it to legal for review, receives tracked-changes feedback, applies edits, exports to PDF, and emails the final version for signature. Average turnaround: 3 business days. One in five contracts contained field errors - wrong company name carried over from the previous contract, outdated payment terms, missing clause updates. The platform needed contracts generated instantly from verified data, with legal-approved templates that eliminated manual assembly entirely.

01.
THE CHALLENGE

The Word Document Pipeline

16 Word templates lived on a shared network drive. Each template had yellow-highlighted placeholder fields that account managers replaced manually. The process broke in predictable ways: placeholders were missed (a contract once went out with '[COMPANY NAME]' in the header), clause versions drifted between templates (the liability cap differed across 4 templates), and version control was nonexistent. When legal updated a clause, someone had to manually propagate the change across all 16 templates. Three months after a liability clause update, two templates still contained the old version. Nobody noticed until a client's lawyer did.

The fastest way to eliminate contract errors is to eliminate the step where humans type contract data.

02.
THE SOLUTION

Template Engine with Variable Injection

Contracts are now Django templates with typed variables. Each template is a database record containing the document structure, clause references, and variable placeholders. When an account manager clicks 'Generate,' the system pulls verified data from the listing record - company name, address, agreed terms, pricing - and injects it into the template. The output is a styled HTML document that renders to PDF via WeasyPrint. Variables are never manually typed. Clauses are shared across templates via Django template inheritance. When legal updates a clause, every template that inherits it gets the update automatically.

The contract generation pipeline:

Python
class ContractGenerator:
    def generate(self, listing, template_type):
        """Generate a contract PDF from verified data."""
        template = ContractTemplate.objects.get(
            type=template_type,
            is_active=True,
        )

        # Pull variables from verified database records
        context = {
            'company': listing.company,
            'address': listing.company.billing_address,
            'terms': listing.agreed_terms,
            'pricing': listing.pricing_schedule,
            'clauses': template.get_active_clauses(),
            'generated_at': timezone.now(),
        }

        # Validate all variables before rendering
        template.validate_context(context)

        # Render HTML → PDF
        html = render_to_string(
            template.django_template_path,
            context,
        )
        pdf = weasyprint.HTML(string=html).write_pdf()

        # Store with audit trail
        return Contract.objects.create(
            listing=listing,
            template_version=template.version,
            clause_versions=template.clause_version_map(),
            pdf_hash=hashlib.sha256(pdf).hexdigest(),
            pdf_file=ContentFile(pdf, name=f'{listing.slug}.pdf'),
        )

Build a Contract

Select a template type, fill in the variables, and generate. The preview shows how typed variables (highlighted) are injected into the document structure. In production, this renders to a styled PDF in under 2 seconds.

Template
Fill in the fields and click Generate
5
Variables
12
Clauses
Generation Time

Template inheritance for clause management:

HTML
{# base_service_agreement.html #}
{# All 16 contract types inherit from this base #}

<h1>Service Agreement</h1>
<p>Between {{ company.name }} and Pushing Pixels.</p>

{% block scope %}
  <h2>§1 Scope of Services</h2>
  <p>{{ terms.scope_description }}</p>
{% endblock %}

{% block compensation %}
  <h2>§2 Compensation</h2>
  <p>Monthly rate: €{{ pricing.monthly_rate|floatformat:2 }}</p>
{% endblock %}

{# §3-§12: shared standard clauses #}
{% for clause in clauses %}
  <h2>§{{ clause.number }} {{ clause.title }}</h2>
  {{ clause.body|safe }}
{% endfor %}

Production Safeguards

Variable Validation

Every variable has a type constraint and a validation rule. Company names must exist in the database. Monetary values must be positive numbers. Date ranges must be logically valid (end date after start date). If any variable fails validation, the generation halts with a specific error message. This catches the class of errors that plagued the manual process: wrong company names, negative amounts from copy-paste mistakes, expired date ranges carried over from previous contracts.

Clause Version Tracking

Each clause has a version number and a changelog. When legal updates a clause, the version increments and the old version is archived. The contract generation log records which clause version was used. If a dispute arises six months later, the system can reconstruct exactly which version of each clause was in effect when the contract was generated. No ambiguity, no searching through email threads.

PDF Rendering Consistency

WeasyPrint renders HTML to PDF with pixel-perfect consistency across environments. The same contract generates the identical PDF whether rendered on the dev server, staging, or production. Font embedding ensures the document looks identical when opened on any device. A hash of each generated PDF is stored alongside the contract record for tamper detection.

03.
THE RESULT

Contracts at the Speed of Data

Contract generation dropped from 3 business days to under 2 seconds. Field errors dropped from 20% to zero - variables come from verified database records, not manual typing. Clause version drift was eliminated entirely. Legal reviews a template change once; it propagates to all 16 contract types automatically. The system generates 40+ contracts per week. Account managers spend their time on client relationships instead of Word documents.

KEY METRICS

0< 2sGeneration Time
0%Field Errors
0+Weekly Contracts
WHAT THE CLIENT SAYS

"I used to spend half my Monday creating contracts. Now I click a button and the PDF is in my inbox before I finish my coffee. The fact that I cannot accidentally put the wrong company name in a contract is worth the entire project."

Senior Account Manager

B2B Marketplace · Sales

FAQ

Why not use a contract management SaaS?

How does legal review template changes?

What about contracts that need custom clauses?

TECHNOLOGY STACK