Skip to content

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:

  • pending
  • accepted
  • declined

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

  • ownerId
  • technicianId
  • poolId
  • status
  • createdAt
  • respondedAt
  • expiresAt

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:

  1. select a pool;
  2. enter technician email;
  3. call POST /pools/{pool_id}/invites?ownerId=...;
  4. 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.