Authentication
Every API request must include three authentication headers. Finan uses SHA-256 HMAC signatures to verify request integrity.
Required Headersβ
| Header | Description |
|---|---|
x-client-id | Your unique client ID (provided by Finan) |
x-signature | SHA-256 hash of the request data (see below) |
x-timestamp | Current Unix timestamp β must be within 30 seconds of server time |
curl -X POST 'https://api.finan.one/open/api/v1/payments' \
-H 'Content-Type: application/json' \
-H 'x-client-id: YOUR_CLIENT_ID' \
-H 'x-signature: YOUR_GENERATED_SIGNATURE' \
-H 'x-timestamp: 1699999999'
How to Generate the Signatureβ
Step 1: Prepare the Fieldsβ
| Field | Type | Description |
|---|---|---|
| Secret Key | string | Your secret key provided by Finan |
| Method | string | HTTP method: GET, POST, PUT, DELETE |
| Relative Path | string | API path without the /open prefix (see note below) |
| Payload | string | JSON request body as string. Empty string "" for GET requests |
| Timestamp | string | Unix timestamp (same value as x-timestamp header) |
The base URL is https://api.finan.one/open/api/v1/... but the signature uses only the path after /open.
Include query string when filtering by query parameters:
| Full URL | Signature Path |
|---|---|
https://api.finan.one/open/api/v1/payments | /api/v1/payments |
https://api.finan.one/open/api/v1/payouts/abc123/otps | /api/v1/payouts/abc123/otps |
https://api.finan.one/open/api/v1/master-bank-accounts?account_id=xxx | /api/v1/master-bank-accounts?account_id=xxx |
Step 2: Concatenate with Underscore Separatorβ
{SECRET_KEY}_{METHOD}_{RELATIVE_PATH}_{PAYLOAD}_{TIMESTAMP}
POST example (with JSON body):
mySecretKey_POST_/api/v1/payments_{"amount":6000000,"payment_method":"bank_transfer"}_1699999999
GET example (empty payload):
mySecretKey_GET_/api/v1/payments__1699999999
For GET requests, the payload is an empty string. This means the concatenated string will have two consecutive underscores (__) between the path and timestamp.
Step 3: Hash with SHA-256β
Apply SHA-256 to the concatenated string. The hex-encoded result is your x-signature.
- Go
- JavaScript
- Python
package main
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"strconv"
"time"
)
func generateSignature(secretKey, method, path, payload, timestamp string) string {
message := secretKey + "_" + method + "_" + path + "_" + payload + "_" + timestamp
hash := sha256.Sum256([]byte(message))
return hex.EncodeToString(hash[:])
}
func main() {
secretKey := "mySecretKey"
timestamp := strconv.FormatInt(time.Now().Unix(), 10)
// POST request with body
postPayload := `{"amount":6000000,"payment_method":"bank_transfer"}`
postSig := generateSignature(secretKey, "POST", "/api/v1/payments", postPayload, timestamp)
fmt.Println("POST Signature:", postSig)
// GET request (empty payload)
getSig := generateSignature(secretKey, "GET", "/api/v1/payments", "", timestamp)
fmt.Println("GET Signature:", getSig)
}
import { createHash } from 'crypto';
function generateSignature(secretKey, method, path, payload, timestamp) {
const message = `${secretKey}_${method}_${path}_${payload}_${timestamp}`;
return createHash('sha256').update(message).digest('hex');
}
// POST request with body
const timestamp = Math.floor(Date.now() / 1000).toString();
const postPayload = JSON.stringify({ amount: 6000000, payment_method: 'bank_transfer' });
const postSig = generateSignature('mySecretKey', 'POST', '/api/v1/payments', postPayload, timestamp);
console.log('POST Signature:', postSig);
// GET request (empty payload)
const getSig = generateSignature('mySecretKey', 'GET', '/api/v1/payments', '', timestamp);
console.log('GET Signature:', getSig);
import hashlib
import time
def generate_signature(secret_key, method, path, payload, timestamp):
message = f"{secret_key}_{method}_{path}_{payload}_{timestamp}"
return hashlib.sha256(message.encode()).hexdigest()
timestamp = str(int(time.time()))
# POST request with body
post_payload = '{"amount":6000000,"payment_method":"bank_transfer"}'
post_sig = generate_signature("mySecretKey", "POST", "/api/v1/payments", post_payload, timestamp)
print("POST Signature:", post_sig)
# GET request (empty payload)
get_sig = generate_signature("mySecretKey", "GET", "/api/v1/payments", "", timestamp)
print("GET Signature:", get_sig)
Complete Request Examplesβ
POST Request (Create Payment)β
- Go
- JavaScript
package main
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"net/http"
"strconv"
"time"
)
func generateSignature(secretKey, method, path, payload, timestamp string) string {
message := secretKey + "_" + method + "_" + path + "_" + payload + "_" + timestamp
hash := sha256.Sum256([]byte(message))
return hex.EncodeToString(hash[:])
}
func main() {
clientID := "YOUR_CLIENT_ID"
secretKey := "YOUR_SECRET_KEY"
timestamp := strconv.FormatInt(time.Now().Unix(), 10)
payload := `{"amount":6000000,"payment_method":"bank_transfer","reference_id":"ORDER-001"}`
path := "/api/v1/payments"
signature := generateSignature(secretKey, "POST", path, payload, timestamp)
req, _ := http.NewRequest("POST", "https://api.finan.one/open"+path, bytes.NewBufferString(payload))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("x-client-id", clientID)
req.Header.Set("x-signature", signature)
req.Header.Set("x-timestamp", timestamp)
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Printf("Status: %d\nBody: %s\n", resp.StatusCode, string(body))
}
import { createHash } from 'crypto';
function generateSignature(secretKey, method, path, payload, timestamp) {
const message = `${secretKey}_${method}_${path}_${payload}_${timestamp}`;
return createHash('sha256').update(message).digest('hex');
}
const clientId = 'YOUR_CLIENT_ID';
const secretKey = 'YOUR_SECRET_KEY';
const timestamp = Math.floor(Date.now() / 1000).toString();
const payload = JSON.stringify({
amount: 6000000,
payment_method: 'bank_transfer',
reference_id: 'ORDER-001',
});
const path = '/api/v1/payments';
const signature = generateSignature(secretKey, 'POST', path, payload, timestamp);
const response = await fetch(`https://api.finan.one/open${path}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-client-id': clientId,
'x-signature': signature,
'x-timestamp': timestamp,
},
body: payload,
});
const data = await response.json();
console.log('Status:', response.status);
console.log('Data:', data);
GET Request (List Payments)β
- Go
- JavaScript
timestamp := strconv.FormatInt(time.Now().Unix(), 10)
path := "/api/v1/payments"
signature := generateSignature(secretKey, "GET", path, "", timestamp)
req, _ := http.NewRequest("GET", "https://api.finan.one/open"+path, nil)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("x-client-id", clientID)
req.Header.Set("x-signature", signature)
req.Header.Set("x-timestamp", timestamp)
const timestamp = Math.floor(Date.now() / 1000).toString();
const path = '/api/v1/payments';
const signature = generateSignature(secretKey, 'GET', path, '', timestamp);
const response = await fetch(`https://api.finan.one/open${path}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'x-client-id': clientId,
'x-signature': signature,
'x-timestamp': timestamp,
},
});
Environmentsβ
| Environment | Base URL |
|---|---|
| Production | https://api.finan.one/open |
| Staging | https://api-stg.finan.one/open |
Common Errorsβ
| Error | Cause | Solution |
|---|---|---|
401 Unauthorized | Invalid signature | Verify secret key, check payload matches body exactly |
401 Timestamp expired | Timestamp > 30s old | Use current Unix timestamp, check server time sync |
401 Invalid client | Wrong client ID | Verify x-client-id value |
Verifying Webhook Signaturesβ
When you receive a webhook from Finan, verify its authenticity by recalculating the signature:
func verifyWebhookSignature(secretKey, body, receivedSignature, timestamp string) bool {
expected := generateSignature(secretKey, "POST", "/your-webhook-path", body, timestamp)
return expected == receivedSignature
}
Use the x-signature and x-timestamp headers from the webhook request for verification.
Postman Collectionβ
Import our Postman collection for quick testing:
Next Stepsβ
- Account Management β Set up bank accounts
- Payment Gateway β Create payment requests