---
title: "Secret Key vs Publishable Key: Understanding the Difference"
description: "Learn the critical differences between Linkbreakers secret keys (full API access, backend-only) and publishable keys (limited scope, frontend-safe) to secure your integration and prevent unauthorized access."
canonical: "https://linkbreakers.com/help/article/secret-key-vs-publishable-key"
last-updated: 2025-03-20
---

# Secret Key vs Publishable Key: Understanding the Difference

> Learn the critical differences between Linkbreakers secret keys (full API access, backend-only) and publishable keys (limited scope, frontend-safe) to secure your integration and prevent unauthorized access.

## Short answer

Linkbreakers provides two types of API keys: **secret keys** for backend environments with full API access, and **publishable keys** for frontend applications with read-only, limited permissions. Secret keys can create links, access analytics, and modify workspace data. Publishable keys can only identify visitors and read limited visitor data. Never expose secret keys in client-side code.

## Key differences at a glance

| Feature | Secret Key | Publishable Key |
|---------|-----------|----------------|
| **Environment** | Backend only | Frontend safe |
| **Permissions** | Full API access | Read-only, limited scope |
| **Create links** | ✅ Yes | ❌ No |
| **Delete links** | ✅ Yes | ❌ No |
| **View analytics** | ✅ Yes | ❌ No |
| **Identify visitors** | ✅ Yes | ✅ Yes |
| **Update visitor profiles** | ✅ Yes | ✅ Yes (own data only) |
| **Access workspace data** | ✅ Yes | ❌ No |
| **Can be exposed publicly** | ❌ Never | ✅ Yes |

## Secret keys: Full API access

### What they do

Secret keys are JWT tokens that grant **complete access** to your Linkbreakers workspace, unless you restrict them to a set of [scopes](/help/article/api-token-scopes). With full access they can:

- Create, update, and delete links
- Generate QR codes
- Identify and update visitors
- Access analytics and event data
- Retrieve workspace metrics
- Manage workspace resources
- Full CRUD operations on all resources

### Where to use them

**Backend environments only:**
- Node.js / Express servers
- Python Django / Flask / FastAPI applications
- Next.js API routes (server-side)
- Serverless functions (AWS Lambda, Vercel, Netlify)
- CI/CD pipelines
- Background jobs and cron tasks
- Server-to-server integrations

### Security requirements

**Critical:** Secret keys must NEVER be exposed in:
- ❌ Client-side JavaScript code
- ❌ Mobile apps (iOS, Android)
- ❌ Browser extensions
- ❌ Public GitHub repositories
- ❌ Frontend environment variables (e.g., `NEXT_PUBLIC_*`)
- ❌ Error messages or logs

**Always:**
- ✅ Store in backend environment variables
- ✅ Use secret management systems (AWS Secrets Manager, Vault)
- ✅ Rotate immediately if accidentally exposed
- ✅ Restrict access to production keys

### Example usage

```typescript
// ✅ BACKEND ONLY - Next.js API route
import { Configuration, LinksApi } from 'linkbreakers';

export async function POST(request: Request) {
  const config = new Configuration({
    accessToken: process.env.LINKBREAKERS_SECRET_KEY,  // Secret JWT
    basePath: 'https://api.linkbreakers.com',
  });

  const linksApi = new LinksApi(config);
  const { destination, name } = await request.json();

  const response = await linksApi.linksServiceCreate({
    createLinkRequest: { destination, name }
  });

  return Response.json({ shortlink: response.link?.shortlink });
}
```

## Publishable keys: Frontend-safe, limited scope

### What they do

Publishable keys are JWT tokens with **restricted permissions** specifically designed for client-side use. They can:

- Identify visitors (associate user data with sessions)
- Update visitor attributes (email, name, custom fields)
- Read visitor profiles (limited to current visitor only)

### What they CANNOT do

Publishable keys have no access to:
- ❌ Link creation, modification, or deletion
- ❌ Workspace analytics or metrics
- ❌ Other visitors' data
- ❌ Workspace settings or configuration
- ❌ Any write operations except visitor identification

### Where to use them

**Frontend environments:**
- React / Vue / Angular applications
- Static websites
- Single-page applications (SPAs)
- Mobile apps (iOS, Android, React Native)
- Browser extensions
- Client-side tracking scripts

### Why they're safe for frontends

Even if a publishable key is extracted from your frontend code:
- Attackers cannot create or delete links
- They cannot access your analytics or workspace data
- They can only identify visitors (which requires an LBID from your links anyway)
- The scope is limited by JWT claims enforced server-side

### Example usage

```typescript
// ✅ FRONTEND SAFE - React component
import { Configuration, VisitorsApi } from 'linkbreakers';
import { useEffect } from 'react';

export function VisitorTracker({ userEmail }: { userEmail: string }) {
  useEffect(() => {
    const config = new Configuration({
      accessToken: process.env.NEXT_PUBLIC_LINKBREAKERS_PK,  // Publishable JWT
      basePath: 'https://api.linkbreakers.com',
    });

    const visitorsApi = new VisitorsApi(config);
    const lbid = new URLSearchParams(window.location.search).get('lbid');

    if (lbid && userEmail) {
      visitorsApi.visitorsServiceIdentify({
        identifyRequest: {
          lbid,
          visitor: {
            data: {
              '$email': userEmail,
              '$firstName': 'John'
            }
          }
        }
      });
    }
  }, [userEmail]);

  return null;
}
```

## How JWT claims enforce permissions

Both key types are JWT tokens, but they contain different **scopes** in their payload:

### Secret key JWT claims (example)

```json
{
  "workspace_id": "1234",
  "type": "secret",
  "scopes": [
    "links:read",
    "links:write",
    "links:delete",
    "visitors:read",
    "visitors:write",
    "analytics:read",
    "workspace:read"
  ],
  "env": "production",
  "iat": 1710936000,
  "exp": null
}
```

### Publishable key JWT claims (example)

```json
{
  "workspace_id": "1234",
  "type": "publishable",
  "scopes": [
    "visitors:identify",
    "visitors:read_self"
  ],
  "env": "production",
  "iat": 1710936000,
  "exp": null
}
```

The API server validates these scopes on every request, ensuring publishable keys cannot perform privileged operations even if someone tries to craft malicious requests.

## Creating API keys

To create either key type:

1. Log in to [app.linkbreakers.com](https://app.linkbreakers.com)
2. Navigate to **Dashboard → API Tokens**
3. Click **Create API Token**
4. Choose the key type:
   - **Secret Key** - For backend use, full API access
   - **Publishable Key** - For frontend use, limited scope
5. Give it a descriptive name (e.g., "Production Backend" or "Frontend Tracker")
6. Copy the JWT token immediately (you won't see it again)

## Best practices

### Use both key types strategically

**Typical architecture:**
- **Frontend**: Use publishable key to identify visitors when they sign up, log in, or submit forms
- **Backend**: Use secret key to create links, generate QR codes, and access analytics
- **Result**: Secure integration with minimal exposure

### Environment variable naming

```bash
# .env file
LINKBREAKERS_SECRET_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...  # Backend only
NEXT_PUBLIC_LINKBREAKERS_PK=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...  # Frontend safe
```

**Next.js convention:** Prefix publishable keys with `NEXT_PUBLIC_` to expose them to the browser, while keeping secret keys private.

### Rotate keys if compromised

If you accidentally expose a secret key:
1. Immediately create a new secret key
2. Update your backend environment variables
3. Delete the compromised key from the dashboard
4. The old key becomes invalid within seconds

## Common mistakes to avoid

**❌ Using secret keys in frontend code**
```typescript
// WRONG - Secret key exposed in browser
const config = new Configuration({
  accessToken: 'eyJhbGciOiJI...secret_key',  // ❌ DO NOT DO THIS
});
```

**❌ Trying to create links with publishable keys**
```typescript
// WRONG - Publishable key doesn't have this permission
const linksApi = new LinksApi(config);  // config has publishable key
await linksApi.linksServiceCreate(...);  // ❌ 403 Forbidden
```

**❌ Committing keys to version control**
```bash
# .env (should be in .gitignore!)
LINKBREAKERS_SECRET_KEY=eyJhbGciOiJI...  # ❌ Never commit this
```

**✅ Correct: Use appropriate keys for each environment**
```typescript
// Frontend - publishable key
const frontendConfig = new Configuration({
  accessToken: process.env.NEXT_PUBLIC_LINKBREAKERS_PK,
});

// Backend - secret key
const backendConfig = new Configuration({
  accessToken: process.env.LINKBREAKERS_SECRET_KEY,
});
```

## Testing with both key types

Test your integration security:

### Test publishable key restrictions

```bash
# This should FAIL with 403 Forbidden
curl -X POST https://api.linkbreakers.com/v1/links \
  -H "Authorization: Bearer your_publishable_jwt" \
  -H "Content-Type: application/json" \
  -d '{"destination":"https://example.com"}'

# Response: {"error": "insufficient_permissions", "message": "Publishable keys cannot create links"}
```

### Test secret key access

```bash
# This should SUCCEED with 200 OK
curl -X POST https://api.linkbreakers.com/v1/links \
  -H "Authorization: Bearer your_secret_jwt" \
  -H "Content-Type: application/json" \
  -d '{"destination":"https://example.com"}'

# Response: {"link": {"id": "...", "shortlink": "https://lbr.ai/abc123"}}
```

## Frequently asked questions

**Can I use the same key for frontend and backend?**

No. Use publishable keys for frontend (limited permissions) and secret keys for backend (full access). This separation ensures your backend remains secure even if frontend code is exposed.

**What happens if someone steals my publishable key?**

They can only identify visitors who clicked your Linkbreakers links (requires LBID). They cannot create links, access analytics, or view your workspace data. The damage is minimal compared to secret key exposure.

**Can I revoke a key without creating a new one?**

Yes, delete the key from your dashboard. All requests using that JWT will immediately fail with 401 Unauthorized. However, you'll need to create a new key to restore functionality.

**How do I know which type of key I have?**

Decode the JWT at [jwt.io](https://jwt.io) and check the `type` claim: `"type": "secret"` or `"type": "publishable"`. You can also check the `scopes` array to see what permissions it has.

**Do publishable keys expire?**

Both key types can optionally have expiration timestamps (`exp` claim). Check your JWT payload or dashboard settings for expiration details.

## Related documentation

- [TypeScript SDK Quick Start](/help/article/typescript-sdk-quickstart) - Learn how to use both key types in TypeScript applications
- [Python SDK Quick Start](/help/article/python-sdk-quickstart) - Backend integration with secret keys
- [Why accessToken vs apiKey](/help/article/api-authentication-accesstoken-vs-apikey) - Understanding JWT authentication
- [What is LBID?](/help/article/what-is-the-linkbreakers-id-lbid) - How visitor identification works with publishable keys

## Last reviewed

This article was last reviewed on March 20, 2025.
