One queue, one record
Every message becomes one job in one store. The same sender, subject and text twice is one job, not two.
Start a project →
Live on this site · every contact-form message goes through it
When you send us a message, it lands in a queue. AI labels it, a fixed set of rules decides what may happen next, and a routine enquiry gets an acknowledgement while anything sensitive waits for a person. Each message gets one reply at most, and every step is logged.
How it is built
Every message becomes one job in one store. The same sender, subject and text twice is one job, not two.
A worker claims a job for a few minutes. If it stops halfway, the lease runs out and the job is picked up again, with a limit on attempts.
Claude returns labels in a fixed format: what kind of message it is, how urgent, and whether a person should read it. It never writes the reply.
A reply is written to an outbox before it is sent, and the provider's answer is recorded. One reply per job, never two.
What happens to your message
The contact form checks the message and queues it. The team is emailed a copy at the same moment, so nothing depends on the queue alone.
Fixed rules drop bulk mail and anything from a no-reply, bounce or internal address, and flag urgent words and support problems.
Claude reads the message as data and returns a category, an urgency from 0 to 5 and a "needs a person" flag. If the call fails, the rules decide alone.
The AI's labels can only make the outcome more careful: an enquiry the rules would acknowledge goes to a person if the AI flags it. Complaints, support problems and urgent messages always wait for a person.
A routine enquiry gets a short, fixed acknowledgement. Everything else sits on a review board until someone approves a reply or closes it.
The worker sends from the outbox and records the result. A failed send is retried after 1, 2, then 4 minutes and so on, and set aside for a person if it keeps failing.
State model
| Status | Meaning | Can move to |
|---|---|---|
| new | Queued, not yet claimed | processing |
| processing | A worker holds the lease and is classifying it | outbox, awaiting review, ignored, dead letter |
| awaiting review | Needs a person before any reply | outbox, ignored |
| outbox | A reply is staged, or retrying after a failed send | sent, dead letter |
| sent | The email provider accepted the reply | closed |
| ignored | Bulk mail, a no-reply sender, or closed by a person | closed |
| dead letter | Kept failing, or the address was refused | a person decides |
Guardrails
The pipeline
Plain PHP and locked JSON files, a worker that runs every minute, and a review board behind a password. The same design works with Gmail, Outlook or a helpdesk as the intake.
Contact form
└─ validate + dedupe → job (new)
└─ worker lease → processing
├─ rules: spam / no-reply / urgent / support
├─ Claude: category · urgency · needs_human
├─ policy gate → acknowledge | review | ignore
├─ outbox (one reply per job)
├─ send → sent | retry with backoff
└─ dead letter → a person decides