feat: add header metadata to emails (#2927)

This commit is contained in:
David Nguyen
2026-06-04 16:48:57 +10:00
committed by GitHub
parent c41e387220
commit 0ecde7ac1e
4 changed files with 44 additions and 0 deletions
@@ -16,6 +16,7 @@ import { createElement } from 'react';
import { getI18nInstance } from '../../../client-only/providers/i18n-server';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../../constants/app';
import { RECIPIENT_ROLE_TO_EMAIL_TYPE, RECIPIENT_ROLES_DESCRIPTION } from '../../../constants/recipient-roles';
import { buildEnvelopeEmailHeaders } from '../../../server-only/email/build-envelope-email-headers';
import { getEmailContext } from '../../../server-only/email/get-email-context';
import { assertOrganisationRatesAndLimits } from '../../../server-only/rate-limit/assert-organisation-rates-and-limits';
import { updateRecipientNextReminder } from '../../../server-only/recipient/update-recipient-next-reminder';
@@ -224,6 +225,11 @@ export const run = async ({ payload, io }: { payload: TSendSigningEmailJobDefini
subject: renderCustomEmailTemplate(documentMeta?.subject || emailSubject, customEmailTemplate),
html,
text,
headers: buildEnvelopeEmailHeaders({
userId,
envelopeId: envelope.id,
teamId: envelope.teamId,
}),
});
});
}
@@ -16,6 +16,7 @@ import { createElement } from 'react';
import { getI18nInstance } from '../../../client-only/providers/i18n-server';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../../constants/app';
import { RECIPIENT_ROLES_DESCRIPTION } from '../../../constants/recipient-roles';
import { buildEnvelopeEmailHeaders } from '../../../server-only/email/build-envelope-email-headers';
import { getEmailContext } from '../../../server-only/email/get-email-context';
import { assertOrganisationRatesAndLimits } from '../../../server-only/rate-limit/assert-organisation-rates-and-limits';
import { updateRecipientNextReminder } from '../../../server-only/recipient/update-recipient-next-reminder';
@@ -211,6 +212,11 @@ export const run = async ({ payload, io }: { payload: TProcessSigningReminderJob
subject: emailSubject,
html,
text,
headers: buildEnvelopeEmailHeaders({
userId: envelope.userId,
envelopeId: envelope.id,
teamId: envelope.teamId,
}),
});
await prisma.documentAuditLog.create({
@@ -27,6 +27,7 @@ import { isDocumentCompleted } from '../../utils/document';
import type { EnvelopeIdOptions } from '../../utils/envelope';
import { isRecipientEmailValidForSending } from '../../utils/recipients';
import { renderEmailWithI18N } from '../../utils/render-email-with-i18n';
import { buildEnvelopeEmailHeaders } from '../email/build-envelope-email-headers';
import { getEmailContext } from '../email/get-email-context';
import { getEnvelopeWhereInput } from '../envelope/get-envelope-by-id';
import { assertOrganisationRatesAndLimits } from '../rate-limit/assert-organisation-rates-and-limits';
@@ -255,6 +256,11 @@ export const resendDocument = async ({ id, userId, recipients, teamId, requestMe
: emailSubject,
html,
text,
headers: buildEnvelopeEmailHeaders({
userId: envelope.userId,
envelopeId: envelope.id,
teamId: envelope.teamId,
}),
});
await prisma.documentAuditLog.create({
@@ -0,0 +1,26 @@
export type BuildEnvelopeEmailHeadersOptions = {
userId: number;
envelopeId: string;
teamId: number;
};
/**
* Builds opaque sender-attribution headers stamped onto outgoing user-triggered
* envelope emails. These appear in AWS SES bounce/complaint notifications (when
* "include original headers" is enabled) so an abusive send can be traced back
* to the originating Documenso user via the admin panel.
*
* Only opaque IDs are included so recipients cannot see the sender's email
* address or name in the delivered message source.
*/
export const buildEnvelopeEmailHeaders = ({
userId,
envelopeId,
teamId,
}: BuildEnvelopeEmailHeadersOptions): Record<string, string> => {
return {
'X-Documenso-Sender-User-Id': String(userId),
'X-Documenso-Envelope-Id': envelopeId,
'X-Documenso-Team-Id': String(teamId),
};
};