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

# Authentication

> HMAC-signed API keys — required headers, signature string, and a worked example

The Platform API authenticates every request with an HMAC-signed API key. When your access is issued you receive two credentials: an **API key**, sent with every request, and an **API secret**, used to sign requests.

<Warning>
  **Keep your secret safe.** The API secret must be kept confidential. Do not embed it in client-side code, mobile apps, or version control. If a secret is exposed, contact support to rotate it.
</Warning>

Your API key identifies your organization, and requests can only access resources that belong to it. A known top-level customer or account owned by another organization returns `403`; an unknown id returns `404`. Once an authorized customer or account has been resolved, a child resource that does not exist within that parent returns `404`.

## Required headers

Include these four headers on every request:

<ParamField header="x-api-key" type="string" required>
  Your API key.
</ParamField>

<ParamField header="x-timestamp" type="string" required>
  Unix epoch seconds as a string. Requests more than 300 seconds off server time are rejected.
</ParamField>

<ParamField header="x-nonce" type="string" required>
  A unique value per request (e.g. a UUID). A replayed nonce is rejected for 300 seconds.
</ParamField>

<ParamField header="x-signature" type="string" required>
  Hex-encoded HMAC-SHA256 of the signature string (below), keyed with your API secret.
</ParamField>

## Signature string

Concatenate the following components exactly, with no separators:

```text Signature string theme={null}
signature_string = {HTTP_METHOD}{path}{x-timestamp}{x-nonce}{body}
```

<ResponseField name="HTTP_METHOD" type="string">
  Uppercase method: `GET`, `POST`, `PATCH`, `PUT`.
</ResponseField>

<ResponseField name="path" type="string">
  The request path including the leading slash and, if present, `?` plus the raw query string, e.g. `/customers/cus_x7k2m9p4q1w8e5r3t6y0u2i4/accounts?page=1&size=10`.
</ResponseField>

<ResponseField name="body" type="string">
  The exact raw request body string you transmit. For GET and other no-body requests this is the empty string `""`.
</ResponseField>

<Warning>
  **Sign the same bytes you transmit.** Build the JSON body once and use that exact string both in the signature and on the wire (e.g. `curl -d '<json>'`) — re-serializing the object can change whitespace or key order and invalidate the signature.
</Warning>

## Building a signed request

```bash sign and send a POST theme={null}
#!/usr/bin/env bash
BASE_URL="https://<base-url>"   # provided when your access is issued
API_KEY="pk_xxxxxxxx"
API_SECRET="sk_xxxxxxxx"

METHOD="POST"
PATH_AND_QUERY="/customers/cus_9f2a7c1e6a2b4f3c9b8d2e5a/accounts"
TIMESTAMP="$(date +%s)"
NONCE="$(uuidgen | tr '[:upper:]' '[:lower:]')"

# Abbreviated body for illustration — see Create Account for the full required schema.
BODY='{"type":"business","business_legal_name":"Acme Pte Ltd"}'

SIGNATURE_STRING="${METHOD}${PATH_AND_QUERY}${TIMESTAMP}${NONCE}${BODY}"
SIGNATURE="$(printf '%s' "$SIGNATURE_STRING" \
  | openssl dgst -sha256 -hmac "$API_SECRET" \
  | awk '{print $NF}')"

curl -s "${BASE_URL}${PATH_AND_QUERY}" \
  -X "$METHOD" \
  -H "x-api-key: $API_KEY" \
  -H "x-timestamp: $TIMESTAMP" \
  -H "x-nonce: $NONCE" \
  -H "x-signature: $SIGNATURE" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d "$BODY"
```

```json worked signature example theme={null}
{
  "method": "POST",
  "path": "/customers/cus_9f2a7c1e6a2b4f3c9b8d2e5a/accounts",
  "timestamp": "1765345200",
  "nonce": "0d1f7c4e-6a2b-4f3c-9b8d-2e5a1c7f9d04",
  "body": "{\"type\":\"business\",\"business_legal_name\":\"Acme Pte Ltd\"}",
  "signature_string": "POST/customers/cus_9f2a7c1e6a2b4f3c9b8d2e5a/accounts17653452000d1f7c4e-6a2b-4f3c-9b8d-2e5a1c7f9d04{\"type\":\"business\",\"business_legal_name\":\"Acme Pte Ltd\"}",
  "headers": {
    "x-api-key": "pk_xxxxxxxx",
    "x-timestamp": "1765345200",
    "x-nonce": "0d1f7c4e-6a2b-4f3c-9b8d-2e5a1c7f9d04",
    "x-signature": "hex(HMAC-SHA256(api_secret, signature_string))"
  }
}
```

<Note>
  The server rebuilds the same signature string and compares. A signature mismatch, a timestamp more than 300 seconds off, or a replayed nonce all return `401`.
</Note>
