Signature Generation

Generate HMAC-SHA256 signatures to authenticate Neutron API requests. Includes code samples in Node.js, Python, Go, C#, and Java.

All Neutron API requests are authenticated using HMAC-SHA256 signatures. This page explains how to generate them and includes ready-to-use code in multiple languages.

How It Works

1. Construct the string to sign:  "{apiKey}&payload={payload}"
2. Compute HMAC-SHA256 using your API secret as the key
3. Send the signature in the X-Api-Signature header

The payload is a JSON string — it can be any valid JSON (e.g., {"test":"auth"}). The same payload is sent in the request body.

Authentication Request

curl -X POST https://api.neutron.me/api/v2/authentication/token-signature \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "X-Api-Signature: YOUR_COMPUTED_SIGNATURE" \
  -d '{"test":"auth"}'

Headers

HeaderValueDescription
Content-Typeapplication/jsonAlways required
X-Api-KeyYour API keyIdentifies your account
X-Api-SignatureHMAC-SHA256 hex stringProves you have the API secret

Response

{
  "accountId": "ne01-abc123def456",
  "accessToken": "eyJhbGciOiJIUzI1NiIs...",
  "expiresAt": 1770428400000
}

Use the accessToken as a Bearer token for all subsequent API calls:

curl https://api.neutron.me/api/v2/account/YOUR_ACCOUNT_ID \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Signature Formula

stringToSign = "{apiKey}&payload={payload}"
signature    = HMAC-SHA256(secret, stringToSign) → hex

Example values:

  • API Key: nk_live_abc123
  • API Secret: ns_live_xyz789
  • Payload: {"test":"auth"}
  • String to sign: nk_live_abc123&payload={"test":"auth"}

Code Examples

Node.js

const crypto = require('crypto');

function computeSignature(apiKey, apiSecret, payload) {
  const stringToSign = `${apiKey}&payload=${payload}`;
  return crypto
    .createHmac('sha256', apiSecret)
    .update(stringToSign)
    .digest('hex');
}

// Usage
const apiKey = 'your-api-key';
const apiSecret = 'your-api-secret';
const payload = JSON.stringify({ test: 'auth' });

const signature = computeSignature(apiKey, apiSecret, payload);

// Make the authentication request
const response = await fetch('https://api.neutron.me/api/v2/authentication/token-signature', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Api-Key': apiKey,
    'X-Api-Signature': signature,
  },
  body: payload,
});

const { accessToken, accountId } = await response.json();

Python

import hmac
import hashlib
import json
import requests

def compute_signature(api_key: str, api_secret: str, payload: str) -> str:
    string_to_sign = f"{api_key}&payload={payload}"
    return hmac.new(
        api_secret.encode('utf-8'),
        string_to_sign.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()

# Usage
api_key = 'your-api-key'
api_secret = 'your-api-secret'
payload = json.dumps({"test": "auth"})

signature = compute_signature(api_key, api_secret, payload)

response = requests.post(
    'https://api.neutron.me/api/v2/authentication/token-signature',
    headers={
        'Content-Type': 'application/json',
        'X-Api-Key': api_key,
        'X-Api-Signature': signature,
    },
    data=payload,
)

data = response.json()
access_token = data['accessToken']

Go

package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "fmt"
    "io"
    "net/http"
    "strings"
)

func computeSignature(apiKey, apiSecret, payload string) string {
    stringToSign := apiKey + "&payload=" + payload
    h := hmac.New(sha256.New, []byte(apiSecret))
    h.Write([]byte(stringToSign))
    return hex.EncodeToString(h.Sum(nil))
}

func main() {
    apiKey := "your-api-key"
    apiSecret := "your-api-secret"
    payload := `{"test":"auth"}`

    signature := computeSignature(apiKey, apiSecret, payload)

    req, _ := http.NewRequest("POST",
        "https://api.neutron.me/api/v2/authentication/token-signature",
        strings.NewReader(payload))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("X-Api-Key", apiKey)
    req.Header.Set("X-Api-Signature", signature)

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    body, _ := io.ReadAll(resp.Body)
    fmt.Println(string(body))
}

C#

using System;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

class NeutronAuth
{
    static string ComputeSignature(string apiKey, string apiSecret, string payload)
    {
        string stringToSign = $"{apiKey}&payload={payload}";
        byte[] keyBytes = Encoding.UTF8.GetBytes(apiSecret);
        byte[] messageBytes = Encoding.UTF8.GetBytes(stringToSign);

        using var hmac = new HMACSHA256(keyBytes);
        byte[] hash = hmac.ComputeHash(messageBytes);
        return BitConverter.ToString(hash).Replace("-", "").ToLower();
    }

    static async Task Main()
    {
        string apiKey = "your-api-key";
        string apiSecret = "your-api-secret";
        string payload = "{\"test\":\"auth\"}";

        string signature = ComputeSignature(apiKey, apiSecret, payload);

        using var client = new HttpClient();
        var request = new HttpRequestMessage(HttpMethod.Post,
            "https://api.neutron.me/api/v2/authentication/token-signature");
        request.Headers.Add("X-Api-Key", apiKey);
        request.Headers.Add("X-Api-Signature", signature);
        request.Content = new StringContent(payload, Encoding.UTF8, "application/json");

        var response = await client.SendAsync(request);
        Console.WriteLine(await response.Content.ReadAsStringAsync());
    }
}

Java

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URI;
import java.net.http.*;
import java.nio.charset.StandardCharsets;

public class NeutronAuth {

    static String computeSignature(String apiKey, String apiSecret, String payload) throws Exception {
        String stringToSign = apiKey + "&payload=" + payload;
        Mac hmac = Mac.getInstance("HmacSHA256");
        hmac.init(new SecretKeySpec(apiSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
        byte[] hash = hmac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));

        StringBuilder hex = new StringBuilder();
        for (byte b : hash) {
            hex.append(String.format("%02x", b));
        }
        return hex.toString();
    }

    public static void main(String[] args) throws Exception {
        String apiKey = "your-api-key";
        String apiSecret = "your-api-secret";
        String payload = "{\"test\":\"auth\"}";

        String signature = computeSignature(apiKey, apiSecret, payload);

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.neutron.me/api/v2/authentication/token-signature"))
            .header("Content-Type", "application/json")
            .header("X-Api-Key", apiKey)
            .header("X-Api-Signature", signature)
            .POST(HttpRequest.BodyPublishers.ofString(payload))
            .build();

        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.body());
    }
}

Webhook Signature Verification

When Neutron sends webhook callbacks to your server, each request includes a signature header for verification. This uses the secret you provided when creating the webhook.

How It Works

1. Neutron computes: HMAC-SHA256(your_webhook_secret, request_body)
2. Sends it in the X-Neutronpay-Signature header
3. Your server recomputes and compares

Verification Example (Node.js)

const crypto = require('crypto');

function verifyWebhookSignature(requestBody, signatureHeader, webhookSecret) {
  const expected = crypto
    .createHmac('sha256', webhookSecret)
    .update(requestBody)  // raw request body string
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signatureHeader),
    Buffer.from(expected)
  );
}

// In your webhook handler
app.post('/webhook', (req, res) => {
  const signature = req.headers['x-neutronpay-signature'];
  const isValid = verifyWebhookSignature(
    JSON.stringify(req.body),
    signature,
    'your-webhook-secret'
  );

  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }

  res.status(200).send('OK');
  // Process the event asynchronously...
});

Verification Example (Python)

import hmac
import hashlib

def verify_webhook(body: str, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode('utf-8'),
        body.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected)

Sample Webhook Payload

{
  "txnId": "41866d80-a46a-4845-a205-5cffc881cad3",
  "extRefId": "ordid-abc1234",
  "txnState": "completed",
  "msg": "",
  "updatedAt": 1676030467492
}
⚠️

Always use constant-time comparison (timingSafeEqual / compare_digest) to prevent timing attacks. Never use === or == for signature comparison.

Related