Authentication
API Key & Secret Authentication
Introduction
Calling every endpoint requires the following HEADERS to be set in the request.
- X-API-KEY: This is the api-key that you get from Fuze.
- X-TIMESTAMP: This is the Unix time in seconds (i.e. the number of seconds that have elapsed since 00:00:00 UTC on 1 January 1970, the beginning of the Unix epoch, less adjustments made due to leap seconds).
- X-SIGNATURE: This is the HMAC signature which is a keyed HMAC SHA256 operation. You need to use the api-secret as the key and stringified JSON object containing request body, request params, url slug and X-TIMESTAMP as the payload for the HMAC operation. Please check the section on how to generate this signature.
Generating the value for X-SIGNATURE request header
HMAC SHA256 signing operation requires two inputs: a key (with which the payload would be signed) and a payload (the value which would be signed using the key).
For generating the X-SIGNATURE value, you would use the api-secret as the key. The payload (X-SIGNATURE) will be a stringified JSON object where the JSON object would be:
{
"body": "<request body as JSON object>",
"query": "<query parameters as key-value fields in a JSON object>",
"url": "<the url slug for the api endpoint>",
"ts": "<the value of X-TIMESTAMP header casted to string>"
}
Please note that the order of these four fields (viz. "body", "query", "url" & "ts") should be the same as depicted above.
Also note, that the order of body is important for signature generation and verification.
Examples of different kinds of API requests and corresponding payload structure for generating X-SIGNATURE
GET request with no query parameters:
Make a GET call to https://staging.api.fuze.finance/api/v1/org/
. This does not contain any body or query parameters.
JSON Payload:
{
"body": {},
"query": {},
"url": "/api/v1/org/",
"ts": "1671444764"
}
Note that the value of "ts" is the value of X-TIMESTAMP header that you would pass in the same API call. You would need to stringify the above JSON Payload before using it as value in the HMAC SHA256 signing operation.
GET request with a few query parameters:
Make a GET call to https://staging.api.fuze.finance/api/v1/org/?k1=v1&k2=v2
. This does not contain any body but has a couple of query parameters.
JSON Payload:
{
"body": {},
"query": {
"k1": "v1",
"k2": "v2"
},
"url": "/api/v1/org/",
"ts": "1671444764"
}
Note that the value of "ts" is the value of X-TIMESTAMP header that you would pass in the same API call. You would need to stringify the above JSON Payload before using it as value in the HMAC SHA256 signing operation.
POST request with a body but no query parameters:
Make a POST call to https://staging.api.fuze.finance/api/v1/user/
. This contains a body but no query parameters. The body is as follows:
{
"orgUserId": "ankitshubham97",
"kyc": false,
"tnc": true
}
JSON Payload:
{
"body": {
"orgUserId": "ankitshubham97",
"kyc": false,
"tnc": true
},
"query": {},
"url": "/api/v1/user/",
"ts": "1671444764"
}
Note that the value of "ts" is the value of X-TIMESTAMP header that you would pass in the same API call. The JSON body passed in the API request should be exactly matching the one provided in the "body" field of the above JSON payload. You would need to stringify the above JSON Payload before using it as value in the HMAC SHA256 signing operation.
POST request with a body and query parameters:
Make a POST call to https://staging.api.fuze.finance/api/v1/user/?k1=v1&k2=v2
. This contains a body and a few query parameters. The body is as follows:
{
"orgUserId": "ankitshubham97",
"kyc": false,
"tnc": true
}
JSON Payload:
{
"body": {
"orgUserId": "ankitshubham97",
"kyc": false,
"tnc": true
},
"query": {
"k1": "v1",
"k2": "v2"
},
"url": "/api/v1/user/",
"ts": "1671444764"
}
Note that the value of "ts" is the value of X-TIMESTAMP header that you would pass in the same API call. The JSON bosy passed in the API request should be exactly matching the one provided in the "body" field of the above JSON payload. You would need to stringify the above JSON Payload before using it as value in the HMAC SHA256 signing operation.
Sample code to generate HMAC SHA256 signature from a key and payload
This section shows how you can generate an HMAC SHA256 signature once you have the key and a JSON payload.
We will work with an example of createUser API (POST /api/v1/user/) throughout this section.
PHP Sample code:
<?php
$API_KEY = "MCowBQYDK2VwAyEA4WzlYqeSEuTIddAOo0VIeaZkjTqp8LUCRZz2qxz7ce4="; // Change this.
$API_SECRET = "MC4CAQAwBQYDK2VwBCIEIEWY0tGWVuA8HEaXFjzC/AT7T2YP9bcW/nsDYnGkk9ib"; // Change this.
$orgUserId = "ankitshubham97"; // Might change this.
$API_ENDPOINT = "https://staging.api.fuze.finance";
$body = array('orgUserId' => $orgUserId, 'kyc' => true, 'tnc' => true);
$query = new \stdClass();
$url = "/api/v1/user/";
$ts = time() + 3600;
// Step 1. Following variable 'payload' contains the stringified JSON payload.
$payload = json_encode(
array(
"body" => $body, "query" => $query, "url" => $url, "ts" => strval($ts)
), JSON_UNESCAPED_SLASHES
);
// Step 2. HMAC SHA256 signature is generated and stored in 'sig' variable.
$sig = hash_hmac('sha256', $payload, $API_SECRET);
echo ($sig);
// Step 3. API call is made via php-curl. Please note the request headers inclusion.
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $API_ENDPOINT.$url);
curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode($body) );
$headers = [
'Content-Type:application/json',
'X-API-KEY:'.$API_KEY,
'X-TIMESTAMP:'.strval($ts),
'X-SIGNATURE:'.$sig,
];
curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
$output = curl_exec($ch);
echo $output;
curl_close($ch);
?>
NodeJS sample code:
import axios from "axios";
import { DateTime } from "luxon";
import * as crypto from "node:crypto";
async function main() {
const API_ENDPOINT = "https://staging.api.fuze.finance";
const API_KEY =
"MCowBQYDK2VwAyEA4WzlYqeSEuTIddAOo0VIeaZkjTqp8LUCRZz2qxz7ce4="; //Change this
const API_SECRET = `MC4CAQAwBQYDK2VwBCIEIEWY0tGWVuA8HEaXFjzC/AT7T2YP9bcW/nsDYnGkk9ib`; // Change this.
const orgUserId = `ankitshubham97`;
const body = {
orgUserId,
kyc: true,
tnc: true,
};
const query = {};
const url = `/api/v1/user/`;
const ts = Math.round(DateTime.utc().toSeconds() + 3600);
const hmac3 = crypto.createHmac("sha256", API_SECRET);
hmac3.update(
JSON.stringify({
body: body,
query: query,
url: `${url}`,
ts: `${ts}`,
})
);
const signature = hmac3.digest("hex");
const response = await axios.post(`${API_ENDPOINT}${url}`, body, {
headers: {
"X-API-KEY": API_KEY,
"X-TIMESTAMP": `${ts}`,
"X-SIGNATURE": signature,
},
});
if (
!(
response.status === 200 &&
response.data?.code === 200 &&
response.data?.data &&
!response.data.error
)
) {
console.error(`Error while calling createUser api`);
return;
}
console.log(`Data response to createUser api: ${response.data}`);
}
main();
IMPORTANT
Note: The signature algorithm assumes that you strip off trailing decimal points when generating the signature.
For example 55000.00
should be represented as 55000
, and 55.50
as 55.5
.
This is to ensure a consistent JSON encoding/decoding across all languages. (We follow the
golang/javascript
version of encoding and decoding.)
OAuth
Introduction
Open Authorization aka OAuth is a widely adopted Authorization framework across the industry. Fuze supports the OAuth client credentials flow, which requires no user involvement & is a machine-to-machine (m2m) flow.
Overview of the workflow for making requests using OAuth
- Generate an access token using
client_id
andclient_secret
provided - Until the access token validity lasts, pass the generated access token in the authorization header.
Prerequisites - You must have client_id
& client_secret
which would've been provided to you by Fuze over a secure channel. Ensure to keep them private & secure at all times.
Generating access token
You need to make a POST
request to https://staging.api.fuze.finance/api/v1/oauth/token
with the request details mentioned below to generate the access token
Request & response format
- Request
- Request content type should be
application/x-www-form-urlencoded
- Request parameters:
grant_type
- Must be
client_credentials
always
- Must be
client_id
- Use the value provided by Fuze
- example value:
ec12dbf0-443c-4ce9-ab90-07fd5200723a
client_secret
- Use the value provided by Fuze
- example value:
9e5ef55b07c4a6085fa89f3befd92480cbf17ecbf17eb2067a11113
scope
- Space separated
<MODULE>:<PERMISSION>
values. PERMISSION
permissible values:READ
WRITE
READWRITE
MODULE
permissible values would be shared to the Org directly- example scope value:
MODULE1:READWRITE
MODULE1:READ MODULE2:WRITE MODULE3:READWRITE
- Space separated
- Sample cURL to make this request:
curl --location 'https://staging.api.fuze.finance/api/v1/oauth/token' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'grant_type=client_credentials' \ --data-urlencode 'scope=<SCOPE>' \ --data-urlencode 'client_id=<CLIENT_ID>' \ --data-urlencode 'client_secret=<CLIENT_SECRET>'
- Request content type should be
- Response
- Failure responses:
{ "code": 401, "error": "Forbidden", "data": null }
- Success Response:
{ "code": 200, "data": { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJGdXplIiwib3JnSWQiOjEwLCJjbGllbnRJZCI6ImVjMTJkYmYwLTQ0M2MtNGNlOS1hYjkwLTA3ZmQ1MmNjNzIzYSIsInNjb3BlIjp7IkJBU0VfTU9EVUxFIjoiV1JJVEUifSwiaWF0IjoxNzQ3ODIzMzY1LCJleHAiOjE3NDc4MjM2NjV9.jP7UeK5Ubui6M0ri6EhCu6EJiTjwXmPP_T-qO8dSgYM", "expires_in": 300000, "token_type": "Bearer", "scope": "BASE_MODULE:WRITE" }, "error": null }
- Failure responses:
Example request & response
200 Success request
We have a valid client_id
& client_secret
has WRITE
access to the BASE_MODULE
, hence this qualifies as a valid request.
Sample Valid Request:
curl --location 'https://staging.api.fuze.finance/api/v1/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=ec12dbf0-443c-4ce9-ab90-07fd52cc723a' \
--data-urlencode 'client_secret=validClientSecret' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'scope=BASE_MODULE:WRITE'
Sample 200 Response:
{
"code": 200,
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJGdXplIiwib3JnSWQiOjEwLCJjbGllbnRJZCI6ImVjMTJkYmYwLTQ0M2MtNGNlOS1hYjkwLTA3ZmQ1MmNjNzIzYSIsInNjb3BlIjp7IkJBU0VfTU9EVUxFIjoiV1JJVEUifSwiaWF0IjoxNzQ3ODIyMzk4LCJleHAiOjE3NDc4MjI2OTh9.Y_jJeTAF9EmI3gZE8YQVwC0gfWV7irmUbYWDBb7AApU",
"expires_in": 300000,
"token_type": "Bearer",
"scope": "BASE_MODULE:WRITE"
},
"error": null
}
401 Failure request
We have a valid client_id
& client_secret
has WRITE
access to the BASE_MODULE
, BUT doesn't have access to MANAGE_USERS
, hence this qualifies as an invalid request.
Sample Invalid Request:
curl --location 'https://staging.api.fuze.finance/api/v1/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=ec12dbf0-443c-4ce9-ab90-07fd52cc723a' \
--data-urlencode 'client_secret=validClientSecret' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'scope=BASE_MODULE:WRITE MANAGE_USERS:WRITE'
Sample 401 Response:
{
"code": 401,
"error": "Forbidden",
"data": null
}
Making requests using access token
You need to pass the access token in the Authorization
header as the bearer token. And if all is good, the request would be authorized successfully.
Note:
- You can make use of the
/api/v1/oauth/test
api to test out the OAuth functionality.- Just ensure the access token has the
BASE_MODULE
'sWRITE
/READWRITE
permission, since the test api requiresWRITE
permission.
Example request & response
- 200 - Sample API request using OAuth access token with
WRITE
, API requiresWRITE
: Request:Response:curl --location 'https://staging.api.fuze.finance/api/v1/oauth/test' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJGdXplIiwib3JnSWQiOjEwLCJjbGllbnRJZCI6ImVjMTJkYmYwLTQ0M2MtNGNlOS1hYjkwLTA3ZmQ1MmNjNzIzYSIsInNjb3BlIjp7IkJBU0VfTU9EVUxFIjoiV1JJVEUifSwiaWF0IjoxNzQ3ODIzNjgzLCJleHAiOjE3NDc4MjM5ODN9.liXtDzlkKHuiMbFA2qg4EJTGKnaAMTDiMOh4lqwINg4' \ --data '{ "hello": "world" }'
{ "code": 200, "data": { "hello": "world" }, "error": null }
- 401 - Sample API request using OAuth access token with
READ
, API requiresWRITE
: Request:Response:curl --location 'https://staging.api.fuze.finance/api/v1/oauth/test' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJGdXplIiwib3JnSWQiOjEwLCJjbGllbnRJZCI6ImVjMTJkYmYwLTQ0M2MtNGNlOS1hYjkwLTA3ZmQ1MmNjNzIzYSIsInNjb3BlIjp7IkJBU0VfTU9EVUxFIjoiV1JJVEUifSwiaWF0IjoxNzQ3ODIzNjgzLCJleHAiOjE3NDc4MjM5ODN9.liXtDzlkKHuiMbFA2qg4EJTGKnaAMTDiMOh4lqwINg4' \ --data '{ "hello": "world" }'
{ "message": "You're not authorized", "status": 401 }