ACH Leads Integration
Automated intake of leads from ACH (Accident Claims Helpline). ACH emails a lead to a dedicated mailbox; Zoho Flow passes the email into a Zoho Creator custom API function, which stores the raw email, calls the Anthropic Claude API to extract the claim details as structured JSON, and creates an ACH Lead record. The Bookings team actions the lead from a report and uses the Create Claim button to turn it into a Claim.
Components
| Component | Type | Purpose |
|---|---|---|
ach-crashclaim@zohomail.com.au | Zoho Mail mailbox | Dedicated inbox that receives lead emails from ACH |
| Zoho Flow | Automation | Watches the mailbox and calls the Ach_incoming_email_recieved_from_zoho_mail custom API function with the parsed email |
| ACH Zoho Mail Payload | Zoho Creator form | Raw capture of the exact payload Zoho Flow sends in, before any parsing |
| ACH Incoming Email | Zoho Creator form | Parsed email (Subject/Body/From/To/cc) + full Claude request/response audit trail |
| ACH Claude Configuration | Zoho Creator form | Single-record settings form holding the Claude prompt, API key, model, and call parameters |
| Claude (Anthropic API) | External API | Extracts structured lead data (JSON) from the email body |
| ACH Lead | Zoho Creator form | Structured lead record parsed from the Claude JSON response |
| New ACH Lead Report | Zoho Creator report | Work queue where the Bookings team actions leads (Create Claim / Cancel Lead / Update Lead) |
| Bookings Dashboard → New ACH Leads tile | Zoho Creator dashboard panel | Stat tile showing the current count of open ACH Leads (New ACH Lead + Duplicate Lead); links through to the New ACH Lead Report |
| Claim Form | Zoho Creator form | Target form, prepopulated from the ACH Lead via the Create Claim button |
End-to-End Flow
ACH sends lead email
│
▼
ach-crashclaim@zohomail.com.au (Zoho Mail)
│ Zoho Flow trigger: new email in inbox
▼
Incoming Email recieved from zoho mail(payload)
│
├─ [1] Insert into "ACH Zoho Mail Payload" (raw payload audit)
├─ [2] Insert into "ACH Incoming Email" (parsed Subject/Body/From/To/cc)
▼
Claude API Integration for Data Extraction
│
├─ [3] Load "ACH Claude Configuration" (prompt, key, model, max_tokens, temperature)
├─ [4] Call Anthropic Messages API → structured JSON text
├─ [5] Write Claude_payload / Raw_AI_Response / Usage / Response / AI_Error_* back onto
│ the ACH Incoming Email record
▼
Create ACH Lead from JSON
│
├─ [6] Clean + parse the Claude JSON, create "ACH Lead" record
├─ [7] Duplicate check (registration this month → email → phone)
│ Status = "New ACH Lead" or "Duplicate Lead" (+ Duplicate_Reason)
▼
Lead appears on New ACH Lead Report (Status == New ACH Lead || Duplicate Lead)
│
├── [Create Claim] → Claim Form opens prepopulated → user submits
│ → Status = "ACH Claim Created", Claim_Form linked
│
├── [Cancel Lead] (confirmation prompt) → Status = "ACH Lead Cancelled"
│ → lead drops off this report
│
└── [Update Lead] → edit/correct lead fields (no status change)
Flow Steps
| # | Step | Detail |
|---|---|---|
| 1 | Email received | ACH sends the lead to ach-crashclaim@zohomail.com.au. Zoho Flow's mail trigger fires and calls the custom API function Ach_incoming_email_recieved_from_zoho_mail, passing the email as a payload map (subj, body, from_address, to_address, cc_address). |
| 2 | Capture raw payload | The function first inserts the untouched payload into ACH Zoho Mail Payload, then inserts a parsed ACH Incoming Email record (Subject, Body, From, To, cc). The new record's ID is passed forward. |
| 3 | AI extraction | claude_api_to_extract_html_to_json loads the single ACH Claude Configuration record and sends the email body to Claude. |
| 4 | Persist AI response | The Claude request, raw API response, token usage, extracted text, and any error details are written back onto the ACH Incoming Email record. |
| 5 | Create lead | create_ach_lead_from_json parses the Claude JSON and creates an ACH Lead record (see mapping table below). |
| 6 | Duplicate handling | The new lead is checked against existing leads by vehicle registration (same calendar month), then email, then phone. Status is set to Duplicate Lead on a match, otherwise New ACH Lead. |
| 7 | Queue | The lead is now visible on the New ACH Lead Report for manual action. |
Anthropic Claude Configuration & Usage
The ACH Claude Configuration record supplies everything the extraction call needs: prompt_template (system prompt), API_key, model_version, max_tokens, and temperature — with claude-haiku-4-5-20251001 / 2048 / 0 used as defaults when a field is left blank.
Model version: claude-haiku-4-5-20251001
System prompt (prompt_template):
You are a data extraction assistant. Extract information from the provided HTML email and return ONLY a valid JSON object — no explanation, no markdown, no code blocks.
Always use this exact JSON structure with these exact key names. If a value is not found in the HTML or the field is empty, set it to null.
Return this structure:
{
"Reference": null,
"Repairer used": null,
"Scheduled Drop Off": null,
"Customer Details": {
"Name": null,
"Address": null,
"City": null,
"State of Province": null,
"Country or Region": null,
"Postal Code": null,
"Date of Birth": null,
"Email": null,
"Tel": null
},
"Vehicle Details": {
"Is the car personal or company?": null,
"License category": null,
"License expiry": null,
"Customer Registration": null,
"Vehicle Make": null,
"Vehicle Model": null,
"Vehicle year": null,
"Tow bar?": null
},
"Accident Details": {
"Is the customer the accident vehicle driver?": null,
"Number of vehicles in the accident?": null,
"Accident Date": null,
"Accident Time": null,
"Accident Location": null,
"Car Parked?": null,
"Travelling Lane": null,
"Travelling Speed": null,
"Change Lanes?": null,
"Is this a rear ender?": null,
"Accident Description": null,
"Vehicle Damage Sustained": null,
"Police Event Number": null
},
"At Fault Details": {
"At fault name": null,
"At fault mobile": null,
"Accident Position": null,
"Insurance Policy?": null,
"Address": null,
"Claim Lodged?": null,
"Insurance claim number?": null,
"Insurance Company": null,
"At fault vehicle make": null,
"At fault vehicle model": null,
"At fault vehicle year": null,
"At fault vehicle registration": null,
"Liability confirmed?": null,
"Liability confirmed by?": null,
"Liability description": null,
"Liability date & time": null
},
"Witness Details": {
"Witness name": null,
"Witness phone": null,
"Witness address": null
}
}
Rules :
1. Never change, rename, add, or remove any key.
2. Never guess or infer a value — if it is not explicitly present in the HTML, set it to null.
3. Empty strings (""), whitespace-only values, and placeholder text (e.g. "N/A", "-", "n/a") must also be returned as null.
4. Do not include any text, explanation, or formatting outside the JSON object. No backticks. No markdown. The response must start with { and end with }.
5. Keep date and time values exactly as they appear in the source.
6. Keep Yes/No values exactly as they appear (e.g. "Yes", "No") — do not convert to boolean.
7. Apply the following date and time formatting rules to these specific fields only:
DATE FIELDS — "Accident Date", "Date of Birth", "License expiry":
- Input format: dd/MM/yyyy (e.g. 14/07/2026)
- Output format: dd-MMM-yyyy (e.g. 14-Jul-2026)
- Month must be 3-letter English abbreviation with first letter uppercase (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec)
TIME FIELD — "Accident Time":
- Convert any time format to 24-hour HH:mm
- Examples: "13.40pm" → "13:40", "1:40 PM" → "13:40", "09:00 AM" → "09:00", "10:00" → "10:00"
DATETIME FIELDS — "Scheduled Drop Off", "Liability date & time":
- Input format: dd/MM/yyyy HH:mm AM/PM (e.g. 14/07/2026 09:00 AM)
- Output format: dd-MMM-yyyy HH:mm using 24-hour time (e.g. 14-Jul-2026 09:00)
8. TIME FIELD — "Accident Time":
- Convert any time format to 24-hour HH:mm
- If the value contains a time range (e.g. "10:30 - 11:00am", "10.30 - 11.00am"), extract the FIRST time only and ignore everything after the dash
- Examples:
"13.40pm" → "13:40"
"1:40 PM" → "13:40"
"09:00 AM" → "09:00"
"10:00" → "10:00"
"10:30 - 11:00am" → "10:30"
"10.30 - 11.00am" → "10:30"
"9:00am - 10:00am" → "09:00"
claude_api_to_extract_html_to_json builds a request to https://api.anthropic.com/v1/messages:
- Headers:
x-api-key,anthropic-version: 2023-06-01,anthropic-beta: prompt-caching-2024-07-31. - System prompt is sent with
cache_control: {"type": "ephemeral"}so Anthropic's prompt caching can reuse it across calls, reducing cost/latency since the same instructions are sent for every email. - User message is
"Extract the claim details from this email:\n\n" + <email body>.
The prompt instructs Claude to return a single JSON object with five top-level sections — Customer Details, Vehicle Details, Accident Details, At Fault Details, Witness Details — which line up directly with the ACH Lead form sections above. The response text is stripped of ```json code fences and newlines before being parsed.
Token usage from the API response (usage) is written to the Usage field on ACH Incoming Email for cost monitoring; the full request/response are kept in Claude_payload / Raw_AI_Response for troubleshooting.
Duplicate check (runs immediately after the lead is created)
Checked in order, first match wins:
- Registration — another ACH Lead with the same
Customer_Registration, created within the current calendar month →Duplicate Lead, reason "Customer Rego match found in this month data". - Email — another ACH Lead (any time) with the same
Email→Duplicate Lead, reason "Customer Email match found". - Phone — another ACH Lead (any time) with the same
Tel→Duplicate Lead, reason "Customer Phone match found". - No match →
New ACH Lead.
Create Claim Process
- From the New ACH Lead Report, the Bookings team clicks Create Claim on a lead row (
create_claim_ach_leadworkflow). - This opens the Claim Form with the ACH Lead's ID passed in as
input.ACH_Lead. - The Claim Form's on-load script (
claims/onload) runs: it looks up the ACH Lead record and prepopulates the Claim Form from it per the mapping table below — including:- Setting
Lead_Source1to the Lead Source record for "Accident Claims Helpline". - Splitting
First_Name_client/First_Nameinto first/last name fields. - Parsing
Date_of_BirthandAccident_Datefromdd/mm/yyyyordd-MMM-yyyytext into real dates, andAccident_Time(e.g."7:40am") into a 24-hour time. - Mapping
Is_the_car_personal_or_companyof "Personal"/"Company" to Registration Type "Private"/"Business". - Looking up the at-fault insurer name against
Insurer_Formto populate the insurer lookup field, in addition to copying the plain insurer name. - Adding a Witness/Police Details row when a witness name or phone is present (witness address is folded into that row's Notes).
- Setting
- The user reviews the prepopulated form, completes any required fields with no source in the ACH Lead (Claim Type, Recovery Type, Relationship to Owner, Transmission, Vehicle Type, rental need details, insurance/renewal info, signatures), and submits.
- On successful submission, the Claim Form's on-success script (
claims/onsuccess of claim form) runs: it sets the ACH Lead'sStatustoACH Claim Created, links the new claim back viaClaim_Form, adds an audit note ("New ACH Lead Created -<File Number>"), and opens the newly created claim.
Mapping: ACH Lead → Claim Form
Set automatically by the on-load script, not part of the field-level mapping: Lead_Source1 = the Lead Source record for "Accident Claims Helpline".
Customer Details
| ACH Email Field | Claim Form Field |
|---|---|
| Name | Driver First Name |
| Address | Driver Address → Address Line 1 |
| City | Driver Address → District/City |
| State of Province | Driver Address → State/Province |
| Country or Region | Driver Address → Country |
| Postal Code | Driver Address → Postal Code |
| Date of Birth | Driver Date of Birth |
| Driver Email | |
| Tel | Driver Phone |
Vehicle Details
| ACH Email Field | Claim Form Field |
|---|---|
| Is the car personal or company? | Registration Type (Personal → Private, Company → Business) |
| License category | — |
| License expiry | — |
| Customer Registration | Registration * |
| Vehicle Make | Make * |
| Vehicle Model | Model * |
| Vehicle year | Year * |
| Tow bar? | — |
Accident Details
| ACH Email Field | Claim Form Field |
|---|---|
| Is the customer the accident vehicle driver? | — |
| Number of vehicles in the accident? | Number of Vehicles Involved * (1 to 5) |
| Accident Date | Date of Accident * |
| Accident Time | Time of Accident * |
| Accident Location | Accident Location * |
| Car Parked? | — |
| Travelling Lane | — |
| Travelling Speed | — |
| Change Lanes? | — |
| Is this a rear ender? | — |
| Accident Description | Accident Description * |
| Vehicle Damage Sustained | Damage Description * |
| Police Event Number | — |
At Fault Details
| ACH Email Field | Claim Form Field |
|---|---|
| At fault name | At Fault Name * |
| At fault mobile | At Fault Phone Number |
| Accident Position | Only captured for the 3rd/4th at-fault party |
| Insurance Policy? | Are you Insured? * |
| Address | At Fault Address |
| Claim Lodged? | Has the accident been reported to the at-fault insurer? |
| Insurance claim number? | Claim Number |
| Insurance Company | At Fault Party Insurer Name |
| At fault vehicle make | At Fault Vehicle Make |
| At fault vehicle model | At Fault Vehicle Model |
| At fault vehicle year | At Fault Vehicle Year |
| At fault vehicle registration | At Fault Registration |
| Liability confirmed? | Did the at fault driver admit liability? |
| Liability confirmed by? | — |
| Liability description | How was liability confirmed? |
| Liability date & time | Claims Management form → Date liability accepted |
Witness Details
| ACH Email Field | Claim Form Field |
|---|---|
| Witness name | Witness/Police Details row — Type = Witness → Name |
| Witness phone | Witness/Police Details row → Phone |
| Witness address | — |
* = required field on the Claim Form.
Not prepopulated (user must complete on the Claim Form)
Claim_Type1,Recovery_Type,Rental_Provider,Rental_Recovery_typeRelationship_to_Owner,Transmission,Vehicle_Type- Rental Need section (
Rental_is_my_only_option_because, etc.) Are_you_insured,Policy_Renewal/ renewal dates- Signatures / Terms & Conditions
New ACH Lead Report
Report on the ACH Lead form: shows records where Status == "New ACH Lead" or "Duplicate Lead", sorted by Added_Time descending. A lead drops off this report once it's cancelled or converted to a claim.
The Bookings Dashboard shows a "New ACH Leads" stat tile alongside the other intake counters (New Claims, New Leads, Pending Validation, etc.), giving the open-lead count at a glance and linking through to this report.
Custom Buttons
| Button | Workflow | Behaviour |
|---|---|---|
| Create Claim | create_claim_ach_lead | Opens the Claim Form prepopulated from the lead (see Create Claim Process). On submission, Status becomes ACH Claim Created. |
| Cancel Lead | Cancel_Lead_ACH | Prompts "Execute the Cancel Lead action for this record?" before running. Sets Status to ACH Lead Cancelled, which removes the lead from this report. |
| Update Lead | Update_ACH_lead | Lets the Bookings team edit/correct lead fields (e.g. fix extraction errors) before creating a claim. Does not change Status. |
Lead Status Lifecycle
| Status | Set when | Set by |
|---|---|---|
| New ACH Lead | Lead created and no duplicate match found | create_ach_lead_from_json |
| Duplicate Lead | Lead created and a registration/email/phone match found | create_ach_lead_from_json |
| ACH Lead Cancelled | User runs Cancel Lead from the report | Cancel_Lead_ACH workflow |
| ACH Claim Created | User completes the prepopulated Claim Form via Create Claim | claims/onsuccess of claim form |
┌──────► Duplicate Lead ──┐
Lead created ───┤ ├──► ACH Claim Created
└──────► New ACH Lead ────┤
└──► ACH Lead Cancelled
Error Handling
| Failure | Where recorded | Behaviour |
|---|---|---|
| Claude API call fails / errors in response | AI_Error_Status + AI_Error_Log on the ACH Incoming Email record | No ACH Lead created; the incoming email record remains for troubleshooting |
| Claude returns empty/unparseable JSON | create_ach_lead_from_json returns a failure status; Response still holds the raw text | No ACH Lead created |
| Function-level exception during the API call | AI_Error_Status = "Failed", AI_Error_Log = exception detail | No ACH Lead created |
Token usage for every extraction call is recorded in Usage on the ACH Incoming Email record for cost monitoring.
Open Items
- Whether a
Duplicate Leadcan still be converted via Create Claim is not explicitly restricted in the code reviewed.