Writing/Deep Dive

Building an Automated Contract Provisioning Queue

March 202618 min read
Deep DiveAutomationEnterprise

Note: This deep dive is drawn from real work but generalized — specific system details, field names, customer data, and proprietary configurations have been removed. The architecture, thought process, and patterns are what matter.

This article is a deep dive on building one specific tool end-to-end: an automated contract provisioning queue. It takes everything from the earlier articles — the build/use decision framework, the infrastructure progression, the AI-assisted development process — and applies it to a real, complex problem.

I chose this example because it touches every layer: CRM integration, AI-powered document extraction, database-backed queue state, automated workflows, human review UI, reconciliation, and audit logging. If you understand how this system was built, you can build anything in the same category.

The Problem

Every enterprise SaaS company has the same contract provisioning bottleneck. A deal closes in the CRM, an order form (PDF) gets signed, and someone on the operations team needs to:

  1. Find the order form attachment in the CRM opportunity
  2. Read it and extract the key terms: seat count, contract dates, prepaid usage commitment, billing frequency, rates
  3. Compare the extracted terms to what's in the CRM fields (they often don't match — sales reps make data entry errors, the order form has terms the CRM doesn't capture)
  4. Provision the customer's access in the billing and platform systems
  5. Confirm everything ties: CRM → order form → billing system → platform access

Before automation, each contract took 30-60 minutes of manual work — reading the PDF, typing values into spreadsheets, toggling between systems, double-checking numbers. With 10-20 new contracts per week during growth periods, that's an entire person's time just on provisioning. And mistakes were common: a misread seat count, a wrong start date, a missed precommit amount.

Why This Was a “Build” Decision

Applying the framework from Article 01: this is clearly a Mode 2 problem. It repeats on every deal close. Multiple people interact with the queue. The logic is stable (extract, validate, provision) but the data changes every time. It has workflow states. It needs an audit trail. And it replaces a manual handoff between people and systems.

A one-time AI conversation couldn't solve this — it required a persistent system.

The Architecture

The system has five core components, plus several supporting surfaces that were added as the system matured:

1. Automated Ingestion

A cron job runs every five minutes, checking the CRM for newly closed deals. When it finds one, it snapshots the key commercial fields (account, deal value, dates, quantities, product type, transaction type) and inserts a row into the queue database with status new. The system handles multiple transaction types — new business, renewals, amendments, and expansions — with distinct handling for each.

The key design decision: snapshot the CRM data at ingestion time. Don't query the CRM live on every page load. CRM data changes (reps update fields after close), and you want to know what the data looked like when the contract entered the queue. This also means the queue loads instantly instead of waiting for CRM API calls.

The ingestion logic also checks provisioning state automatically — cross-referencing the CRM deal against the billing and platform systems to determine whether it's already provisioned, partially provisioned, or not yet set up. This means items that are already handled can be auto-dismissed, keeping the active queue focused on what actually needs attention.

2. AI-Powered PDF Extraction

This is the core of the system. When a queue item has a PDF attachment, the auto-processing cron job picks it up and sends the PDF to an AI model with a structured extraction prompt.

The prompt is versioned and stored in the database — not hardcoded. This was a critical early decision. As we discovered edge cases (order forms with different layouts, addendums, multi-product deals), we could update the prompt without deploying code. The prompt includes:

  • The specific commercial terms to extract (customer, dates, quantities, rates, commitment amounts, billing frequency, payment terms, pricing model, renewal terms)
  • Instructions for handling ambiguity (“If the order form references a master agreement for the term, note this and extract what's available”)
  • Output format (structured data with confidence scores per field)
  • Non-standard terms detection — the extraction flags unusual clauses, non-standard commitments, or atypical pricing structures with severity levels so the reviewer is alerted to anything that needs special attention

The confidence scores matter. If the AI is highly confident on one field but uncertain on another, the reviewer knows where to focus their attention.

The Prompt Lab

The prompt versioning evolved into a full prompt lab — a dedicated tab where you can browse version history, edit prompts, test them against specific PDFs, and compare extraction results across versions side-by-side. Each version tracks its parent, change notes, and the model it was designed for. This turned prompt iteration from a code deployment into a workflow that the accounting team owns directly.

3. Automated Validation

After extraction, the system automatically compares the AI-extracted terms to the corresponding CRM deal data. Discrepancies are flagged with severity levels:

  • Critical: Key quantities don't match, contract amount differs materially, dates are off by more than a month, pricing model mismatch between the order form and the CRM
  • Warning: Minor differences in billing frequency, payment terms not captured in CRM
  • Info: Fields present in the order form but not in the CRM (common for usage rates, precommit details)

The validation also performs internal consistency checks on the extracted data itself — for example, flagging when a contract start date doesn't align with the signing date, or when pricing terms are internally contradictory. The discrepancy count and severity roll up to the queue card, so the reviewer can see at a glance which contracts need attention and which are clean.

4. Human Review Queue

The UI is a queue of contract cards, each showing:

  • Customer name, deal details, and transaction type badge (new business, renewal, amendment, expansion) from CRM
  • Current status through the workflow
  • Discrepancy indicators (count and severity)
  • Provisioning status — whether the account is already set up in the billing and platform systems

Opening a contract shows the PDF side-by-side with extracted terms and a detailed comparison table — the extracted values vs. the CRM values, with discrepancies highlighted. The reviewer can:

  • Provision — generates a pre-populated link to the provisioning system with all the extracted terms filled in, so the operator doesn't re-enter data manually
  • Dismiss — requires a reason (duplicates, cancelled deals, already provisioned) and can be restored later if the reason changes
  • Re-scan — re-run the extraction if the PDF was updated or a new prompt version is available
  • Add notes — per-item notes for context that persists through the workflow

The reviewer's job shifted from “read the PDF and type values into a spreadsheet” to “verify the AI's extraction and resolve flagged discrepancies.” On a clean contract with no discrepancies, the review takes two minutes instead of thirty. On a complex one with issues, the reviewer focuses only on the flagged fields.

5. Reconciliation and Coverage

Two supporting views complete the picture:

  • Reconciliation tab: Compares CRM closed-won deals against provisioned accounts to find gaps — contracts that closed but never got provisioned, provisioned accounts with no matching CRM record, or mismatches between what was sold and what was set up.
  • Coverage widget: Embedded on the queue itself, shows live coverage statistics — what percentage of CRM deals are in the queue, which ones are excluded and why (already provisioned, zero commitment, missing data). Prevents things from falling through cracks without requiring a separate navigation.

Additional supporting tabs include a full audit log (every action, who did it, when), a data explorer for ad hoc analysis of queue data, and the prompt lab mentioned above.

The Build Process

This system didn't emerge fully formed. It was built iteratively over about two weeks, following the infrastructure progression from Article 03.

Day 1-2: PDF Extraction Prototype

I started with the hardest part: can AI reliably extract structured data from order form PDFs? I grabbed five order forms, pointed the AI at them, and iterated on the extraction prompt until the results were accurate. This was a local prototype — just me testing extraction quality.

Day 3-4: Queue and Database

With extraction working, I built the queue. Described the data model to the AI: “I need a table for contract queue items with CRM snapshot fields, extraction results, status tracking, and reviewer metadata.” It generated the schema, migrations, and CRUD functions.

Day 5-7: CRM Integration and Cron Jobs

Connected the CRM for automatic ingestion. Set up the OAuth flow, wrote the ingestion cron job, and added auto-processing so new contracts get extracted without manual trigger.

Week 2: Validation, UI Polish, and Deployment

Added the validation logic (comparing extracted terms to CRM fields), built the review UI with side-by-side PDF viewing, added the reconciliation and coverage tabs, and deployed. The team started using it immediately.

Ongoing: Prompt Iteration

The first extraction prompt handled maybe 80% of order forms cleanly. Over the next few weeks, we encountered edge cases: multi-year deals with different rates per year, addendums that modify the base agreement, order forms that reference a master agreement for the term. Each edge case was a prompt update — stored in the database, no deployment needed.

The Status Progression

The queue has a well-defined status flow:

new → pending → auto_scanning → pending_review → reviewed → provisioned
       ↓            ↓                                          ↓
  pdf_updated  extraction_failed                           exception

Each transition is logged in the audit trail with the actor, timestamp, and any notes. This matters for compliance — auditors want to know who approved what, when.

Items can also be dismissed (with a reason) if they shouldn't be in the queue — duplicates, cancelled deals, non-standard arrangements handled outside the normal flow. Dismissed items can be restored if the reason changes. The system also handles PDF updates gracefully — if a revised order form is attached to the CRM deal, the item status resets so the new version gets extracted and validated.

How the System Evolved

The initial build covered the core provisioning workflow. But once the team was using it daily, it became clear that the queue was a natural hub for adjacent workflows. Over the following weeks, the system grew:

ASC 606 Accounting Review

Certain contract structures require formal revenue recognition assessment — new pricing models, non-standard terms, material rights, multi-element arrangements. The system now flags contracts that need accounting review based on configurable rules (deal size thresholds, non-standard terms detected during extraction, specific product types). These flagged contracts flow into a parallel accounting review workflow with their own statuses (pending review, in review, reviewed, escalated) and AI-generated draft assessments that the accountant reviews and finalizes.

Early Renewal and Amendment Handling

Renewals and amendments turned out to be different enough from new business that they needed their own handling. Early renewals involve proration credit calculations — how much of the existing contract term is remaining, what credit does the customer receive, how does that affect the new contract's billing. This became a separate queue surface with its own extraction logic, linked to the parent contract.

Pricing Model Validation

As the company's pricing evolved, the validation layer needed to understand different pricing models — not just “do the numbers match between the order form and the CRM?” but “is the pricing model consistent?” The comparison table now shows rate comparisons, discount structures, and commitment tiers, catching cases where the order form reflects one pricing structure but the CRM has another.

What Went Right

Versioned Prompts

Being able to update the extraction prompt without deploying code was transformative. When we found an order form format the AI struggled with, I'd open the prompt lab tab, refine the prompt, test it against the problem PDF, and save the new version. The next extraction would use the updated prompt automatically.

Snapshot Over Live Query

Snapshotting CRM data at ingestion time (rather than querying live on every page load) was the right call. It made the queue fast, provided a point-in-time record, and meant CRM outages didn't break the tool.

Discrepancy-Driven Review

Shifting the reviewer's focus from “check everything” to “resolve flagged discrepancies” dramatically reduced review time and improved accuracy. When you're checking 15 fields manually, you miss things. When the system highlights the three fields that don't match, you catch them.

What I'd Do Differently

Start with the Validation Logic Earlier

I built extraction first, then validation. In hindsight, I should have built them together from day one. The validation logic catches extraction errors, so having it earlier would have accelerated prompt iteration.

Build the Reconciliation Tab Sooner

The reconciliation tab (CRM closed-won vs. provisioned) was added later, but it's what gives the team confidence that nothing fell through the cracks. It should have been part of the initial build.

The Before and After

MetricBeforeAfter
Time per contract (clean deal)30-45 min2-5 min
Time per contract (complex deal)45-90 min10-15 min
Missed discrepanciesCommon (manual comparison)Rare (automated validation)
Contracts falling through cracksPeriodic (manual tracking)Zero (auto-ingestion + reconciliation)
Audit trailSpreadsheet notesTimestamped, per-action log
Time to buildN/A~2 weeks

Replicating This at Your Company

The contract provisioning queue is specific to enterprise SaaS, but the pattern applies to any queue-based workflow in finance:

  • AP invoice processing: Ingest vendor invoices, AI-extract terms, validate against PO data, route for approval
  • Revenue contract review: Flag new contracts for ASC 606 assessment, extract terms, compare to recognition policy
  • Expense report review: Ingest submissions, AI-categorize, validate against policy, flag exceptions
  • Journal entry approval: Queue manual JEs, validate against thresholds, route for approval with supporting documentation

The building blocks are the same every time: ingestion (get data into the queue), extraction (AI understands the content), validation (compare against source of truth), human review (focused on exceptions), and audit trail (who did what, when).

If you can describe the workflow in those terms, you can build it with an AI coding assistant in a couple of weeks.