Contact Request Management
Summary
When a visitor sends an inquiry about a property, a ContactRequest record is created linked to both the property and its owning Pro. The Pro can then list all their incoming contact requests (paginated, with property context) and update the status of each request to track their follow-up workflow (new → read → replied).
State Diagram
stateDiagram-v2 state "Nouvelle demande" as new_request state "Lu par le pro" as read state "Répondu" as replied [*] --> new_request: Visitor submits contact form new_request --> read: Pro marks as read read --> replied: Pro marks as replied new_request --> replied: Pro replies directly replied --> [*]
Steps
1. List Contact Requests (Actor: pro)
GET /pro/contact-requests. Returns all contact requests where proId = request.proId, ordered by createdAt DESC, id DESC. Includes the linked property’s id, slug, and title for display context in the Pro’s inbox UI.
Supports cursor-based pagination (compound cursor (createdAt, id) base64url-encoded) with a limit query parameter. Returns { data, cursor: { next }, hasMore }.
Triggers: Pro opens their inquiries inbox Outcome: Paginated list of contact requests with property info
2. Update Contact Request Status (Actor: pro)
PUT /pro/contact-requests/:id with body { status: string }.
Ownership check: prisma.contactRequest.findFirst({ where: { id, proId: request.proId } }). If not found (or belongs to another pro’s property) → 404 NOT_FOUND.
Valid statuses: new_request (maps to DB value “new”), read, replied. The status is cast directly to the Prisma enum — the schema or enum definition enforces valid values.
Returns the updated ContactRequest record.
Triggers: Pro reads or responds to an inquiry Outcome: Contact request status updated in DB
Error States
- Contact request not found or owned by different pro → 404
NOT_FOUND - Unauthenticated → 401 (authenticatePro hook)
- CNI gate active → 403
CNI_REQUIRED
Notes
- Contact requests are created by the public-facing contact form (not in scope of this process file — that is an Acheteur/public-facing flow).
- The
ContactStatusenum in the DB uses"new"for what the application layer callsnew_request. - There is no bulk status update endpoint — each request must be updated individually.
Related Processes
- property-listing-crud — contact requests are associated with property listings