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
Updated 8 days ago
