> For the complete documentation index, see [llms.txt](https://docs-sdk.crypto-chief.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs-sdk.crypto-chief.com/processing/php/authentication.md).

# Authentication

How the Crypto Chief PHP SDK authenticates and signs every request.

Every request to the Crypto Processing API is authenticated with HTTP headers. **The SDK builds and sets them for you** — you only provide your Merchant ID and API key.

```
Merchant:       <your Merchant ID>
X-CC-Timestamp: <Unix time, seconds>
X-CC-Nonce:     <32 hex, new for every request>
X-CC-Signature: v1=hex(hmacSHA256(API_KEY, stringToSign))
```

## Credentials

Both values come from your dashboard → **Integration** tab:

* **Merchant ID** — identifies your project.
* **API key** — the **signing secret**. It never leaves your server; the SDK uses it to sign requests and to verify incoming webhooks.

{% hint style="warning" %}
Treat the API key like a password. Load it from an environment variable and keep it server-side — never commit it or ship it in client apps.
{% endhint %}

## Initialize the client

```php
use CryptoChief\Processing\Client;

$client = new Client(
    merchantId: getenv('MERCHANT_ID'),
    apiKey:     getenv('API_KEY'),
);
```

Construct the client once at startup and reuse it for the lifetime of your process. Besides its configuration it keeps the clock offset learned from `SIGNATURE_TIMESTAMP_OUT_OF_RANGE` and applies it to later requests. It owns a Guzzle HTTP client internally; no manual cleanup needed.

## Configuration options

```php
use CryptoChief\Processing\Client;

$client = new Client(
    merchantId:    getenv('MERCHANT_ID'),
    apiKey:        getenv('API_KEY'),
    baseUrl:       Client::DEFAULT_BASE_URL,                  // override for staging
    userAgent:     'my-service/1.0',
    retries:       3,                                         // retry 5xx + transport errors
    timeoutSec:    60.0,                                      // per-attempt timeout
    retryBaseMs:   200.0,                                     // exponential + jitter
    retryMaxMs:    5000.0,
    rsaPrivateKey: file_get_contents('/path/to/rsa.pem'),     // optional — wallet decryption
    // httpClient: $myPsr18Client,                            // optional — inject any PSR-18 client
);
```

To swap in your own HTTP client (Guzzle, Symfony HttpClient, or any PSR-18 implementation), pass it via `httpClient`. The SDK will use it for every signed request.

{% hint style="info" %}
**Test mode** is a per-project toggle in the dashboard, not a separate base URL. Point a test-mode project's credentials at the same client.
{% endhint %}

## How signing works

Every request is signed with HMAC-SHA256 over the request and your API key: `X-CC-Timestamp`, `X-CC-Nonce`, `X-CC-Signature`.

* The timestamp, nonce and signature are computed on every attempt, retries included.
* On `SIGNATURE_TIMESTAMP_OUT_OF_RANGE` the client sets its clock offset from `server_time` and repeats the request once.
* Spaces and tabs at the ends of header values are not part of the HMAC-SHA256 v1 signature: the API removes them before checking `X-CC-Signature`.
* `$client->withIdempotencyKey($key)` sends `Idempotency-Key` on the calls of that client and includes it in the signature.
* `$client->request($path, $body, $idempotencyKey, $method)` signs a request to a route the SDK has no method for, with any HTTP method.

The string to sign is in the [SDKs overview](/processing/processing.md#authentication).

Webhooks are signed with the same API key: `X-Webhook-Delivery`, `X-CC-Timestamp`, `X-CC-Signature` over the raw body. See [Webhooks](/processing/php/guides/webhooks.md).
