Contact Request Submission
Summary
Any visitor (authenticated or not) can send an inquiry about a specific property to the listing Pro. The form includes a hidden honeypot field to silently discard bot submissions. Rate limiting prevents spam at the infrastructure level. A ContactRequest record is created linking the sender’s details, the property, and the property’s Pro. The Pro sees incoming requests in their contacts dashboard.
State Diagram
stateDiagram-v2 state "Formulaire soumis" as FormSubmitted state "Anti-spam déclenché" as HoneypotTripped state "Bien introuvable" as PropertyNotFound state "Demande enregistrée" as RequestCreated [*] --> FormSubmitted: visitor submits contact form FormSubmitted --> HoneypotTripped: website field has a value (bot) HoneypotTripped --> [*]: 201 success (silent discard, bot not alerted) FormSubmitted --> PropertyNotFound: propertyId does not exist PropertyNotFound --> [*]: 404 NOT_FOUND FormSubmitted --> RequestCreated: property found, honeypot empty RequestCreated --> [*]: 201 ContactRequest record
Steps
1. Submit Contact Form (Actor: acheteur)
Client POSTs to POST /contact-requests with:
propertyId— UUID of the target propertysenderName— requester’s full namesenderEmail— requester’s email addresssenderPhone— requester’s phone number (optional)message— free-text inquirywebsite— honeypot field (hidden via CSS in the UI, never shown to the user)
Rate limit: 5 requests per minute per IP.
Validation is handled by createContactSchema (JSON Schema).
Outcome: Request reaches the handler.
2. Honeypot Check (Actor: system)
If the website field contains any value, the submission is silently accepted with a 201 success response. No database record is created. The bot receives no indication that the submission was discarded, preventing adaptive behavior.
Outcome: Bot submissions are dropped invisibly; legitimate submissions continue.
3. Property Lookup (Actor: system)
prisma.property.findUnique({ where: { id: propertyId } }). If not found → 404 NOT_FOUND. The proId is extracted from the property to link the contact request to the correct Pro.
Outcome: Property and its proId confirmed.
4. Create Contact Request (Actor: system)
prisma.contactRequest.create() stores: propertyId, proId (from the property), senderName, senderEmail, senderPhone, message. Initial status is new_request (maps to DB value "new").
Outcome: 201 response with the created ContactRequest record.
5. Pro Reviews Request (Actor: pro)
The Pro accesses their contacts dashboard (authenticated, separate route: GET /pro/contacts). Contact requests are displayed with status new_request, read, or replied. The Pro can update status and respond directly to the sender via their own email client.
Outcome: Lead received and managed by the Pro.
Error States
- Honeypot field filled → 201 silent discard (intentional, not an error state for the client)
- Property not found → 404
NOT_FOUND— “Property not found” - Rate limit exceeded → 429 (handled by Fastify rate-limit plugin)
Related Processes
- browsing — contact requests are initiated from property detail pages
- registration-email-verification — no authentication required; acheteurs do not need an account to submit