API Reference

Authentication

Token authentication for ZineCore2 API write operations

The ZineCore2 API uses token-based authentication for write operations. Read operations are public and do not require authentication.

Authentication Model

Public Read Access

All GET requests are public — no authentication required:

# These work without authentication
curl http://localhost:8000/api/zines/
curl http://localhost:8000/api/agents/
curl http://localhost:8000/api/holdings/
curl http://localhost:8000/api/repositories/

Anyone can:

  • List resources
  • Retrieve individual records
  • Search and filter
  • Export data in all formats

Authenticated Write Access

POST, PUT, PATCH, DELETE require authentication:

# These require Authorization header
curl -X POST http://localhost:8000/api/zines/ \
  -H "Authorization: Token YOUR_TOKEN" \
  -d '{...}'

Authenticated users can:

  • Create new records
  • Update existing records
  • Delete records

Token Authentication

ZineCore2 uses Django REST Framework Token Authentication.

How It Works

  1. User creates account (via Django admin or registration)
  2. User obtains token (via /api/auth/token/ endpoint)
  3. User includes token in Authorization header for all write requests
  4. Server validates token and authorizes the request

Token Characteristics

  • One token per user — tokens are unique to each user account
  • Persistent — tokens don't expire (unless manually regenerated)
  • Random — 40-character hexadecimal string
  • Secure — transmitted via HTTPS in production

Obtaining a Token

Create User Account

First, create a user account via the Django admin interface:

  1. Visit http://localhost:8000/admin/
  2. Log in with superuser credentials
  3. Navigate to Authentication and Authorization > Users
  4. Click Add User
  5. Set username and password
  6. Save

Get Token

Request a token using username and password:

POST /api/auth/token/
Content-Type: application/json

Request body:

{
  "username": "your_username",
  "password": "your_password"
}

Example:

curl -X POST http://localhost:8000/api/auth/token/ \
  -H "Content-Type: application/json" \
  -d '{
    "username": "admin",
    "password": "yourpassword"
  }'

Response (200 OK):

{
  "token": "9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b"
}

Save this token — you'll use it for all authenticated requests.

Invalid Credentials

Response (400 Bad Request):

{
  "non_field_errors": [
    "Unable to log in with provided credentials."
  ]
}

Using Tokens

Authorization Header

Include the token in the Authorization header:

Authorization: Token YOUR_TOKEN_HERE

Format: Token {token} (note the space after "Token")

Examples

Create a zine:

curl -X POST http://localhost:8000/api/zines/ \
  -H "Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b" \
  -H "Content-Type: application/json" \
  -d '{
    "zine_id": "zine_my_zine",
    "title": "My Zine",
    "creator": ["agent_me"],
    "subject": ["feminism"],
    "genre": ["personal-zine"],
    "date": ["2024"],
    "language": ["en"],
    "rights": ["cc-by-4.0"]
  }'

Update a zine:

curl -X PATCH http://localhost:8000/api/zines/zine_my_zine/ \
  -H "Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b" \
  -H "Content-Type: application/json" \
  -d '{
    "abstract": "Updated description"
  }'

Delete a zine:

curl -X DELETE http://localhost:8000/api/zines/zine_my_zine/ \
  -H "Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b"

Code Examples

Python

import requests

API_BASE = "http://localhost:8000/api"

# Obtain token
auth_response = requests.post(
    f"{API_BASE}/auth/token/",
    json={
        "username": "admin",
        "password": "yourpassword"
    }
)

token = auth_response.json()["token"]
print(f"Token: {token}")

# Use token for authenticated requests
headers = {
    "Authorization": f"Token {token}",
    "Content-Type": "application/json"
}

# Create zine
zine_data = {
    "zine_id": "zine_my_zine",
    "title": "My Zine",
    "creator": ["agent_me"],
    "subject": ["feminism"],
    "genre": ["personal-zine"],
    "date": ["2024"],
    "language": ["en"],
    "rights": ["cc-by-4.0"]
}

response = requests.post(
    f"{API_BASE}/zines/",
    headers=headers,
    json=zine_data
)

if response.status_code == 201:
    print(f"Created: {response.json()['title']}")
else:
    print(f"Error: {response.status_code} - {response.json()}")

JavaScript

const API_BASE = "http://localhost:8000/api";

// Obtain token
const authResponse = await fetch(`${API_BASE}/auth/token/`, {
  method: "POST",
  headers: {
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    username: "admin",
    password: "yourpassword"
  })
});

const { token } = await authResponse.json();
console.log(`Token: ${token}`);

// Use token for authenticated requests
const zineData = {
  zine_id: "zine_my_zine",
  title: "My Zine",
  creator: ["agent_me"],
  subject: ["feminism"],
  genre: ["personal-zine"],
  date: ["2024"],
  language: ["en"],
  rights: ["cc-by-4.0"]
};

const response = await fetch(`${API_BASE}/zines/`, {
  method: "POST",
  headers: {
    "Authorization": `Token ${token}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify(zineData)
});

if (response.status === 201) {
  const zine = await response.json();
  console.log(`Created: ${zine.title}`);
}

Environment Variables

Best practice: Store token in environment variable:

# .env
ZINECORE_TOKEN=9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b

Python:

import os
import requests

TOKEN = os.getenv("ZINECORE_TOKEN")
headers = {
    "Authorization": f"Token {TOKEN}",
    "Content-Type": "application/json"
}

response = requests.post(
    "http://localhost:8000/api/zines/",
    headers=headers,
    json=zine_data
)

JavaScript:

const TOKEN = process.env.ZINECORE_TOKEN;
const headers = {
  "Authorization": `Token ${TOKEN}`,
  "Content-Type": "application/json"
};

const response = await fetch("http://localhost:8000/api/zines/", {
  method: "POST",
  headers,
  body: JSON.stringify(zineData)
});

Session Authentication (Browsable API)

The Django REST Framework browsable API uses session authentication instead of token authentication.

How to Use

  1. Visit http://localhost:8000/api/ in your web browser
  2. Click Log in in the top right corner
  3. Enter your username and password
  4. You're now authenticated for browsable API use

Features

  • Interactive HTML forms for POST/PUT/PATCH
  • Syntax-highlighted responses
  • No need to include Authorization header (uses session cookie)

Use for:

  • Manual testing
  • API exploration
  • Quick debugging

Not for:

  • API clients (use token authentication)
  • Automated scripts (use token authentication)

Error Responses

Missing Authentication

Request:

curl -X POST http://localhost:8000/api/zines/ \
  -H "Content-Type: application/json" \
  -d '{...}'

Response (401 Unauthorized):

{
  "detail": "Authentication credentials were not provided."
}

Fix: Include Authorization: Token YOUR_TOKEN header.

Invalid Token

Request:

curl -X POST http://localhost:8000/api/zines/ \
  -H "Authorization: Token invalid_token_here" \
  -d '{...}'

Response (401 Unauthorized):

{
  "detail": "Invalid token."
}

Fix:

  • Check token is correct
  • Regenerate token if needed

Insufficient Permissions

Response (403 Forbidden):

{
  "detail": "You do not have permission to perform this action."
}

Fix:

  • Ensure user account has appropriate permissions
  • Contact administrator to grant permissions

Token Management

Regenerate Token

If your token is compromised or you need a new one:

Via Django shell:

python manage.py shell

from django.contrib.auth import get_user_model
from rest_framework.authtoken.models import Token

User = get_user_model()
user = User.objects.get(username='your_username')

# Delete old token
Token.objects.filter(user=user).delete()

# Create new token
token = Token.objects.create(user=user)
print(f"New token: {token.key}")

Via Django admin:

  1. Visit http://localhost:8000/admin/
  2. Navigate to Auth Token > Tokens
  3. Find your user's token
  4. Delete it
  5. A new token will be created automatically on next login

View Token

Via Django shell:

from rest_framework.authtoken.models import Token
token = Token.objects.get(user__username='your_username')
print(token.key)

Via Django admin:

  1. Visit http://localhost:8000/admin/
  2. Navigate to Auth Token > Tokens
  3. Find your user's token
  4. View the key

Security Best Practices

Use HTTPS in Production

Always use HTTPS when transmitting tokens:

# Production (HTTPS)
curl https://api.zinecore.example.com/api/zines/ \
  -H "Authorization: Token YOUR_TOKEN"

# NOT http:// (insecure!)

Don't Commit Tokens

Never commit tokens to version control:

# .gitignore
.env
*.token
secrets.json

Rotate Tokens Regularly

Regenerate tokens periodically, especially:

  • After suspected compromise
  • When team members leave
  • Every 6-12 months

Limit Token Scope

Consider creating separate user accounts for different purposes:

  • One for development/testing
  • One for production scripts
  • One for each external integration

Monitor Token Usage

Check server logs for unusual patterns:

  • Requests from unexpected IPs
  • High request volumes
  • Failed authentication attempts

Alternative Authentication Methods

ZineCore2 also supports session authentication (for browsable API) but token authentication is recommended for API clients.

Why Token Authentication?

Advantages:

  • Stateless — no server-side session storage
  • Simple — just include token in header
  • Portable — works across different clients
  • Secure — no cookies, no CSRF concerns

Disadvantages:

  • No expiration — tokens don't expire automatically
  • Manual management — must regenerate manually if compromised

Testing Authentication

Test Token Endpoint

# Should succeed
curl -X POST http://localhost:8000/api/auth/token/ \
  -H "Content-Type: application/json" \
  -d '{"username": "admin", "password": "yourpassword"}'

# Should return token
# {"token": "9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b"}

Test Authenticated Request

# Get your token first
TOKEN=$(curl -X POST http://localhost:8000/api/auth/token/ \
  -H "Content-Type: application/json" \
  -d '{"username": "admin", "password": "yourpassword"}' \
  | jq -r '.token')

# Use token for authenticated request
curl -X POST http://localhost:8000/api/zines/ \
  -H "Authorization: Token $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{...}'

Test Public Access

# Should work without authentication
curl http://localhost:8000/api/zines/

# Should return 401 without authentication
curl -X POST http://localhost:8000/api/zines/ \
  -H "Content-Type: application/json" \
  -d '{...}'

Troubleshooting

"Unable to log in with provided credentials"

Cause: Incorrect username or password

Fix:

  • Check username and password are correct
  • Ensure user account exists
  • Reset password via Django admin if needed

"Invalid token"

Cause: Token is incorrect or has been regenerated

Fix:

  • Request a new token via /api/auth/token/
  • Check token for typos
  • Ensure token format is correct: Authorization: Token YOUR_TOKEN

"Authentication credentials were not provided"

Cause: Missing Authorization header

Fix:

  • Include header: -H "Authorization: Token YOUR_TOKEN"
  • Check header name is Authorization (capital A)

Token not working after regeneration

Cause: Using old token

Fix:

  • Delete old token from database
  • Request new token
  • Update your environment variables/config

Next Steps

    • Zines API — Create your first authenticated request
    • Filtering — Advanced filtering and search
    • First Steps — Complete tutorial with authentication
Copyright ©2026 ZineCore2 Contributors,