How to upgrade the Go SDK from v1 to v2
This guide will help you navigate through changes included in version 2 of the Account Protect Go SDK for a successful upgrade.
Version 2 is generated from the Account Protect OpenAPI Specification, which changes the client, event, and response types throughout the SDK.
Overview of changes
- Update the module path from
github.com/datadome/fraud-sdk-go-packagetogithub.com/datadome/fraud-sdk-go-package/v2. - Replace the
Eventinterface and theClient.ValidateandClient.Collectmethods with one dedicated type per operation, such asValidateLoginandCollectCustom. - Replace the per-event builders with functional options passed to per-operation constructors, such as
NewValidateLogin(account, status, opts...). - Validate the payload at construction time and fail on missing required fields, instead of accepting it silently.
- Replace
Validate,ValidateWithRequestMetadata,Collect, andCollectWithRequestMetadatawith aPerformOperation(ctx, client, request, requestMetadata)method on each operation, which requires an explicitcontext.Context. - Return a Go
errorfrom collect calls, instead of anErrorResponsePayloadwith aStatusfield. Validate calls still fail open and return anallowaction. - Rename
ResponsePayload,SuccessResponsePayload, andErrorResponsePayloadtoResponse,ResponseLogin, andError, remove theStatusfield, and uppercase theResponseActionconstants:AllowbecomesALLOW,DenybecomesDENY,ReviewbecomesREVIEW, andChallengebecomesCHALLENGE. - Rename
LoginStatustoLoginPayloadStatus, and itsFailedandSucceededconstants toLoginPayloadStatusFailedandLoginPayloadStatusSucceeded. - Add support for the feedback endpoint with
NewFeedback.
Migration steps
Step 1: Upgrade to the latest package version
go get github.com/datadome/fraud-sdk-go-package/v2
go mod tidyStep 2: Update the client instantiation
Update the import path. The client's package alias and the NewClient signature stay the same:
dd "github.com/datadome/fraud-sdk-go-package/v2"
client, err := dd.NewClient(
"FRAUD_API_KEY",
dd.ClientWithEndpoint("account-api.datadome.co"),
dd.ClientWithTimeout(1500),
)dd "github.com/datadome/fraud-sdk-go-package"
client, err := dd.NewClient(
"FRAUD_API_KEY",
dd.ClientWithEndpoint("account-api.datadome.co"),
dd.ClientWithTimeout(1500),
)Step 3: Add the addOpt helper
Every …PayloadWithX functional option now returns an (option, error) pair instead of just an option, because payload validation happens as options are applied. Add the helper below to your code and wrap each option call with it:
// addOpt appends an option to opts. If err is non-nil, the option is skipped and
// the error is logged.
func addOpt[T any](opts []T, opt T, err error) []T {
if err != nil {
log.Printf("option error: %v", err)
return opts
}
return append(opts, opt)
}Step 4: Update event submission and payload construction
Replace client.Validate/client.Collect and the New<Event>Event constructors with the dedicated operation for your event. The table below maps every event to its v2 operations:
| Event | v1 constructor | v2 validate operation | v2 collect operation |
|---|---|---|---|
| Login | NewLoginEvent | NewValidateLogin | NewCollectLogin |
| Registration | NewRegistrationEvent | NewValidateRegistration | NewCollectRegistration |
| Account update | NewAccountUpdateEvent | NewValidateAccountUpdate | NewCollectAccountUpdate |
| Password update | NewPasswordUpdateEvent | NewValidatePasswordUpdate | NewCollectPasswordUpdate |
| Custom event | NewCustomEvent | NewValidateCustom | NewCollectCustom |
The example below migrates a login event. Registration, account update, password update, and custom events follow the same pattern.
var opts []dd.LoginPayloadOption
userOpt, err := dd.LoginPayloadWithUser(user)
opts = addOpt(opts, userOpt, err)
authOpt, err := dd.LoginPayloadWithAuthentication(authentication)
opts = addOpt(opts, authOpt, err)
sessionOpt, err := dd.LoginPayloadWithSession(session)
opts = addOpt(opts, sessionOpt, err)
op, err := dd.NewValidateLogin(login, dd.LoginPayloadStatusSucceeded, opts...)
if err != nil {
log.Printf("error creating validate login operation: %v\n", err)
http.Error(w, "invalid request", http.StatusBadRequest)
return
}
validate, err := op.PerformOperation(r.Context(), client, r, nil)
if err != nil {
log.Printf("error during validation: %v\n", err)
}
if validate.Action == dd.ALLOW {
w.WriteHeader(http.StatusOK)
return
}validate, err := client.Validate(r, dd.NewLoginEvent(
login,
dd.Succeeded,
dd.LoginWithUser(user),
dd.LoginWithAuthentication(authentication),
dd.LoginWithSession(session),
))
if err != nil {
log.Printf("error during validation: %v\n", err)
}
if validate.Action == dd.Allow {
w.WriteHeader(http.StatusOK)
return
}For a failed login, PerformOperation on a collect operation requires a context.Context and returns only an error:
op, err := dd.NewCollectLogin(login, dd.LoginPayloadStatusFailed)
if err != nil {
log.Printf("error creating collect login operation: %v\n", err)
} else if err := op.PerformOperation(r.Context(), client, r, nil); err != nil {
log.Printf("error during collection: %v\n", err)
}_, err := client.Collect(r, dd.NewLoginEvent(login, dd.Failed))
if err != nil {
log.Printf("error during collection: %v\n", err)
}
Constructor argument order changed for two events
NewValidatePasswordUpdate(account, reason, status, user, opts...)takesuseras its fourth argument, whileNewPasswordUpdateEvent(account, user, reason, status, opts...)took it as its second.NewValidateCustom(eventName, opts...)no longer takesaccountas a positional argument. Set it with thedd.CustomActionPayloadWithAccount(account)option instead.
Step 5: Update your models
This table maps the renamed types:
| v1 | v2 |
|---|---|
dd.Address | dd.UserAllOfAddress |
dd.User | dd.LoginPayloadAllOfUser, dd.RegistrationPayloadAllOfUser, dd.AccountUpdatePayloadAllOfUser, or dd.PasswordUpdatePayloadAllOfUser, depending on the event |
dd.CustomEventUser | dd.CustomActionPayloadAnyOf1AllOfUser |
dd.Authentication | dd.LoginPayloadAllOfAuthentication, dd.RegistrationPayloadAllOfAuthentication, dd.AccountUpdatePayloadAllOfAuthentication, or dd.PasswordUpdatePayloadAllOfAuthentication, depending on the event |
And the renamed or retyped fields on the user model:
| v1 | v2 |
|---|---|
ID string | Id *string for login, account update, and custom events; Id string (still required) for registration and password update |
ExternalURLs *[]string | ExternalUrls []string |
PictureURLs *[]string | PictureUrls []string |
Title *string (for example, "mr") | Title dd.UserTitle (for example, dd.MR) |
Step 6: Update your response and error handling
- Replace
dd.Allow,dd.Deny,dd.Review, anddd.Challengewith the uppercasedd.ALLOW,dd.DENY,dd.REVIEW, anddd.CHALLENGEconstants. - Replace
LoginStatus.Succeeded/.Failedwithdd.LoginPayloadStatusSucceeded/dd.LoginPayloadStatusFailed. Each event has its own status type, following the same naming pattern. - Stop checking the response's
Statusfield: it no longer exists. Validate operations still fail open and return anallowaction on error; collect operations now return a Goerror, including a newHTTPErrortype for API error responses.
Reference
Response actions
| v1 | v2 |
|---|---|
dd.Allow | dd.ALLOW |
dd.Deny | dd.DENY |
dd.Review | dd.REVIEW |
dd.Challenge | dd.CHALLENGE |
Response types
| v1 | v2 |
|---|---|
ResponsePayload | Response |
SuccessResponsePayload | ResponseLogin |
ErrorResponsePayload | Error |
Authentication constants
| v1 | v2 |
|---|---|
dd.Password | dd.AuthenticationModePassword |
dd.Google | dd.AuthenticationSocialProviderGoogle |
dd.Social | dd.AuthenticationTypeSocial |
Updated 8 days ago

