Hello. Sorry for the AI-generated ticket, this is just not a good timing, taking into account Travel Rule going live. Thank you :)
What's wrong
Two Travel Rule endpoints in the wallet module can't be called through the SDK's typed methods at all:
submitDepositQuestionnaire → PUT /sapi/v1/localentity/broker/deposit/provide-info
brokerWithdraw → POST /sapi/v1/localentity/broker/withdraw/apply
The problem is that both request models (SubmitDepositQuestionnaireRequest, BrokerWithdrawRequest) treat signature as a field you're supposed to fill in. But signature isn't something a caller provides — it's the HMAC that the SDK's own auth layer computes and attaches at send time. Modelling it as a required request field puts it in the wrong place, and that breaks the request two different ways depending on what you do with it.
What we tried, and what happened
| Attempt |
Result |
Leave signature unset (as you would for any auth-computed value) |
❌ The SDK throws ConstraintViolationException before the HTTP call — the model's @NotNull validation rejects it. The request never even goes out. |
Set signature("") as a placeholder to get past validation (this is what the SDK's own example does) |
❌ Binance rejects it: HTTP 400 {"code":-1022,"msg":"Signature for this request is not valid."} |
The reason the placeholder fails: the SDK copies your signature value into the request body, and the auth layer then signs the whole body — so the placeholder signature= ends up inside the signed data. Binance's signing scheme never includes the signature parameter in the string being signed, so the SDK's signature is computed over different bytes than the server expects. They can't match.
What works
- The non-broker versions of these same endpoints work fine —
/sapi/v1/localentity/deposit/provide-info and /sapi/v1/localentity/withdraw/apply in the very same TravelRuleApi do not model signature, so the auth layer signs them correctly and Binance accepts them. This is the tell: the two broker endpoints are specified differently from their working twins.
- Our workaround works: we bypass the typed SDK call entirely — build the query string ourselves, sign exactly those bytes, and send them unchanged so what's signed equals what's transmitted.
What doesn't work
- Both
broker Travel Rule endpoints via the typed SDK methods — there's no combination of inputs that produces a valid request, because the auth layer always re-signs and always includes the body's signature field in the signed data.
Versions
- Reproduced on
binance-wallet 4.0.1.
- Still present on
master (wallet v5.0.0, commit 5bc4d813) — upgrading does not fix it.
What the fix might be
Remove signature from the request schema of the two broker endpoints so they match their already-correct non-broker twins. The auth layer already adds the right signature; the model field is both unnecessary and the direct cause of the failure.
🤖 Full technical context for AI agents
Summary
Two TravelRuleApi endpoints in the wallet module model the authentication signature as a required request field. Because signature is an output of the signing pipeline (not an input), this makes the typed SDK calls unusable: they either fail Jakarta bean validation before the HTTP call, or — if a placeholder is supplied — get rejected by Binance with -1022 Signature for this request is not valid.
Affected versions
- Confirmed on
binance-wallet 4.0.1.
- Still present on
master @ 5bc4d813ee055412c00670923cc0e2e4fed1b627 (wallet v5.0.0).
Affected endpoints / symbols
submitDepositQuestionnaire → PUT /sapi/v1/localentity/broker/deposit/provide-info
- Model
SubmitDepositQuestionnaireRequest (field signature, @NotNull, in openapiRequiredFields).
brokerWithdraw → POST /sapi/v1/localentity/broker/withdraw/apply
- Model
BrokerWithdrawRequest (field signature).
Code links (permalinks, pinned to the v5.0.0 commit)
Everything verified against commit 5bc4d813ee055412c00670923cc0e2e4fed1b627 (wallet v5.0.0, current master). Here's what you need.
The broken serialization — API method writes the auth signature into the request body:
The model that forces signature to be non-null (fails bean validation before signing):
The auth layer that signs body-including-signature (behaving correctly — shown for contrast):
Contrast — the non-broker twins that omit signature (the correct spec):
Root cause (two coupled defects)
-
Model requires signature.
SubmitDepositQuestionnaireRequest.getSignature() is annotated @NotNull (line ~294) and signature is added to openapiRequiredFields (line 448).
submitDepositQuestionnaireValidateBeforeCall(...) runs ExecutableValidator.validateParameters(...) BEFORE the HTTP call, so a null signature throws ConstraintViolationException and the request is never sent.
-
API method serializes signature into the signed body.
TravelRuleApi.submitDepositQuestionnaireCall(...) does localVarFormParams.put("signature", ...getSignature()) (line 1456), with Content-Type: application/x-www-form-urlencoded, method PUT, auth name binanceSignature. (Broker withdraw: line 193.)
The auth layer SignatureAuthentication.applyToParams(...) then signs joinQueryParameters(query) + payload, where payload is the form body — which now contains signature=<placeholder> (lines 33–39). Binance's signing convention excludes the signature parameter from the signed string, so the SDK's signed input contains an extra signature= token that the server's expected input does not. The HMACs cannot match → -1022.
(See https://developers.binance.com/docs/binance-spot-api-docs/rest-api/request-security)
Evidence of internal inconsistency
The non-broker twins in the SAME TravelRuleApi are specified correctly and
do NOT model signature:
PUT /sapi/v1/localentity/deposit/provide-info (line ~1596)
POST /sapi/v1/localentity/withdraw/apply
Only the two broker endpoints wrongly include signature as a request field.
Minimal repro
WalletRestApi api = new WalletRestApi(clientConfig); // configured with API key + HMAC secret
SubmitDepositQuestionnaireRequest req = new SubmitDepositQuestionnaireRequest()
.subAccountId("...").depositId(123L).questionnaire("{...}").beneficiaryPii("{...}");
// (a) signature unset -> ConstraintViolationException at validateBeforeCall
// (b) req.signature("") -> HTTP 400 {"code":-1022,"msg":"Signature for this request is not valid."}
api.submitDepositQuestionnaire(req);
Expected behaviour
signature (and timestamp) are injected by the signing/auth layer and must NOT be part of the request model or serialized into the signed payload — exactly as the non-broker provide-info / withdraw/apply endpoints already work.
Suggested fix
Remove signature from the request schema of the two broker Travel Rule endpoints in the OpenAPI spec (align them with their non-broker twins) and regenerate. The auth layer already adds the correct signature to the query string; the model field is both redundant and actively breaks signing.
Current workaround (for reference)
Bypass the typed call: build the percent-encoded query, HMAC-sign that exact string, and send those identical bytes (empty body), so signed == transmitted.
Note on one inferred detail
The server-side reason ("Binance excludes signature from the signed string") is from Binance's public request-security convention plus the observed -1022, not from server code. The SDK-side facts (model requires it; API method serializes it into the signed body; auth layer signs that body) are read directly from the linked source.
Hello. Sorry for the AI-generated ticket, this is just not a good timing, taking into account Travel Rule going live. Thank you :)
What's wrong
Two Travel Rule endpoints in the
walletmodule can't be called through the SDK's typed methods at all:submitDepositQuestionnaire→PUT /sapi/v1/localentity/broker/deposit/provide-infobrokerWithdraw→POST /sapi/v1/localentity/broker/withdraw/applyThe problem is that both request models (
SubmitDepositQuestionnaireRequest,BrokerWithdrawRequest) treatsignatureas a field you're supposed to fill in. Butsignatureisn't something a caller provides — it's the HMAC that the SDK's own auth layer computes and attaches at send time. Modelling it as a required request field puts it in the wrong place, and that breaks the request two different ways depending on what you do with it.What we tried, and what happened
signatureunset (as you would for any auth-computed value)ConstraintViolationExceptionbefore the HTTP call — the model's@NotNullvalidation rejects it. The request never even goes out.signature("")as a placeholder to get past validation (this is what the SDK's own example does)HTTP 400 {"code":-1022,"msg":"Signature for this request is not valid."}The reason the placeholder fails: the SDK copies your
signaturevalue into the request body, and the auth layer then signs the whole body — so the placeholdersignature=ends up inside the signed data. Binance's signing scheme never includes thesignatureparameter in the string being signed, so the SDK's signature is computed over different bytes than the server expects. They can't match.What works
/sapi/v1/localentity/deposit/provide-infoand/sapi/v1/localentity/withdraw/applyin the very sameTravelRuleApido not modelsignature, so the auth layer signs them correctly and Binance accepts them. This is the tell: the twobrokerendpoints are specified differently from their working twins.What doesn't work
brokerTravel Rule endpoints via the typed SDK methods — there's no combination of inputs that produces a valid request, because the auth layer always re-signs and always includes the body'ssignaturefield in the signed data.Versions
binance-wallet4.0.1.master(wallet v5.0.0, commit5bc4d813) — upgrading does not fix it.What the fix might be
Remove
signaturefrom the request schema of the twobrokerendpoints so they match their already-correct non-broker twins. The auth layer already adds the rightsignature; the model field is both unnecessary and the direct cause of the failure.🤖 Full technical context for AI agents
Summary
Two
TravelRuleApiendpoints in thewalletmodule model the authenticationsignatureas a required request field. Becausesignatureis an output of the signing pipeline (not an input), this makes the typed SDK calls unusable: they either fail Jakarta bean validation before the HTTP call, or — if a placeholder is supplied — get rejected by Binance with-1022 Signature for this request is not valid.Affected versions
binance-wallet4.0.1.master@5bc4d813ee055412c00670923cc0e2e4fed1b627(wallet v5.0.0).Affected endpoints / symbols
submitDepositQuestionnaire→PUT /sapi/v1/localentity/broker/deposit/provide-infoSubmitDepositQuestionnaireRequest(fieldsignature,@NotNull, inopenapiRequiredFields).brokerWithdraw→POST /sapi/v1/localentity/broker/withdraw/applyBrokerWithdrawRequest(fieldsignature).Code links (permalinks, pinned to the v5.0.0 commit)
Everything verified against commit
5bc4d813ee055412c00670923cc0e2e4fed1b627(wallet v5.0.0, currentmaster). Here's what you need.The broken serialization — API method writes the auth
signatureinto the request body:TravelRuleApi.java#L1456(path #L1405, methodPUT#L1479)TravelRuleApi.java#L193The model that forces
signatureto be non-null (fails bean validation before signing):SubmitDepositQuestionnaireRequest.java#L98@NotNullgetter: #L294-L297BrokerWithdrawRequest.javaThe auth layer that signs body-including-
signature(behaving correctly — shown for contrast):SignatureAuthentication.java#L33-L39Contrast — the non-broker twins that omit
signature(the correct spec):provide-info:TravelRuleApi.java#L1596— nosignaturein its form params.Root cause (two coupled defects)
Model requires
signature.SubmitDepositQuestionnaireRequest.getSignature()is annotated@NotNull(line ~294) andsignatureis added toopenapiRequiredFields(line 448).submitDepositQuestionnaireValidateBeforeCall(...)runsExecutableValidator.validateParameters(...)BEFORE the HTTP call, so a nullsignaturethrowsConstraintViolationExceptionand the request is never sent.API method serializes
signatureinto the signed body.TravelRuleApi.submitDepositQuestionnaireCall(...)doeslocalVarFormParams.put("signature", ...getSignature())(line 1456), withContent-Type: application/x-www-form-urlencoded, methodPUT, auth namebinanceSignature. (Broker withdraw: line 193.)The auth layer
SignatureAuthentication.applyToParams(...)then signsjoinQueryParameters(query) + payload, wherepayloadis the form body — which now containssignature=<placeholder>(lines 33–39). Binance's signing convention excludes thesignatureparameter from the signed string, so the SDK's signed input contains an extrasignature=token that the server's expected input does not. The HMACs cannot match →-1022.(See https://developers.binance.com/docs/binance-spot-api-docs/rest-api/request-security)
Evidence of internal inconsistency
The non-broker twins in the SAME
TravelRuleApiare specified correctly anddo NOT model
signature:PUT /sapi/v1/localentity/deposit/provide-info(line ~1596)POST /sapi/v1/localentity/withdraw/applyOnly the two
brokerendpoints wrongly includesignatureas a request field.Minimal repro
Expected behaviour
signature(andtimestamp) are injected by the signing/auth layer and must NOT be part of the request model or serialized into the signed payload — exactly as the non-brokerprovide-info/withdraw/applyendpoints already work.Suggested fix
Remove
signaturefrom the request schema of the twobrokerTravel Rule endpoints in the OpenAPI spec (align them with their non-broker twins) and regenerate. The auth layer already adds the correctsignatureto the query string; the model field is both redundant and actively breaks signing.Current workaround (for reference)
Bypass the typed call: build the percent-encoded query, HMAC-sign that exact string, and send those identical bytes (empty body), so signed == transmitted.
Note on one inferred detail
The server-side reason ("Binance excludes
signaturefrom the signed string") is from Binance's public request-security convention plus the observed-1022, not from server code. The SDK-side facts (model requires it; API method serializes it into the signed body; auth layer signs that body) are read directly from the linked source.