Read Card Details

Retrieve sensitive card data (PAN, CVV, PIN) with user confirmation.

Before You Start

Read the following guides before proceeding:

GuideWhy
Getting StartedPlatform overview and setup
Api BasicsRequired headers and request configuration
AuthenticationHow to obtain access tokens
OnboardingUser and wallet registration
KYCKYC verification requirements

Overview

Sensitive card data (PAN, CVV, PIN) requires user confirmation before retrieval. Two confirmation methods are supported:

  • Signature-Based — User signs a message with their wallet private key (recommended)
  • SMS OTP — User receives and enters a code sent to their registered phone

Confirmation Methods

Signature-Based Confirmation (Recommended)

User signs a message with their wallet private key.

POST /api/v1/confirmation/signature/verify

Message format: By signing this I confirm that I am executing action {ActionType} at {Nonce}

ParameterDescription
action_typeGetCardDetails
message_signatureSignature of the message
nonceUnix timestamp when message was signed (valid for 5 minutes)
const nonce = Math.floor(Date.now() / 1000);
const message = `By signing this I confirm that I am executing action GetCardDetails at ${nonce}`;
const signature = await wallet.signMessage(message);

const response = await fetch(`${baseUrl}/api/v1/confirmation/signature/verify`, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${accessToken}`,
    'X-User-Address': userEoaAddress,
    'X-Chain-Id': chainId,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    action_type: 'GetCardDetails',
    message_signature: signature,
    nonce: nonce
  })
});
const { action_token } = await response.json();
import time

nonce = int(time.time())
message = f"By signing this I confirm that I am executing action GetCardDetails at {nonce}"
signature = wallet.sign_message(message)

response = requests.post(
    f"{base_url}/api/v1/confirmation/signature/verify",
    headers={
        "Authorization": f"Bearer {access_token}",
        "X-User-Address": user_eoa_address,
        "X-Chain-Id": chain_id,
        "Content-Type": "application/json"
    },
    json={
        "action_type": "GetCardDetails",
        "message_signature": signature,
        "nonce": nonce
    }
)
action_token = response.json()["action_token"]
nonce := time.Now().Unix()
message := fmt.Sprintf("By signing this I confirm that I am executing action GetCardDetails at %d", nonce)
signature, _ := wallet.SignMessage(message)

body, _ := json.Marshal(map[string]interface{}{
    "action_type":       "GetCardDetails",
    "message_signature": signature,
    "nonce":             nonce,
})
req, _ := http.NewRequest("POST", baseURL+"/api/v1/confirmation/signature/verify", bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("X-User-Address", userEoaAddress)
req.Header.Set("X-Chain-Id", chainId)
req.Header.Set("Content-Type", "application/json")

resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()

var result struct{ ActionToken string `json:"action_token"` }
json.NewDecoder(resp.Body).Decode(&result)

SMS OTP Confirmation

User receives an SMS code to their registered phone number.

Step 1: Request SMS code

POST /api/v1/confirmation/sms
const response = await fetch(`${baseUrl}/api/v1/confirmation/sms`, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${accessToken}`,
    'X-User-Address': userEoaAddress,
    'X-Chain-Id': chainId,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    action_type: 'GetCardDetails'
  })
});
const { session_id, code_length, expires_at, resend_at } = await response.json();
response = requests.post(
    f"{base_url}/api/v1/confirmation/sms",
    headers={
        "Authorization": f"Bearer {access_token}",
        "X-User-Address": user_eoa_address,
        "X-Chain-Id": chain_id,
        "Content-Type": "application/json"
    },
    json={"action_type": "GetCardDetails"}
)
session = response.json()
session_id = session["session_id"]
body, _ := json.Marshal(map[string]string{"action_type": "GetCardDetails"})
req, _ := http.NewRequest("POST", baseURL+"/api/v1/confirmation/sms", bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("X-User-Address", userEoaAddress)
req.Header.Set("X-Chain-Id", chainId)
req.Header.Set("Content-Type", "application/json")

resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()

var session struct {
    SessionID  string `json:"session_id"`
    CodeLength int    `json:"code_length"`
}
json.NewDecoder(resp.Body).Decode(&session)

Response:

{
  "session_id": "550e8400-e29b-41d4-a716-446655440000",
  "attempts_left": 3,
  "expires_at": "2024-01-15T10:05:00Z",
  "code_length": 6,
  "resend_at": "2024-01-15T10:01:00Z"
}

Step 2: Verify SMS code

POST /api/v1/confirmation/sms/verify
const response = await fetch(`${baseUrl}/api/v1/confirmation/sms/verify`, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${accessToken}`,
    'X-User-Address': userEoaAddress,
    'X-Chain-Id': chainId,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    session_id: sessionId,
    code: userEnteredCode
  })
});
const { token: action_token } = await response.json();
response = requests.post(
    f"{base_url}/api/v1/confirmation/sms/verify",
    headers={
        "Authorization": f"Bearer {access_token}",
        "X-User-Address": user_eoa_address,
        "X-Chain-Id": chain_id,
        "Content-Type": "application/json"
    },
    json={
        "session_id": session_id,
        "code": user_entered_code
    }
)
action_token = response.json()["token"]
body, _ := json.Marshal(map[string]string{
    "session_id": sessionID,
    "code":       userEnteredCode,
})
req, _ := http.NewRequest("POST", baseURL+"/api/v1/confirmation/sms/verify", bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("X-User-Address", userEoaAddress)
req.Header.Set("X-Chain-Id", chainId)
req.Header.Set("Content-Type", "application/json")

resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()

var result struct{ Token string `json:"token"` }
json.NewDecoder(resp.Body).Decode(&result)
actionToken := result.Token

Get Card Number and Expiry

POST /api/v1/cards/{cardId}/details
const response = await fetch(`${baseUrl}/api/v1/cards/${cardId}/details`, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${accessToken}`,
    'X-User-Address': userEoaAddress,
    'X-Chain-Id': chainId,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    action_token: actionToken
  })
});
const { card_number, expiry_date } = await response.json();
response = requests.post(
    f"{base_url}/api/v1/cards/{card_id}/details",
    headers={
        "Authorization": f"Bearer {access_token}",
        "X-User-Address": user_eoa_address,
        "X-Chain-Id": chain_id,
        "Content-Type": "application/json"
    },
    json={"action_token": action_token}
)
details = response.json()
card_number = details["card_number"]
expiry_date = details["expiry_date"]
body, _ := json.Marshal(map[string]string{"action_token": actionToken})
req, _ := http.NewRequest("POST", baseURL+"/api/v1/cards/"+cardID+"/details", bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("X-User-Address", userEoaAddress)
req.Header.Set("X-Chain-Id", chainId)
req.Header.Set("Content-Type", "application/json")

resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()

var details struct {
    CardNumber string `json:"card_number"`
    ExpiryDate string `json:"expiry_date"`
}
json.NewDecoder(resp.Body).Decode(&details)

Response:

{
  "card_number": "4111111111111234",
  "expiry_date": "12/27"
}

Get CVV

POST /api/v1/cards/{cardId}/cvv
const response = await fetch(`${baseUrl}/api/v1/cards/${cardId}/cvv`, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${accessToken}`,
    'X-User-Address': userEoaAddress,
    'X-Chain-Id': chainId,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    action_token: actionToken
  })
});
const { cvv } = await response.json();
response = requests.post(
    f"{base_url}/api/v1/cards/{card_id}/cvv",
    headers={
        "Authorization": f"Bearer {access_token}",
        "X-User-Address": user_eoa_address,
        "X-Chain-Id": chain_id,
        "Content-Type": "application/json"
    },
    json={"action_token": action_token}
)
cvv = response.json()["cvv"]
body, _ := json.Marshal(map[string]string{"action_token": actionToken})
req, _ := http.NewRequest("POST", baseURL+"/api/v1/cards/"+cardID+"/cvv", bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("X-User-Address", userEoaAddress)
req.Header.Set("X-Chain-Id", chainId)
req.Header.Set("Content-Type", "application/json")

resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()

var result struct{ CVV string `json:"cvv"` }
json.NewDecoder(resp.Body).Decode(&result)

Response:

{
  "cvv": "123"
}

Get PIN

PIN is only available for physical cards.

POST /api/v1/cards/{cardId}/pin
const response = await fetch(`${baseUrl}/api/v1/cards/${cardId}/pin`, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${accessToken}`,
    'X-User-Address': userEoaAddress,
    'X-Chain-Id': chainId,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    action_token: actionToken
  })
});
const { pin } = await response.json();
response = requests.post(
    f"{base_url}/api/v1/cards/{card_id}/pin",
    headers={
        "Authorization": f"Bearer {access_token}",
        "X-User-Address": user_eoa_address,
        "X-Chain-Id": chain_id,
        "Content-Type": "application/json"
    },
    json={"action_token": action_token}
)
pin = response.json()["pin"]
body, _ := json.Marshal(map[string]string{"action_token": actionToken})
req, _ := http.NewRequest("POST", baseURL+"/api/v1/cards/"+cardID+"/pin", bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("X-User-Address", userEoaAddress)
req.Header.Set("X-Chain-Id", chainId)
req.Header.Set("Content-Type", "application/json")

resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()

var result struct{ PIN string `json:"pin"` }
json.NewDecoder(resp.Body).Decode(&result)

Response:

{
  "pin": "1234"
}

Action Token Validity

PropertyValue
Validity period5 minutes
Single useYes
Reusable across endpointsYes (same token works for PAN, CVV, and PIN)

Error Handling

Action Token Errors

Errors returned when the action token is invalid or expired.

Error ReasonError DetailsDescription
ErrorInvalidFieldaction_token: invalid_tokenToken format is invalid or malformed
ErrorInvalidFieldaction_token: invalid_claimsToken claims cannot be parsed
ErrorExpiredaction_token: expiredToken has expired (5 minute validity)
ErrorInvalidFieldaction_token: invalid_purposeToken was issued for a different action type
ErrorMissingFieldaction_token: missingAction token not provided in request

Card Status Errors

Errors returned when the card is not in a valid state for retrieving details.

Error ReasonError DetailsDescription
ErrorInvalidStatuscard_status: not_activeCard must be active to retrieve details
ErrorNotSupportedcard_format: not_physicalPIN is only available for physical cards

Signature Confirmation Errors

Errors returned when verifying a wallet signature.

Error ReasonError DetailsDescription
ErrorMissingFieldaction_type: missingAction type not provided
ErrorInvalidFieldaction_type: invalid_valueInvalid action type value
ErrorMissingFieldmessage_signature: missingSignature not provided
ErrorMissingFieldnonce: missingNonce not provided
ErrorInvalidFieldnonce: expiredNonce is older than 5 minutes
ErrorInvalidFieldsignature: invalid_signatureSignature does not match the user's wallet

SMS Confirmation Errors

Errors returned during SMS OTP verification.

Error ReasonError DetailsDescription
ErrorMissingFieldcode: missingOTP code not provided
ErrorMissingFieldsession_id: missingSession ID not provided
ErrorInvalidFieldsession_id: invalid_uuidSession ID is not a valid UUID

Error Response Format

{
  "error_reason": "ErrorInvalidField",
  "error_description": "Invalid action token",
  "error_category": {
    "category": "CategoryValidationFailure",
    "http_status_code": 400
  },
  "error_details": [
    { "key": "field", "details": "action_token" },
    { "key": "issue", "details": "expired" }
  ]
}