Capabilities

Understand and evaluate user capabilities for feature access.

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

Overview

Capabilities are the mechanism for determining which features a user can access. Each capability represents a specific action or product (e.g., sending SEPA transfers, issuing cards). Capabilities have:

  • Status — whether the user can currently use this feature
  • Verification Requirements — KYC levels that must be completed
  • Prerequisites — other capabilities that must be active first

Your application should check capability statuses to decide which features to expose to each user and what actions they need to take to unlock additional features.


Reading Capabilities

Capabilities are returned as part of the GET /api/v2/user response.

{
  "capabilities": [
    {
      "type": "SepaAccount",
      "status": "Active",
      "verification_requirements": [
        { "type": "SDD", "order": 1 }
      ],
      "prerequisites": []
    },
    {
      "type": "SepaOut1stParty",
      "status": "NotFulfilled",
      "status_reason": "Prerequisite SepaAccount is not active",
      "verification_requirements": [
        { "type": "SDD", "order": 1 }
      ],
      "prerequisites": ["SepaAccount"]
    },
    {
      "type": "VisaVirtualCard",
      "status": "ActivationNotStarted",
      "verification_requirements": [
        { "type": "SDD", "order": 1 }
      ],
      "prerequisites": []
    }
  ]
}

Capability Structure

FieldTypeDescription
typestringCapability identifier
statusstringCurrent capability status
status_reasonstringExplanation when status is not Active (optional)
verification_requirementsarrayRequired KYC levels
prerequisitesarrayOther capability types that must be active first

Capability Statuses

StatusDescriptionUser Action
ActiveCapability is available for useFeature is accessible
ActivationNotStartedRequirements met, activation neededCall activation endpoint
InProgressActivation in progressWait for completion
NotFulfilledRequirements not metComplete KYC or prerequisites
NotAvailableNot available for this userCannot be activated

Status Flow

NotFulfilled  →  ActivationNotStarted  →  InProgress  →  Active
     ↑                   ↑                     ↑
 requirements         activated            completes
    met

Capability Types

Bank Account Capabilities

Bank account capabilities require KYC verification before activation.

CapabilityDescription
SepaAccountAbility to have a SEPA bank account (IBAN)
SepaIn1stPartyReceive SEPA transfers from own accounts
SepaIn3rdPartyReceive SEPA transfers from third parties
SepaOut1stPartySend SEPA transfers to own accounts
SepaOut3rdPartySend SEPA transfers to third parties
AchAccountAbility to have an ACH bank account
AchIn1stPartyReceive ACH transfers from own accounts
AchIn3rdPartyReceive ACH transfers from third parties
AchOut1stPartySend ACH transfers to own accounts
AchOut3rdPartySend ACH transfers to third parties
FasterPaymentsAccountUK Faster Payments bank account
FasterPaymentsIn1stPartyReceive FPS from own accounts
FasterPaymentsIn3rdPartyReceive FPS from third parties
FasterPaymentsOut1stPartySend FPS to own accounts
FasterPaymentsOut3rdPartySend FPS to third parties
SpeiAccountMexican SPEI bank account
SpeiIn1stPartyReceive SPEI from own accounts
SpeiIn3rdPartyReceive SPEI from third parties
SpeiOut1stPartySend SPEI to own accounts
SpeiOut3rdPartySend SPEI to third parties

Card Capabilities

CapabilityDescription
VisaVirtualCardIssue virtual Visa cards
VisaPlasticCardIssue physical Visa cards
CardTransferPush to card transfers

Other Capabilities

CapabilityDescription
PixOut3rdPartySend PIX transfers (Brazil)
FpsHkOut3rdPartySend FPS-HK transfers (Hong Kong)
ImpsOut3rdPartySend IMPS transfers (India)
InstapayOut3rdPartySend Instapay transfers (Philippines)
BiFastOut3rdPartySend BI-FAST transfers (Indonesia)
NipOut3rdPartySend NIP transfers (Nigeria)
IppOut3rdPartySend IPP transfers (Thailand)
PseOut3rdPartySend PSE transfers (Colombia)

Verification Requirements

Capabilities may require specific verification levels. The verification_requirements array shows which levels are needed, ordered by the order field.

LevelDescription
N/ANo verification required
BDDBasic Due Diligence
SDDStandard Due Diligence
EDDEnhanced Due Diligence

Example:

{
  "type": "SepaOut3rdParty",
  "verification_requirements": [
    { "type": "SDD", "order": 1 }
  ]
}

This means the user must have SDD verification approved to use this capability.


Prerequisites

Some capabilities require other capabilities to be active first. The prerequisites array lists the capability types that must be active.

Example:

{
  "type": "SepaOut1stParty",
  "prerequisites": ["SepaAccount"]
}

This means SepaAccount must be active before the user can activate SepaOut1stParty.


Evaluating Capabilities

Check if Feature is Available

function canUseFeature(capabilities, featureType) {
  const capability = capabilities.find(c => c.type === featureType);
  if (!capability) return false;
  return capability.status === 'Active';
}

// Usage
const user = await getUser();
if (canUseFeature(user.capabilities, 'AchOut1stParty')) {
  showAchTransferButton();
}
def can_use_feature(capabilities, feature_type):
    capability = next(
        (c for c in capabilities if c['type'] == feature_type),
        None
    )
    if not capability:
        return False
    return capability['status'] == 'Active'

# Usage
user = get_user()
if can_use_feature(user.get('capabilities', []), 'AchOut1stParty'):
    show_ach_transfer_button()
func canUseFeature(capabilities []Capability, featureType string) bool {
    for _, cap := range capabilities {
        if cap.Type == featureType {
            return cap.Status == "Active"
        }
    }
    return false
}

// Usage
if canUseFeature(user.Capabilities, "AchOut1stParty") {
    showAchTransferButton()
}

Check What's Needed to Unlock

function getUnlockRequirements(capabilities, featureType) {
  const capability = capabilities.find(c => c.type === featureType);
  if (!capability) return null;

  const requirements = [];

  // Check verification
  for (const req of capability.verification_requirements) {
    requirements.push({ type: 'verification', level: req.type });
  }

  // Check prerequisites
  for (const prereq of capability.prerequisites) {
    const prereqCapability = capabilities.find(c => c.type === prereq);
    if (!prereqCapability || prereqCapability.status !== 'Active') {
      requirements.push({ type: 'prerequisite', capability: prereq });
    }
  }

  return requirements;
}
def get_unlock_requirements(capabilities, feature_type):
    capability = next(
        (c for c in capabilities if c['type'] == feature_type),
        None
    )
    if not capability:
        return None

    requirements = []

    # Check verification
    for req in capability.get('verification_requirements', []):
        requirements.append({'type': 'verification', 'level': req['type']})

    # Check prerequisites
    for prereq in capability.get('prerequisites', []):
        prereq_cap = next(
            (c for c in capabilities if c['type'] == prereq),
            None
        )
        if not prereq_cap or prereq_cap['status'] != 'Active':
            requirements.append({'type': 'prerequisite', 'capability': prereq})

    return requirements
type Requirement struct {
    Type       string `json:"type"`
    Level      string `json:"level,omitempty"`
    Capability string `json:"capability,omitempty"`
}

func getUnlockRequirements(capabilities []Capability, featureType string) []Requirement {
    var capability *Capability
    for i := range capabilities {
        if capabilities[i].Type == featureType {
            capability = &capabilities[i]
            break
        }
    }
    if capability == nil {
        return nil
    }

    var requirements []Requirement

    // Check verification
    for _, req := range capability.VerificationRequirements {
        requirements = append(requirements, Requirement{
            Type:  "verification",
            Level: req.Type,
        })
    }

    // Check prerequisites
    for _, prereq := range capability.Prerequisites {
        active := false
        for _, cap := range capabilities {
            if cap.Type == prereq && cap.Status == "Active" {
                active = true
                break
            }
        }
        if !active {
            requirements = append(requirements, Requirement{
                Type:       "prerequisite",
                Capability: prereq,
            })
        }
    }

    return requirements
}

Handle Activation Flow

async function activateCapability(capabilities, featureType) {
  const capability = capabilities.find(c => c.type === featureType);

  switch (capability.status) {
    case 'Active':
      return { ready: true };

    case 'ActivationNotStarted':
      // Call POST /api/v1/bank/accounts/activate
      if (featureType === 'AchAccount') {
        await activateBankAccount('ach');
      }
      return { activating: true };

    case 'InProgress':
      return { activating: true, message: 'Activation in progress' };

    case 'NotFulfilled':
      return {
        ready: false,
        reason: capability.status_reason,
        requirements: getUnlockRequirements(capabilities, featureType)
      };

    case 'NotAvailable':
      return { ready: false, unavailable: true };
  }
}
async def activate_capability(capabilities, feature_type):
    capability = next(
        (c for c in capabilities if c['type'] == feature_type),
        None
    )
    if not capability:
        return {'ready': False, 'unavailable': True}

    status = capability['status']

    if status == 'Active':
        return {'ready': True}

    if status == 'ActivationNotStarted':
        # Call POST /api/v1/bank/accounts/activate
        if feature_type == 'AchAccount':
            await activate_bank_account('ach')
        return {'activating': True}

    if status == 'InProgress':
        return {'activating': True, 'message': 'Activation in progress'}

    if status == 'NotFulfilled':
        return {
            'ready': False,
            'reason': capability.get('status_reason'),
            'requirements': get_unlock_requirements(capabilities, feature_type)
        }

    if status == 'NotAvailable':
        return {'ready': False, 'unavailable': True}

    return {'ready': False}
type ActivationResult struct {
    Ready        bool          `json:"ready"`
    Activating   bool          `json:"activating,omitempty"`
    Message      string        `json:"message,omitempty"`
    Reason       string        `json:"reason,omitempty"`
    Unavailable  bool          `json:"unavailable,omitempty"`
    Requirements []Requirement `json:"requirements,omitempty"`
}

func activateCapability(capabilities []Capability, featureType string) ActivationResult {
    var capability *Capability
    for i := range capabilities {
        if capabilities[i].Type == featureType {
            capability = &capabilities[i]
            break
        }
    }
    if capability == nil {
        return ActivationResult{Unavailable: true}
    }

    switch capability.Status {
    case "Active":
        return ActivationResult{Ready: true}

    case "ActivationNotStarted":
        // Call POST /api/v1/bank/accounts/activate
        if featureType == "AchAccount" {
            activateBankAccount("ach")
        }
        return ActivationResult{Activating: true}

    case "InProgress":
        return ActivationResult{Activating: true, Message: "Activation in progress"}

    case "NotFulfilled":
        return ActivationResult{
            Reason:       capability.StatusReason,
            Requirements: getUnlockRequirements(capabilities, featureType),
        }

    case "NotAvailable":
        return ActivationResult{Unavailable: true}
    }

    return ActivationResult{}
}

Common Patterns

Feature Gating

Before showing a feature in your UI, check if the user has the required capability:

  1. Retrieve user with capabilities via GET /api/v2/user
  2. Find the relevant capability in the capabilities array
  3. If status is Active: show the feature
  4. If status is ActivationNotStarted: show activation prompt
  5. If status is NotFulfilled: show requirements (KYC, prerequisites)
  6. If status is NotAvailable: hide the feature

Progressive Disclosure

Use capabilities to progressively unlock features as users complete verification:

  1. After registration: capabilities are NotFulfilled until KYC is completed
  2. After BDD completion: check which capabilities became ActivationNotStarted
  3. After SDD completion: check for newly available capabilities
  4. Present activation prompts for capabilities ready to activate

Prerequisite Chains

Some capabilities form chains. For example with ACH:

  1. User completes SDD verification → AchAccount becomes ActivationNotStarted
  2. User activates ACH account via POST /api/v1/bank/accounts/activateAchAccount becomes Active
  3. AchOut1stParty was blocked by prerequisite → now becomes Active
  4. User can send ACH transfers