# Authentication (https://docs.chargeflow.io/docs/reference/api-fundamentals/authentication)



Chargeflow provides a REST API that enables you to programmatically view and update data in your Chargeflow account, and receive event notifications via [Webhooks](../concepts/webhooks).

<Callout title="Note">
  All endpoints require authentication unless explicitly stated otherwise.
</Callout>

## Overview [#overview]

Chargeflow uses API key authentication. Every request must include your `x-api-key` header. Optionally, you can enable HMAC signature validation to add a second layer of security that guarantees both the integrity and authenticity of your requests.

**Required credentials:**

* `API Access Key`: included in the `x-api-key` header on every request.
* `API Secret Key`: only needed if HMAC signature validation is enabled.

Generate your keys in the Chargeflow App under **Settings → Developers → Generate Keys** -
see [API keys](./test-credentials) for the full walkthrough and how to use them. Any active
Chargeflow account can generate a key: there is no plan requirement and no approval step.

### Where the key belongs [#where-the-key-belongs]

One key pair covers both directions of the integration. The Access Key authenticates the
REST calls you make, and generating it is also what unlocks webhook registration, where the
Secret Key is used to sign the deliveries Chargeflow sends you (see
[Webhooks](../concepts/webhooks)). So the key is not "just for webhooks".

Use it wherever your own code or automation calls Chargeflow: your backend, a
[Zapier](https://docs.chargeflow.io/docs/merchants/automation/zapier-no-code) connection, or your CRM's outbound HTTP
step. It is not entered in your payment processor's portal, and it must never appear in
client-side code.

<Callout type="warn" title="Getting 401 or 403 on every call?">
  A body of `{"message": "Missing API Key header x-api-key"}` means the header did not
  arrive: check the exact spelling and that no proxy strips it. A `403` on **every**
  endpoint, health check included, points at the request rather than the account (there is
  no "enable API access" toggle): the signing string below must match `METHOD\nPATH\nBODY`
  byte for byte. Full triage in [Error handling](./error-handling).
</Callout>

## HMAC signature validation (optional) [#hmac-signature-validation-optional]

By default, HMAC signature verification is disabled on newly generated access keys. In this mode, the API key alone authenticates your requests, making it easy to integrate in your codebase or in tools like Zapier or Make.

If you prefer an additional layer of security, ensuring data integrity (no tampering) and authenticity (requests come from you), you can enable **HMAC Signature Validation** in the Developers section of your Chargeflow settings.

Once enabled, Chargeflow's servers will always verify the HMAC signature of your incoming requests.

<Accordions type="single">
  <Accordion title="What is HMAC?">
    HMAC (Hash-based Message Authentication Code) is a mechanism that produces a cryptographic signature from your request data and a shared secret key. The server independently computes the same signature and rejects any request where the signatures don't match. This prevents both forgery and tampering.
  </Accordion>

  <Accordion title="How to generate an HMAC signature">
    In addition to the `x-api-key` header, you include the generated signature in the `x-chargeflow-hmac-sha256` header.

    **Signing string format:**

    The string to sign is composed as:

    ```
    METHOD\nPATH\nBODY
    ```

    Where:

    * `METHOD` is the HTTP method in uppercase (e.g., `POST`)
    * `PATH` is the request path (e.g., `/public/2025-04-01/disputes/dispute-id/subscription`)
    * `BODY` is the raw request body string (empty string `""` for requests with no body)
  </Accordion>

  <Accordion title="Authentication flow">
    <Steps>
      <Step>
        Along with the request body, you send headers containing your **API key** and the HMAC signature generated from your request data and secret key.
      </Step>

      <Step>
        The Chargeflow server receives the request and validates the **API key**.
      </Step>

      <Step>
        The server retrieves your **Secret Key** from its database and independently generates a verification signature using HMAC-SHA256.
      </Step>

      <Step>
        If both signatures match, the request is authenticated.
      </Step>
    </Steps>
  </Accordion>
</Accordions>

## Code examples [#code-examples]

<Tabs items="['Node.js (JSON)', 'Python (JSON)', 'Node.js (Multipart)']">
  <Tab value="Node.js (JSON)">
    ```typescript title="Node.js"
    const crypto = require('crypto');

    // Function to generate HMAC-SHA256 signature
    function generateHmacSignature(data, secretKey) {
      const hmac = crypto.createHmac('sha256', secretKey);
      hmac.update(data);
      return hmac.digest('hex');
    }

    function calculateHmac(method, path, body, secretKey) {
      // Example request data
      const requestData = {
        method: method.toUpperCase(),
        path,
        body,
      };

      // Compose string to sign from request data
      const dataToSign = `${requestData.method}\n${requestData.path}\n${requestData.body}`;

      // Generate HMAC signature
      const hmacSignature = generateHmacSignature(dataToSign, secretKey);
      return hmacSignature;
    }

    // Example request data
    const method = 'POST';
    const path = '/public/2025-04-01/disputes/dispute-id/subscription';
    // # Example request body. Work with the same string when sending to ensure that body is sent exactly as signed.
    const body = JSON.stringify({ param: 'value' });

    // Calculate HMAC signature
    const secretKey = 'your-secret-key';
    const hmacSignature = calculateHmac(method, path, body, secretKey);

    console.log('Generated HMAC-SHA256 Signature:', hmacSignature);

    // You can now use the generated signature as the 'x-chargeflow-hmac-sha256' header value
    ```
  </Tab>

  <Tab value="Python (JSON)">
    ```python title="Python"
    import hashlib
    import hmac
    import json

    # Function to generate HMAC-SHA256 signature
    def generate_hmac_signature(data, secret_key):
        signature = hmac.new(
            bytes(secret_key, 'utf-8'),
            msg = bytes(data, 'utf-8'),
            digestmod = hashlib.sha256).hexdigest()
        return signature

    def calculate_hmac(method, path, body, secret_key):
        # Example request data
        request_data = {
            'method': method.upper(),
            'path': path,
            'body': body,
        }

        # Compose string to sign from request data
        data_to_sign = f"{request_data['method']}\n{request_data['path']}\n{request_data['body']}"

        # Generate HMAC signature
        hmac_signature = generate_hmac_signature(data_to_sign, secret_key)
        return hmac_signature

    # Example request data
    method = 'POST'
    path = '/public/2025-04-01/disputes/dispute-id/subscription'
    # Example request body. Work with the same string when sending to ensure that body is sent exactly as signed.
    body = json.dumps({'param': 'value'})

    # Calculate HMAC signature
    secret_key = 'your-secret-key'
    hmac_signature = calculate_hmac(method, path, body, secret_key)

    print('Generated HMAC-SHA256 Signature:', hmac_signature)

    # You can now use the generated signature as the 'x-chargeflow-hmac-sha256' header value
    ```
  </Tab>

  <Tab value="Node.js (Multipart)">
    When sending `multipart/form-data` requests with files (such as for the Evidence Upload API), the body used for signing is a sorted list of `key=value` pairs joined with `;`. Each value is an MD5 hash of the content: file values are first converted to Base64 before hashing.

    ```javascript title="Node.js"
    // Compatible with Node.JS 18+

    const crypto = require('crypto');
    const fs = require('fs');
    const { blob } = require('stream/consumers');

    // Function to generate HMAC-SHA256 signature
    function generateHmacSignature(data, secretKey) {
      const hmac = crypto.createHmac('sha256', secretKey);
      hmac.update(data);
      return hmac.digest('hex');
    }

    // Function to create MD5 hash of a FormData part
    async function createPartHash(part) {
      const isFile = part instanceof File;
      const partString = isFile ? await fileToBase64(part) : part;
      return getMd5(partString);
    }

    function getMd5(contents) {
      return crypto.createHash('md5').update(contents).digest('hex');
    }

    async function fileToBase64(file) {
      return Buffer.from(await file.arrayBuffer()).toString('base64');
    }

    // Function to normalize FormData into a signing string
    async function formDataToSignString(formData) {
      const formDataEntries = Array.from(formData);
      const hashedParts = [];
      for (const [key, value] of formDataEntries) {
        const partHash = await createPartHash(value);
        hashedParts.push(`${key}=${partHash}`);
      }

      return hashedParts.sort().join(';');
    }

    // Function to calculate HMAC signature
    async function calculateHmacForFormData(method, path, formData, secretKey) {
      const body = await formDataToSignString(formData);

      const requestData = {
        method: method.toUpperCase(),
        path,
        body,
      };

      // Compose string to sign from request data
      const dataToSign = `${requestData.method}\n${requestData.path}\n${requestData.body}`;

      // Generate HMAC signature
      const hmacSignature = generateHmacSignature(dataToSign, secretKey);
      return hmacSignature;
    }

    async function signRequest() {
      // Example request data
      const method = 'POST';
      const path = '/public/2025-04-01/disputes/dispute-id/evidence';
      // Prepare FormData, same as when sending a multipart/form-data request
      const formData = new FormData();
      formData.append('description', 'File description');
      formData.append('file', await blob(fs.createReadStream('./foo/bar.jpg')), 'bar.jpg');

      // Calculate HMAC signature
      const secretKey = 'your-secret-key';
      const hmacSignature = await calculateHmacForFormData(method, path, formData, secretKey);

      console.log('Generated HMAC-SHA256 Signature:', hmacSignature);

      // You can now use the generated signature as the 'x-chargeflow-hmac-sha256' header value
    }

    signRequest();
    ```
  </Tab>
</Tabs>

## Next steps [#next-steps]

<Cards>
  <Card title="Quickstart" href="/docs/merchants/quickstart">
    Make your first authenticated request.
  </Card>

  <Card title="Idempotency" href="/docs/reference/api-fundamentals/idempotency">
    Retry requests safely without creating duplicates.
  </Card>

  <Card title="Error Handling" href="/docs/reference/api-fundamentals/error-handling">
    Resolve 401 and 403 responses.
  </Card>

  <Card title="Webhooks" href="/docs/reference/concepts/webhooks">
    Verify the HMAC signature on incoming webhook events.
  </Card>
</Cards>
