An offer letter generator on AWS for a few dollars a month
The gap between agreeing an offer and sending the letter is where small businesses lose candidates. Not because the letter is hard, but because it needs somebody with the template, the right numbers and half an hour, and those three things rarely coincide on the day the conversation happened. Four days later the candidate has had another offer in writing. This post walks through a small system that closes that gap to about ten minutes without letting anything improvise a contract.

Key takeaways
- The wording is always your approved template. Nothing generates contractual language.
- The terms come from a short form, or from a note that a model turns into form fields.
- A term with no matching template clause stops the draft and asks for one.
- The output is a draft plus a diff against the standard template, so review is quick.
- Designed on AWS for about $2 a month.
The whole system on one page
Before any code, here is the shape of what we are designing.
- Machine learning
- Analytics
- Front-end & mobile
- People
What you set up once (the outside)
- Your templates. The offer letter you already use, broken into clauses, with the variable bits marked. Most businesses have two or three variants — salaried, hourly, fixed term — and doing this once is a morning’s work with whoever approved the original wording.
- A clause library. The optional bits: probation lengths, notice periods, car allowances, bonus structures, relocation. Each one is approved wording with variables. The library is what determines which offers the system can produce without stopping.
- A terms form. Eight or nine fields: name, role, start date, salary or rate, hours, probation, notice, and anything else. Covered in Part 2, along with the note lane for people who would rather forward the email where it was agreed.
What runs on every offer (the inside)
- The terms reader. If a form was filled in, this does nothing. If somebody forwarded “agreed with Kwame: 38k, starts 28th, 3 month probation, standard notice”, the reader turns that into fields and shows them for confirmation. It is the only place a model appears, and its output is checked by a person before anything is drafted.
- The clause matcher. Takes each term and finds the approved clause that expresses it. A three-month probation matches the probation clause with a variable filled. A four-day week matches the part-time hours clause. A term with no match does not get improvised wording — it stops the draft and says which term needs a clause.
- The drafter. Assembles the letter and produces two things: the document, and a diff against the standard template showing exactly which clauses differ and how. The diff is what makes review take two minutes rather than fifteen, because the reviewer reads the differences rather than the whole letter.
One offer, end to end
- Analytics
- Front-end & mobile
- People
In plain words
A hiring manager finishes a call at 11:40 having agreed an offer. They open the form on their phone: name, role, £38,000, starts the 28th, three-month probation, one-month notice, standard everything else. The clause matcher finds an approved clause for every one of those. The drafter assembles the letter from the salaried template and produces a diff showing two differences from standard: the probation is three months rather than six, and the start date is inside the usual notice window.
At 11:52 whoever approves offers gets a message with the draft and those two lines. They read two differences rather than a four-page letter, agree with both, and send it. The candidate has a written offer before lunch on the day they agreed it, which is the single most effective thing a small business can do to stop losing people to larger employers with faster HR functions.
Design rules that shaped every decision
- The system never writes contractual wording. Every sentence in the letter came from a template somebody approved.
- An unmatched term stops the draft. Improvising a clause is the one failure that could cost real money.
- Show a diff, not a document. Reviewing two differences is quick; reviewing four pages is not, and the second one does not actually happen.
- The model reads, it does not write. Its only job is turning a note into fields somebody then confirms.
- Nothing is sent by the system. A draft is ready; a person sends it.
- Every sent offer keeps the exact document and the clause versions it used, because contract wording changes and letters get read back years later.
Why this shape
The temptation with document generation and a capable model is to let it write the letter. It would do a plausible job, and plausible is exactly the wrong standard for a document that forms part of an employment contract. A clause that reads well and differs subtly from the one your solicitor approved is a liability that will not be noticed until it matters.
So the design confines generation entirely to assembly, and puts the model somewhere genuinely useful and completely safe: reading a hurried note into structured fields that a human confirms before anything is produced. The letter itself is your wording, every time, with variables filled in.
The next four posts walk through each piece: how the terms get captured, how clauses get matched, what happens to an unusual term, and what a sent offer leaves behind. One diagram per post, a cost breakdown, and an engineering reference at the end.
All posts