> For the complete documentation index, see [llms.txt](https://developers.kwikpaisa.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developers.kwikpaisa.com/v3-guide/authentication/signature.md).

# Signature

KwikPaisa APIs use HMAC SHA256 based request signing to authenticate and validate every API request securely.

Each request must include a generated signature in the X-SIGNATURE header. The signature helps verify:

* Request authenticity
* Payload integrity
* Timestamp validity
* Merchant authorization

This security mechanism protects APIs against unauthorized access, payload tampering, and replay attacks.

***

## Signature Formula

```http
HMAC_SHA256(payload + timestamp, secret_key)
```

## How Signature Generation Works

To generate a valid signature:

1. Prepare the request payload
2. Recursively sort all payload keys
3. Convert the payload into JSON format
4. Generate current UNIX timestamp in seconds
5. Concatenate:

```http
payload + timestamp
```

6. Generate HMAC SHA256 hash using your `secret_key`

## Example Payload

```json
{ 
 "order_id": "123456",
 "amount": "100"
}
```

## Important Payload Rules

Before generating the signature:

* Payload keys must be sorted recursively
* JSON payload must remain unchanged
* Use unescaped slashes in JSON encoding
* Do not modify payload after signature generation
* Generate a fresh timestamp for every request

Any mismatch between:

* Request payload
* Timestamp
* Secret key

will result in authentication failure.

{% tabs %}
{% tab title="Node.js" %}

```http
const crypto = require('crypto');
const data = {
  order_id: '123456',
  amount: '100'
};
const sortedData = Object.keys(data)
  .sort()
  .reduce((obj, key) => {
    obj[key] = data[key];
    return obj;
  }, {});
const payload = JSON.stringify(sortedData);
const timestamp = Math.floor(Date.now() / 1000);
const secretKey = 'YOUR_SECRET_KEY';
const signature = crypto
  .createHmac('sha256', secretKey)
  .update(payload + timestamp)
  .digest('hex');
console.log(signature);
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
$data = [
    'order_id' => '123456',
    'amount' => '100'
];
function sortPayload($data)
{
    if (!is_array($data)) {
        return $data;
    }
    ksort($data);
    foreach ($data as $key => $value) {
        $data[$key] = sortPayload($value);
    }
    return $data;
}
$payload = json_encode(
    sortPayload($data),
    JSON_UNESCAPED_SLASHES
);
$timestamp = time();
$secretKey = 'YOUR_SECRET_KEY';
$signature = hash_hmac(
    'sha256',
    $payload . $timestamp,
    $secretKey
);
echo $signature;
```

{% endtab %}

{% tab title="Python" %}

```python
import json
import hmac
import hashlib
import time
data = {
    "order_id": "123456",
    "amount": "100"
}
sorted_data = dict(sorted(data.items()))
payload = json.dumps(
    sorted_data,
    separators=(',', ':')
)
timestamp = str(int(time.time()))
secret_key = "YOUR_SECRET_KEY"
signature = hmac.new(
    secret_key.encode(),
    (payload + timestamp).encode(),
    hashlib.sha256
).hexdigest()
print(signature)
```

{% endtab %}

{% tab title="Java" %}

```java
import com.fasterxml.jackson.databind.ObjectMapper;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.*;
public class SignatureGenerator {
    public static String generateSignature(
        Map<String, Object> data,
        String secretKey,
        long timestamp
    ) throws Exception {
        TreeMap<String, Object> sortedData = new TreeMap<>(data);
        ObjectMapper mapper = new ObjectMapper();
        String payload = mapper.writeValueAsString(sortedData);
        String signString = payload + timestamp;
        Mac sha256Hmac = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKeySpec = new SecretKeySpec(
            secretKey.getBytes(),
            "HmacSHA256"
        );
        sha256Hmac.init(secretKeySpec);
        byte[] hash = sha256Hmac.doFinal(
            signString.getBytes()
        );
        StringBuilder hexString = new StringBuilder();
        for (byte b : hash) {
            hexString.append(String.format("%02x", b));
        }
        return hexString.toString();
    }
}
```

{% endtab %}

{% tab title=".NET (C#)" %}

```c
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using Newtonsoft.Json;
class Program
{
    static void Main()
    {
        var data = new SortedDictionary<string, object>
        {
            { "order_id", "123456" },
            { "amount", "100" }
        };
        string payload = JsonConvert.SerializeObject(data);
        long timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
        string signString = payload + timestamp;
        string secretKey = "YOUR_SECRET_KEY";
        using(var hmac = new HMACSHA256(
            Encoding.UTF8.GetBytes(secretKey)))
        {
            byte[] hash = hmac.ComputeHash(
                Encoding.UTF8.GetBytes(signString)
            );
            string signature = BitConverter
                .ToString(hash)
                .Replace("-", "")
                .ToLower();
            Console.WriteLine(signature);
        }
    }
}
```

{% endtab %}

{% tab title="Flutter / Dart" %}

```dart
import 'dart:convert';
import 'package:crypto/crypto.dart';
void main() {
  Map<String, dynamic> data = {
    "order_id": "123456",
    "amount": "100"
  };
  final sortedKeys = data.keys.toList()..sort();
  final sortedData = {
    for (var key in sortedKeys) key: data[key]
  };
  String payload = jsonEncode(sortedData);
  int timestamp = DateTime.now()
      .millisecondsSinceEpoch ~/ 1000;
  String secretKey = "YOUR_SECRET_KEY";
  String signString = payload + timestamp.toString();
  var hmacSha256 = Hmac(
    sha256,
    utf8.encode(secretKey)
  );
  var digest = hmacSha256.convert(
    utf8.encode(signString)
  );
  String signature = digest.toString();
  print(signature);
}
```

{% endtab %}

{% tab title="Go" %}

```go
package main
import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"time"
)
func main() {
	payload := map[string]string{
		"order_id": "123456",
		"amount":   "100",
	}
	payloadJSON, _ := json.Marshal(payload)
	timestamp := fmt.Sprintf("%d", time.Now().Unix())
	message := string(payloadJSON) + timestamp
	secretKey := "YOUR_SECRET_KEY"
	h := hmac.New(sha256.New, []byte(secretKey))
	h.Write([]byte(message))
	signature := hex.EncodeToString(h.Sum(nil))
	fmt.Println(signature)
}
```

{% endtab %}
{% endtabs %}

## Common Authentication Errors

### 401 Unauthorized

Possible reasons:

* Invalid signature
* Wrong secret key
* Expired timestamp
* Incorrect payload format
* Missing headers

### Invalid Signature

This usually happens when:

* Payload is modified after signing
* Keys are not sorted correctly
* JSON formatting differs
* Timestamp mismatch occurs

## Security Best Practices

* Never expose your `secret_key` publicly
* Always generate signatures on the server side
* Use HTTPS for all API requests
* Generate unique timestamps for every request
* Rotate credentials periodically
* Validate webhook signatures before processing callbacks

## Recommended Workflow

1. Generate timestamp
2. Prepare payload
3. Generate signature
4. Attach required headers
5. Send API request
6. Validate API response


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://developers.kwikpaisa.com/v3-guide/authentication/signature.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
