Skip to main content

Update 3DS Contact Information

Overview

Use this endpoint to update a cardholder's contact information for 3D Secure authentication. This includes updating the mobile number, email address, date of birth, address, country, and security questions. All updated information will be used for OTP delivery and verification during 3DS transactions.

NOTE: This API is offered on a client-by-client approval basis. Speak to your account manager to check eligibility.

Resource Access

Production (api.ahrvo.network)

POST https://api.ahrvo.network/card/issuance/api/issuing/cardholders/v2/update

Staging (gateway.ahrvo.network)

POST https://gateway.ahrvo.network/card/issuance/api/issuing/cardholders/v2/update

Request Headers

HeaderValueRequiredDescription
Acceptapplication/jsonYesContent type for the response
AuthorizationBearer {access_token}YesBearer token for authentication
x-api-keyAPI KeyYesAPI key for authentication
Content-Typeapplication/jsonYesRequest body content type

Request Body

The request body should contain a JSON object with the following structure:

{
"cardholder_id": "471041678120300545",
"call_prefix": "+1",
"mobile": "2125551234",
"email": "emma.johnson.updated@example.com",
"date_of_birth": "1996-10-31",
"state": "Illinois",
"city": "Chicago",
"address_line": "789 Oak Street",
"post_code": "60601",
"country_code": "US",
"security_index": "2",
"security_answer": "Smith"
}

Request Fields

FieldTypeRequiredDescription
cardholder_idstringYesA unique ID of the cardholder to update
call_prefixstringYesCountry code of the mobile. Supported: +61, +1, +44, +852, +81, +82, +65, +90, +86, +33
mobilestringYesMobile number of the cardholder, without country code
emailstringYesEmail of the cardholder
date_of_birthstringYesDate of birth of the cardholder (YYYY-MM-DD)
address_linestringYesAddress of the cardholder
citystringYesCity of the cardholder
statestringYesState of the cardholder
post_codestringYesPost code of the cardholder
country_codestringYesCountry of the cardholder in ISO 3166 format (e.g., US)
security_indexstringYesIndex of security question (1-5)
security_answerstringYesAnswer to the security question

Security Questions

The security_index field corresponds to the following questions:

IndexSecurity Question
1What was your first pet's name?
2What is your maternal grandmother's maiden name?
3What is the name of your favourite childhood friend?
4What was the make of your first car?
5In what city or town did your mother and father meet?

Supported Country Codes (call_prefix)

  • +1 - United States, Canada
  • +33 - France
  • +44 - United Kingdom
  • +61 - Australia
  • +65 - Singapore
  • +81 - Japan
  • +82 - South Korea
  • +86 - China
  • +90 - Turkey
  • +852 - Hong Kong

Response

Success Response (200 OK)

{
"code": "SUCCESS"
}

Response Fields

FieldTypeDescription
codestringStatus string indicating the result. "SUCCESS" refers to successful update

Error Responses

  • 400 Bad Request: Invalid input data, missing required fields, or invalid email/mobile format
  • 401 Unauthorized: Invalid or missing authentication token
  • 403 Forbidden: 3DS feature not enabled for this account
  • 404 Not Found: Cardholder does not exist

Code Examples

cURL

curl -X POST \
'https://gateway.ahrvo.network/card/issuance/api/issuing/cardholders/v2/update' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"cardholder_id": "471041678120300545",
"call_prefix": "+1",
"mobile": "2125551234",
"email": "emma.johnson.updated@example.com",
"date_of_birth": "1996-10-31",
"state": "Illinois",
"city": "Chicago",
"address_line": "789 Oak Street",
"post_code": "60601",
"country_code": "US",
"security_index": "2",
"security_answer": "Smith"
}'

Python

import requests

url = "https://gateway.ahrvo.network/card/issuance/api/issuing/cardholders/v2/update"
headers = {
"Accept": "application/json",
"Authorization": "Bearer YOUR_ACCESS_TOKEN",
"x-api-key": "YOUR_API_KEY",
"Content-Type": "application/json"
}
data = {
"cardholder_id": "471041678120300545",
"call_prefix": "+1",
"mobile": "2125551234",
"email": "emma.johnson.updated@example.com",
"date_of_birth": "1996-10-31",
"state": "Illinois",
"city": "Chicago",
"address_line": "789 Oak Street",
"post_code": "60601",
"country_code": "US",
"security_index": "2",
"security_answer": "Smith"
}

response = requests.post(url, headers=headers, json=data)
print(response.json())

JavaScript (Node.js)

const axios = require('axios');

const url = 'https://gateway.ahrvo.network/card/issuance/api/issuing/cardholders/v2/update';
const headers = {
'Accept': 'application/json',
'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
'x-api-key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
};
const data = {
cardholder_id: '471041678120300545',
call_prefix: '+1',
mobile: '2125551234',
email: 'emma.johnson.updated@example.com',
date_of_birth: '1996-10-31',
state: 'Illinois',
city: 'Chicago',
address_line: '789 Oak Street',
post_code: '60601',
country_code: 'US',
security_index: '2',
security_answer: 'Smith'
};

axios.post(url, data, { headers })
.then(response => {
console.log(response.data);
})
.catch(error => {
console.error(error.response.data);
});

Usage Notes

  • All Fields Required: This endpoint requires all fields to be provided, even if only updating one field
  • Complete Replacement: All values will be replaced with the new data provided in the request
  • Email & Mobile Critical: These fields are used for OTP delivery during 3DS transactions
  • Immediate Effect: Changes take effect immediately for all enrolled cards
  • Validation: Email and mobile number formats are validated before update
  • Security Question: You can change both the security question and answer

Common Use Cases

Update Email Address

When a cardholder changes their email:

def update_cardholder_email(cardholder_id, new_email):
# First, get current cardholder details
current_data = query_cardholder_details(cardholder_id)

# Update only the email, keep other fields the same
update_data = {
"cardholder_id": cardholder_id,
"email": new_email,
"call_prefix": current_data['call_prefix'],
"mobile": current_data['mobile'],
"date_of_birth": current_data['date_of_birth'],
"state": current_data['state'],
"city": current_data['city'],
"address_line": current_data['address_line'],
"post_code": current_data['post_code'],
"country_code": current_data['country_code'],
"security_index": current_data['security_index'],
"security_answer": current_data['security_answer']
}

response = update_3ds_contact(update_data)
return response

Update Mobile Number

When a cardholder gets a new phone number:

async function updateCardholderMobile(cardholderId, newCallPrefix, newMobile) {
// Get current cardholder data
const currentData = await queryCardholderDetails(cardholderId);

// Update mobile fields
const updateData = {
cardholder_id: cardholderId,
call_prefix: newCallPrefix,
mobile: newMobile,
email: currentData.email,
date_of_birth: currentData.date_of_birth,
state: currentData.state,
city: currentData.city,
address_line: currentData.address_line,
post_code: currentData.post_code,
country_code: currentData.country_code,
security_index: currentData.security_index,
security_answer: currentData.security_answer
};

return await update3DSContact(updateData);
}

Update Address Information

When a cardholder moves to a new address:

async function updateCardholderAddress(cardholderId, newAddress) {
const currentData = await queryCardholderDetails(cardholderId);

const updateData = {
cardholder_id: cardholderId,
address_line: newAddress.address_line,
city: newAddress.city,
state: newAddress.state,
post_code: newAddress.post_code,
country_code: newAddress.country_code,
// Keep existing contact info
call_prefix: currentData.call_prefix,
mobile: currentData.mobile,
email: currentData.email,
date_of_birth: currentData.date_of_birth,
security_index: currentData.security_index,
security_answer: currentData.security_answer
};

return await update3DSContact(updateData);
}

Update Security Question

When a cardholder wants to change their security question:

def update_security_question(cardholder_id, new_security_index, new_security_answer):
current_data = query_cardholder_details(cardholder_id)

update_data = {
"cardholder_id": cardholder_id,
"security_index": new_security_index,
"security_answer": new_security_answer,
# Keep all other fields the same
"call_prefix": current_data['call_prefix'],
"mobile": current_data['mobile'],
"email": current_data['email'],
"date_of_birth": current_data['date_of_birth'],
"state": current_data['state'],
"city": current_data['city'],
"address_line": current_data['address_line'],
"post_code": current_data['post_code'],
"country_code": current_data['country_code']
}

return update_3ds_contact(update_data)

Bulk Update with Validation

Update multiple cardholders with proper validation:

def bulk_update_cardholders(updates):
results = {
'success': [],
'failed': []
}

for update in updates:
try:
# Validate email format
if not is_valid_email(update['email']):
raise ValueError(f"Invalid email format: {update['email']}")

# Validate mobile number
if not is_valid_mobile(update['call_prefix'], update['mobile']):
raise ValueError(f"Invalid mobile number")

# Perform update
response = update_3ds_contact(update)

if response['code'] == 'SUCCESS':
results['success'].append(update['cardholder_id'])
else:
results['failed'].append({
'cardholder_id': update['cardholder_id'],
'error': response.get('message', 'Unknown error')
})
except Exception as e:
results['failed'].append({
'cardholder_id': update['cardholder_id'],
'error': str(e)
})

return results

Best Practices

  • Retrieve First: Always query current cardholder details before updating to avoid data loss
  • Validate Input: Validate email and mobile formats before sending the update request
  • Notify Cardholder: Inform the cardholder when their contact information is updated
  • Verify Update: Query the cardholder details after update to confirm changes
  • Audit Trail: Log all updates for compliance and security purposes
  • User Confirmation: Require user confirmation before updating critical fields like email and mobile

Update Workflow

Standard Update Process

  1. Query Current Data: Retrieve existing cardholder information
  2. Modify Fields: Change only the fields that need updating
  3. Validate Changes: Ensure all data is valid before submission
  4. Submit Update: Send the update request with all required fields
  5. Verify Success: Confirm the update was successful
  6. Notify User: Inform the cardholder of the changes

Self-Service Update Portal

async function handleCardholderUpdate(cardholderId, updatedFields) {
try {
// Step 1: Get current data
const currentData = await queryCardholderDetails(cardholderId);

// Step 2: Merge with updates
const updatePayload = {
cardholder_id: cardholderId,
...currentData.data,
...updatedFields
};

// Step 3: Validate critical fields
if (updatedFields.email && !validateEmail(updatedFields.email)) {
throw new Error('Invalid email format');
}

if (updatedFields.mobile && !validateMobile(updatedFields.mobile)) {
throw new Error('Invalid mobile number');
}

// Step 4: Update
const response = await update3DSContact(updatePayload);

// Step 5: Verify
if (response.code === 'SUCCESS') {
const verifyData = await queryCardholderDetails(cardholderId);
console.log('Update verified:', verifyData);

// Step 6: Notify
await sendUpdateNotification(cardholderId, updatedFields);
}

return response;
} catch (error) {
console.error('Update failed:', error);
throw error;
}
}

Data Validation

Email Validation

function validateEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}

Mobile Validation

function validateMobile(callPrefix, mobile) {
const validPrefixes = ['+1', '+33', '+44', '+61', '+65', '+81', '+82', '+86', '+90', '+852'];

if (!validPrefixes.includes(callPrefix)) {
return false;
}

// Mobile should be digits only
const mobileRegex = /^\d+$/;
return mobileRegex.test(mobile) && mobile.length >= 7 && mobile.length <= 15;
}

Date Validation

from datetime import datetime

def validate_date_of_birth(date_str):
try:
dob = datetime.strptime(date_str, '%Y-%m-%d')
# Check if date is in the past and person is at least 18 years old
today = datetime.now()
age = today.year - dob.year - ((today.month, today.day) < (dob.month, dob.day))
return age >= 18
except ValueError:
return False
  • Create Cardholder 3DS Contact - Create new cardholder profiles
  • Query 3DS Contact Details - Retrieve cardholder information
  • Query 3DS Enrollment Details - Check enrollment status by card
  • Enroll Card for 3DS - Link cardholder to a card

Troubleshooting

Cardholder Not Found (404)

  • Cause: Invalid cardholder_id or cardholder doesn't exist
  • Solution: Verify the cardholder_id is correct and the cardholder exists in your system

Invalid Email Format (400)

  • Cause: Email format validation failed
  • Solution: Ensure email follows standard format (user@domain.com)

Invalid Mobile Number (400)

  • Cause: Mobile number format or country code is invalid
  • Solution:
    • Verify call_prefix is one of the supported country codes
    • Ensure mobile contains only digits without country code
    • Check mobile number length is appropriate

Missing Required Fields (400)

  • Cause: One or more required fields are missing
  • Solution: Ensure all 12 required fields are included in the request

Update Not Reflecting (200 but no change)

  • Cause: Cached data or verification issue
  • Solution: Query the cardholder details again to verify the update

Security Considerations

  • Authentication Required: All update requests must include valid Bearer token and API key
  • Data Encryption: All communication uses HTTPS encryption
  • Sensitive Data: Security answers are stored and can be updated - handle with care
  • Access Control: Implement proper authorization checks before allowing updates
  • Audit Logging: Log all update operations for security and compliance
  • Rate Limiting: Implement rate limiting to prevent abuse

Interactive API Explorer