Capabilities
Understand and evaluate user capabilities for feature access.
Before You Start
Read the following guides before proceeding:
| Guide | Why |
|---|---|
| Getting Started | Platform overview and setup |
| Api Basics | Required headers and request configuration |
| Authentication | How 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
| Field | Type | Description |
|---|---|---|
type | string | Capability identifier |
status | string | Current capability status |
status_reason | string | Explanation when status is not Active (optional) |
verification_requirements | array | Required KYC levels |
prerequisites | array | Other capability types that must be active first |
Capability Statuses
| Status | Description | User Action |
|---|---|---|
Active | Capability is available for use | Feature is accessible |
ActivationNotStarted | Requirements met, activation needed | Call activation endpoint |
InProgress | Activation in progress | Wait for completion |
NotFulfilled | Requirements not met | Complete KYC or prerequisites |
NotAvailable | Not available for this user | Cannot 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.
| Capability | Description |
|---|---|
SepaAccount | Ability to have a SEPA bank account (IBAN) |
SepaIn1stParty | Receive SEPA transfers from own accounts |
SepaIn3rdParty | Receive SEPA transfers from third parties |
SepaOut1stParty | Send SEPA transfers to own accounts |
SepaOut3rdParty | Send SEPA transfers to third parties |
AchAccount | Ability to have an ACH bank account |
AchIn1stParty | Receive ACH transfers from own accounts |
AchIn3rdParty | Receive ACH transfers from third parties |
AchOut1stParty | Send ACH transfers to own accounts |
AchOut3rdParty | Send ACH transfers to third parties |
FasterPaymentsAccount | UK Faster Payments bank account |
FasterPaymentsIn1stParty | Receive FPS from own accounts |
FasterPaymentsIn3rdParty | Receive FPS from third parties |
FasterPaymentsOut1stParty | Send FPS to own accounts |
FasterPaymentsOut3rdParty | Send FPS to third parties |
SpeiAccount | Mexican SPEI bank account |
SpeiIn1stParty | Receive SPEI from own accounts |
SpeiIn3rdParty | Receive SPEI from third parties |
SpeiOut1stParty | Send SPEI to own accounts |
SpeiOut3rdParty | Send SPEI to third parties |
Card Capabilities
| Capability | Description |
|---|---|
VisaVirtualCard | Issue virtual Visa cards |
VisaPlasticCard | Issue physical Visa cards |
CardTransfer | Push to card transfers |
Other Capabilities
| Capability | Description |
|---|---|
PixOut3rdParty | Send PIX transfers (Brazil) |
FpsHkOut3rdParty | Send FPS-HK transfers (Hong Kong) |
ImpsOut3rdParty | Send IMPS transfers (India) |
InstapayOut3rdParty | Send Instapay transfers (Philippines) |
BiFastOut3rdParty | Send BI-FAST transfers (Indonesia) |
NipOut3rdParty | Send NIP transfers (Nigeria) |
IppOut3rdParty | Send IPP transfers (Thailand) |
PseOut3rdParty | Send 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.
| Level | Description |
|---|---|
N/A | No verification required |
BDD | Basic Due Diligence |
SDD | Standard Due Diligence |
EDD | Enhanced 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 requirementstype 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:
- Retrieve user with capabilities via
GET /api/v2/user - Find the relevant capability in the
capabilitiesarray - If status is
Active: show the feature - If status is
ActivationNotStarted: show activation prompt - If status is
NotFulfilled: show requirements (KYC, prerequisites) - If status is
NotAvailable: hide the feature
Progressive Disclosure
Use capabilities to progressively unlock features as users complete verification:
- After registration: capabilities are
NotFulfilleduntil KYC is completed - After BDD completion: check which capabilities became
ActivationNotStarted - After SDD completion: check for newly available capabilities
- Present activation prompts for capabilities ready to activate
Prerequisite Chains
Some capabilities form chains. For example with ACH:
- User completes SDD verification →
AchAccountbecomesActivationNotStarted - User activates ACH account via
POST /api/v1/bank/accounts/activate→AchAccountbecomesActive AchOut1stPartywas blocked by prerequisite → now becomesActive- 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_reason | Cause | What unblocks it |
|---|---|---|
Verification requirements not fulfilled | A required verification level is not approved | Compare verification_requirements against the user's passed levels, then complete the missing level — see KYC |
User verification is not approved | KYC has not been approved | Wait for approval, or resolve the rejection |
User profile is not active | The user's profile status is not Active | Wirex advances it; no partner call changes this |
Prerequisite capability is not active yet | A prerequisite exists but is not Active | Find each entry of prerequisites in the same response and diagnose that one first |
Prerequisite capability is not available | A prerequisite is not offered at all in this country | No API call changes this |
Account should be activated in order to use this capability | No bank account details of this type exist yet | Call the account activation endpoint — see Bank Accounts |
Account activation is in progress | Details exist but are still Pending | Poll GET /api/v1/bank/accounts until they are Active |
Registration with the external provider is still pending | The external provider has not finished onboarding the user | Wait; the capability moves on its own |
You details are being created | Account details are being provisioned | Wait and re-read |
This capability is no longer available for this account | Details of this type exist but none is Active or Pending | Contact Wirex |
Country is not supported | The product is not offered where the user is registered | No API call changes this |
Capability is not available for this user | A per-user override disables it | Contact Wirex |
Metal card issuance requires the Private or Bespoke reward tier | The user's reward tier is too low for a metal card | Issue a virtual or plastic card instead |
You have reached the maximum number of virtual cards allowed | The per-user ceiling for that format is reached | Close a card before issuing another |
You have reached the maximum number of plastic cards allowed | As above | As above |
You have reached the maximum number of metal cards allowed | As above | As 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_reasonnames
the first unmet condition, not every unmet condition. Re-read after each fix.
Updated 20 days ago

