Onboard as a developer by following the steps below:
Onboarding Flow
Register Your Application
Start by registering your application in the sandbox environment:
curl -X POST ${ AUTH_BASE_URL } /dcr \
-H "Content-Type: application/json" \
-d '{
"username": "john.doe@example.com",
"password": "password",
"app_name": "My Open Banking App",
"app_description": "A test application for Open Banking integration",
"app_developer_name": "Fantastic Fintech Ltd",
"app_developer_address": "Plot 1354, Okokomaiko, Lagos",
"app_developer_contact_email": "john.doe@example.com",
"app_developer_contact_phone": "+2348012345678",
"redirect_url": "https://myapp.com/callback"
}'
const response = await fetch ( '${AUTH_BASE_URL}/dcr' , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
username: "john.doe@example.com" ,
password: "password" ,
app_name: "My Open Banking App" ,
app_description: "A test application for Open Banking integration" ,
app_developer_name: "Fantastic Fintech Ltd" ,
app_developer_address: "Plot 1354, Okokomaiko, Lagos" ,
app_developer_contact_email: "john.doe@example.com" ,
app_developer_contact_phone: "+2348012345678" ,
redirect_url: "https://myapp.com/callback"
})
});
const credentials = await response . json ();
console . log ( 'Client credentials:' , credentials );
import requests
response = requests.post( '$ {AUTH_BASE_URL} /dcr' , json = {
"username" : "john.doe@example.com" ,
"password" : "password" ,
"app_name" : "My Open Banking App" ,
"app_description" : "A test application for Open Banking integration" ,
"app_developer_name" : "Fantastic Fintech Ltd" ,
"app_developer_address" : "Plot 1354, Okokomaiko, Lagos" ,
"app_developer_contact_email" : "john.doe@example.com" ,
"app_developer_contact_phone" : "+2348012345678" ,
"redirect_url" : "https://myapp.com/callback"
})
credentials = response.json()
print ( 'Client credentials:' , credentials)
Username is unique, once taken cannot be re-used.
Sandbox Environment Sample Response In sandbox, you’ll receive immediate approval with full credentials:
{
"status" : "APPROVED" ,
"client_credentials" : {
"client_id" : "client_id" ,
"client_secret" : "client_secret" ,
"client_name" : "My Open Banking App" ,
"redirect_url" : "https://myapp.com/callback" ,
"participant_id" : "participant_id" ,
"connection_id" : "connection_id" ,
"auth_base_url" : "http://identity.sparkle.fyi" ,
"api_base_url" : "http://api.avidrone.fyi"
}
}
Production Environment Sample Response In production, you’ll receive a pending approval status with a registration token:
{
"status" : "PENDING_APPROVAL" ,
"client_credentials" : {
"client_id" : null ,
"client_secret" : null ,
"client_name" : "My Open Banking App" ,
"redirect_url" : "https://myapp.com/callback" ,
"participant_id" : null ,
"connection_id" : null ,
"auth_base_url" : "https://identity.sparkle.fyi" ,
"api_base_url" : "https://api.sparkle.fyi"
}
}
After approval, use the username/password basic authorization to retrieve your credentials via the Get Client Credentials (GET /dcr) endpoint.
Retrieve Your Application Credentials (Production only)
In production environment, the call to register an application returns PENDING_APPROVAL. Once approved, call the GET /dcr API to retrieve application credentials:
curl -X GET ${ AUTH_BASE_URL } /dcr \
-H "Authorization: Basic base64(username:password)"
const response = await fetch ( '${AUTH_BASE_URL}/dcr' , {
method: 'GET' ,
headers: {
'Authorization' : 'Basic base64(username:password)'
}
});
const credentials = await response . json ();
console . log ( 'credentials:' , credentials );
Production Environment Sample Response
{
"status" : "APPROVED" ,
"client_credentials" : {
"client_id" : "client_id" ,
"client_secret" : "client_secret" ,
"client_name" : "My Open Banking App" ,
"redirect_url" : "https://myapp.com/callback" ,
"participant_id" : "participant_id" ,
"connection_id" : "connection_id" ,
"auth_base_url" : "https://identity.sparkle.fyi" ,
"api_base_url" : "https://api.avidrone.fyi"
}
}
Test OAuth Flow
Test the complete OAuth authentication flow using different methods:
PKCE Flow
Authorization Code Flow
Device Code Authorization
// Generate PKCE challenge
const generatePKCE = () => {
const codeVerifier = crypto . randomUUID ();
const codeChallenge = btoa ( codeVerifier );
return { codeVerifier , codeChallenge };
};
// Redirect to authorization
const { codeVerifier , codeChallenge } = generatePKCE ();
const authUrl = ` ${ AUTH_BASE_URL } /oauth/authorize?` +
`client_id= ${ credentials . client_id } &` +
`redirect_uri= ${ encodeURIComponent ( credentials . redirect_url ) } &` +
`response_type=code&` +
`scope=customers.readonly accounts.list.readonly&` +
`code_challenge= ${ codeChallenge } &` +
`code_challenge_method=S256` ;
// Store code verifier for token exchange
sessionStorage . setItem ( 'code_verifier' , codeVerifier );
window . location . href = authUrl ;
// Handle callback and exchange code for token
const handleCallback = async ( code ) => {
const codeVerifier = sessionStorage . getItem ( 'code_verifier' );
const tokenResponse = await fetch ( ` ${ AUTH_BASE_URL } /oauth/token` , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/x-www-form-urlencoded' ,
'Authorization' : `Basic ${ btoa ( ` ${ credentials . client_id } : ${ credentials . client_secret } ` ) } `
},
body: new URLSearchParams ({
grant_type: 'authorization_code' ,
code: code ,
redirect_uri: credentials . redirect_url ,
code_verifier: codeVerifier
})
});
const tokens = await tokenResponse . json ();
console . log ( 'Access token:' , tokens . access_token );
return tokens ;
};
// Step 1: Redirect to authorization
const authUrl = ` ${ AUTH_BASE_URL } /oauth/authorize?` +
`client_id= ${ credentials . client_id } &` +
`redirect_uri= ${ encodeURIComponent ( credentials . redirect_url ) } &` +
`response_type=code&` +
`scope=customers.readonly accounts.list.readonly&` +
`state= ${ crypto . randomUUID () } ` ;
window . location . href = authUrl ;
// Step 2: Handle callback and exchange code for token
const handleCallback = async ( code ) => {
const tokenResponse = await fetch ( ` ${ AUTH_BASE_URL } /oauth/token` , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/x-www-form-urlencoded' ,
'Authorization' : `Basic ${ btoa ( ` ${ credentials . client_id } : ${ credentials . client_secret } ` ) } `
},
body: new URLSearchParams ({
grant_type: 'authorization_code' ,
code: code ,
redirect_uri: credentials . redirect_url
})
});
const tokens = await tokenResponse . json ();
console . log ( 'Access token:' , tokens . access_token );
return tokens ;
};
// Step 3: Refresh token when needed
const refreshToken = async ( refreshToken ) => {
const response = await fetch ( ` ${ AUTH_BASE_URL } /oauth/token` , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/x-www-form-urlencoded' ,
'Authorization' : `Basic ${ btoa ( ` ${ credentials . client_id } : ${ credentials . client_secret } ` ) } `
},
body: new URLSearchParams ({
grant_type: 'refresh_token' ,
refresh_token: refreshToken
})
});
const tokens = await response . json ();
console . log ( 'New access token:' , tokens . access_token );
return tokens ;
};
// Step 1: Request device authorization
const requestDeviceCode = async () => {
const response = await fetch ( ` ${ AUTH_BASE_URL } /oauth2/device/code` , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/x-www-form-urlencoded'
},
body: new URLSearchParams ({
scope: 'customers.readonly accounts.list.readonly' ,
client_id: '${client_id}' ,
target_connection_id: '${target_connection_id}'
})
});
const deviceCode = await response . json ();
console . log ( 'Device code:' , deviceCode . device_code );
console . log ( 'User code:' , deviceCode . user_code );
console . log ( 'Verification URI:' , deviceCode . verification_uri );
return deviceCode ;
};
// Step 2: Poll for token
const pollForToken = async ( deviceCode , interval = 5000 ) => {
const pollInterval = setInterval ( async () => {
try {
const response = await fetch ( ` ${ AUTH_BASE_URL } /oauth/token` , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/x-www-form-urlencoded'
},
body: new URLSearchParams ({
grant_type: 'urn:ietf:params:oauth:grant-type:device_code' ,
device_code: deviceCode . device_code ,
client_id: $ { client_id }
})
});
if ( response . status === 200 ) {
const tokens = await response . json ();
console . log ( 'Access token received:' , tokens . access_token );
clearInterval ( pollInterval );
return tokens ;
} else if ( response . status === 400 ) {
const error = await response . json ();
if ( error . error === 'authorization_pending' ) {
console . log ( 'Waiting for user authorization...' );
} else if ( error . error === 'authorization_declined' ) {
console . log ( 'User declined authorization' );
clearInterval ( pollInterval );
} else if ( error . error === 'expired_token' ) {
console . log ( 'Device code expired' );
clearInterval ( pollInterval );
}
}
} catch ( error ) {
console . error ( 'Polling error:' , error );
clearInterval ( pollInterval );
}
}, interval );
};
For device code authorization, display the instruction returned obn_custom_metadata.consent_message field in the response json
Sample Response for Device Code Authorization
{
"user_code" : "{user_code}" ,
"device_code" : "{device_code}" ,
"verification_uri" : "https://idp.sparkle.fyi/device/verification" ,
"interval" : 5 ,
"expires_in" : 600 ,
"obn_custom_metadata" : {
"consent_validation_method" : "USSD" ,
"consent_message" : "please dial *556*{user_code}# and follow instruction to give consent to My Open Banking App" ,
"consent_status" : "PENDING_VALIDATION"
}
}
Test API Endpoints
Once you have an access token, test various API endpoints:
# Get accounts
curl -X GET ${ API_BASE_URL } /accounts \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "idempotency_key: IDEMPOTENCY_KEY" \
-H "signature: SIGNATURE"
// Test account information
const accountsResponse = await fetch ( '${API_BASE_URL}/accounts' , {
headers: {
'Authorization' : `Bearer ${ accessToken } ` ,
'idempotency_key' : $idempotency_key ,
'signature' : $signature
}
});
const accounts = await accountsResponse . json ();
console . log ( 'Test accounts:' , accounts );
Next Steps
✅ Build your application
✅ Move to production
Moving to Production
Once you’ve thoroughly tested in sandbox:
Register in Production : Use the same application details
Wait for Approval : Production registration requires review (24-48 hours). You will be informed once successfully registered. Alternative you can use the Get Client Credetnials API to get your client credentials
Update Configuration : Switch to production endpoints
Test Again : Verify everything works in production
Go Live : Deploy your application
Always test thoroughly in sandbox before moving to production. The sandbox environment is designed to catch issues early and prevent problems in production.