Skip to content

Commit e1ef8ef

Browse files
fix(enrichment): address PR review on Icypeas success + Datagma billing
- Icypeas find_email/verify_email postProcess return success:true for all terminal statuses (NOT_FOUND/DEBITED_NOT_FOUND included) so the cascade runner calls mapOutput and records invalid/not-found verdicts instead of throwing and inflating the error count - Bill Icypeas verify FOUND (not just DEBITED*) per the documented 0.1-credit charge - Datagma enrich_person only applies the 30-credit phone surcharge when a phone lookup (phoneFull) was requested - Note Datagma's URL-param (apiId) auth in the hosted-key doc comment - Update hosting tests to match Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent b0515da commit e1ef8ef

6 files changed

Lines changed: 37 additions & 13 deletions

File tree

apps/sim/tools/datagma-hosting.test.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,16 +55,26 @@ describe('Datagma enrich person pricing', () => {
5555
).toBeCloseTo(2 * DATAGMA_CREDIT_USD)
5656
})
5757

58-
it('charges 32 credits (2 + 30) when phone is also found', () => {
58+
it('charges 32 credits (2 + 30) when a phone lookup was requested and found', () => {
5959
expect(
6060
cost(
6161
enrichPersonTool,
62-
{},
62+
{ phoneFull: true },
6363
{ name: 'John Doe', email: 'john@stripe.com', phone: '+14155551234' }
6464
).cost
6565
).toBeCloseTo(32 * DATAGMA_CREDIT_USD)
6666
})
6767

68+
it('does not charge the phone surcharge when phoneFull was not requested', () => {
69+
expect(
70+
cost(
71+
enrichPersonTool,
72+
{},
73+
{ name: 'John Doe', email: 'john@stripe.com', phone: '+14155551234' }
74+
).cost
75+
).toBeCloseTo(2 * DATAGMA_CREDIT_USD)
76+
})
77+
6878
it('charges 0 credits on no match', () => {
6979
expect(cost(enrichPersonTool, {}, { name: null, email: null }).cost).toBe(0)
7080
expect(cost(enrichPersonTool, {}, {}).cost).toBe(0)

apps/sim/tools/datagma/enrich_person.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ export const enrichPersonTool: ToolConfig<DatagmaEnrichPersonParams, DatagmaEnri
2222
const name = output.name as string | null
2323
const email = output.email as string | null
2424
if (!name && !email) return 0
25-
const phoneCredits = output.phone ? 30 : 0
25+
// The 30-credit phone surcharge applies only when the caller requested a
26+
// phone lookup (phoneFull); a phone that rides along otherwise isn't charged.
27+
const phoneCredits = params.phoneFull && output.phone ? 30 : 0
2628
return 2 + phoneCredits
2729
}),
2830

apps/sim/tools/datagma/hosting.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ import type { ToolHostingConfig } from '@/tools/types'
33
/**
44
* Env var prefix for Datagma hosted keys. Provide keys as
55
* `DATAGMA_API_KEY_COUNT` plus `DATAGMA_API_KEY_1..N`.
6+
*
7+
* Note: Datagma authenticates via an `apiId` URL query parameter (its only
8+
* documented scheme), so the key appears verbatim in every request URL and may
9+
* be captured by Datagma's and any intermediary's access logs. Treat a leaked
10+
* key accordingly and rotate via the env vars above.
611
*/
712
export const DATAGMA_API_KEY_PREFIX = 'DATAGMA_API_KEY'
813

apps/sim/tools/icypeas-hosting.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ describe('Icypeas find-email postProcess poll', () => {
138138
expect((result.output as any).status).toBe('FOUND')
139139
})
140140

141-
it('returns success=false for NOT_FOUND terminal status', async () => {
141+
it('returns success=true with a null email for NOT_FOUND terminal status', async () => {
142142
vi.useFakeTimers()
143143

144144
const fetchMock = vi.fn().mockResolvedValue(
@@ -172,8 +172,9 @@ describe('Icypeas find-email postProcess poll', () => {
172172
await vi.advanceTimersByTimeAsync(3000)
173173
const result = await promise
174174

175-
expect(result.success).toBe(false)
175+
expect(result.success).toBe(true)
176176
expect((result.output as any).status).toBe('NOT_FOUND')
177+
expect((result.output as any).email).toBeNull()
177178
})
178179
})
179180

apps/sim/tools/icypeas/find_email.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,12 @@ export const icypeasFindEmailTool: ToolConfig<IcypeasFindEmailParams, IcypeasFin
162162
const status = (item.status as string | undefined) ?? null
163163

164164
if (status && TERMINAL_STATUSES.has(status)) {
165+
// Any terminal status is a successful run — a clean no-match is not a
166+
// failure. The enrichment cascade only calls mapOutput when success is
167+
// true, so returning false would skip the verdict and inflate the
168+
// runner's error count. A null email signals "not found" downstream.
165169
return {
166-
success: FOUND_STATUSES.has(status),
170+
success: true,
167171
output: mapItem(item),
168172
}
169173
}

apps/sim/tools/icypeas/verify_email.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,14 @@ export const icypeasVerifyEmailTool: ToolConfig<
5858
version: '1.0.0',
5959

6060
hosting: icypeasHosting<IcypeasVerifyEmailParams>((_params, output) => {
61-
// 0.1 credit per verification attempt that was processed (FOUND, DEBITED,
62-
// or DEBITED_NOT_FOUND — any status containing "DEBITED" means credits were
63-
// consumed). BAD_INPUT / INSUFFICIENT_FUNDS / ABORTED / NOT_FOUND indicate
64-
// the search was not processed or charged.
61+
// 0.1 credit per verification that consumed credits: FOUND/DEBITED (verdict
62+
// delivered) and DEBITED_NOT_FOUND (debited even though unresolved).
63+
// BAD_INPUT / INSUFFICIENT_FUNDS / ABORTED / NOT_FOUND are never charged.
6564
const status = output.status as string | undefined
6665
if (!status) {
6766
throw new Error('Icypeas verify-email: cannot determine cost — status is missing')
6867
}
69-
// Billable when the status name contains DEBITED (i.e. DEBITED or DEBITED_NOT_FOUND).
70-
const billable = status.includes('DEBITED')
68+
const billable = status === 'FOUND' || status.includes('DEBITED')
7169
// 0.1 credit; express as a fractional number so ICYPEAS_CREDIT_USD math works.
7270
return billable ? 0.1 : 0
7371
}),
@@ -155,8 +153,12 @@ export const icypeasVerifyEmailTool: ToolConfig<
155153
const status = (item.status as string | undefined) ?? null
156154

157155
if (status && TERMINAL_STATUSES.has(status)) {
156+
// Any terminal status is a successful run — NOT_FOUND/DEBITED_NOT_FOUND are
157+
// definitive verdicts, not failures. The enrichment cascade only calls
158+
// mapOutput when success is true, so returning false here would skip those
159+
// verdicts and inflate the runner's error count. `valid` carries the result.
158160
return {
159-
success: VALID_STATUSES.has(status),
161+
success: true,
160162
output: mapItem(item),
161163
}
162164
}

0 commit comments

Comments
 (0)