Skip to main content

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
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.
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 TypeRequired 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.
After address submission finalizes registration and returns a permanent userId, link it to the consent set created in Step 1. 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.
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.
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

StatusMeaningAction
completeAll required consents granted, no revocationsAllow access
incompleteMissing required consents OR has denials/revocationsRestrict access, prompt re-consent
noneNo consent sets foundInitiate consent collection
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"
    }
  }
}

Pagination Parameters

ParameterDescriptionDefault
limitRecords per page50
offsetStarting position0
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:
  1. User’s overall consent status may become incomplete
  2. Check consent status to determine access restrictions
  3. 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

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 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.
Include IP address, user agent, timestamp, and client version in metadata for comprehensive audit trails:
metadata: {
  ipAddress: req.ip,
  userAgent: req.headers['user-agent'],
  timestamp: new Date().toISOString(),
  clientId: 'mobile-app-ios-v2.1.0',
  sessionId: req.session.id
}
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
  }
}
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' });
}

Next Steps