# API specs of Markets data sharing
This page describes API specs that Markets need to expose for data sharing. If you do not use the data sharing feature, you can skip this page.
# Overview
The following information needs to be provided by a Market to Credify for Market integration.
- List of basic profile data
- Definition of custom data scope
Endpoints:
- Push claims API endpoint
- User segment API endpoint
- Offer evaluation API endpoint
- Encryption claims API endpoint
- Offer filtering API endpoint
These API endpoints need to be registered on Dashboard, to add the endpoints, please go to Settings -> General information -> Update -> Account details -> update API Base Url
TIP
We have developed a skeleton service to minimize the integration work, which handles most common logic and SDK interaction. We provide the skeleton service in Node.js as of May 2022. All you would have to do is to fork the repository and to customize some database interaction only.
The following sections relating to the backend implementation are not necessary if you use the skeleton service.
# List of basic profile data
Each Market needs to set what piece of profile information it can provide with Service Providers. Here is a list of data points:
phone
- e.g.,+84381234567
email
- e.g.,test@credify.one
address
- e.g.,1st Floor, Savimex Building, 194 Đ. Nguyễn Công Trứ, Phường Nguyễn Thái Bình, Quận 1, Thành phố Hồ Chí Minh
gender
- e.g.,male
/female
dob
- e.g.,1991-12-25
name
- e.g.,Nguyển Minh Long
If a Market does not provide any of user data, then it can be empty list.
# Definition of custom data scope
Scope definition is a custom data schema that reflects a user's personal information outside of basic profile data that a Market will share with Financial Institutions. An example of custom data scope would be an end user's monthly transaction. The users need to agree to share these data for the data transmission to go through. By doing so, users can get better financial product as it leverages their data to improve their credibility. Financial Institutions can filter scope definition to find which users fit their target.
This property is optional. You may provide this information to enhance UX to offer financial products for right user segments.
# Push claims API endpoint
This API is used to verify that a Market has data about a specific user. When a user logs in to your platform with their Credify account, you can call this API with their internal ID in your system. The API will then map this ID with the user's unique identifier in the ServiceX system, verifying that the user has consented to sharing their data with your Market.
# API definition
# Implementation
Here is what we need you to do in this API endpoint
- Extract the internal user ID and their Credify ID from the API request payload.
- Save the mapping between the internal user ID and their Credify ID in your database.
- Compose a claim object with the user's data stored in your database.
- Generate a random string value for each scope and save it for later use.
- Authenticate a client by calling the Client Auth API with an API key.
- Generate a claim token (certificate) and send it via the Update Claim API along with the access token obtained from step 5.
- Return a 200 status code (response body is not necessary).
To implement these steps, you need to find the user associated with the internal ID passed from the request, save their Credify ID along with the user, and save the commitments returned by the SDK's function in your database.
A claim token is a JWT whose payload is following.
scope_name: string
- Unique scope name registered in serviceX Dashboard
- In case of standard claims, this will be predefined, such as
profile
,phone
,email
, andaddress
.
scope_hash: string
- sha256 hashed value, like
CREDIT_SCORE_SCOPE_HASH
/EMAIL_SCOPE_HASH
in the above description.
- sha256 hashed value, like
timestamp: integer
- Unix timestamp in sec
user_claim_token: string
- JWT to ensure this data attachment is coming from a Market. Its payload is
provider_id
,user_id
,scope_name
,scope_hash
, andtimestamp
, likeCREDIT_SCORE_CLAIM_TOKEN_PAYLOAD
/EMAIL_CLAIM_TOKEN_PAYLOAD
.
- JWT to ensure this data attachment is coming from a Market. Its payload is
Here is a sample.
// In the case of your `custom` claims
const CREDIT_SCORE_SCOPE_DATA = {
YOUR_CLAIM_A: "CLAIM_A_VALUE",
YOUR_CLAIM_B: "CLAIM_B_VALUE",
YOUR_CLAIM_C: "CLAIM_C_VALUE",
"credit-score:commitment": "SECRET_RANDOM_COMMITMEMT",
};
const CREDIT_SCORE_SCOPE_HASH = sha256(JSON(CREDIT_SCORE_SCOPE_DATA));
const CREDIT_SCORE_CLAIM_TOKEN_PAYLOAD = {
scope_name: "credit-score",
user_id: "a6367e50-2d4c-...", // passed from the mobile SDK
scope_hash: CREDIT_SCORE_SCOPE_HASH,
timestamp: 1612197298,
};
// In the case of your `standard` claims
const EMAIL_SCOPE_DATA = {
email: "test@credify.one",
email_commitment: "SECRET_RANDOM_COMMITMEMT",
};
const EMAIL_SCOPE_HASH = sha256(JSON(CREDIT_SCORE_SCOPE_DATA));
const EMAIL_CLAIM_TOKEN_PAYLOAD = {
scope_name: "email",
user_id: "a6367e50-2d4c-...", // passed from the mobile SDK
scope_hash: EMAIL_SCOPE_HASH,
timestamp: 1612197298,
};
- Node.js
- Java
- .NET
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const { Credify } = require("@credify/nodejs");
// You obtain these on the serviceX dashboard.
const signingPrivateKey = `-----BEGIN PRIVATE KEY-----
your private key...
-----END PRIVATE KEY-----`;
const apiKey = "YOUR_API_KEY";
const id = "YOUR_ORGANIZATION_ID";
const app = express();
app.use(bodyParser.json());
app.use(cors());
app.post("/api/claims/push", async (req, res) => {
// mode is `sandbox` or `production`
const credify = await Credify.create(signingPrivateKey, apiKey, { mode: "sandbox" });
const internalId = req.body.id;
const credifyId = req.body.credify_id;
// 1. Find a user with `internalId`
const user = ...;
// 2. Update the user to save `credifyId` in your DB
// 3. Compose a claim object with this user's data stored in your DB
const claims = ...;
// This is an example:
// const claims = {
// "phone": {
// "phoneNumber": "+84381234567"
// },
// "credit-score": {
// "scope": 124,
// "fraud": false,
// "updatedAt": "2021-02-16 10:25:14.599+07"
// },
// "transactions": {
// "totalCount": 142,
// "totalPaymentAmount": 8911,
// "currency": "usd"
// }
// }
// 4. Send the digest data of the claims and obtain the commitments
const commitments = await credify.claims.push(id, credifyId, claims);
// 5. Save the commitments in your DB
return res.status(200).json(response);
});
# User segment API endpoint
This API is used to retrieve user segments based on specified thresholds. When Service Providers configure offers in Credify dashboard, they can set thresholds for certain criteria (e.g. credit score, income, etc.). The User Segment API can then be used to check how many users in your system fall under each threshold. This can help Service Providers better target their offers to users who meet their criteria.
# API definition
# Implementation
What you have to do by yourself is
- Validate access token attached in the API request authorization header
- Extract offer conditions from our API request payload
- Count the number of users who meet the extracted offer conditions
- Return the counts
- Node.js
- Java
- .NET
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const { Credify } = require("@credify/nodejs");
// You obtain these on the serviceX dashboard.
const signingPrivateKey = `-----BEGIN PRIVATE KEY-----
your private key...
-----END PRIVATE KEY-----`;
const apiKey = "YOUR_API_KEY";
const app = express();
app.use(bodyParser.json());
app.use(cors());
app.post("/api/offers/segment", async (req, res) => {
// mode is `sandbox` or `production`
const credify = await Credify.create(signingPrivateKey, apiKey, {
mode: "sandbox",
});
// Provided you have `extractToken` function to obtain Bearer access token from Authorization header of the request.
const token = extractToken(req);
const isValidToken = await credify.auth.introspectToken(
token,
"oidc_client:read_user_counts"
);
if (!isValidToken) {
return res.status(401).send({ message: "Unauthorized" });
}
const conditions = req.body.conditions;
if (!conditions) {
return res.status(400).send({ message: "Invalid body" });
}
// Your current users
const users = ...
// do your calculation with `conditions`
let counts = Array(conditions.length).fill(0)
await conditions.forEach(async (c, index) => {
if (Object.keys(c).length === 0) {
counts[index] = users.length
} else {
await users.forEach(async (u) => {
// You user's claim composed object
const userClaims = ...
//*** Our SDK already supports offer evaluation for you
const res = await credify.offer.evaluateOffer(
[c],
requiredCustomScopes,
userClaims
)
if (res.rank === 1) {
counts[index] += 1
}
})
}
})
const response = {
data: {
counts,// return the calculation result
},
};
res.json(response);
});
# Offer evaluation API endpoint
We use this endpoint when finalizing offer redemption. This evaluation result will be sent to the Service Provider for them to give right benefits to users.
# API definition
# Implementation
What you have to do by yourself is
- Find the user that is associated with the credify ID passed from the request
- Evaluate the user's level based on their profile and provided offer conditions
- Return the evaluation result
- Node.js
- Java
- .NET
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const { Credify } = require("@credify/nodejs");
// You obtain these on the serviceX dashboard.
const signingPrivateKey = `-----BEGIN PRIVATE KEY-----
your private key...
-----END PRIVATE KEY-----`;
const apiKey = "YOUR_API_KEY";
const app = express();
app.use(bodyParser.json());
app.use(cors());
app.post("/api/offers/evaluate", async (req, res) => {
// mode is `sandbox` or `production`
const credify = await Credify.create(signingPrivateKey, apiKey, { mode: "sandbox" });
// Provided you have `extractToken` function to obtain Bearer access token from Authorization header of the request.
const token = extractToken(req);
const isValidToken = await credify.auth.introspectToken(token, "individual:read_evaluated_offer");
if (!isValidToken) {
return res.status(401).send({ message: "Unauthorized" });
}
if (!req.body.credify_id || !req.body.conditions || !req.body.scopes) {
return res.status(400).send({ message: "Invalid body" });
}
// 1. Use credify_id to find an user in your system associated with this ID
const user = ...;
const conditions = req.body.conditions || [{}]
const requiredCustomScopes = req.body.required_custom_scopes || []
// 2. Do your evaluation with `conditions` and `requiredCustomScopes`
// Your user's claims
const allUserClaims = ...
// This is scope that user want to share:
const sharedScopes = req.body.scopes
// The claims that user want to share
let userSharedClaims = {}
for (let scope of sharedScopes) {
userSharedClaims[scope] = allUserClaims[scope]
}
//*** Our SDK already supports offer evaluation for you
const result = await credify.offer.evaluateOffer(
conditions,
requiredCustomScopes,
userSharedClaims
)
const response = {
data: {
rank: result.rank,
used_scopes: result.usedScopes,
requested_scopes: result.requestedScopes,
}
};
res.json(response);
});
# Encrypted claims API endpoint
This API is used to encrypt user data with the Financial Institution's key and pass the encrypted data to the Financial Institution through OpenID Connect. This helps ensure that the user's data remains secure and private during transmission to the Financial Institution. The API is called after the user has agreed to share their data, and can be used to help protect sensitive information from unauthorized access.
# API definition
API doc for Encrypted claims API
# Implementation
What you have to do by yourself is
- Validate the access token from Credify using the token introspection API, which requires an API key-generated token with the appropriate scope (
organizataion
ororganization:introspect_token
scope). - Introspect the access token you get from the request to validate the
oidc_client:read_encrypted_claims
scope. - Find the user associated with the Credify ID provided in the request.
- Create a claim object from
user
data that contains request-grantedscopes
information. - Return the claim object to Credify (the claim object is encrypted before sending to Credify).
The below code is an example of how a claim object composer look like. We recommend you to follow the format of the object below so a Service Provider can decrypt the claims given from your side without any unexpected issues.
const claims = {};
claims["b09e8f99-6d89-4e7d-83ea-a43a1787b3e0:market-name-history-data"] = {
"3285592c-9aaf-4182-bff5-941ce5dac483:purchase-count": `${user.transactionsCount}`,
"3285592c-9aaf-4182-bff5-941ce5dac483:total-payment-amount": `${user.totalPaymentAmount}`,
"b09e8f99-6d89-4e7d-83ea-a43a1787b3e0:market-name-history-data:commitment": "some randon value"
};
claims["b09e8f99-6d89-4e7d-83ea-a43a1787b3e0:market-name%20score"] = {
"3285592c-9aaf-4182-bff5-941ce5dac483:market-name%20score": `${user.creditScore}`,
"b09e8f99-6d89-4e7d-83ea-a43a1787b3e0:market-name%20score:commitment": "some random value"
};
// This is the sample response object after processing the claim object
const data = {
_claim_names: {
'3285592c-9aaf-4182-bff5-941ce5dac483:purchase-count': 'b09e8f99-6d89-4e7d-83ea-a43a1787b3e0:market-name-history-data',
'3285592c-9aaf-4182-bff5-941ce5dac483:total-payment-amount': 'b09e8f99-6d89-4e7d-83ea-a43a1787b3e0:market-name-history-data',
'b09e8f99-6d89-4e7d-83ea-a43a1787b3e0:market-name-history-data:commitment': 'b09e8f99-6d89-4e7d-83ea-a43a1787b3e0:market-name-history-data',
'3285592c-9aaf-4182-bff5-941ce5dac483:market-name%20score': 'b09e8f99-6d89-4e7d-83ea-a43a1787b3e0:market-name%20score',
'b09e8f99-6d89-4e7d-83ea-a43a1787b3e0:market-name%20score:commitment': 'b09e8f99-6d89-4e7d-83ea-a43a1787b3e0:market-name%20score'
},
_claim_resources: {
'b09e8f99-6d89-4e7d-83ea-a43a1787b3e0:market-name-history-data': {
JWT: "JWE encoding the claim object"
},
'b09e8f99-6d89-4e7d-83ea-a43a1787b3e0:market-name%20score': {
JWT: "JWE encoding the claim object"
}
}
}
- Node.js
- Java
- .NET
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const { Credify } = require("@credify/nodejs");
// You obtain these on the serviceX dashboard.
const signingPrivateKey = `-----BEGIN PRIVATE KEY-----
your private key...
-----END PRIVATE KEY-----`;
const apiKey = "YOUR_API_KEY";
const app = express();
app.use(bodyParser.json());
app.use(cors());
app.post("/api/claims/request", async (req, res) => {
// mode is `sandbox` or `production`
const credify = await Credify.create(signingPrivateKey, apiKey, { mode: "sandbox" });
// Provided you have `extractToken` function to obtain Bearer access token from Authorization header of the request.
const accessToken = extractToken(req);
if (accessToken === "") {
return res.status(401).send({ message: "Unauthorized" });
}
if (!req.body.user_id || !req.body.request_token || !req.body.approval_token) {
return res.status(400).send({ message: "Invalid body" });
}
const credifyId = req.body.user_id;
const requestToken = req.body.request_token;
const approvalToken = req.body.approval_token;
const { publicKey, scopes } = await credify.claims.validateRequest(accessToken, requestToken, approvalToken);
// 1. Use `credifyId` to find an user in your system associated with `credifyId`
const user = ...;
// 2. The commitment for user claim that already created before
const commitment =
// 3. Compose a claim object with `user` and `commitment`
const claims = ...;
const encrypted = await credify.claims.encrypt(claims, publicKey)
/** sample data
{
_claim_names: {
'3285592c-9aaf-4182-bff5-941ce5dac483:purchase-count': 'b09e8f99-6d89-4e7d-83ea-a43a1787b3e0:market-name-history-data',
'3285592c-9aaf-4182-bff5-941ce5dac483:total-payment-amount': 'b09e8f99-6d89-4e7d-83ea-a43a1787b3e0:market-name-history-data',
'b09e8f99-6d89-4e7d-83ea-a43a1787b3e0:market-name-history-data:commitment': 'b09e8f99-6d89-4e7d-83ea-a43a1787b3e0:market-name-history-data',
'3285592c-9aaf-4182-bff5-941ce5dac483:market-name%20score': 'b09e8f99-6d89-4e7d-83ea-a43a1787b3e0:market-name%20score',
'b09e8f99-6d89-4e7d-83ea-a43a1787b3e0:market-name%20score:commitment': 'b09e8f99-6d89-4e7d-83ea-a43a1787b3e0:market-name%20score'
},
_claim_resources: {
'b09e8f99-6d89-4e7d-83ea-a43a1787b3e0:market-name-history-data': {
JWT: "JWE encoding the claim object"
},
'b09e8f99-6d89-4e7d-83ea-a43a1787b3e0:market-name%20score': {
JWT: "JWE encoding the claim object"
}
}
}
**/
res.json({ data: encrypted });
});
# Offer filtering API endpoint
The Offer Filtering API provides a list of available offers for each user with evaluation result. This is needed for the Credify SDK to efficiently display the offers on your platform.
# API definition
API doc for Offer filtering API
# Implementation
What you have to do by yourself is
- Find the user that is associated with the credify ID or local ID passed from the request
- Evaluate the user's level based on their profile and return this evaluation result along with the offers
- Return the list of filtered offers
- Node.js
- Java
- .NET
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const { Credify } = require("@credify/nodejs");
// You obtain these on the serviceX dashboard.
const signingPrivateKey = `-----BEGIN PRIVATE KEY-----
your private key...
-----END PRIVATE KEY-----`;
const apiKey = "YOUR_API_KEY";
const app = express();
app.use(bodyParser.json());
app.use(cors());
app.post("/api/offers/filter", async (req, res) => {
// mode is `sandbox` or `production`
const credify = await Credify.create(signingPrivateKey, apiKey, { mode: "sandbox" });
// Provided you have `extractToken` function to obtain Bearer access token from Authorization header of the request.
const token = extractToken(req);
const isValidToken = await credify.auth.introspectToken(token, "claim_provider:read_filtered_offers");
if (!isValidToken) {
return res.status(401).send({ message: "Unauthorized" });
}
const credifyId = req.body.credify_id;
const localId = req.body.local_id;
const offers = req.body.offers;
if (!credifyId && !localId) {
return res.status(400).send({ message: "No ID found" });
}
if (offers === undefined) {
const response = {
data: {
offers: [],
},
};
return res.status(200).json(response);
}
// 1. Use `credifyId` or `localId` to find an user in your system. `localId` means ID in your system.
const user = ...;
// 2. Personalize the offers with `user`.
const personalizedOffers = []
await offers.forEach(async (offer) => {
const userClaims = composeClaimObject(u)
//*** Our SDK already supports offer evaluation for you
const result = await credify.offer.evaluateOffer(
offer.conditions,
offer.required_custom_scopes,
userClaims
)
const formattedOffer = {
...offer,
evaluation_result: {
rank: result.rank,
used_scopes: result.usedScopes,
requested_scopes: result.requestedScopes,
},
}
if (result.rank > 0) {
// Return only qualified offers
personalizedOffers.push(formattedOffer)
}
})
const response = {
data: {
offers: personalizedOffers,
},
}
res.json(response);
});
In case you want to have more flexible when introspecting the token (Eg: testing the API without forcing scope usage strictly or using the integration test), we have introspectTokenReturnResult
function returns the introspection result like that:
const credify = await Credify.create(signingPrivateKey, apiKey, { mode: "sandbox" });
// Provided you have `extractToken` function to obtain Bearer access token from Authorization header of the request.
const token = extractToken(req);
const introspectResult = await credify.auth.introspectTokenReturnResult(
token
);
let validToken;
if (
introspectResult.data &&
introspectResult.data.active &&
introspectResult.data.scope.includes("claim_provider")
) {
validToken = true;
}
if (!validToken) {
return res.status(401).send({ message: "Unauthorized" });
}