Introduction
Let's get you started with Citadel by walking through this Quickstart app. You'll need a set of API keys which you can get by signing up at https://dashboard.citadelid.com
You'll have two different API keys used by the back end, client_id
and access_key
.
Set up the Quickstart
Follow the README.md
file for the language you would like to implement in. If you don't see the language you are working with, send an email to developer-relations@citadelid.com
Run your first verification
Overview
The Quickstart app emulates the experience of an applicant going through a background check/income verification and visiting the applicant portal.
Before using Citadel for verification, an applicant fills out the form.
To streamline the process and make employment/income verification easy and instant, we "hide" the form behind the button.
If the verification is successful via Citadel, then we show to the applicant the data that we found in their payroll account.
If the verification isn't successful or the applicant decided to exit Citadel's widget, the applicant will see the form, fill it out and the verification can be done via an existing verification process.
Successful verification
After opening the Quickstart app running locally, click the Verify employment
/Verify income
button, search for a company, (e.g., Facebook
) and select a provider.
Use the Sandbox credentials to simulate a successful login. If you are performing an employment or income verification, use the following credentials:
username: goodlogin
password: goodpassword
If you are performing an admin function, use the following API key:
Skx8LTnyrLiw4SYk8xfkRwOt5OGQbNulypqdsqd
Once you have entered your credentials and moved to the next screen, you have succesfully done your first verification.
The API call will be executed and the data will be loaded into the form.
No verification
Now click the Add employer
button, search for a company, eg Facebook
and select any provider.
Click the exit icon at the top right of the widget and you'll see the empty form.
What happened under the hood
- 😃 = User
- 💻 = Front End/Client App
- ☁️ = Back End/Server
Here is the flow that a successful verification process takes in our example. The below code will be shown in Python but each language has it's own
examples in the respective README.md
files:
- 💻 sends request to ☁️ for
bridge_token
- ☁️ sends API request to Citadel for
bridge_token
, sends response to 💻 - 💻 runs
CitadelBridge.init
withbridge_token
- 😃 clicks
Verify Income/
Verify Employment` button - 💻 displays Citadel widget, fires
onLoad
function executed - 😃 selects employer, choses provider, logs in, clicks
Done
- 💻 first onSuccess function, sends request to ☁️ with temporary
token
, closes widget, firstonClose
- ☁️ sends API request to Citadel exchanging temporary
token
foraccess_token
- ☁️ sends API request to Citadel with
access_token
for employment/income verification - ☁️ sends employment/income verification information back to 💻
- 💻 renders the verification info sent back by ☁️ for 😃 to view
1. 💻 sends request to ☁️ for bridge_token
const getBridgeToken = async () => {
const response = await fetch(apiEnpoint + `getBridgeToken`, {
method: 'get',
headers,
}).then((r) => r.json());
return response;
}
2. ☁️ sends API request to Citadel for bridge_token
, sends response to 💻
def get_bridge_token(self) -> Any:
"""
https://docs.citadelid.com/?python#bridge-tokens_create
:param public_token:
:return:
"""
tokens: Any = requests.post(
self.API_URL + 'bridge-tokens/',
headers=self.API_HEADERS,
).json()
return tokens
@app.route('/getBridgeToken', methods=['GET'])
def create_bridge_token():
return api_client.get_bridge_token()
3. 💻 runs CitadelBridge.init
with bridge_token
const bridge = CitadelBridge.init({
clientName: 'Citadel NodeJS Quickstart',
bridgeToken: bridgeToken.bridge_token,
product: 'income',
trackingInfo: 'any data for tracking current user',
...
});
window.bridge = bridge;
4. 😃 clicks Verify Income/
Verify Employment` button
5. 💻 displays Citadel widget, fires onLoad
function executed
onLoad: function () {
console.log('loaded');
successClosing = null
},
6. 😃 selects employer, choses provider, logs in, clicks Done
7. 💻 first onSuccess function, sends request to ☁️ with temporary token
, closes widget, first onClose
onSuccess: async function (token) {
console.log('token: ', token);
successClosing = true
const content = document.querySelector('.spinnerContainer');
content.classList.remove('hidden');
let verificationInfo;
try {
verificationInfo = await apiRequests.getVerificationInfoByToken(token);
} catch(e) {
console.error(e)
content.classList.add('hidden');
return;
}
content.classList.add('hidden');
if (!verificationInfo.length) {
return;
}
setUserInfo(verificationInfo[0]);
renderEmploymentHistory(verificationInfo);
},
...
onClose: function () {
console.log('closed');
if (successClosing !== true) {
renderEmploymentHistory([{ company: { address: {} } }]);
}
},
8. ☁️ sends API request to Citadel exchanging temporary token
for access_token
def get_access_token(self, public_token: str) -> str:
"""
https://docs.citadelid.com/?python#exchange-token-flow
:param public_token:
:return:
"""
class AccessTokenRequest(TypedDict):
public_tokens: List[str]
class AccessTokenResponse(TypedDict):
access_tokens: List[str]
request_data: AccessTokenRequest = {
'public_tokens': [public_token],
}
tokens: AccessTokenResponse = requests.post(
self.API_URL + 'access-tokens/',
json=request_data,
headers=self.API_HEADERS,
).json()
return tokens['access_tokens'][0]
9. ☁️ sends API request to Citadel with access_token
for employment/income verification
def get_employment_info_by_token(self, access_token: str) -> Any:
"""
https://docs.citadelid.com/#employment-verification
:param access_token:
:return:
"""
class VerificationRequest(TypedDict):
access_token: str
request_data: VerificationRequest = {'access_token': access_token}
return requests.post(
self.API_URL + 'verifications/employments/',
json=request_data,
headers=self.API_HEADERS,
).json()
def get_income_info_by_token(self, access_token: str) -> Any:
"""
https://docs.citadelid.com/#income-verification
:param access_token:
:return:
"""
class VerificationRequest(TypedDict):
access_token: str
request_data: VerificationRequest = {'access_token': access_token}
return requests.post(
self.API_URL + 'verifications/incomes/',
json=request_data,
headers=self.API_HEADERS,
).json()
10. ☁️ sends employment/income verification information back to 💻
@app.route('/getVerifications/<public_token>', methods=['GET'])
def get_verification_info_by_token(public_token: str):
""" getVerificationInfoByToken """
# First exchange public_token to access_token
access_token = api_client.get_access_token(public_token)
# Use access_token to retrieve the data
if product_type == 'employment':
verifications = api_client.get_employment_info_by_token(access_token)
elif product_type == 'income':
verifications = api_client.get_income_info_by_token(access_token)
else:
raise Exception('Unsupported product type!')
return verifications
11. 💻 renders the verification info sent back by ☁️ for 😃 to view
function renderEmploymentHistory(employments) {
const result = employments.map(createEmploymentCard).reduce((acc, cur) => {
acc.appendChild(cur);
return acc;
}, document.createDocumentFragment());
const historyContainer = document.querySelector('#history');
historyContainer.appendChild(result);
const button = document.getElementById('verify-button')
button.style.display = 'none'
}