The Contact Form Is a Product: Reliability, Privacy, and Zero Duplicate Webhooks
What looks like three fields and a button is actually a delivery system—and it deserves the same engineering care as the rest of the product.
A contact form is one of the smallest interfaces on a portfolio, but it carries one of the most important responsibilities: delivering a real person’s message exactly once.
I learned this after seeing a common failure mode. Visitor tracking fired one webhook anonymously, then the completed form fired another webhook with details. From the owner’s perspective it looked like duplicate spam. From the visitor’s perspective, the form appeared normal.
The interface was not the problem. The event model was.
Separate intent from submission
Typing into a form is not a submission. Opening a page is not a submission. A contact notification should be sent only after the visitor explicitly presses Send and the server validates the payload.
Analytics may record an anonymous page view, but it must use a separate event and destination. Mixing visitor analytics with contact delivery creates confusion and makes debugging harder.
Keep secrets on the server
Webhook URLs, mail credentials, and service keys should never be placed in browser code. The browser submits a small validated payload to a server route. The server decides where that message should go.
This architecture protects credentials and gives one reliable place to add validation, rate limits, logging, and error handling.
Store before delivering
External services fail. Email can time out. A webhook can be temporarily unavailable. If the message exists only during that network request, it may be lost.
The portfolio now stores contact messages in a private inbox before attempting delivery. Admin can mark them unread, read, replied, or archived. Email and Discord become delivery channels—not the only copy of the message.
Make duplicates difficult
A robust submission flow disables the button while sending and treats one click as one request. For higher-volume products, I would also add an idempotency key so retried requests cannot create multiple records.
Be honest about success
A green success message should mean the message was accepted by the system. If delivery fails completely, the interface should say so clearly and preserve enough information to retry safely.
The lesson
Small features deserve serious thinking when they carry important user intent. Reliability is part of design. The best contact form is not the one with the most animation—it is the one both sides can trust.