# API keys

> Create and manage API keys for programmatic access to the Nexpay API.

Nexpay authenticates server-to-server (M2M) traffic using **API keys**. Each key is a pair: a public `clientId` and a private `secret`. The two are combined and sent on the `X-API-Key` header of every request. Browser apps authenticate differently — see [Authentication](/docs/authentication.md) for the Bearer-token flow.

---

## Key format

Both halves of the key carry a prefix so they're easy to recognise in code and secrets stores:

- **`clientId`** — `nxp_ck_<24 hex chars>` (32 characters total). Treat it as public — it appears in logs and audit records.
- **`secret`** — `nxp_sk_<64 hex chars>` (71 characters total). Treat it as fully private. Nexpay stores it as an HMAC-SHA256 hash and **cannot return it again** after creation.

---

## Creating an API key

You can create a key from the dashboard or via the `POST /v2/dev/api-keys` endpoint. Either way, only a human-authenticated user (with an active dashboard session) can create keys — API keys cannot create other API keys.

### Via the dashboard

1. Open the Nexpay Dashboard.
2. Click your avatar in the top-right corner and select **Settings**.
3. In the **API Keys** section, click **Create API Key**.
4. Enter a descriptive label (e.g. "Production Backend", "Staging Integration").
5. Click **Create**.
6. Copy both the **Client ID** and **Secret** immediately and store the secret in your secrets manager.

### Via the API

You need to be signed in with a dashboard Bearer session token to call this endpoint.

**cURL**

```bash
curl -X POST 'https://api.nexpay.com.au/v2/dev/api-keys' \
  -H 'Authorization: Bearer your-session-token' \
  -H 'Content-Type: application/json' \
  -d '{
  "label": "Production Backend"
}'
```

**JavaScript**

```javascript
const response = await fetch('https://api.nexpay.com.au/v2/dev/api-keys', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${sessionToken}`,
  },
  body: JSON.stringify({
    label: 'Production Backend',
  }),
});

const { data: apiKey } = await response.json();
console.log('clientId:', apiKey.clientId); // nxp_ck_787f5b97469f65c4ca97da31
console.log('secret:', apiKey.secret);     // nxp_sk_b7c41bf9... — only returned once
```

**PHP (Laravel)**

```php
use Illuminate\Support\Facades\Http;

$response = Http::withHeaders(['Authorization' => 'Bearer your-session-token'])
    ->post('https://api.nexpay.com.au/v2/dev/api-keys', [
        'label' => 'Production Backend',
    ]);

$apiKey = $response->json('data');
echo $apiKey['clientId']; // nxp_ck_787f5b97469f65c4ca97da31
echo $apiKey['secret'];   // nxp_sk_b7c41bf9... — only returned once
```

The response (unwrapped from the `{ data }` envelope) looks like this:

```json
{
  "id": "67a1b2c3d4e5f6789abcdef0",
  "clientId": "nxp_ck_787f5b97469f65c4ca97da31",
  "secret": "nxp_sk_b7c41bf9a02f43e6b8d12345678901234567890abcdef1234567890abcdef1234",
  "tenantId": 51,
  "userId": 7372,
  "createdByUserId": 7372,
  "label": "Production Backend",
  "isActive": true,
  "createdAt": "2026-06-03T10:05:06.007Z"
}
```

> **Warning — Save your secret**
>
> The secret is only returned once at creation. Nexpay stores only a hash and cannot recover it later. If you lose it, revoke the key and create a new one.

---

## Using your API key

Combine `clientId` and `secret` with a colon (`:`) and send them on the **`X-API-Key`** header:

**cURL**

```bash
curl 'https://api.nexpay.com.au/v2/users/me' \
  -H 'X-API-Key: nxp_ck_your-client-id:nxp_sk_your-secret'
```

**JavaScript**

```javascript
const response = await fetch('https://api.nexpay.com.au/v2/users/me', {
  method: 'GET',
  headers: {
    'X-API-Key': `${clientId}:${secret}`,
    // for example: 'nxp_ck_787f5b97469f65c4ca97da31:nxp_sk_b7c41bf9a02f43e6b8d1...'
  },
});

const { data: me } = await response.json();
console.log(me.email);     // "api-key:nxp_ck_787f5b97469f65c4ca97da31"
console.log(me.roles);     // ["APIKey"]
console.log(me.tenantId);  // 51
```

**PHP (Laravel)**

```php
use Illuminate\Support\Facades\Http;

$response = Http::withHeaders(['X-API-Key' => 'nxp_ck_your-client-id:nxp_sk_your-secret'])
    ->get('https://api.nexpay.com.au/v2/users/me');

$me = $response->json('data');
echo $me['email'];    // "api-key:nxp_ck_787f5b97469f65c4ca97da31"
echo $me['roles'][0]; // "APIKey"
echo $me['tenantId']; // 51
```

See [Authentication](/docs/authentication.md) for the full request-time contract, including the Bearer scheme used by browser sessions.

---

## Listing your API keys

**cURL**

```bash
curl 'https://api.nexpay.com.au/v2/dev/api-keys' \
  -H 'Authorization: Bearer your-session-token'
```

**JavaScript**

```javascript
const response = await fetch('https://api.nexpay.com.au/v2/dev/api-keys', {
  method: 'GET',
  headers: { 'Authorization': `Bearer ${sessionToken}` },
});

const { data } = await response.json();
data.apiKeys.forEach((key) => {
  console.log(key.id, key.clientId, key.label, key.isActive, key.lastUsedAt);
});
```

**PHP (Laravel)**

```php
use Illuminate\Support\Facades\Http;

$response = Http::withHeaders(['Authorization' => 'Bearer your-session-token'])
    ->get('https://api.nexpay.com.au/v2/dev/api-keys');

$data = $response->json('data');
foreach ($data['apiKeys'] as $key) {
    echo "{$key['id']} {$key['clientId']} {$key['label']} {$key['isActive']} {$key['lastUsedAt']}";
}
```

Secrets are never returned by the list endpoint — only `clientId`, label, and metadata.

---

## Revoking an API key

If a key is compromised or no longer needed, revoke it immediately. Any integration using a revoked key will stop working.

### Via the dashboard

1. Open the Nexpay Dashboard.
2. Go to **Settings → API Keys**.
3. Hover the key and click **Revoke**.
4. Confirm the dialog.

### Via the API

**cURL**

```bash
curl -X DELETE 'https://api.nexpay.com.au/v2/dev/api-keys/67a1b2c3d4e5f6789abcdef0' \
  -H 'Authorization: Bearer your-session-token'
```

**JavaScript**

```javascript
await fetch(`https://api.nexpay.com.au/v2/dev/api-keys/${keyId}`, {
  method: 'DELETE',
  headers: { 'Authorization': `Bearer ${sessionToken}` },
});
// Returns 204 No Content
```

**PHP (Laravel)**

```php
use Illuminate\Support\Facades\Http;

$response = Http::withHeaders(['Authorization' => 'Bearer your-session-token'])
    ->delete('https://api.nexpay.com.au/v2/dev/api-keys/67a1b2c3d4e5f6789abcdef0');
// Returns 204 No Content
```

Revoke is a soft-delete: the key is set to `isActive: false` and remains visible in the list endpoint for audit. Revoked keys cannot be reactivated — create a new key and update your integration to use it.

---

## Keep your keys safe

Anyone with your `clientId:secret` pair can make API calls on behalf of your account, scoped to the tenant the key was issued for. Follow these practices:

- Never store secrets in version control. Use environment variables, AWS Secrets Manager, Google Secret Manager, HashiCorp Vault, etc.
- Never embed API keys in client-side code (browser, mobile) — they'd be exposed to anyone inspecting the bundle.
- Rotate keys periodically. Create the new key first, deploy it, then revoke the old one once nothing is using it (check `lastUsedAt` to confirm).
- Grant access only to people who need to manage keys — only human users with dashboard sessions can create or revoke keys.
- One key per integration (or per environment), with a clear `label`, so you can revoke individually without taking down everything else.
