Specification — Technician Invitations in Poolia
1. Objective
Allow pool owners to invite technicians to collaborate on specific pools, while preserving a clear acceptance workflow and preventing invalid or duplicate pending invitations.
This domain is important because technician assignment is not modeled as direct open editing from the frontend. Instead, the system uses invitations as the controlled entry point for owner-to-technician association.
2. Functional Approach
An invitation is represented by an Invite document linked to:
- one owner;
- one technician;
- one pool.
The invitation has a lifecycle defined by status:
pendingaccepteddeclined
The current implementation supports both:
- direct invite CRUD;
- business-oriented pool-specific invite creation by technician email.
3. Data Design
3.1. Invite
The invites collection stores invitation records.
Main fields
ownerIdtechnicianIdpoolIdstatuscreatedAtrespondedAtexpiresAt
Expiration behavior
If expiresAt is not explicitly provided, the model sets it automatically to:
createdAt + 30 days
This is implemented through a model validator.
4. Business Rules
Owner validation
An owner referenced during invite creation must exist and have role owner.
Technician validation
A technician referenced during invite creation must exist and have role technician.
Pool validation
The target pool must exist.
Duplicate pending prevention
The pool-specific business flow prevents creating more than one pending invite for the same:
- pool
- technician
Accepting an invite
Accepting an invite:
- requires status
pending; - requires the pool to still exist;
- fails if the technician is already assigned to the pool;
- appends the technician id to
pool.technicianIds; - updates invitation status to
accepted; - sets
respondedAt.
Declining an invite
Declining an invite:
- requires status
pending; - updates status to
declined; - sets
respondedAt.
5. REST Endpoints
5.1. Create invite directly
POST /invites
Creates an invite from explicit ids.
Request body
{
"ownerId": "owner-id",
"technicianId": "technician-id",
"poolId": "pool-id",
"status": "pending"
}
5.2. List invites
GET /invites
Returns all invites.
5.3. List invites by technician
GET /invites/technicians/{technician_id}
Returns invites for a technician.
5.4. List pending invites by technician
GET /invites/technicians/{technician_id}/pending
Returns only pending invites for a technician.
5.5. Get invite
GET /invites/{invite_id}
Returns one invite or 404.
5.6. Accept invite
POST /invites/{inviteId}/accept
Behavior
- loads the invite;
- validates pending status;
- loads the pool;
- checks whether the technician is already assigned;
- adds the technician to the pool;
- marks the invite as accepted.
5.7. Decline invite
POST /invites/{inviteId}/decline
Behavior
- loads the invite;
- validates pending status;
- marks it as declined.
5.8. Update invite
PATCH /invites/{invite_id}
Generic partial update over the invite document.
5.9. Delete invite
DELETE /invites/{invite_id}
Deletes the invite document.
5.10. Create invite for pool by technician email
POST /pools/{pool_id}/invites?ownerId=...
Request body
{
"technicianEmail": "tech@example.com"
}
Behavior
- validates the owner;
- validates the pool;
- finds a technician by email;
- validates that the user is a technician;
- rejects if a pending invite already exists for that technician and pool;
- creates a new pending invite.
This is the most business-friendly invitation flow for the frontend.
6. Backend Logic Summary
Generic invite CRUD
The /invites router exposes direct CRUD-style operations.
Business invite flow
The pool-specific invite flow encapsulates the owner + pool + technician-email business rule and avoids requiring frontend to know technician ids in advance.
Acceptance flow
Invite acceptance also performs the pool assignment, making the invitation lifecycle operational rather than purely informational.
7. Frontend Integration Notes
Owner workflow
Recommended owner flow:
- select a pool;
- enter technician email;
- call
POST /pools/{pool_id}/invites?ownerId=...; - show pending/accepted/declined invite status in the UI.
Technician workflow
Technician-facing screens can use:
GET /invites/technicians/{technician_id}GET /invites/technicians/{technician_id}/pending
to list relevant invitations.
Acceptance flow
When a technician accepts an invite, frontend does not need an extra pool-assignment request because the backend already performs the assignment.
8. UX Considerations
Duplicate pending prevention
Frontend should surface the backend conflict case clearly, because owners may try to re-invite a technician who already has a pending invite for the same pool.
Invitation status clarity
Because invitations have explicit status and response timestamps, the UI can clearly distinguish between:
- pending
- accepted
- declined
9. Current Scope
The current implementation covers:
- direct invitation CRUD;
- invitation creation by technician email;
- pending invite filtering;
- acceptance and decline flows;
- technician assignment on acceptance.
It does not currently cover:
- invitation cancellation endpoint distinct from delete;
- expiration enforcement at read time;
- email delivery or notifications;
- owner access control at the route layer.
10. Future Recommendations
A future version could add:
- expiration checks during accept;
- notification delivery by email or push;
- resend flow;
- invite search and pagination;
- explicit owner authorization rules on invite endpoints.
11. Final Summary
Technician invitations in Poolia provide a controlled pool-assignment workflow built on explicit invite documents and a simple status lifecycle. The design is aligned with the existing REST architecture and keeps technician assignment auditable and easy to consume from frontend flows.