# Authentication
Source: https://paylinky.vercel.app/docs/authentication

Authenticate server-side requests with Bearer API keys.

# Authentication

Authenticate every request with your secret API key in the `Authorization` header:

```http
Authorization: Bearer plk_live_YOUR_SECRET_KEY
```

:::tabs
```bash cURL
curl https://paylinky.vercel.app/api/v1/balance -H "Authorization: Bearer $PAYLINKY_KEY"
```
```javascript Node.js
const r = await fetch("https://paylinky.vercel.app/api/v1/balance", {
  headers: { Authorization: `Bearer ${process.env.PAYLINKY_KEY}` },
});
```
```python Python
requests.get("https://paylinky.vercel.app/api/v1/balance", headers={"Authorization": f"Bearer {os.environ['PAYLINKY_KEY']}"})
```
```php PHP
$ch = curl_init("https://paylinky.vercel.app/api/v1/balance");
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer " . getenv("PAYLINKY_KEY")]]);
```
:::

## Key format

| Key | Where | Description |
| - | - | - |
| `plk_test_<48 hex>` | Server only | Sandbox key — simulated payments |
| `plk_live_<48 hex>` | Server only | Live key — real money, requires activation |
| `whsec_…` | Server only | Webhook signing secret (not an API key) |

Keys are stored hashed. The full key is shown **only once** at creation or when you **roll** it. If you lose it, roll the key from **Developers → API Keys**.

## Authentication errors

| HTTP | `error` | Cause |
| - | - | - |
| 401 | `unauthorized` | Missing, malformed, unknown, rejected or revoked key |
| 403 | `key_not_activated` | Key is waiting for administrator activation |
| 403 | `kyc_required` | Live key but KYC is not approved |
| 403 | `account_suspended` | Account suspended |
| 429 | `rate_limited` | Default 120 requests per minute per key |
| 503 | `maintenance` | Live API temporarily under maintenance |

:::warning
Never put a Paylinky key in a browser, mobile app, or Git repository. Call the API from your backend and expose only the resulting `payment_link` or payment status to clients.
:::
