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

# Authentication

> Bearer tokens for every request, HMAC-SHA512 signatures for integrity.

Float uses two credentials, issued per merchant integration:

| Credential        | Used for                                              | Where it appears                               |
| ----------------- | ----------------------------------------------------- | ---------------------------------------------- |
| **Client secret** | Authenticating every API request                      | `Authorization: Bearer <client_secret>` header |
| **Signing key**   | Signing your requests and verifying Float's callbacks | `X-Signature` header (both directions)         |

<Warning>
  Both values are secrets. Keep them server-side only — never embed them in web or mobile clients. If a secret leaks, contact Float immediately to rotate it.
</Warning>

## Bearer authentication

Every API request must include your client secret as a Bearer token:

```bash theme={null}
curl https://uat-secure.choosefloat.com/api/config \
  -H "Authorization: Bearer $FLOAT_CLIENT_SECRET"
```

A missing or invalid token returns `401 Unauthorized`:

```json theme={null}
{
  "errors": [
    {
      "status": "401",
      "code": "unauthorized",
      "title": "Unauthorized",
      "detail": "Please provide a valid Bearer token in the Authorization header."
    }
  ]
}
```

## Request signing

Signing proves the request body wasn't tampered with in transit and that it originated from you. It is **optional to start, but strongly recommended** — and required for production go-live.

<Note>
  **Signing is sticky.** The first time Float receives a correctly signed request from your integration, signing becomes **mandatory** for all subsequent mutating requests (`POST /api/checkouts`, `POST /api/refunds`). Unsigned requests after that point are rejected with `400 Bad Request`. Enable signing in sandbox first and roll it out deliberately.
</Note>

### How to sign

Compute an HMAC-SHA512 digest of the **exact raw JSON request body**, keyed with your signing key, and Base64-encode it. Send it in the `X-Signature` header.

```
X-Signature: Base64( HMAC-SHA512( signing_key, raw_request_body ) )
```

<CodeGroup>
  ```ruby Ruby theme={null}
  require "openssl"

  body = payload.to_json
  signature = OpenSSL::HMAC.base64digest("SHA512", ENV.fetch("FLOAT_SIGNING_KEY"), body)

  # Send `body` as the request body and `signature` in the X-Signature header.
  ```

  ```javascript Node.js theme={null}
  const crypto = require("crypto");

  const body = JSON.stringify(payload);
  const signature = crypto
    .createHmac("sha512", process.env.FLOAT_SIGNING_KEY)
    .update(body)
    .digest("base64");
  ```

  ```php PHP theme={null}
  $body = json_encode($payload);
  $signature = base64_encode(
      hash_hmac('sha512', $body, getenv('FLOAT_SIGNING_KEY'), true)
  );
  ```

  ```python Python theme={null}
  import base64, hashlib, hmac, json, os

  body = json.dumps(payload, separators=(",", ":"))
  signature = base64.b64encode(
      hmac.new(os.environ["FLOAT_SIGNING_KEY"].encode(), body.encode(), hashlib.sha512).digest()
  ).decode()
  ```
</CodeGroup>

<Warning>
  **Always send compact JSON — never pretty-print.** Float verifies your signature against a *compact re-serialisation* of your payload (key order preserved, whitespace stripped). A pretty-printed body will therefore never match, even if you signed exactly what you sent. The safe pattern: serialise compactly (the default in every example above), sign that string, and send that same string. A mismatch returns `400 Bad Request`:

  ```json theme={null}
  {
    "errors": [
      {
        "status": "400",
        "code": "bad_request",
        "title": "Bad Request",
        "detail": "Request signature does not match. The X-Signature header should match '<expected signature>'."
      }
    ]
  }
  ```

  The `detail` includes the signature Float expected — compare it to the one you computed to debug quickly.
</Warning>

### Verifying Float's callbacks

Float signs its callbacks to your `notify_url` with the **same signing key and algorithm**. Always verify before trusting the payload — see [Callbacks → Verifying the signature](/guides/callbacks#verifying-the-signature).

## Credential handling checklist

* Store secrets in your secret manager or environment variables, never in source control.
* Use separate credentials for sandbox and production.
* Verify callback signatures with a constant-time comparison (`Rack::Utils.secure_compare`, `crypto.timingSafeEqual`, `hash_equals`).
* Rotate immediately on suspected exposure via your Float account manager.
