Skip to content

fix: send the documented field set and honour every base URL override - #40

Merged
franciscbalint merged 1 commit into
mainfrom
fix/documented-field-sets-and-base-url-overrides
Oct 25, 2026
Merged

franciscbalint merged 1 commit into
mainfrom
fix/documented-field-sets-and-base-url-overrides

Conversation

@franciscbalint

Copy link
Copy Markdown
Collaborator

Fixes found auditing the SDK against the 74 documented ZeroBounce operations. Each item is a real disagreement between the client and the API, not a tidy-up: the client either sends a field the endpoint does not document, refuses a call the endpoint accepts, or drops a value the caller passed.

No version bump — 3.0.0 is already published, so the entries go under ## [Unreleased].

Fixed

  • validateBatch sends JSON as JSON. The body was handed to fetch as a pre-stringified string, so createRequest never set Content-Type and fetch labelled the payload text/plain;charset=UTF-8. The live API tolerates it today; that is the reason to fix it now rather than later. It goes out as jsonBody.
  • validateSFTPfile passed a third argument (false) to _bulkRootFileJob, which takes two parameters, so it was silently dropped. Removed, and its jsdoc no longer promises an optional file_name that the helper has required for some time.
  • Warmup addSendingAccount dropped engagementRuleId for zb_managed. The field was only added to the body inside the self_managed branch, so a caller who sent one for a managed account got no error and no field. It is rejected explicitly now, as email_content and managed_sending already are for self_managed.

Changed

  • emailFinderBaseURL and domainSearchBaseURL now work. init validated and stored both, but nothing ever read them — ops 20–27 hardcoded the bulk root, so two advertised overrides did nothing at all. Both are now passed to createRequest as baseURL with a path relative to them, the same way the list evaluator and the Inbox Placement Tester already work. The defaults are unchanged, so a client that does not set them sends exactly the same URLs as before.
  • findEmail* accept a missing first_name. /guessformat documents it as optional for the find-email use: without a name the API answers with the domain's or company's email format. It is left out of the query rather than refused. domain / company_name is still required, and _findEmailFormat, the domain-search helpers and the deprecated guessFormat are untouched.
  • exportS3file accepts a missing export_bucket, matching its sibling exportSFTPfile's handling of export_path. Without it the output bucket from your ZeroBounce settings is used.
  • BREAKING: the bulk email finder requires a name column. sendEmailFinderFile and sendEmailFinderFileStream now require first_name_column or full_name_column as well as domain_column. 0 and false remain the "leave this column out" sentinel, so neither counts as given.
  • BREAKING: validateS3file and scoreS3file send exactly the documented set. Both had bespoke implementations that sent seven undocumented fields (email_address_column, first_name_column, last_name_column, gender_column, ip_address_column, allow_phase_2, and a singular remove_duplicate) and made return_url optional. The live API requires it — POST /validateS3file without it answers 400 {"error_message":"Provided return URL is not permitted: None"} — so both now route through the existing _bulkRootFileJob helper with their six siblings and send file_name, has_header_row, remove_duplicates and return_url, nothing more. allowPhase2 stays available on sendFile / sendFileStream, where it is documented.

Tests

Jest with mocked HTTP for every changed method, asserting the method, the full URL, the parameters and where the key travels, and the parsed response including error bodies:

  • validateBatch sends Content-Type: application/json, the key in the body and not the query, and timeout as the string the live API wants.
  • findEmail* with and without first_name, asserting the exact query string both ways.
  • The email finder's name-column rule, including the 0 / false sentinel, and that no request goes out when it is unmet.
  • All ten finder / domain-search operations against overridden base URLs, plus the https:// check on both overrides and the unchanged default host.
  • All eight S3 / SFTP jobs: exact body, undocumented extras passed in and verified absent, and each of the three required fields missing in turn.
  • exportS3file with and without export_bucket.
  • The API's own 400 rejection body surfaced as a short message with its status code.
  • Warmup addSendingAccount rejecting engagementRuleId for zb_managed before sending anything.

zeroBounceSDK.d.ts is updated to match (the two S3 option types now extend BulkRootFileJobOptions, whose file_name and return_url are required; first_name is optional on the finder options; the two base URL overrides are declared), and tests/ts-consumer.ts exercises the new shapes.

$ npm test
> jest && npm run test:types
Test Suites: 4 passed, 4 total
Tests:       150 passed, 150 total

$ npm run build
> webpack --mode=production
asset zeroBounceSDK.js 38.2 KiB [emitted] [minimized] (name: main)
webpack 5.109.2 compiled successfully in 395 ms

npm run dts-check and npm run test:coverage pass as well.

🤖 Generated with Claude Code

An audit against the 74 documented operations turned up a group of places
where the client and the API disagree. None of them are cosmetic: each one
either sends something the endpoint does not document, refuses something the
endpoint accepts, or quietly drops a value the caller passed.

- validateBatch handed its JSON body to fetch as a plain string, so
  createRequest never set Content-Type and fetch labelled the JSON
  text/plain;charset=UTF-8. The API tolerates it today, which is exactly why
  it is worth fixing before it stops doing so. It now goes out as jsonBody.

- findEmailByDomain / findEmailByCompanyName rejected a missing first_name
  although /guessformat documents it as optional for the find-email use:
  without a name the API answers with the domain's or company's format. The
  name is now left out of the query rather than refused. The domain-search
  helpers and the deprecated guessFormat are untouched.

- The bulk email finder needs a name to work from, so sendEmailFinderFile and
  sendEmailFinderFileStream now require first_name_column or full_name_column
  instead of only domain_column. 0 and false stay the "leave this column out"
  sentinel, so neither counts as given.

- exportS3file rejected a missing export_bucket although it is optional and
  its sibling exportSFTPfile already treats export_path that way. Without it
  the bucket from the account's settings is used.

- validateS3file and scoreS3file had bespoke bodies that sent seven
  undocumented fields (email_address_column, the name/gender/IP columns,
  allow_phase_2 and a singular remove_duplicate) and made return_url
  optional. The live API requires return_url -- POST /validateS3file without
  it answers 400 "Provided return URL is not permitted: None" -- so both now
  go through _bulkRootFileJob with their six siblings and send exactly
  file_name, has_header_row, remove_duplicates and return_url.

- init validated and stored emailFinderBaseURL and domainSearchBaseURL but
  nothing ever read them: ops 20-27 hardcoded the bulk root, so two
  advertised overrides silently did nothing. Both are now passed to
  createRequest as baseURL with a path relative to them, the way the list
  evaluator and the Inbox Placement Tester already work. The defaults are
  unchanged, so a client that does not set them sends the same URLs.

- validateSFTPfile passed a third argument to a two-parameter helper, where
  it was silently dropped, and its jsdoc still promised an optional file_name
  that the helper has required for some time.

- Warmup addSendingAccount only added engagement_rule_id inside the
  self_managed branch, so a caller who sent one for a zb_managed account had
  it dropped without a word. It is rejected now, as email_content and
  managed_sending already are for self_managed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@franciscbalint
franciscbalint merged commit 6027a9f into main Oct 25, 2026
6 checks passed
@franciscbalint
franciscbalint deleted the fix/documented-field-sets-and-base-url-overrides branch October 25, 2026 21:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant