Business process automation services, built one process at a time
This page assumes you can name the process that is hurting, down to the specific task.
The rest is build detail, including one enquiry-to-CRM process written out in enough depth to rebuild and a table of what the system does when a dependency stops responding. Apefo Ltd trades as JNET.support; builds are delivered remotely from the UK.
RouteRule basedCategory-to-owner lookup with a documented fallback. Needs the category, so it runs after the model call.
WriteRule basedContact, opportunity, one task with a named owner and a due time.
ReviewHuman reviewOwner confirms the category on the CRM card and checks the summary kept the constraints.
MonitorRule basedRetries, alerts, and a queue depth someone actually looks at.
Failure queue
Any step that fails a check routes here instead of continuing. A named person clears it, and the item keeps
its submission ID so it can be replayed once the cause is fixed.
Model-bounded stepOne call, constrained output, no authority to act.
Human reviewThe record exists, but nobody acts on it until a person has confirmed the category.
Failure queueAnything that fails a check waits for a person. The only thing discarded without a record is a bot submission.
Which processes are worth automating
Automation earns its build cost through repetition and predictability. A process that runs often but changes shape every time is a worse candidate than one that runs less often in a fixed shape. This is the test applied before anything is quoted.
Test
Line worth building against
Why the line sits there
TestFrequency and total time
Line worth building againstWeekly, or 20+ runs a month, or rarer runs that each eat hours
Why the line sits thereRare and quick does not repay build and maintenance. A monthly report that swallows a day still does
TestHandling time
Line worth building against4+ minutes of attention per run, or a wait longer than a working day
Why the line sits thereSub-minute tasks are usually a UI problem
TestInput shape
Line worth building againstThe same 6-12 fields in a recognisable structure, 8 runs in 10
Why the line sits thereBelow 8 in 10 you are building an exception handler with an automation attached
TestRule stability
Line worth building againstDecision rules unchanged for six months
Why the line sits thereFaster-moving rules outrun any build
TestNamed owner
Line worth building againstOne person, by name, whose work gets easier
Why the line sits thereWithout a name, nobody notices when the output goes wrong
TestReversibility
Line worth building againstA bad output is correctable the same day without contacting a customer
Why the line sits thereThis decides how much sits behind a human confirmation
TestSystems
Line worth building againstEvery system has an API, a webhook, or a scheduled export
Why the line sits thereDriving a desktop app through simulated clicks is a different, more fragile category
This is the build-scoping version of the screen published in full in the eight-factor article. Rule stability and API availability are build-time checks that sit outside the scored model, alongside the exception-rate and observability checks.
Four conditions take a process off the build list until something changes. No named owner, which stops the work outright instead of reshaping it. Rules that change every month, because the build will encode last month's rules and keep applying them long after they stop being true. A free-text email as the only input, with no consistent structure and no sender template you can influence. Runs four times a year, so the operator has forgotten it exists by the second run.
An error that cannot be undone is a different kind of problem. It is a design constraint, not a disqualifier. Irreversible steps get a person on the trigger, not on the review.
How the choice between rules and AI gets made
Most of this work is deterministic, and that is the correct engineering answer. A rule you can write down is testable, cheap per run, and behaves identically on the ten-thousandth execution. A model does none of those things. AI belongs in a narrow band: reading messy text, pulling fields from documents with no fixed layout, drafting a first version, compressing something long into something short.
Step
Built as
Reason
StepRoute on a picklist value
Built asDeterministic
ReasonThe value is already a closed set
StepArithmetic: totals, VAT, due dates, ageing
Built asDeterministic
ReasonModels are not calculators. Sums are computed in code, then shown to a model, never derived by one
StepDuplicate detection
Built asDeterministic
ReasonA hash of the normalised email and the opening of the message, inside a 24-hour window, is exact
StepField validation and required-field checks
Built asDeterministic
ReasonThe schema is known in advance
StepClassify free text into a fixed category list
Built asAI, closed list, confidence threshold
ReasonThe input varies without limit, the output does not
StepExtract supplier, date, net and total from PDFs with dozens of layouts
Built asAI, then validated in code
ReasonLayout varies. Values are range-checked and cross-footed afterwards
StepSummarise a long thread or ticket history
Built asAI
ReasonNo rule expresses this, and a person reads the result
StepDraft a first reply or document
Built asAI
ReasonFaster writing. A person still sends it
StepDecide a refund, discount, credit or hiring outcome
Built asNeither
ReasonThe automation prepares the decision and a person makes it
In a fifteen-step build, expect the model to touch one or two of them. If a proposal has AI in every step, someone is selling model calls, not a working process.
Anatomy of one automated step
An enquiry arrives through the website form and has to become a CRM record with an owner and a task somebody is accountable for. Here it is in enough detail to rebuild.
Trigger and input
The form sends a signed POST to a webhook. The signature is verified first; unsigned or stale payloads are dropped and counted. The raw JSON is written to durable storage with a submission ID before processing, so if every downstream step fails the enquiry still exists somewhere retrievable.
Twelve fields: full_name, work_email, company, phone (optional), country, enquiry_text (free text, 2,000 characters), source_page, utm_source, utm_campaign, consent_marketing, submitted_at (server time, not browser time), and a hidden honeypot.
Validation, in order
Cheap rejections first, history last
Each check runs before the next, so nothing expensive runs on input that was never going to survive.
What happens to a failure depends on which check caught it. Bot submissions are dropped, duplicates are
annotated on the record that already exists, and anything genuinely ambiguous waits in a review queue
with a named owner.
01
Bot check
Condition
Hidden honeypot field is filled, or the form is submitted less than two seconds after page load.
Action
Discarded before any processing.
Exception
Counted for monitoring. No alert, no record, no notification.
02
Email validation
Condition
work_email fails syntax parsing, or its domain has no MX record.
Action
Not created in the CRM.
Exception
Held in the review queue with the reason recorded, because a typo and a fake address look identical to a validator.
03
Company inference
Condition
company is empty.
Action
Free-mail domain: queued. Corporate domain: the domain becomes a provisional company name.
Exception
Queued items carry the reason no identifiable organisation. Inferred names are flagged as inferred, never presented as confirmed.
04
Deduplication
Condition
SHA-256 of the lowercased email plus the first 200 characters of enquiry_text matches a submission from the last 24 hours.
Action
A note is appended to the existing record.
Exception
No second record, no second task, no second notification. The duplicate is visible on the original.
The AI step, and its boundaries
Survivors get one model call. It receives enquiry_text and returns a two-sentence summary for whoever picks it up, plus a category from a closed list of five (new project, existing client support, recruitment, supplier pitch, unclear).
It does not choose the owner, set priority, judge value, write to the CRM, or reply to the sender. It cannot return a category outside the list; anything unparseable becomes unclear. The model is asked to return a confidence score alongside the category, and below a threshold the category is overwritten with unclear regardless of what came back. The threshold is set per build by running the classifier over a labelled sample of real enquiries and choosing the point where false confident answers stop; on our own intake that landed at 0.7, and it is not a number to copy without doing that exercise. The original text is stored verbatim in its own field, so the summary is never the only surviving version of what the person wrote.
Review and output
The owner gets a CRM card carrying the summary, the category, and the raw text underneath. They check two things: that the category is right, and that the summary has not dropped a hard constraint such as a deadline, a named system or a budget ceiling. Reassigning the category rewrites the task title and the owner, and the correction is logged with its original value, which is what tells you months later whether the classifier is drifting.
The output is a contact and an opportunity record with source = web form, category set, summary as the first note, raw text preserved, owner assigned from a category-to-person lookup with a documented fallback for absence. Then one task, titled Respond to {company} ({category}), due 17:00 the next working day, assigned to that owner personally rather than to a shared queue. Queued items produce a single 09:00 digest to one named person.
What happens when it breaks
Each response is decided at build time and written into the runbook, not improvised during an outage.
Failure
What the system does
FailureCRM API returns 5xx or times out
What the system doesPayload is already persisted. Retries at 1, 5 and 25 minutes with jitter. Still failing after the third, the item stays queued and an alert names the submission ID
FailureCRM API returns 401 or 403
What the system doesNo retry. Credentials or scopes changed. Immediate alert, and that integration pauses so the queue does not drain into failures
FailureRequired field empty
What the system doesHeld in the review queue with the missing field named. Nothing is written partially, because a queued item gets opened and looked at, while a record that is only two-thirds filled just gets trusted
FailureSame enquiry arrives twice
What the system doesHash match inside 24 hours. Note appended to the existing record. No duplicate, no second task, no second notification
FailureClassifier returns low confidence
What the system doesCategory forced to unclear and routed to the daily digest. The record is still created; only the routing is withheld
FailureScanned image with no text layer
What the system doesDetected before extraction by checking for extractable text. Not sent to the model, because guessing on a blank read is the worst outcome. Filed for manual keying
FailureSomeone renames a spreadsheet column
What the system doesThe job asserts the header row against the expected schema before reading a single data row. Mismatch stops the run and alerts. Silent column drift corrupting six months of data is the failure this exists to prevent
FailureModel API unavailable or rate-limited
What the system doesDeterministic steps continue. The record is created with category unclear and an empty summary. The process degrades rather than stopping
The pattern underneath: retry what time alone might fix, queue what a person has to decide, alert on anything that will not clear itself, and make sure a failed step never leaves behind something that looks finished.
Who owns it afterwards
Automations decay because the world around them moves. A vendor renames a field or retires an API version. Someone adds a question to the form and nobody tells whoever maintains the endpoint. The colleague who owned the recruitment category leaves and the lookup table still points at their mailbox. Volume triples and a theoretical rate limit becomes a daily one. An OAuth token expires on a schedule nobody wrote down.
Two roles have to exist by name before handover. Someone in your business who reads the output and would notice it was wrong. That is usually the person whose work the automation makes easier, which is why the named-owner test sits at the top of this page. And someone technical holding the credentials who can change the code, whether that is your team, us, or a mix.
Handover covers a runbook for each failure above, the alert destinations, a credential inventory with expiry dates, the schema assertions, and the category-to-owner lookup as an editable table rather than a value buried in code. How much involvement continues afterwards depends on who holds the credentials and how often the surrounding systems change. Anything running on a schedule also needs somewhere to run, with monitoring and backups; where a build needs infrastructure rather than an application account, that part sits with YNVAR, the hosting side of the same house.
Narrow pilots that tend to work
Small enough to prove in weeks, with output someone reads daily so problems surface fast.
Form to CRM to owned taskThe process above, and a sound place to start because the trigger is clean and a dropped enquiry is visible to management.
Support inbox triageMessages tagged into a fixed set of request types, with deterministic rules doing the routing and the model only reading text.
Recurring report assemblyThe same handful of exports pulled on a schedule, schemas asserted, joined, and turned into the pack a person then interprets. All arithmetic in code.
Document field extraction into a review sheet: supplier documents parsed into a spreadsheet with a confidence column, low-confidence rows sorted to the top for keying.
Threshold notificationsSomething crosses a defined line and the responsible person is told once, with the context needed to act.
Versions of several of these run inside our own systems, which is where the failure table above comes from. That is why the table can name particular behaviours at all.
Processes we turn down
Nobody can describe it
We turn down processes nobody can describe. If two people in the same team explain the current steps differently, the disagreement is the actual work, and encoding one version of it makes that disagreement permanent and harder to see. Map it first. That is what a readiness assessment is for.
The model would decide, not prepare
We turn down builds where the model would make the decision rather than prepare it: approving spend, setting a price, judging an application, deciding a refund. Those get assembled, summarised and queued automatically, then a person decides.
Simulated clicks are the only path
We turn down anything whose only integration path is driving a desktop application through simulated clicks; it breaks on the next update and neither of us enjoys the aftermath. And we turn down work under pressure to ship into production before anyone has watched the output for a fortnight, because that is where automations quietly corrupt data for weeks before anybody notices.
Decision system
How the design gets chosen
Once a workflow passes the screen above, two things set its shape. How messy the input is decides whether a model is involved. How hard the outcome is to reverse decides where the person sits.
Reversibility of the outcomeShape of the input
Structured input, reversible outcome Deterministic, end to end
Field-mapped rules do the whole job. No model is involved, because there is nothing to interpret.
01 Messy input, reversible outcome A model reads, rules act
A model extracts and classifies the text. Deterministic steps do the writing, so a bad reading fails visibly.
02 Structured input, hard to reverse Deterministic, person commits
The preparation is automated. A named person confirms the step that cannot be taken back.
03 Messy input, hard to reverse A model drafts, a person decides
The draft saves the typing. The decision, and the accountability for it, stays with the person.
04
Workflow contrast
From scattered work to reviewable workflow
Before
Scattered requests
Manual copy-paste
Unclear owner
Repeated documents
No review trail
After
Single intake path
Structured handoff
Clear owner
Reusable workflow
Review point before sensitive output
FAQ
How long does one process take to build?
It depends on how many systems it crosses and whether they have usable APIs. A two-system build like form-to-CRM runs to a small number of weeks including a shadow period. Anything relying on a file export because a system has no API takes longer, and most of the extra time goes on that export's edge cases rather than the core logic.
Do we need to buy new software?
Rarely. Most of this sits on top of the CRM, forms, spreadsheets and inboxes you already run. Where a piece is genuinely missing, such as a queue, a scheduler or somewhere for the code to live, you hear about it during scoping and not as a surprise line in an invoice. The reason for it gets written down next to the alternative we rejected.
Can you automate something that only lives in one person's head?
Yes, but the first phase is getting it out of their head, which means sitting through several real runs, not one interview. Expect the written version and the actual version to differ. That gap is normal, and better found before the build than during it.
What happens if we change CRM in a year?
The deterministic logic, validation rules and classifier prompt survive. The connector and field mappings do not. Builds keep system-specific mapping in one place rather than scattered through the flow, which keeps migration a bounded job. It still costs money, and we will say so at the time.
How is it tested before going live?
It runs alongside the manual process on real inputs, without writing to production systems, for long enough to hit the odd cases. Outputs are compared with what the person actually did, and every disagreement is examined: each one is either a bug or a rule nobody had written down.
Which of our data ever reaches a model?
Only the fields a step genuinely needs. In the enquiry build above that is the free-text body and nothing else. The email address, the phone number and anything else you nominate never enter the call, because the classification does not need them and the exclusion is easier to enforce at the boundary than to promise afterwards. Build access itself is scoped the same way, to specific API scopes rather than admin credentials. What nobody should offer you on a page like this is a legal or compliance guarantee.
Next step
Start with the process, not the tool
Describe one task your team repeats and you get a straight view on whether it is worth changing.