Repository navigation
fix: make the S3 jobs, the email finder and the IPT match the live API - #60
Merged
franciscbalint merged 1 commit intoOct 25, 2026
Merged
Conversation
Five of these calls could not do what their signatures promised. scoreS3File had no returnUrl field at all, so return_url was never sent and the call could never succeed: the live API answers 400 "Provided return URL is not permitted: None". validateS3File had the field but sent it only when set, so the same 400 was one default argument away. Both now go through bulkRootFileJob, which already enforced file_name and return_url for the other six S3/SFTP jobs, so all eight reject a missing field with the same message before spending a request on it. The bulk email finder accepted a file with no name column. The docs require first_name_column or full_name_column next to domain_column, and without one the job has nothing to build an address from, so it is rejected up front. createInboxPlacementTest only rejected a blank name, and a test the API refuses still costs a round trip on an endpoint that spends inbox-test credits, so the documented limits are checked first: name at most 64 characters, testType 1, 2 or 3, and a senderEmail for type 1. listInboxPlacementTests sent the page size as pageSize, which the API accepts and then ignores, so callers silently got the whole list however small a page they asked for. It now sends page_size, bounds it to 1-100, and carries the createdAfter / createdBefore filters that were missing entirely. Also: setListEvaluatorBaseUrl redirected only the result fetch. The upload and send-evaluated-file were built from the bulk base, so overriding the host split one flow across two hosts; they now follow the list evaluator base too. And two comments that documented behaviour the code had already moved past: validateSFTPFile's KDoc offered a null fileName that bulkRootFileJob rejects, and the Inbox Placement Tester block still described an api_key query string where the code sends Authorization: Bearer. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
franciscbalint
deleted the
fix/api-parity-s3-return-url-and-ipt-limits
branch
October 25, 2026 21:19
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Five calls whose signatures promised something the live API would not accept, plus two stale comments.
Fixed
scoreS3Filecould never succeed.ZBScoreS3FileOptionshad noreturnUrlfield, soreturn_urlwas never sent, and the live API answers400 {"error_message":"Provided return URL is not permitted: None"}. The field is added and required.validateS3File'sreturn_urlwas optional (options.returnUrl?.let) though the API requires it, so the same 400 was one default argument away.Both now route through
bulkRootFileJob, which already enforcedfile_nameandreturn_urlfor the other six S3/SFTP jobs. All eight reject a missing field with the same message before a request goes out. The jobs keep their own extra fields (email_address_column, the name/gender/IP columns,allow_phase_2,remove_duplicate), andhas_header_rowis still omitted when unset on the two S3 jobs.The bulk email finder accepted a file with no name column. Only
domain_column >= 1was checked; the docs requirefirst_name_columnorfull_name_columnalongside it.sendEmailFinderFileandsendEmailFinderFileStreamnow reject that call.createInboxPlacementTestonly rejected a blank name. It now checks the documented limits before spending a round trip on an endpoint that bills inbox-test credits: name at most 64 characters,testTypein 1|2|3, andsenderEmailrequired whentestType == 1.listInboxPlacementTestssentpageSize, which the API silently ignores (page_size=1paginates;pageSize=1returns the full list), so a caller asking for a small page quietly got everything. It now sendspage_size, bounds it to 1-100, and gains thecreatedAfter/createdBeforefilters, which were absent fromZBListInboxPlacementTestsOptionsentirely.setListEvaluatorBaseUrlredirected only half the List Evaluator. The result fetch usedlistEvaluatorBaseUrl, but the upload andsend-evaluated-filewere built frombulkApiBaseUrl, so overriding the host split one flow across two hosts. All three legs now follow the List Evaluator base;send-evaluated-fileresolves one path segment above it, which leaves the default URLs unchanged.Docs
validateSFTPFile's KDoc told callers they could leavefileNamenull, butbulkRootFileJoberrors withEmpty parameter: file_namefirst. The code is right per the live API; the comment was stale.Authorization: Bearer.documentation.mdupdated;documentation.mdalso still carried both stale claims.Public API
No names change.
ZBScoreS3FileOptions.returnUrlandZBListInboxPlacementTestsOptions.createdAfter/createdBeforeare new fields with defaults, appended so existing positional construction keeps working. The new requirements surface through the error callback, matching howZBBulkFileJobOptionsalready enforces its own.Tests
Seven new offline tests and six updated ones, all against the repo's fake OkHttp transport (no network, no key): method, host, path, fields, auth placement, and the error bodies (
success: falseon a 200, the IPTdetailkey)../gradlew :zero_bounce_sdk:testDebugUnitTest— 124 tests, 0 failures../gradlew :zero_bounce_sdk:assembleDebug— BUILD SUCCESSFUL.The opt-in live suite (
-PzbLive) was not run here; it needs real keys from the environment.🤖 Generated with Claude Code