FlowAlp

Merchant API authentication

July 30, 2026

Authenticate FlowAlp Pay Merchant API requests with the x-api-key header or the ApiSignature HMAC-SHA256 parameter, with code examples.

Every Merchant API request must prove that it comes from your backend. You need your instance name plus its API secret, and one of two authentication methods:

  • `x-api-key` header (recommended): send the API secret with each request.
  • `ApiSignature` parameter: send an HMAC-SHA256 signature computed from the request parameters — the secret itself is never transmitted.

If you build on PHP, the PHP SDK applies authentication for you; this page is mainly relevant for direct HTTP integrations.

Prerequisites

  • Your instance name
  • Your API credentials
  • The API secret stored outside your code, for example in the FLOWALP_PAY_API_SECRET environment variable

Send the API secret in the x-api-key HTTP header. Header names are case-insensitive, so X-API-KEY is equivalent:

HeaderHTTP
x-api-key: <api-secret>
Test the header with SignatureCheckbash
curl --request GET \
  --url "https://api.pay.flowalp.com/v1.16/SignatureCheck/?instance=<instance>" \
  --header "x-api-key: <api-secret>"
Raw PHP (cURL) with x-api-keyPHP
<?php
$instance  = getenv('FLOWALP_PAY_INSTANCE');
$apiSecret = getenv('FLOWALP_PAY_API_SECRET');

$ch = curl_init(
    'https://api.pay.flowalp.com/v1.16/SignatureCheck/?instance=' . urlencode($instance)
);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['x-api-key: ' . $apiSecret]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
$status   = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

if ($status !== 200) {
    throw new RuntimeException('FlowAlp Pay auth check failed: ' . $status);
}
Node.js with x-api-keyJavaScript
const instanceName = process.env.FLOWALP_PAY_INSTANCE;
const apiSecret = process.env.FLOWALP_PAY_API_SECRET;

const response = await fetch(
  `https://api.pay.flowalp.com/v1.16/SignatureCheck/?instance=${encodeURIComponent(instanceName)}`,
  { headers: { "x-api-key": apiSecret } }
);

if (!response.ok) {
  throw new Error(`FlowAlp Pay auth check failed: ${response.status}`);
}

Option 2: ApiSignature (HMAC-SHA256)

In signature mode you do not transmit the secret. Instead you sign the request parameters and send the result in the ApiSignature parameter. The signature is an HMAC (RFC 2104), built as follows:

  1. Collect all request parameters except instance.
  2. Build the URL-encoded query string from these parameters, for example amount=2500&currency=CHF.
  3. Compute the binary HMAC-SHA256 of that string, using the API secret as the key.
  4. Base64-encode the result.
  5. Send the value as the ApiSignature parameter alongside the other parameters.
Canonical algorithm (PHP one-liner)PHP
$apiSignature = base64_encode(
    hash_hmac('sha256', http_build_query($params, '', '&'), $apiSecret, true)
);
Complete PHP example: signed Gateway requestPHP
<?php
$instance  = getenv('FLOWALP_PAY_INSTANCE');
$apiSecret = getenv('FLOWALP_PAY_API_SECRET');

$params = [
    'amount'      => 8925, // CHF 89.25 in minor units
    'currency'    => 'CHF',
    'referenceId' => 'ORDER-975382',
];

// Sign exactly the query string you send as the body.
$body = http_build_query($params, '', '&');
$params['ApiSignature'] = base64_encode(
    hash_hmac('sha256', $body, $apiSecret, true)
);

$ch = curl_init(
    'https://api.pay.flowalp.com/v1.16/Gateway/?instance=' . urlencode($instance)
);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params, '', '&'));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/x-www-form-urlencoded']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);
Bash (openssl)bash
API_SECRET="<api-secret>"
QUERY_STRING="amount=2500&currency=CHF"

SIGNATURE=$(printf '%s' "$QUERY_STRING" \
  | openssl dgst -sha256 -hmac "$API_SECRET" -binary \
  | openssl enc -base64)

curl --request POST \
  --url "https://api.pay.flowalp.com/v1.16/Gateway/?instance=<instance>" \
  --header "Content-Type: application/x-www-form-urlencoded" \
  --data "$QUERY_STRING" \
  --data-urlencode "ApiSignature=$SIGNATURE"

Query string encoding

The string you sign must be RFC 1738 form-encoded — spaces become +. The output of PHP's http_build_query() is the reference implementation. Other languages must match it exactly; pay attention to characters such as !'()*~, which some libraries encode differently (see the JavaScript helper below). The form-urlencoded request body itself uses percent-encoding, with spaces as %20.

Do not design your own variant of the algorithm. If your library produces a different encoding, the signature will not match. Validate your implementation with the SignatureCheck endpoint before going live.

JavaScript signing helper (crypto-js)JavaScript
const qs = require("qs");
const Base64 = require("crypto-js/enc-base64");
const hmacSHA256 = require("crypto-js/hmac-sha256");

function buildSignature(data, secret) {
  let queryStr = "";
  if (data) {
    queryStr = qs.stringify(data, { format: "RFC1738" });
    queryStr = queryStr.replace(
      /[!'()*~]/g,
      (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`
    );
  }
  return Base64.stringify(hmacSHA256(queryStr, secret));
}

const params = { amount: 2500, currency: "CHF" };
const signed = {
  ...params,
  ApiSignature: buildSignature(params, process.env.FLOWALP_PAY_API_SECRET),
};

Which method should you use?

Criterionx-api-keyApiSignature
Implementation effortMinimal — one headerRequires exact query-string encoding
Secret in transitSent with every request (always over HTTPS)Never transmitted; only the HMAC travels
Typical useServer-to-server integrationsEnvironments with strict secret-handling policies

Verify your setup

Call SignatureCheck after configuring either method; it validates your instance, your secret and — in signature mode — your encoding. Details: SignatureCheck: verify API credentials.

Typical success responseJSON
{
  "status": "success",
  "data": [
    {
      "id": 1
    }
  ]
}

Security recommendations

  • Keep the API secret on your backend; never embed it in browsers, mobile apps or other client-side code.
  • Store secrets in environment variables or a secret manager, not in the repository.
  • Never log the x-api-key header or ApiSignature values.
  • Rotate the secret immediately if you suspect it leaked.
  • Always call the API over HTTPS.

Authenticated? Continue with the request format and send your first API request.