Two-factor authentication
Bit2Me Legal and Compliance must review 2FA copy before partner go-live. This page is operational, not legal advice.
| Status | Meaning |
|---|---|
| 420 | Invalid code |
| 428 | 2FA required (including some stablecoin withdrawals) |
On withdrawals and other protected REST calls, send headers x-totp (6 digits) and x-totp-type. For subaccounts, type is always gauth (not SMS or email). A Withdraw-scoped API key is documented as bypassing two-factor authentication — do not use that scope if you need TOTP on every send.
Partners who operate subaccounts enroll Google Authenticator in the backend and generate TOTP from the returned secret. Store that seed with the same controls as an API secret. End-user UX for showing a challenge is your product decision; the gateway only validates x-totp.
Enroll a subaccount
Subaccount enrollment uses a JSON body, not those headers.
POST /v1/account/subaccount/sfa
Required: { "subaccountUserId": "<uuid>" }.
200 includes secret (required) and may include algorithm (example SHA1), digits (6), period (30), qrCode (data-URL PNG).
PUT /v1/account/subaccount/sfa
Required: { "subaccountUserId": "<uuid>", "totp": "<6-digit code from that secret>" }.
200 is a success payload (SuccessResponse).
Enroll the parent account
The parent account proves its current 2FA method before it switches to Google Authenticator.
GET /v3/signin/sfa returns sfaType and sends a valid code through that method.
POST /v1/account/settings/sfa with headers x-totp (the code you received) and x-totp-type (the sfaType from the previous step). The response returns the secret (and a QR image) to configure Google Authenticator.
PUT /v1/account/settings/sfa with a code generated from that secret as totp in the JSON body.
Fresh codes only. Clock skew on the authenticator causes 420.