From 26104d47a61421033122a9108c791aff63a2edee Mon Sep 17 00:00:00 2001
From: Marie <51697796+ijreilly@users.noreply.github.com>
Date: Thu, 6 Aug 2026 10:01:34 +0200
Subject: [PATCH] Report a sending domain as pending while SES waits for the
DKIM records (#23818)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Problem
A sending domain that is simply waiting on its DKIM records is displayed
as **Failed**, with every DKIM row marked **Error**, even when the DNS
is correct and AWS has already published the key.
Hit while setting up `twenty.dev` for a demo. All five CNAMEs resolve
correctly from the authoritative nameserver and from a public resolver,
none are proxied, the unsubscribe row is green, and the first DKIM token
already resolves through to its published key at AWS:
```
$ dig +short TXT abbr…._domainkey.twenty.dev
abbr….dkim.amazonses.com.
"p=MIIBIjANBgkq…"
```
Yet all three DKIM rows read Error, which tells the user to go fix DNS
that isn't broken.
## Cause
`determineVerificationStatus` treats `VerifiedForSendingStatus ===
false` as terminal:
```ts
if (
identityResponse.VerifiedForSendingStatus === false ||
dkimStatus === 'FAILED'
) {
return EmailingDomainStatus.FAILED;
}
return EmailingDomainStatus.PENDING;
```
SES reports `VerifiedForSendingStatus: false` for the entire period it
is waiting to detect the DKIM CNAMEs, which is the normal state of every
domain between setup and verification. So a pending domain returns
FAILED, and the PENDING branch is unreachable for any identity where the
field is present at all. `TEMPORARY_FAILURE`, which SES documents as
retryable, was also reported as Failed.
The status is then stamped onto each DKIM row by `withRecordStatus`,
which is why all three rows change together and none of them reflects
its own record.
---
...s-ses-driver-domain-status.service.spec.ts | 94 +++++++++++++++++++
.../services/aws-ses-driver.service.ts | 15 ++-
2 files changed, 100 insertions(+), 9 deletions(-)
create mode 100644 packages/twenty-server/src/engine/core-modules/emailing-domain/drivers/aws-ses/services/__tests__/aws-ses-driver-domain-status.service.spec.ts
diff --git a/packages/twenty-server/src/engine/core-modules/emailing-domain/drivers/aws-ses/services/__tests__/aws-ses-driver-domain-status.service.spec.ts b/packages/twenty-server/src/engine/core-modules/emailing-domain/drivers/aws-ses/services/__tests__/aws-ses-driver-domain-status.service.spec.ts
new file mode 100644
index 0000000000..98ee10637f
--- /dev/null
+++ b/packages/twenty-server/src/engine/core-modules/emailing-domain/drivers/aws-ses/services/__tests__/aws-ses-driver-domain-status.service.spec.ts
@@ -0,0 +1,94 @@
+import { type AwsSesClientProvider } from 'src/engine/core-modules/emailing-domain/drivers/aws-ses/providers/aws-ses-client.provider';
+import { AwsSesDriver } from 'src/engine/core-modules/emailing-domain/drivers/aws-ses/services/aws-ses-driver.service';
+import { type AwsSesHandleErrorService } from 'src/engine/core-modules/emailing-domain/drivers/aws-ses/services/aws-ses-handle-error.service';
+import { type AwsSesRegisterDomainService } from 'src/engine/core-modules/emailing-domain/drivers/aws-ses/services/aws-ses-register-domain.service';
+import { type AwsSesSendEmailService } from 'src/engine/core-modules/emailing-domain/drivers/aws-ses/services/aws-ses-send-email.service';
+import { type AwsSesDriverConfig } from 'src/engine/core-modules/emailing-domain/drivers/interfaces/driver-config.interface';
+import { EmailingDomainDriver } from 'src/engine/core-modules/emailing-domain/drivers/types/emailing-domain-driver.type';
+import { EmailingDomainStatus } from 'src/engine/core-modules/emailing-domain/drivers/types/emailing-domain-status.type';
+import { type UnsubscribeContentService } from 'src/engine/core-modules/emailing-domain/services/unsubscribe-content.service';
+
+describe('AwsSesDriver getDomainStatus', () => {
+ const config: AwsSesDriverConfig = {
+ driver: EmailingDomainDriver.AWS_SES,
+ region: 'us-east-1',
+ accountId: '123456789012',
+ };
+
+ const setUp = (identityResponse: Record) => {
+ const send = jest.fn().mockResolvedValue(identityResponse);
+ const clientProvider = {
+ getSESClient: () => ({ send }),
+ } as unknown as AwsSesClientProvider;
+
+ const driver = new AwsSesDriver(
+ config,
+ clientProvider,
+ {} as unknown as AwsSesHandleErrorService,
+ {} as unknown as AwsSesRegisterDomainService,
+ {} as unknown as AwsSesSendEmailService,
+ {} as unknown as UnsubscribeContentService,
+ );
+
+ return driver.getDomainStatus({
+ domain: 'twenty.dev',
+ workspaceId: 'workspace-id',
+ });
+ };
+
+ it('should report pending while SES is still waiting for the DKIM records', async () => {
+ const result = await setUp({
+ VerifiedForSendingStatus: false,
+ DkimAttributes: { SigningEnabled: true, Status: 'PENDING', Tokens: [] },
+ });
+
+ expect(result.status).toBe(EmailingDomainStatus.PENDING);
+ });
+
+ it('should report pending when a DKIM check fails temporarily', async () => {
+ const result = await setUp({
+ VerifiedForSendingStatus: false,
+ DkimAttributes: {
+ SigningEnabled: true,
+ Status: 'TEMPORARY_FAILURE',
+ Tokens: [],
+ },
+ });
+
+ expect(result.status).toBe(EmailingDomainStatus.PENDING);
+ });
+
+ it('should report failed when SES gave up on the DKIM records', async () => {
+ const result = await setUp({
+ VerifiedForSendingStatus: false,
+ DkimAttributes: { SigningEnabled: true, Status: 'FAILED', Tokens: [] },
+ });
+
+ expect(result.status).toBe(EmailingDomainStatus.FAILED);
+ });
+
+ it('should report verified once SES signs with DKIM', async () => {
+ const result = await setUp({
+ VerifiedForSendingStatus: true,
+ DkimAttributes: { SigningEnabled: true, Status: 'SUCCESS', Tokens: [] },
+ });
+
+ expect(result.status).toBe(EmailingDomainStatus.VERIFIED);
+ });
+
+ it('should mark every DKIM record pending while the domain is pending', async () => {
+ const result = await setUp({
+ VerifiedForSendingStatus: false,
+ DkimAttributes: {
+ SigningEnabled: true,
+ Status: 'PENDING',
+ Tokens: ['token1', 'token2', 'token3'],
+ },
+ });
+
+ expect(result.verificationRecords).toHaveLength(3);
+ expect(
+ result.verificationRecords.every((record) => record.status === 'pending'),
+ ).toBe(true);
+ });
+});
diff --git a/packages/twenty-server/src/engine/core-modules/emailing-domain/drivers/aws-ses/services/aws-ses-driver.service.ts b/packages/twenty-server/src/engine/core-modules/emailing-domain/drivers/aws-ses/services/aws-ses-driver.service.ts
index ac89c383ac..bd283b637b 100644
--- a/packages/twenty-server/src/engine/core-modules/emailing-domain/drivers/aws-ses/services/aws-ses-driver.service.ts
+++ b/packages/twenty-server/src/engine/core-modules/emailing-domain/drivers/aws-ses/services/aws-ses-driver.service.ts
@@ -16,16 +16,16 @@ import {
import { isNonEmptyString } from '@sniptt/guards';
+import {
+ EmailingDomainDriverException,
+ EmailingDomainDriverExceptionCode,
+} from 'src/engine/core-modules/emailing-domain/drivers/exceptions/emailing-domain-driver.exception';
import { type AwsSesDriverConfig } from 'src/engine/core-modules/emailing-domain/drivers/interfaces/driver-config.interface';
import {
type EmailingDomainDriverInterface,
type EmailingDomainResourceInput,
type EmailingDomainVerificationResult,
} from 'src/engine/core-modules/emailing-domain/drivers/interfaces/emailing-domain-driver.interface';
-import {
- EmailingDomainDriverException,
- EmailingDomainDriverExceptionCode,
-} from 'src/engine/core-modules/emailing-domain/drivers/exceptions/emailing-domain-driver.exception';
import { type EmailingDomainSendEmailRequest } from 'src/engine/core-modules/emailing-domain/drivers/types/emailing-domain-send-email-input.type';
import { type EmailingDomainSendEmailResult } from 'src/engine/core-modules/emailing-domain/drivers/types/emailing-domain-send-email-result.type';
import { UnsubscribeHostnameStatus } from 'src/engine/core-modules/emailing-domain/drivers/types/unsubscribe-hostname-status.type';
@@ -34,8 +34,8 @@ import { type UnsubscribeContentService } from 'src/engine/core-modules/emailing
import { AWS_SES_RESOURCE_NAME_PREFIX } from 'src/engine/core-modules/emailing-domain/drivers/aws-ses/constants/aws-ses-resource-name-prefix.constant';
import { type AwsSesClientProvider } from 'src/engine/core-modules/emailing-domain/drivers/aws-ses/providers/aws-ses-client.provider';
-import { AwsSesRegisterDomainService } from 'src/engine/core-modules/emailing-domain/drivers/aws-ses/services/aws-ses-register-domain.service';
import { type AwsSesHandleErrorService } from 'src/engine/core-modules/emailing-domain/drivers/aws-ses/services/aws-ses-handle-error.service';
+import { AwsSesRegisterDomainService } from 'src/engine/core-modules/emailing-domain/drivers/aws-ses/services/aws-ses-register-domain.service';
import { type AwsSesSendEmailService } from 'src/engine/core-modules/emailing-domain/drivers/aws-ses/services/aws-ses-send-email.service';
import { EmailingDomainStatus } from 'src/engine/core-modules/emailing-domain/drivers/types/emailing-domain-status.type';
import { type VerificationRecordDTO } from 'src/engine/core-modules/emailing-domain/dtos/verification-record.dto';
@@ -376,10 +376,7 @@ export class AwsSesDriver implements EmailingDomainDriverInterface {
return EmailingDomainStatus.VERIFIED;
}
- if (
- identityResponse.VerifiedForSendingStatus === false ||
- dkimStatus === 'FAILED'
- ) {
+ if (dkimStatus === 'FAILED') {
return EmailingDomainStatus.FAILED;
}