Skip to main content

Pre-request feedback

User story​

As a Zenika employee, I would like to request feedback from colleagues when I don't know their email addresses upfront. Instead of specifying individual emails, I can generate a shareable "magic link" that colleagues can use to provide their email address and trigger a feedback request.

This is particularly useful when:

  • I want to share the request in a Slack channel or Teams group
  • My colleagues' company blocks emails sent via Mailgun. However, they can still give me feedback using their personal email addresses, which I don't know in advance.

Once a colleague uses the magic link and provides their email, they receive a feedback request email just like in the standard feedback request workflow.

Technical specifications​

Be sure to read Request feedback first, as this feature is an enhancement of that workflow.

  1. The requester (authenticated user) navigates to /request
  2. Selects "With a magic link" method
  3. Fills and Submits the form (the recipients field is disabled for this method)
  4. Client calls POST /feedback/pre-request/token
  5. Server creates a document in the feedbackPreRequestToken collection and returns { token: string }
  6. Client navigates to the success page displaying the magic link

The requester can copy this link and share it through any channel (Slack, Teams, email, etc.).

Firestore schema​

A document is added in the feedbackPreRequestToken collection to store magic link tokens:

const preRequestToken: FeedbackPreRequestToken = {
receiverEmail: 'pinocchio@zenika.com', // Who will receive the feedback
message: 'Hi team, please share your feedback...', // Encrypted
shared: true, // Whether to share with manager
expiresAt: 1711662999463, // Timestamp
usedBy: ['gimini@gmail.com', 'gepetto@gmail.com'], // Emails that used this token
};
  • The document ID is the token itself (auto-generated by Firestore).
  • The usedBy array tracks which emails have already used this token
  • Each use of the token triggers a separate feedback request

The magic link format is: {origin}/pre-request/token/{token}

Example:

https://feedzback.znk.io/pre-request/token/abc123xyz

The magic link does not contain the locale. The redirection to /fr or /en occurs when the colleague visits the magic link page (see src/404.html for details).

However, since this redirection is not functional in the dev-local environment, the locale is explicitly added only in this case.

Example in dev-local environment:

https://feedzback.znk.io/fr/pre-request/token/abc123xyz

When a colleague accesses the magic link:

  1. The colleague (not authenticated) visits /pre-request/token/{token}
  2. The colleague's browser redirects to the appropriate locale (example: /fr/pre-request/token/{token})
  3. Client calls GET /feedback/check-pre-request/{token} to validate the token
  4. If valid, the page displays the details of the pre-request and a form field allowing the colleague to enter their email address
  5. The colleague enters their email and submits
  6. Client calls POST /feedback/pre-request/email with { token, recipient }
  7. Server validates the token and email, then:
    • Adds the email to the usedBy array in feedbackPreRequestToken document
    • Triggers the standard feedback request workflow:
      • Creates feedback and feedbackRequestToken documents
      • Sends a feedback request email to the colleague
  8. Client navigates to a success page confirming the email was sent

From this point forward, the flow is identical to the standard Reply to feedback request workflow.

Token constraints​

  • Expiration configured via FEEDBACK_PRE_REQUEST_EXPIRATION_IN_DAYS constant (3 days)
  • Maximum uses configured via FEEDBACK_PRE_REQUEST_MAX_USES constant (10 uses per token)

When a colleague attempts to use a magic link, the following checks are performed:

ValidationError TypeDescription
Token existstoken_invalidThe token doesn't exist in the database
Not expiredtoken_expiredThe current time exceeds expiresAt
Under max usestoken_max_uses_reachedThe usedBy array length has reached the limit
Email not usedrecipient_already_usedThe email is already in the usedBy array
Not self-requestrecipient_forbiddenThe email matches the receiverEmail

If any validation fails, a BadRequestException or ForbiddenException is thrown with the corresponding error type.