Skip to main content

Authentication

Every API request must include three authentication headers. Finan uses SHA-256 HMAC signatures to verify request integrity.

Required Headers​

HeaderDescription
x-client-idYour unique client ID (provided by Finan)
x-signatureSHA-256 hash of the request data (see below)
x-timestampCurrent 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​

FieldTypeDescription
Secret KeystringYour secret key provided by Finan
MethodstringHTTP method: GET, POST, PUT, DELETE
Relative PathstringAPI path without the /open prefix (see note below)
PayloadstringJSON request body as string. Empty string "" for GET requests
TimestampstringUnix timestamp (same value as x-timestamp header)
Relative Path

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 URLSignature 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
GET Requests

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.

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)
}

Complete Request Examples​

POST Request (Create Payment)​

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))
}

GET Request (List Payments)​

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)

Environments​

EnvironmentBase URL
Productionhttps://api.finan.one/open
Staginghttps://api-stg.finan.one/open

Common Errors​

ErrorCauseSolution
401 UnauthorizedInvalid signatureVerify secret key, check payload matches body exactly
401 Timestamp expiredTimestamp > 30s oldUse current Unix timestamp, check server time sync
401 Invalid clientWrong client IDVerify 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:

Download Postman Collection


Next Steps​