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

# Authentication

> Authenticate with the ParsaLink API using JWT tokens or API keys.

<div
  style={{ 
display: "inline-flex",
alignItems: "center",
gap: "8px",
background: "linear-gradient(90deg, #143D4E 0%, #1C758A 100%)",
color: "#FFFFFF",
fontSize: "12px",
fontWeight: "700",
marginBottom: "10px",
padding: "8px 14px",
letterSpacing: "0.08em",
textTransform: "uppercase",
borderRadius: "999px",
boxShadow: "0 8px 24px rgba(20, 61, 78, 0.18)"
}}
>
  Authentication
</div>

## Login

<div
  style={{

border: "1px solid rgba(48, 200, 202, 0.22)",
borderRadius: "16px",
padding: "16px 18px",
marginBottom: "20px",
color: "#2C768D",
boxShadow: "0 10px 26px rgba(28, 117, 138, 0.08)"
}}
  className="dark:!bg-[#0B0B0B] dark:!border-gray-800 dark:!text-cyan-300 dark:shadow-none"
>
  Exchange your email and password for a JWT access token and refresh token.
</div>

```http theme={null}
POST /api/auth/login/
```

**Request body:**

```json theme={null}
{
  "email": "you@example.com",
  "password": "your_password"
}
```

**Response:**

```json theme={null}
{
  "access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMDFIWC4uLiIsImV4cCI6MTcxMTUwMzYwMH0...",
  "refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMDFIWC4uLiIsImV4cCI6MTcxNDA2NTYwMH0...",
  "user": {
    "id": "usr_01HX...",
    "email": "you@example.com",
    "firstName": "Jane",
    "lastName": "Smith",
    "workspaceId": "ws_01HX..."
  }
}
```

<div
  style={{

border: "1px solid rgba(48, 200, 202, 0.22)",
borderRadius: "16px",
padding: "16px 18px",
marginBottom: "20px",
color: "#2C768D",
boxShadow: "0 10px 26px rgba(28, 117, 138, 0.08)"
}}
  className="dark:!bg-[#0B0B0B] dark:!border-gray-800 dark:!text-cyan-300 dark:shadow-none"
>
  The `access` token expires after **1 hour**. The `refresh` token expires after **30 days**.
</div>

***

<div
  style={{ 
display: "inline-flex",
alignItems: "center",
gap: "8px",
background: "rgba(48, 200, 202, 0.12)",
color: "#1C758A",
fontSize: "12px",
fontWeight: "700",
marginBottom: "10px",
padding: "8px 14px",
letterSpacing: "0.08em",
textTransform: "uppercase",
borderRadius: "999px",
border: "1px solid rgba(48, 200, 202, 0.28)"
}}
>
  Tokens
</div>

## Token Refresh

<div
  style={{

border: "1px solid rgba(48, 200, 202, 0.22)",
borderRadius: "16px",
padding: "16px 18px",
marginBottom: "20px",
color: "#2C768D",
boxShadow: "0 10px 26px rgba(28, 117, 138, 0.08)"
}}
  className="dark:!bg-[#0B0B0B] dark:!border-gray-800 dark:!text-cyan-300 dark:shadow-none"
>
  Use the refresh token to obtain a new access token without re-authenticating.
</div>

```http theme={null}
POST /api/auth/token/refresh/
```

**Request body:**

```json theme={null}
{
  "refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
```

**Response:**

```json theme={null}
{
  "access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
```

<div
  style={{

border: "1px solid rgba(48, 200, 202, 0.22)",
borderRadius: "16px",
padding: "16px 18px",
marginBottom: "20px",
color: "#2C768D",
boxShadow: "0 10px 26px rgba(28, 117, 138, 0.08)"
}}
  className="dark:!bg-[#0B0B0B] dark:!border-gray-800 dark:!text-cyan-300 dark:shadow-none"
>
  If the refresh token has expired, you must log in again to get a new pair of tokens. Refresh tokens are rotated on each use — the old refresh token is invalidated when a new one is issued.
</div>

***

<div
  style={{ 
display: "inline-flex",
alignItems: "center",
gap: "8px",
background: "rgba(255, 111, 97, 0.12)",
color: "#FF6F61",
fontSize: "12px",
fontWeight: "700",
marginBottom: "10px",
padding: "8px 14px",
letterSpacing: "0.08em",
textTransform: "uppercase",
borderRadius: "999px",
border: "1px solid rgba(255, 111, 97, 0.24)"
}}
>
  Usage
</div>

## Using the Access Token

<div
  style={{

border: "1px solid rgba(48, 200, 202, 0.22)",
borderRadius: "16px",
padding: "16px 18px",
marginBottom: "20px",
color: "#2C768D",
boxShadow: "0 10px 26px rgba(28, 117, 138, 0.08)"
}}
  className="dark:!bg-[#0B0B0B] dark:!border-gray-800 dark:!text-cyan-300 dark:shadow-none"
>
  Include the access token in the `Authorization` header of every API request:
</div>

```http theme={null}
GET /api/contacts/
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```

***

<div
  style={{ 
display: "inline-flex",
alignItems: "center",
gap: "8px",
background: "linear-gradient(90deg, #143D4E 0%, #1C758A 100%)",
color: "#FFFFFF",
fontSize: "12px",
fontWeight: "700",
marginBottom: "10px",
padding: "8px 14px",
letterSpacing: "0.08em",
textTransform: "uppercase",
borderRadius: "999px",
boxShadow: "0 8px 24px rgba(20, 61, 78, 0.18)"
}}
>
  API Keys
</div>

## API Key Authentication

<div
  style={{

border: "1px solid rgba(48, 200, 202, 0.22)",
borderRadius: "16px",
padding: "16px 18px",
marginBottom: "20px",
color: "#2C768D",
boxShadow: "0 10px 26px rgba(28, 117, 138, 0.08)"
}}
  className="dark:!bg-[#0B0B0B] dark:!border-gray-800 dark:!text-cyan-300 dark:shadow-none"
>
  For server-to-server integrations where storing user credentials is not ideal, ParsaLink supports workspace-level API keys.
</div>

### Creating an API Key

1. Go to **Settings → API Keys**.
2. Click **Create API Key**.
3. Give the key a name (e.g., "Zapier Integration" or "Internal Dashboard").
4. Copy the key — it is only shown once.

<Warning>
  API keys are shown only at the time of creation. If you lose a key, you must revoke it and create a new one. Store keys securely in environment variables or a secrets manager — never in client-side code or version control.
</Warning>

***

<div
  style={{ 
display: "inline-flex",
alignItems: "center",
gap: "8px",
background: "rgba(48, 200, 202, 0.12)",
color: "#1C758A",
fontSize: "12px",
fontWeight: "700",
marginBottom: "10px",
padding: "8px 14px",
letterSpacing: "0.08em",
textTransform: "uppercase",
borderRadius: "999px",
border: "1px solid rgba(48, 200, 202, 0.28)"
}}
>
  Usage
</div>

### Using an API Key

<div
  style={{

border: "1px solid rgba(48, 200, 202, 0.22)",
borderRadius: "16px",
padding: "16px 18px",
marginBottom: "20px",
color: "#2C768D",
boxShadow: "0 10px 26px rgba(28, 117, 138, 0.08)"
}}
  className="dark:!bg-[#0B0B0B] dark:!border-gray-800 dark:!text-cyan-300 dark:shadow-none"
>
  API keys use the same `Authorization: Bearer` header format:
</div>

```http theme={null}
GET /api/contacts/
Authorization: Bearer pk_live_01HX...
```

<div
  style={{

border: "1px solid rgba(48, 200, 202, 0.22)",
borderRadius: "16px",
padding: "16px 18px",
marginBottom: "20px",
color: "#2C768D",
boxShadow: "0 10px 26px rgba(28, 117, 138, 0.08)"
}}
  className="dark:!bg-[#0B0B0B] dark:!border-gray-800 dark:!text-cyan-300 dark:shadow-none"
>
  API keys are scoped to the workspace, not to an individual user. Actions performed with an API key are attributed to a system user in activity logs.
</div>

***

<div
  style={{ 
display: "inline-flex",
alignItems: "center",
gap: "8px",
background: "rgba(255, 111, 97, 0.12)",
color: "#FF6F61",
fontSize: "12px",
fontWeight: "700",
marginBottom: "10px",
padding: "8px 14px",
letterSpacing: "0.08em",
textTransform: "uppercase",
borderRadius: "999px",
border: "1px solid rgba(255, 111, 97, 0.24)"
}}
>
  Management
</div>

### Revoking an API Key

Go to **Settings → API Keys**, find the key, and click **Revoke**. The key stops working immediately.

***

<div
  style={{ 
display: "inline-flex",
alignItems: "center",
gap: "8px",
background: "linear-gradient(90deg, #143D4E 0%, #1C758A 100%)",
color: "#FFFFFF",
fontSize: "12px",
fontWeight: "700",
marginBottom: "10px",
padding: "8px 14px",
letterSpacing: "0.08em",
textTransform: "uppercase",
borderRadius: "999px",
boxShadow: "0 8px 24px rgba(20, 61, 78, 0.18)"
}}
>
  Example
</div>

## Example: Full Authentication Flow

<div
  style={{

border: "1px solid rgba(48, 200, 202, 0.22)",
borderRadius: "16px",
padding: "16px 18px",
marginBottom: "20px",
color: "#2C768D",
boxShadow: "0 10px 26px rgba(28, 117, 138, 0.08)"
}}
  className="dark:!bg-[#0B0B0B] dark:!border-gray-800 dark:!text-cyan-300 dark:shadow-none"
>
  Example flow showing login, authenticated request, and token refresh.
</div>

```python theme={null}
import requests

# Step 1: Log in
response = requests.post("https://api.parsalink.io/api/auth/login/", json={
    "email": "you@example.com",
    "password": "your_password"
})
tokens = response.json()
access_token = tokens["access"]
refresh_token = tokens["refresh"]

# Step 2: Make an authenticated request
headers = {"Authorization": f"Bearer {access_token}"}
contacts = requests.get("https://api.parsalink.io/api/contacts/", headers=headers)

# Step 3: Refresh when token expires
new_tokens = requests.post("https://api.parsalink.io/api/auth/token/refresh/", json={
    "refresh": refresh_token
})
access_token = new_tokens.json()["access"]
```

***

<div
  style={{ 
display: "inline-flex",
alignItems: "center",
gap: "8px",
background: "rgba(48, 200, 202, 0.12)",
color: "#1C758A",
fontSize: "12px",
fontWeight: "700",
marginBottom: "10px",
padding: "8px 14px",
letterSpacing: "0.08em",
textTransform: "uppercase",
borderRadius: "999px",
border: "1px solid rgba(48, 200, 202, 0.28)"
}}
>
  Security
</div>

## Security Best Practices

<div
  style={{

border: "1px solid rgba(48, 200, 202, 0.22)",
borderRadius: "16px",
padding: "16px 18px",
marginBottom: "20px",
color: "#2C768D",
boxShadow: "0 10px 26px rgba(28, 117, 138, 0.08)"
}}
  className="dark:!bg-[#0B0B0B] dark:!border-gray-800 dark:!text-cyan-300 dark:shadow-none"
>
  Follow these best practices to keep your API usage secure.
</div>

* **Never hardcode credentials** — Use environment variables for tokens and API keys.
* **Store refresh tokens securely** — Treat them like passwords.
* **Rotate API keys regularly** — Create a new key and revoke the old one periodically.
* **Use HTTPS only** — All API communication must be over TLS. HTTP is not supported.
* **Monitor key usage** — Review API key activity logs in **Settings → API Keys** to detect unauthorized access.
