API 2: Submit Address Proof
Where should I integrate
Description
After receiving the challengeMsg from API 1, your VASP must build the proofPayload, sign it with the corresponding private key, and submit both the payload and signature to GTR.
GTR will verify:
- the request is authenticated
- the address is not already ownership-proved
- the
challengeMsgmatches the expected value - the signature is valid for the submitted payload
If the verification succeeds, GTR records the address as an ownership-proved address.
For further details of this API, use the following endpoint:
POST $BASE_URL/ext/api/cosmos/v1/address/proof/submit
Step 1: Build the Proof Payload
Your proofPayload must use the following exact format:
PublicKey:{publicKey};PublicKeyType:{publicKeyType};WalletAddress:{walletAddress};Vendor:gtr;ChallengeMsg:{challengeMsg};ProofAlgo:{proofAlgo};UserNonce:{userNonce};PublicKeyFormat:{publicKeyFormat};
Field Rules
| Field | Description |
|---|---|
PublicKey | The same Base64 public key you used in the Generate Challenge request |
PublicKeyType | The same publicKeyType you used in the Generate Challenge request |
WalletAddress | The target wallet address |
Vendor | Must be the fixed value gtr |
ChallengeMsg | The challengeMsg returned from API 1 |
ProofAlgo | The same proofAlgo you used in API 1 |
UserNonce | The same userNonce you used in API 1 |
PublicKeyFormat | The same publicKeyFormat you used in API 1. If omitted in API 1, use PEM |
⚠️ The field order and separators should remain unchanged.
GTR parses this string by field name and semicolon separators.
Step 2: Sign the Proof Payload
Use the private key corresponding to PublicKey to sign the full proofPayload string.
Then Base64-encode the resulting signature and send it as base64SignedMessage.
Step 3: Submit Request
curl --silent --location --request POST "$BASE_URL/ext/api/cosmos/v1/address/proof/submit" \
--header 'Content-Type: application/json' \
--header 'X-Authorization: [YOUR_ADDRESS_PROVING_TOKEN]' \
--data-raw '{
"proofPayload": "PublicKey:[BASE64_PUBLIC_KEY];PublicKeyType:[PUBLIC_KEY_TYPE];WalletAddress:[WALLET_ADDRESS];Vendor:gtr;ChallengeMsg:[CHALLENGE_MSG];ProofAlgo:[PROOF_ALGORITHM];UserNonce:[YOUR_RANDOM_NONCE];PublicKeyFormat:[PEM_OR_DER];",
"base64SignedMessage": "[BASE64_SIGNATURE]"
}'
Request Fields
| Field | Required | Description |
|---|---|---|
proofPayload | Yes | The exact payload string that contains the proving metadata |
base64SignedMessage | Yes | Base64-encoded signature of the full proofPayload |
Success Response
You can expect to receive the following response on success:
{
"verifyStatus": 100000,
"verifyMessage": "proved, thank you!",
"data": null
}
Verification Logic
Internally, GTR performs the following checks:
- Resolve your VASP identity from
X-Authorization - Parse the submitted
proofPayload - Check whether the wallet address already exists with ownership proof level 3 or above
- Recompute the expected
challengeMsgfrom the authenticated VASP and payload fields - Verify the cryptographic signature using
publicKeyType,proofAlgo,publicKey,base64SignedMessage, andpublicKeyFormat - If verification succeeds, insert or update the address record as
OWNERSHIP_PROVING
Error Handling
Authentication Errors
| verifyStatus | HTTP Status | verifyMessage | Cause | Action |
|---|---|---|---|---|
100001 | 200 | unauthorized | Missing or invalid X-Authorization header | Check the token in your request |
Business Errors
| verifyStatus | HTTP Status | verifyMessage | Cause | Action |
|---|---|---|---|---|
100001 | 200 | address already exists with proof type >= 3 | The address has already been ownership-proved | Use another address, or contact GTR support if you believe the existing record is incorrect |
100001 | 200 | challenge message is not match | The submitted ChallengeMsg does not match the expected challenge | Make sure the payload fields are exactly the same as those used in API 1 |
100001 | 200 | invalid public key: the provided base64 public key does not match the expected format for key type '[TYPE]' | The public key content does not match the declared publicKeyType or publicKeyFormat | Verify your key type, key bytes, and key encoding |
100001 | 200 | invalid base64 encoding in public key or signature | The submitted public key or signature is not valid Base64 | Re-encode your public key and signature using standard Base64 |
Client Errors
| verifyStatus | HTTP Status | verifyMessage | Cause | Action |
|---|---|---|---|---|
100004 | 400 | Validation error text | Required request field is missing or invalid | Check your request body |
100001 | 200 | missing request body, have to check all the required parameters? | Request body is missing | Send a valid JSON body |
100001 | 200 | wrong json request body format, please check if theres any additional , in the tail of key:value, the last key-value should not have , concat at the tail | JSON syntax is invalid | Fix the JSON formatting |
Server Errors
| verifyStatus | HTTP Status | verifyMessage | Cause | Action |
|---|---|---|---|---|
100006 | 500 | Internal Server Error, Please contact to GTR official service, Support Id: [SUPPORT_ID] | Unsupported publicKeyType, unsupported proofAlgo, invalid format combination, or another unexpected internal exception | Verify your request values are within the supported list. If the problem continues, contact GTR support with the supportId |
Important Notes
- If your request uses unsupported
publicKeyTypeorproofAlgovalues, the current server implementation may return a server-side error instead of a business validation error. - If
publicKeyFormatis omitted in the Generate Challenge step, you should usePEMwhen building the finalproofPayload. - The same address cannot be repeatedly registered once its proof level is already
3or above. - The values used in API 1 and API 2 should remain consistent.