> ## Documentation Index
> Fetch the complete documentation index at: https://docs.paycashless.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Signing API requests

All endpoints require signature validation for the request to be accepted for processing. We use HMAC SHA-512 based signatures to valiate requests. Add `Request-Signature` and `Request-Timestamp` as required header parameters.

## Signing Key

Get your API secret that will be used for signing requests on the dashboard. [How to get your API credentials](/getting-api-keys)

Please note that your API secret should be stored securely. It should be used only for generating `Request-Signature` header, do not add the API secret directly to any API request to avoid exposure. Paycashless will never request you to share your API key or API secret.

## Signing a request

| Step | Action      | Description                                                                                                                                                                                               |
| ---- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1    | Sort        | If the request has a body, sort it alphabetically.                                                                                                                                                        |
| 2    | Hash        | If the request has a body, turn the sorted body into a string and hash it with HMAC SHA-512 algorithm using your API secret as the signing key (output should be hex-encoded).                            |
| 3    | Concatenate | Concatenate the `request path`, `hashed body` (if present), and `timestamp`. There are no spaces or other characters between these values. The order of the fields must follow the order stipulated here. |
| 4    | Sign        | Take the string from the Concatenate step and generate a HMAC SHA-512 signature using your API secret as the signing key.                                                                                 |
| 5    | Encode      | Take the output of the Sign step and hex-encode it.                                                                                                                                                       |

The fields used to generate the signature are as follows. If the conditions below are not met, your signature will not validate.

| Field        | Description                                                                                                                                                                                                  |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Request Path | Lowercased URL path without the base url and search parameters (e.g. `/v1/virtual_account/va_84jdvcy3gyt5bfsczdaooy4/transactions`).                                                                         |
| Hashed Body  | Should be omitted if request does not have a body (e.g. GET requests). The alphabetically sorted, `stringified` and `HMAC SHA-512` hashed http request body.                                                 |
| Timestamp    | The number of seconds since the Unix Epoch in UTC, and must be within five minutes of the API service's time when the request arrives. The same value MUST also be passed in the `Request-Timestamp` header. |

## API Signature example

You can use the example values below to test and validate your signing code logic;

| Field        | Value                                                                                                                                                                                                                                                                                                             |
| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Request Path | /v1/payouts                                                                                                                                                                                                                                                                                                       |
| Sorted Body  | `{"amount":{"currency":"NGN","value":10000},"bankId":"bank_538ed2056326432ba8e6853b613997bb","callbackUrl":"https://webhook.site/99a59e8a-bd0b-485d-8249-fccb5eb62e27","destinationAccountNumber":"9845648577","metadata":{"category":"transfer"},"narration":"zapped","reference":"trx_fWQ7b31pbs5mmT3k3qfb46"}` |
| Hashed Body  | 61ce72561daddb581abbd83c731dc5421b062157f707b1f683086bccbe85d8b14b7a4df6a1cdb7c14230a631d8ad7d82536f28c2e67717e6cf6673d8b6df3a23                                                                                                                                                                                  |
| Timestamp    | 1749163599                                                                                                                                                                                                                                                                                                        |
| Signing Key  | live\_sk\_bqf5evl708c5arkfv16g37glc4isxsup.pc                                                                                                                                                                                                                                                                     |
| Signature    | 95013b0b1e41f36b2de57cd6ef08ecc4d0f8ff846c98e1470f3ef8bce90012133a7c867b7d21e4c27cc68c1bde0bb3fc63e960c892ac82c8ef74b9f793854d7d                                                                                                                                                                                  |

Below is a node.js code sample used in signing the values above;

<CodeGroup>
  ```js request_signature.js theme={null}
  import crypto from 'crypto';
  import { sortObjectAlphabetically } from "./sort.js";

  function sha512Sign(message, secret) {
    return crypto.createHmac('sha512', secret).update(message).digest('hex');
  }

  // Get the timestamp (in seconds) and pass it to the header as 'Request-Timestamp'
  const timestamp = Math.floor(Date.now() / 1000);
  const requestPath = "/v1/payouts";
  const API_SECRET = "YOUR_API_SECRET"; // replace these with your actual API secret value

  // Get the request body 
  let body = {
    "amount":{
      "currency":"NGN",
      "value":10000
    },
    "bankId":"bank_538ed2056326432ba8e6853b613997bb",
    "callbackUrl":"https://webhook.site/99a59e8a-bd0b-485d-8249-fccb5eb62e27",
    "destinationAccountNumber":"9845648577",
    "metadata":{
      "category":"transfer"
    },
    "narration":"zapped",
    "reference":"trx_fWQ7b31pbs5mmT3k3qfb46"
  };

  // Sort the body alphabetically
  body = sortObjectAlphabetically(body);

  // Create the body hash to be passed as payload
  const bodyHash = sha512Sign(JSON.stringify(body), API_SECRET);

  // Concatenate the string to sign
  const stringToSign = `${requestPath}${bodyHash}${timestamp}`;

  // Generate the Request-Signature value for the header
  const signature = sha512Sign(stringToSign, API_SECRET);

  console.log(`signature: ${signature}`);
  ```

  ```js sort.js theme={null}
  export function sortObjectAlphabetically(obj) {
    if (!obj || typeof obj !== 'object' || Array.isArray(obj)) {
      return obj;
    }

    const sortedKeys = Object.keys(obj).sort();
    const sortedObj = {};

    // Add each property to the new object in alphabetical order
    sortedKeys.forEach(key => {
      // Recursively sort nested objects
      if (typeof obj[key] === 'object' && !Array.isArray(obj[key]) && obj[key] !== null) {
        sortedObj[key] = sortObjectAlphabetically(obj[key]);
      } else {
        sortedObj[key] = obj[key];
      }
    });

    return sortedObj;
  }
  ```
</CodeGroup>
