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

# Authentication

> Learn how to authenticate your API requests with LuxCore

# Authentication

All API requests to LuxCore must be authenticated. Most integrations use API keys on API v1. Merchants explicitly onboarded to API v2 use HMAC signatures for payment and balance requests.

## Obtaining API Keys

<Info>
  API keys and merchant accounts are created by the LuxCore team during
  onboarding. Self-registration is not available.
</Info>

To obtain your API keys:

1. Contact your LuxCore account manager
2. Or email [developers@lux-core.io](mailto:developers@lux-core.io)

Once your account is set up, you can view your API keys in the [Merchant Dashboard](https://admin.lux-core.io) under **Settings → API Keys**.

## API Key Types

LuxCore uses API keys to authenticate requests.

### Key Types

| Key Prefix    | Environment | Description                                    |
| ------------- | ----------- | ---------------------------------------------- |
| `qp_prod_sk_` | Production  | Real transactions with actual money movement   |
| `qp_test_sk_` | Test        | Simulated transactions, no real money movement |

<Warning>
  Keep your API keys secure! Never expose them in client-side code, public
  repositories, or browser requests.
</Warning>

## Making API v1 Authenticated Requests

Include your API key in the `X-API-Key` header with every request:

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET "https://api.lux-core.io/api/v1/payments" \
    -H "X-API-Key: qp_prod_sk_your_api_key_here" \
    -H "Content-Type: application/json"
  ```

  ```python Python theme={null}
  import requests

  headers = {
      "X-API-Key": "qp_prod_sk_your_api_key_here",
      "Content-Type": "application/json"
  }

  response = requests.get(
      "https://api.lux-core.io/api/v1/payments",
      headers=headers
  )
  ```

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

  const response = await axios.get("https://api.lux-core.io/api/v1/payments", {
    headers: {
      "X-API-Key": "qp_prod_sk_your_api_key_here",
      "Content-Type": "application/json",
    },
  });
  ```

  ```php PHP theme={null}
  <?php
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL, "https://api.lux-core.io/api/v1/payments");
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_HTTPHEADER, [
      "X-API-Key: qp_prod_sk_your_api_key_here",
      "Content-Type: application/json"
  ]);

  $response = curl_exec($ch);
  curl_close($ch);
  ```

  ```go Go theme={null}
  package main

  import (
      "log"
      "net/http"
  )

  func main() {
      req, err := http.NewRequest("GET", "https://api.lux-core.io/api/v1/payments", nil)
      if err != nil {
          log.Fatal(err)
      }
      req.Header.Set("X-API-Key", "qp_prod_sk_your_api_key_here")
      req.Header.Set("Content-Type", "application/json")

      client := &http.Client{}
      resp, err := client.Do(req)
      if err != nil {
          log.Fatal(err)
      }
      defer resp.Body.Close()
  }
  ```
</CodeGroup>

## API v2 HMAC Requests

Merchants onboarded to API v2 use HMAC-authenticated endpoints:

```text theme={null}
POST https://api.lux-core.io/api/v2/payments
GET  https://api.lux-core.io/api/v2/payments/{payment_id}
GET  https://api.lux-core.io/api/v2/balance
GET  https://api.lux-core.io/api/v2/balance/all
```

API v2 does not accept `X-API-Key`. Each request must be signed with HMAC-SHA256 using the payment HMAC secret issued during onboarding or API v2 enablement. This secret is separate from webhook signing secrets.

### Required Headers

| Header                | Description                              |
| --------------------- | ---------------------------------------- |
| `X-Merchant-Id`       | Numeric merchant ID                      |
| `X-Timestamp`         | Unix timestamp in seconds                |
| `X-Nonce`             | Unique nonce, 8-128 characters           |
| `X-Signature-Version` | Canonicalization version, currently `v1` |
| `X-Signature`         | `hmac_sha256=<hex digest>`               |

### Canonical String

Build the canonical string exactly as:

```text theme={null}
v1.{timestamp}.{nonce}.{method}.{path_with_query}.{sha256_raw_body_hex}
```

Rules:

* `method` is uppercase, for example `POST` or `GET`
* `path_with_query` includes the API v2 path and any query string exactly as sent
* `sha256_raw_body_hex` is the SHA-256 hash of the raw request body bytes
* GET requests use an empty raw body hash: `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`
* Reject locally if the timestamp differs from current time by more than 300 seconds
* Never reuse a nonce within the 300-second replay window

Examples:

```text theme={null}
v1.1777893479.1777893479267617000zi3o8qdb.GET./api/v2/payments/pay_1777886114621_2dff0055.e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
v1.1777893479.1777893479267617000zi3o8qdb.GET./api/v2/balance?currency=ARS&balance_type=main.e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
v1.1777893479.1777893479267617000zi3o8qdb.GET./api/v2/balance/all.e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
```

### Node.js Example

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

const merchantId = "123";
const secret = process.env.LUXCORE_HMAC_SECRET;
const body = JSON.stringify({
  amount: 100050,
  currency: "MXN",
  method: "spei",
  type: "deposit",
  merchant_reference: "order_123456789",
  customer: { name: "Juan Perez", email: "juan@example.com" },
});

const timestamp = Math.floor(Date.now() / 1000).toString();
const nonce = crypto.randomUUID().replace(/-/g, "");
const path = "/api/v2/payments";
const bodyHash = crypto.createHash("sha256").update(body).digest("hex");
const canonical = `v1.${timestamp}.${nonce}.POST.${path}.${bodyHash}`;
const digest = crypto
  .createHmac("sha256", secret)
  .update(canonical)
  .digest("hex");

await fetch("https://api.lux-core.io/api/v2/payments", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-Merchant-Id": merchantId,
    "X-Timestamp": timestamp,
    "X-Nonce": nonce,
    "X-Signature-Version": "v1",
    "X-Signature": `hmac_sha256=${digest}`,
  },
  body,
});
```

## JWT Bearer Token

Some endpoints also support JWT Bearer token authentication as an alternative to API keys.

```bash theme={null}
curl -X GET "https://api.lux-core.io/api/v1/payments" \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \
  -H "Content-Type: application/json"
```

<Note>
  For API v1 merchant integrations, API keys are the recommended authentication
  method. API v2 payment creation uses HMAC. JWT Bearer tokens are available for
  advanced use cases — contact support for details.
</Note>

## Test Mode vs Production

<Info>
  Test and production requests use the **same API endpoint**. The environment is
  determined by your API key type.
</Info>

### Test Mode Behavior

When using test API keys (`qp_test_sk_*`):

* Payments are simulated and do not process real funds
* Webhooks are delivered normally for testing integrations
* All API responses mirror production behavior
* Balance operations use test balances isolated from production
* No actual bank transfers or card charges occur

### Switching Environments

Simply change your API key to switch between test and production:

```bash theme={null}
# Test mode
curl -H "X-API-Key: qp_test_sk_abc123..." https://api.lux-core.io/api/v1/payments

# Production mode
curl -H "X-API-Key: qp_prod_sk_xyz789..." https://api.lux-core.io/api/v1/payments
```

## API Key Scopes

API keys can be configured with specific scopes to limit access:

| Scope              | Description                                             |
| ------------------ | ------------------------------------------------------- |
| `payments.create`  | Create new payments                                     |
| `payments.read`    | View payment details                                    |
| `payments.view`    | View payment details (alias for `payments.read`)        |
| `payments.cancel`  | Cancel pending payments                                 |
| `payments.methods` | Access payment methods info                             |
| `webhooks.create`  | Create webhook endpoints                                |
| `webhooks.read`    | View webhook configurations                             |
| `webhooks.view`    | View webhook configurations (alias for `webhooks.read`) |
| `webhooks.update`  | Modify webhook settings                                 |
| `webhooks.delete`  | Delete webhooks                                         |
| `balance.read`     | View account balances                                   |
| `balance.view`     | View account balances (alias for `balance.read`)        |

## Rate Limits

API requests are rate-limited to ensure fair usage:

| Endpoint Type      | Limit                                   |
| ------------------ | --------------------------------------- |
| Payment creation   | 5000 requests/minute (burst: 500/10sec) |
| Standard endpoints | 100 requests/minute                     |

<Note>
  Rate limits are applied per API key. If you exceed the limit, you'll receive a
  `429 Too Many Requests` response.
</Note>

## Error Responses

Authentication errors return standard HTTP status codes:

| Status Code             | Description                  |
| ----------------------- | ---------------------------- |
| `401 Unauthorized`      | Missing or invalid API key   |
| `403 Forbidden`         | API key lacks required scope |
| `429 Too Many Requests` | Rate limit exceeded          |

```json Example Error Response theme={null}
{
  "statusCode": 401,
  "message": "Authentication required. Use either Bearer token or X-API-Key header.",
  "error": "Unauthorized"
}
```

## Best Practices

<CardGroup cols={2}>
  <Card title="Use Environment Variables" icon="lock">
    Store API keys in environment variables, never in code
  </Card>

  <Card title="Rotate Keys Regularly" icon="arrows-rotate">
    Regenerate API keys periodically for security
  </Card>

  <Card title="Use Minimal Scopes" icon="shield">
    Request only the scopes your application needs
  </Card>

  <Card title="Monitor Usage" icon="chart-line">
    Track API usage in the dashboard for anomalies
  </Card>
</CardGroup>

## Key Rotation

To rotate your API keys:

1. Generate a new API key in the [Dashboard](https://admin.lux-core.io) under **Settings -> API Keys**
2. Update your application to use the new key
3. Verify the new key works correctly
4. Deactivate the old key in the Dashboard

<Note>
  Multiple API keys can be active simultaneously, allowing zero-downtime
  rotation. For webhook secret rotation, delete and recreate the webhook
  endpoint with a new secret.
</Note>
