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

Diagnosing a Blocked Capability

status_reason carries the specific cause and is the field to log. Work outward from it:

status_reasonCauseWhat unblocks it
Verification requirements not fulfilledA required verification level is not approvedCompare verification_requirements against the user's passed levels, then complete the missing level — see KYC
User verification is not approvedKYC has not been approvedWait for approval, or resolve the rejection
User profile is not activeThe user's profile status is not ActiveWirex advances it; no partner call changes this
Prerequisite capability is not active yetA prerequisite exists but is not ActiveFind each entry of prerequisites in the same response and diagnose that one first
Prerequisite capability is not availableA prerequisite is not offered at all in this countryNo API call changes this
Account should be activated in order to use this capabilityNo bank account details of this type exist yetCall the account activation endpoint — see Bank Accounts
Account activation is in progressDetails exist but are still PendingPoll GET /api/v1/bank/accounts until they are Active
Registration with the external provider is still pendingThe external provider has not finished onboarding the userWait; the capability moves on its own
You details are being createdAccount details are being provisionedWait and re-read
This capability is no longer available for this accountDetails of this type exist but none is Active or PendingContact Wirex
Country is not supportedThe product is not offered where the user is registeredNo API call changes this
Capability is not available for this userA per-user override disables itContact Wirex
Metal card issuance requires the Private or Bespoke reward tierThe user's reward tier is too low for a metal cardIssue a virtual or plastic card instead
You have reached the maximum number of virtual cards allowedThe per-user ceiling for that format is reachedClose a card before issuing another
You have reached the maximum number of plastic cards allowedAs aboveAs above
You have reached the maximum number of metal cards allowedAs aboveAs above

Card ceilings are configured per company, not fixed by the platform — the limit that applies to
your users comes from your card configuration. Read the capability rather than assuming a number, and
contact Wirex to change it.

A capability check that fails at one stage is never evaluated at the next, so status_reason names
the first unmet condition, not every unmet condition. Re-read after each fix.


Did this page help you?