Skip to content

Commit 6d73c8b

Browse files
oschwaldclaude
andcommitted
Add email domain outputs for Insights and Factors
Adds support for new email domain fields: - classification, risk, and volume - visit sub-object with status, lastVisitedOn, and hasRedirect 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 16e476e commit 6d73c8b

File tree

6 files changed

+144
-6
lines changed

6 files changed

+144
-6
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,17 @@ CHANGELOG
1212
* Added the input `/payment/method`. This is the payment method associated
1313
with the transaction. You may provide this by providing `method` to
1414
`Payment`.
15+
* Added new `response.email.domain` outputs:
16+
* `classification` - The domain type (business, education, government,
17+
isp_email).
18+
* `risk` - A risk score from 0.01 to 99 associated with the email domain.
19+
* `volume` - Activity level for the domain across the minFraud network,
20+
expressed in sightings per million.
21+
* Added new `response.email.domain.visit` outputs for low-volume domains:
22+
* `status` - Domain status from automated visit (live, dns_error,
23+
network_error, http_error, parked, pre_development).
24+
* `lastVisitedOn` - The date the automated visit was completed.
25+
* `hasRedirect` - Whether the domain redirects to another URL.
1526

1627
8.1.0 (2025-05-23)
1728
------------------

fixtures/insights.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,15 @@
177177

178178
"email": {
179179
"domain": {
180-
"first_seen": "2016-01-23"
180+
"classification": "business",
181+
"first_seen": "2016-01-23",
182+
"risk": 15.5,
183+
"visit": {
184+
"has_redirect": false,
185+
"last_visited_on": "2024-11-15",
186+
"status": "live"
187+
},
188+
"volume": 6300
181189
},
182190
"first_seen": "2016-02-03",
183191
"is_disposable": false,

src/response/models/insights.spec.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,34 @@ describe('Insights()', () => {
3333
const model = new Insights(response as InsightsResponse);
3434
expect(model.email?.domain?.firstSeen).toBe('2016-01-23');
3535
});
36+
37+
it('allows /email/domain/classification to be accessed', () => {
38+
const model = new Insights(response as InsightsResponse);
39+
expect(model.email?.domain?.classification).toBe('business');
40+
});
41+
42+
it('allows /email/domain/risk to be accessed', () => {
43+
const model = new Insights(response as InsightsResponse);
44+
expect(model.email?.domain?.risk).toBe(15.5);
45+
});
46+
47+
it('allows /email/domain/volume to be accessed', () => {
48+
const model = new Insights(response as InsightsResponse);
49+
expect(model.email?.domain?.volume).toBe(6300);
50+
});
51+
52+
it('allows /email/domain/visit/status to be accessed', () => {
53+
const model = new Insights(response as InsightsResponse);
54+
expect(model.email?.domain?.visit?.status).toBe('live');
55+
});
56+
57+
it('allows /email/domain/visit/last_visited_on to be accessed', () => {
58+
const model = new Insights(response as InsightsResponse);
59+
expect(model.email?.domain?.visit?.lastVisitedOn).toBe('2024-11-15');
60+
});
61+
62+
it('allows /email/domain/visit/has_redirect to be accessed', () => {
63+
const model = new Insights(response as InsightsResponse);
64+
expect(model.email?.domain?.visit?.hasRedirect).toBe(false);
65+
});
3666
});

src/response/records.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import {
33
CreditCardType,
44
DispositionAction,
55
DispositionReason,
6+
EmailDomainClassification,
7+
EmailDomainVisitStatus,
68
} from './web-records';
79

810
/**
@@ -191,15 +193,60 @@ export interface Device {
191193
readonly localTime: string;
192194
}
193195

196+
/**
197+
* Status of an automated visit to the email domain. This object is only
198+
* available for low-volume domains. High-volume domains (email providers,
199+
* large businesses) will not include visit data.
200+
*/
201+
export interface EmailDomainVisit {
202+
/**
203+
* Indicates if the domain redirects to another URL.
204+
*/
205+
readonly hasRedirect: boolean;
206+
/**
207+
* The date the automated visit was completed, in ISO 8601 format (YYYY-MM-DD).
208+
*/
209+
readonly lastVisitedOn: string;
210+
/**
211+
* Classification of the domain based on the automated visit.
212+
* Possible values include: `live`, `dns_error`, `network_error`,
213+
* `http_error`, `parked`, `pre_development`.
214+
*/
215+
readonly status: EmailDomainVisitStatus;
216+
}
217+
194218
/**
195219
* This object contains information about the email address domain passed in the
196220
* request.
197221
*/
198222
export interface EmailDomain {
223+
/**
224+
* Classification of the email domain type. Possible values include:
225+
* `business`, `education`, `government`, `isp_email`.
226+
* Additional values may be added in the future.
227+
*/
228+
readonly classification?: EmailDomainClassification;
199229
/**
200230
* The date the email address domain was first seen by MaxMind.
201231
*/
202232
readonly firstSeen: string;
233+
/**
234+
* A risk score from 0.01 to 99 associated with the email domain.
235+
* Higher scores indicate higher risk.
236+
*/
237+
readonly risk?: number;
238+
/**
239+
* Information from an automated visit to the email domain. This is only
240+
* available for low-volume domains and may be initially unavailable for
241+
* newly-sighted domains, populating later after automated visits occur.
242+
*/
243+
readonly visit?: EmailDomainVisit;
244+
/**
245+
* Activity indicator for the email domain across the minFraud network,
246+
* expressed in sightings per million. Values range from 0.001 to 1,000,000
247+
* and are rounded to 2 significant figures.
248+
*/
249+
readonly volume?: number;
203250
}
204251

205252
/**

src/response/web-records.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,32 @@ export interface DeviceWebRecord {
7474
readonly local_time: string;
7575
}
7676

77+
export type EmailDomainClassification =
78+
| 'business'
79+
| 'education'
80+
| 'government'
81+
| 'isp_email';
82+
83+
export type EmailDomainVisitStatus =
84+
| 'live'
85+
| 'dns_error'
86+
| 'network_error'
87+
| 'http_error'
88+
| 'parked'
89+
| 'pre_development';
90+
91+
export interface EmailDomainVisitWebRecord {
92+
readonly has_redirect: boolean;
93+
readonly last_visited_on: string;
94+
readonly status: EmailDomainVisitStatus;
95+
}
96+
7797
export interface EmailDomainWebRecord {
98+
readonly classification?: EmailDomainClassification;
7899
readonly first_seen: string;
100+
readonly risk?: number;
101+
readonly visit?: EmailDomainVisitWebRecord;
102+
readonly volume?: number;
79103
}
80104

81105
export interface EmailWebRecord {

src/webServiceClient.spec.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ describe('WebServiceClient', () => {
3737
});
3838

3939
it('handles "full" responses', async () => {
40-
expect.assertions(174);
40+
expect.assertions(180);
4141

4242
nockInstance
4343
.post(fullPath('factors'), factors.request.basic)
@@ -211,6 +211,12 @@ describe('WebServiceClient', () => {
211211
expect(got.device?.localTime).toEqual('2018-01-02T10:40:11-08:00');
212212

213213
expect(got.email?.domain?.firstSeen).toEqual('2016-01-23');
214+
expect(got.email?.domain?.classification).toEqual('business');
215+
expect(got.email?.domain?.risk).toEqual(15.5);
216+
expect(got.email?.domain?.volume).toEqual(6300);
217+
expect(got.email?.domain?.visit?.status).toEqual('live');
218+
expect(got.email?.domain?.visit?.lastVisitedOn).toEqual('2024-11-15');
219+
expect(got.email?.domain?.visit?.hasRedirect).toEqual(false);
214220
expect(got.email?.firstSeen).toEqual('2016-02-03');
215221
expect(got.email?.isDisposable).toEqual(false);
216222
expect(got.email?.isFree).toEqual(false);
@@ -293,7 +299,7 @@ describe('WebServiceClient', () => {
293299
});
294300

295301
it('handles "full" responses', async () => {
296-
expect.assertions(149);
302+
expect.assertions(155);
297303

298304
nockInstance
299305
.post(fullPath('insights'), insights.request.basic)
@@ -467,6 +473,12 @@ describe('WebServiceClient', () => {
467473
expect(got.device?.localTime).toEqual('2018-01-02T10:40:11-08:00');
468474

469475
expect(got.email?.domain?.firstSeen).toEqual('2016-01-23');
476+
expect(got.email?.domain?.classification).toEqual('business');
477+
expect(got.email?.domain?.risk).toEqual(15.5);
478+
expect(got.email?.domain?.volume).toEqual(6300);
479+
expect(got.email?.domain?.visit?.status).toEqual('live');
480+
expect(got.email?.domain?.visit?.lastVisitedOn).toEqual('2024-11-15');
481+
expect(got.email?.domain?.visit?.hasRedirect).toEqual(false);
470482
expect(got.email?.firstSeen).toEqual('2016-02-03');
471483
expect(got.email?.isDisposable).toEqual(false);
472484
expect(got.email?.isFree).toEqual(false);
@@ -530,16 +542,16 @@ describe('WebServiceClient', () => {
530542

531543
switch (property) {
532544
case 'billing_address':
533-
expect.assertions(134);
545+
expect.assertions(140);
534546
break;
535547
case 'credit_card':
536-
expect.assertions(128);
548+
expect.assertions(134);
537549
break;
538550
case 'email':
539551
expect.assertions(134);
540552
break;
541553
case 'shipping_address':
542-
expect.assertions(132);
554+
expect.assertions(138);
543555
break;
544556
}
545557

@@ -569,6 +581,12 @@ describe('WebServiceClient', () => {
569581

570582
if (property != 'email') {
571583
expect(got.email?.domain?.firstSeen).toEqual('2016-01-23');
584+
expect(got.email?.domain?.classification).toEqual('business');
585+
expect(got.email?.domain?.risk).toEqual(15.5);
586+
expect(got.email?.domain?.volume).toEqual(6300);
587+
expect(got.email?.domain?.visit?.status).toEqual('live');
588+
expect(got.email?.domain?.visit?.lastVisitedOn).toEqual('2024-11-15');
589+
expect(got.email?.domain?.visit?.hasRedirect).toEqual(false);
572590
expect(got.email?.firstSeen).toEqual('2016-02-03');
573591
expect(got.email?.isDisposable).toEqual(false);
574592
expect(got.email?.isFree).toEqual(false);

0 commit comments

Comments
 (0)