Integration Overview
This guide walks through implementing the complete consent management workflow in your application. We’ll cover collecting consent during onboarding, linking users, checking status, and handling revocations.
All examples use the US policy with 5 required consents (including eSignAct for E-Sign Act compliance). For Global policy, exclude the eSignAct consent.
Prerequisites
Before implementing consent management, ensure you have:
Baanx API credentials (x-client-key and x-secret-key)
User registration flow in your application
Ability to track temporary identifiers before user creation
Step 1: Collect Consent During Onboarding
Create a consent set during user registration before address submission finalizes the registration.
Use Onboarding ID from Registration
The onboardingId is obtained from the email verification step of the user registration flow :
// Step 2 of registration: Email verification returns onboardingId
const { onboardingId } = await fetch ( '/v1/auth/register/email/verify' , {
method: 'POST' ,
headers: { 'x-client-key' : 'YOUR_CLIENT_KEY' },
body: JSON . stringify ({
email: '[email protected] ' ,
password: 'SecurePassword123!' ,
verificationCode: '123456' ,
contactVerificationId: verificationId ,
countryOfResidence: 'US'
})
}). then ( res => res . json ());
// Use this onboardingId for consent creation (Step 4 of registration)
// Example onboardingId: "100a99cf-f4d3-4fa1-9be9-2e9828b20ebb"
Do not generate a new onboardingId - always use the onboardingId returned from email verification (POST /v1/auth/register/email/verify). This ID links the consent to the user’s registration session.
Create Consent Set
const response = await fetch ( 'https://api.baanx.com/v2/consent/onboarding' , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
'x-client-key' : 'your_client_key' ,
'x-secret-key' : 'your_secret_key'
},
body: JSON . stringify ({
onboardingId: onboardingId ,
tenantId: 'tenant_baanx_prod' ,
policyType: 'US' ,
consents: [
{
consentType: 'eSignAct' ,
consentStatus: 'granted'
},
{
consentType: 'termsAndPrivacy' ,
consentStatus: 'granted'
},
{
consentType: 'marketingNotifications' ,
consentStatus: 'granted'
},
{
consentType: 'smsNotifications' ,
consentStatus: 'denied'
},
{
consentType: 'emailNotifications' ,
consentStatus: 'granted'
}
],
metadata: {
ipAddress: '192.168.1.1' ,
userAgent: navigator . userAgent ,
timestamp: new Date (). toISOString (),
clientId: 'web-app-v1.2.0'
}
})
});
const { consentSetId } = await response . json ();
// Store this consentSetId for linking later
Response
{
"consentSetId" : "550e8400-e29b-41d4-a716-446655440001" ,
"onboardingId" : "onboarding_abc123xyz" ,
"tenantId" : "tenant_baanx_prod" ,
"createdAt" : "2024-01-15T10:30:00Z" ,
"_links" : {
"self" : {
"href" : "https://api.baanx.com/v2/consent/consentSet/550e8400-e29b-41d4-a716-446655440001" ,
"method" : "GET"
}
}
}
Validation Rules
The API validates that all required consents are present based on policy type:
Policy Type Required Consents USAll 5 types (including eSignAct for E-Sign Act compliance) global4 types (excludes eSignAct)
Consents can have granted or denied status during creation. Both are valid, but the user’s overall consent status will be incomplete if required consents are denied.
Step 2: Link User to Consent Set
After address submission finalizes registration and returns a permanent userId, link it to the consent set created in Step 1.
When to Link
Call this endpoint after:
✅ Physical address submitted (POST /v1/auth/register/address) returns userId and accessToken
✅ OR mailing address submitted (POST /v1/auth/register/mailing-address) returns userId and accessToken (US users only)
✅ Registration is complete and you have the userId
The address endpoints finalize registration. Non-US users and US users with same mailing address get userId from the address endpoint. US users with different mailing addresses get userId from the mailing address endpoint.
Link Request
const response = await fetch (
`https://api.baanx.com/v2/consent/onboarding/ ${ consentSetId } ` ,
{
method: 'PATCH' ,
headers: {
'Content-Type' : 'application/json' ,
'x-client-key' : 'your_client_key' ,
'x-secret-key' : 'your_secret_key'
},
body: JSON . stringify ({
userId: 'user_123abc456def'
})
}
);
const linkedConsent = await response . json ();
Response
{
"consentSetId" : "550e8400-e29b-41d4-a716-446655440001" ,
"userId" : "user_123abc456def" ,
"completedAt" : "2024-01-15T10:35:00Z" ,
"consentSet" : {
"consentSetId" : "550e8400-e29b-41d4-a716-446655440001" ,
"userId" : "user_123abc456def" ,
"onboardingId" : "onboarding_abc123xyz" ,
"tenantId" : "tenant_baanx_prod" ,
"policyType" : "US" ,
"completedAt" : "2024-01-15T10:35:00Z" ,
"createdAt" : "2024-01-15T10:30:00Z" ,
"updatedAt" : "2024-01-15T10:35:00Z" ,
"consents" : [
{
"consentId" : "consent_001" ,
"consentType" : "eSignAct" ,
"consentStatus" : "granted" ,
"metadata" : {
"timestamp" : "2024-01-15T10:30:00Z" ,
"ipAddress" : "192.168.1.1"
},
"createdAt" : "2024-01-15T10:30:00Z" ,
"updatedAt" : "2024-01-15T10:30:00Z"
}
]
},
"_links" : {
"self" : {
"href" : "https://api.baanx.com/v2/consent/consentSet/550e8400-e29b-41d4-a716-446655440001" ,
"method" : "GET"
},
"audit" : {
"href" : "https://api.baanx.com/v2/consent/user/user_123abc456def/audit" ,
"method" : "GET"
}
}
}
A consent set can only be linked to one user . Attempting to link again will result in a 409 Conflict error.
Step 3: Check Consent Status
Retrieve a user’s consent status to validate compliance or gate features.
Short Status Check (Fast)
Get quick status summary:
const response = await fetch (
'https://api.baanx.com/v2/consent/user/user_123abc456def' ,
{
headers: {
'x-client-key' : 'your_client_key'
}
}
);
const { userId , consentStatus } = await response . json ();
Response:
{
"userId" : "user_123abc456def" ,
"consentStatus" : "complete" ,
"_links" : {
"self" : {
"href" : "https://api.baanx.com/v2/consent/user/user_123abc456def" ,
"method" : "GET"
},
"full" : {
"href" : "https://api.baanx.com/v2/consent/user/user_123abc456def?full=true" ,
"method" : "GET"
},
"audit" : {
"href" : "https://api.baanx.com/v2/consent/user/user_123abc456def/audit" ,
"method" : "GET"
}
}
}
Status Values
Status Meaning Action completeAll required consents granted, no revocations Allow access incompleteMissing required consents OR has denials/revocations Restrict access, prompt re-consent noneNo consent sets found Initiate consent collection
Full Consent Details
Get all consent sets with detailed records:
const response = await fetch (
'https://api.baanx.com/v2/consent/user/user_123abc456def?full=true' ,
{
headers: {
'x-client-key' : 'your_client_key'
}
}
);
const { userId , consentStatus , consentSets } = await response . json ();
Use Cases:
Detailed consent review pages
User privacy dashboards
Compliance reporting
Consent preference management
Step 4: Access Audit Trail
Retrieve complete consent change history for compliance reporting.
Paginated Audit Request
const response = await fetch (
'https://api.baanx.com/v2/consent/user/user_123abc456def/audit?limit=50&offset=0' ,
{
headers: {
'x-client-key' : 'your_client_key'
}
}
);
const { userId , auditRecords , pagination } = await response . json ();
Response
{
"userId" : "user_123abc456def" ,
"auditRecords" : [
{
"auditId" : "audit_001" ,
"action" : "created" ,
"timestamp" : "2024-01-15T10:30:00Z" ,
"consentSetId" : "550e8400-e29b-41d4-a716-446655440001" ,
"changes" : {
"before" : null ,
"after" : {
"consentType" : "termsAndPrivacy" ,
"consentStatus" : "granted"
}
},
"metadata" : {
"ipAddress" : "192.168.1.1" ,
"userAgent" : "Mozilla/5.0..."
}
},
{
"auditId" : "audit_002" ,
"action" : "revoked" ,
"timestamp" : "2024-01-20T14:22:00Z" ,
"consentSetId" : "550e8400-e29b-41d4-a716-446655440001" ,
"changes" : {
"before" : {
"consentType" : "marketingNotifications" ,
"consentStatus" : "granted"
},
"after" : {
"consentType" : "marketingNotifications" ,
"consentStatus" : "revoked"
}
}
}
],
"pagination" : {
"total" : 2 ,
"limit" : 50 ,
"offset" : 0
},
"_links" : {
"self" : {
"href" : "https://api.baanx.com/v2/consent/user/user_123abc456def/audit?limit=50&offset=0" ,
"method" : "GET"
}
}
}
Parameter Description Default limitRecords per page 50 offsetStarting position 0
Step 5: Revoke Consent
Allow users to withdraw specific consents while maintaining audit trail.
Revocation Request
const response = await fetch (
`https://api.baanx.com/v2/consent/consentSet/ ${ consentSetId } /consent/ ${ consentId } ` ,
{
method: 'DELETE' ,
headers: {
'x-client-key' : 'your_client_key' ,
'x-secret-key' : 'your_secret_key'
}
}
);
const revokedConsent = await response . json ();
Response
{
"consentId" : "consent_new_revoked_001" ,
"consentSetId" : "550e8400-e29b-41d4-a716-446655440001" ,
"consentType" : "marketingNotifications" ,
"consentStatus" : "revoked" ,
"revocationTimestamp" : "2024-01-20T14:22:00Z" ,
"_links" : {
"consentSet" : {
"href" : "https://api.baanx.com/v2/consent/consentSet/550e8400-e29b-41d4-a716-446655440001" ,
"method" : "GET"
},
"audit" : {
"href" : "https://api.baanx.com/v2/consent/user/user_123abc456def/audit" ,
"method" : "GET"
}
}
}
Revocation creates a new consent record with revoked status. The original consent record is preserved for audit purposes.
Post-Revocation Handling
After revocation:
User’s overall consent status may become incomplete
Check consent status to determine access restrictions
Prompt user to re-grant consent if needed for service access
Complete Integration Example
Here’s a full TypeScript implementation:
class ConsentManager {
private baseUrl = 'https://api.baanx.com' ;
private clientKey : string ;
private secretKey : string ;
constructor ( clientKey : string , secretKey : string ) {
this . clientKey = clientKey ;
this . secretKey = secretKey ;
}
async createOnboardingConsent (
onboardingId : string ,
consents : Array <{ type : string ; status : 'granted' | 'denied' }>,
metadata ?: Record < string , any >
) {
const response = await fetch ( ` ${ this . baseUrl } /v2/consent/onboarding` , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
'x-client-key' : this . clientKey ,
'x-secret-key' : this . secretKey
},
body: JSON . stringify ({
onboardingId ,
tenantId: 'tenant_baanx_prod' ,
policyType: 'US' ,
consents: consents . map ( c => ({
consentType: c . type ,
consentStatus: c . status
})),
metadata: {
ipAddress: metadata ?. ipAddress ,
userAgent: metadata ?. userAgent ,
timestamp: new Date (). toISOString (),
... metadata
}
})
});
if ( ! response . ok ) {
throw new Error ( `Consent creation failed: ${ response . statusText } ` );
}
return response . json ();
}
async linkUserToConsent ( consentSetId : string , userId : string ) {
const response = await fetch (
` ${ this . baseUrl } /v2/consent/onboarding/ ${ consentSetId } ` ,
{
method: 'PATCH' ,
headers: {
'Content-Type' : 'application/json' ,
'x-client-key' : this . clientKey ,
'x-secret-key' : this . secretKey
},
body: JSON . stringify ({ userId })
}
);
if ( ! response . ok ) {
throw new Error ( `User linking failed: ${ response . statusText } ` );
}
return response . json ();
}
async getUserConsentStatus ( userId : string , full : boolean = false ) {
const url = full
? ` ${ this . baseUrl } /v2/consent/user/ ${ userId } ?full=true`
: ` ${ this . baseUrl } /v2/consent/user/ ${ userId } ` ;
const response = await fetch ( url , {
headers: {
'x-client-key' : this . clientKey
}
});
if ( ! response . ok ) {
throw new Error ( `Status check failed: ${ response . statusText } ` );
}
return response . json ();
}
async getAuditTrail ( userId : string , limit : number = 50 , offset : number = 0 ) {
const response = await fetch (
` ${ this . baseUrl } /v2/consent/user/ ${ userId } /audit?limit= ${ limit } &offset= ${ offset } ` ,
{
headers: {
'x-client-key' : this . clientKey
}
}
);
if ( ! response . ok ) {
throw new Error ( `Audit retrieval failed: ${ response . statusText } ` );
}
return response . json ();
}
async revokeConsent ( consentSetId : string , consentId : string ) {
const response = await fetch (
` ${ this . baseUrl } /v2/consent/consentSet/ ${ consentSetId } /consent/ ${ consentId } ` ,
{
method: 'DELETE' ,
headers: {
'x-client-key' : this . clientKey ,
'x-secret-key' : this . secretKey
}
}
);
if ( ! response . ok ) {
throw new Error ( `Revocation failed: ${ response . statusText } ` );
}
return response . json ();
}
}
Usage Example
const consentManager = new ConsentManager (
'your_client_key' ,
'your_secret_key'
);
// Get onboardingId from registration email verification (Step 2)
const { onboardingId } = await registrationApi . verifyEmail ( '[email protected] ' , '123456' );
// Create consent during registration (Step 4 - before address submission)
const { consentSetId } = await consentManager . createOnboardingConsent (
onboardingId ,
[
{ type: 'eSignAct' , status: 'granted' },
{ type: 'termsAndPrivacy' , status: 'granted' },
{ type: 'marketingNotifications' , status: 'granted' },
{ type: 'smsNotifications' , status: 'denied' },
{ type: 'emailNotifications' , status: 'granted' }
],
{
ipAddress: '192.168.1.1' ,
userAgent: 'web-app-v1.2.0'
}
);
// Continue registration: submit address (Step 5)
// Address endpoint finalizes registration and returns userId
const { userId , accessToken } = await registrationApi . submitAddress ( onboardingId , addressData );
// Link consent to userId (Step 7)
await consentManager . linkUserToConsent ( consentSetId , userId );
// Verify consent status
const { consentStatus } = await consentManager . getUserConsentStatus ( userId );
if ( consentStatus === 'complete' ) {
console . log ( 'Registration complete with consent audit trail' );
} else {
console . log ( 'Consent incomplete - user needs to grant required consents' );
}
Best Practices
Use Registration OnboardingId
Always use the onboardingId returned from email verification (POST /v1/auth/register/email/verify). Do not generate a new ID - the onboardingId links the consent to the registration session. // ✅ Correct: Use onboardingId from registration
const { onboardingId } = await verifyEmail ( email , code );
await createConsent ( onboardingId , consents );
// ❌ Wrong: Do not generate new ID
// const onboardingId = `onboarding_${uuid()}`;
Store ConsentSetId Temporarily
Store the consentSetId in your session or database between consent creation (Step 4) and linking (Step 7). You’ll need it to link the consent after address submission returns the userId.
Implement retry logic for network failures and provide clear user feedback for validation errors: try {
await consentManager . createOnboardingConsent ( ... );
} catch ( error ) {
if ( error . status === 409 ) {
// Duplicate onboardingId - generate new one
} else if ( error . status === 400 ) {
// Validation error - check required consents
} else {
// Network error - retry with backoff
}
}
Use Short Status for Gating
For access control and feature gating, use the short status check (no ?full=true) for optimal performance: const { consentStatus } = await consentManager . getUserConsentStatus ( userId );
if ( consentStatus !== 'complete' ) {
return res . status ( 403 ). json ({ error: 'Incomplete consent' });
}
Monitor Consent Status Changes
Periodically check consent status and notify users when action is needed (e.g., revoked required consents).
Next Steps