MemosMemos
API Documentation

API Authentication

Learn how to authenticate with the Memos API using access tokens and manage API security.

API Authentication

The Memos API uses token-based authentication to secure access to your data. This guide covers everything you need to know about API authentication, token management, and security best practices.

Authentication Methods

Memos API supports the following authentication methods:

Access Tokens

Access tokens are the recommended way to authenticate with the Memos API. They provide secure, long-lived authentication without exposing user credentials.

Creating Access Tokens

Via Web Interface

  1. Login to your Memos instance
  2. Navigate to SettingsAccess Tokens
  3. Click "Create Token"
  4. Configure token settings:
    • Name: Descriptive name for the token
    • Description: Optional description
    • Expires: Token expiration date (optional)
  5. Copy the token - it's only shown once!

Save Your Token: Access tokens are only displayed once during creation. Store them securely immediately after creation.

Via API (Admin Only)

curl -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -d '{
    "name": "My API Token",
    "description": "Token for automation scripts",
    "expiresAt": "2026-08-19T00:00:00Z"
  }' \
  "$MEMOS_HOST/api/v1/access-tokens"

Using Access Tokens

Include the token in the Authorization header using the Bearer scheme:

curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
     "$MEMOS_HOST/api/v1/memos"

Examples by Language

JavaScript/Node.js
const MEMOS_API_TOKEN = process.env.MEMOS_API_TOKEN;
const MEMOS_HOST = process.env.MEMOS_HOST;

const response = await fetch(`${MEMOS_HOST}/api/v1/memos`, {
  headers: {
    'Authorization': `Bearer ${MEMOS_API_TOKEN}`,
    'Content-Type': 'application/json',
  },
});

const memos = await response.json();
Python
import os
import requests

MEMOS_API_TOKEN = os.environ['MEMOS_API_TOKEN']
MEMOS_HOST = os.environ['MEMOS_HOST']

headers = {
    'Authorization': f'Bearer {MEMOS_API_TOKEN}',
    'Content-Type': 'application/json',
}

response = requests.get(f'{MEMOS_HOST}/api/v1/memos', headers=headers)
memos = response.json()
Go
package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
)

func main() {
    token := os.Getenv("MEMOS_API_TOKEN")
    host := os.Getenv("MEMOS_HOST")
    
    client := &http.Client{}
    req, _ := http.NewRequest("GET", host+"/api/v1/memos", nil)
    req.Header.Add("Authorization", "Bearer "+token)
    
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    
    body, _ := io.ReadAll(resp.Body)
    fmt.Println(string(body))
}
PHP
<?php
$token = $_ENV['MEMOS_API_TOKEN'];
$host = $_ENV['MEMOS_HOST'];

$context = stream_context_create([
    'http' => [
        'method' => 'GET',
        'header' => [
            'Authorization: Bearer ' . $token,
            'Content-Type: application/json',
        ],
    ],
]);

$response = file_get_contents($host . '/api/v1/memos', false, $context);
$memos = json_decode($response, true);
?>

Token Management

List Access Tokens

View all your access tokens:

curl -H "Authorization: Bearer $MEMOS_TOKEN" \
     "$MEMOS_HOST/api/v1/access-tokens"

Token Information

Get details about a specific token:

curl -H "Authorization: Bearer $MEMOS_TOKEN" \
     "$MEMOS_HOST/api/v1/access-tokens/{token-id}"

Revoke Token

Delete/revoke an access token:

curl -X DELETE \
     -H "Authorization: Bearer $MEMOS_TOKEN" \
     "$MEMOS_HOST/api/v1/access-tokens/{token-id}"

Token Revocation: Revoked tokens become invalid immediately. Any applications using the token will need to be updated with a new token.

Session Authentication

Session authentication uses browser cookies and is primarily for web applications that integrate directly with the Memos web interface.

Login Process

// Login to get session cookie
const loginResponse = await fetch('/api/v1/auth/signin', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  credentials: 'include', // Important: include cookies
  body: JSON.stringify({
    username: 'your-username',
    password: 'your-password',
  }),
});

// Subsequent requests use the session cookie automatically
const memosResponse = await fetch('/api/v1/memos', {
  credentials: 'include', // Include cookies
});

Session Management

Check Session Status

curl -c cookies.txt -b cookies.txt \
     "$MEMOS_HOST/api/v1/auth/status"

Logout

curl -X POST \
     -c cookies.txt -b cookies.txt \
     "$MEMOS_HOST/api/v1/auth/signout"

Security Best Practices

Token Storage

✅ Secure Storage

# Environment variables
export MEMOS_API_TOKEN="your-token-here"

# Configuration files (with proper permissions)
chmod 600 ~/.memos/config
echo "token=your-token-here" > ~/.memos/config

# Secure credential stores
# macOS Keychain, Windows Credential Store, Linux Secret Service

❌ Insecure Storage

// Never store tokens in client-side code
const apiToken = "memos_access_token_123456"; // DON'T DO THIS

// Never commit tokens to version control
// tokens.js
export const MEMOS_TOKEN = "memos_access_token_123456"; // DON'T DO THIS

Token Lifecycle Management

Token Rotation

#!/bin/bash
# Token rotation script

OLD_TOKEN=$MEMOS_API_TOKEN

# Create new token
NEW_TOKEN=$(curl -X POST \
  -H "Authorization: Bearer $OLD_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Rotated Token"}' \
  "$MEMOS_HOST/api/v1/access-tokens" | jq -r '.accessToken')

# Update environment/config with new token
export MEMOS_API_TOKEN=$NEW_TOKEN

# Verify new token works
if curl -H "Authorization: Bearer $NEW_TOKEN" "$MEMOS_HOST/api/v1/auth/status" > /dev/null; then
  # Revoke old token
  curl -X DELETE -H "Authorization: Bearer $NEW_TOKEN" "$MEMOS_HOST/api/v1/access-tokens/$OLD_TOKEN_ID"
  echo "Token rotated successfully"
else
  echo "New token verification failed"
  export MEMOS_API_TOKEN=$OLD_TOKEN
fi

Token Expiration

class MemosAPI {
  constructor(baseURL, token) {
    this.baseURL = baseURL;
    this.token = token;
    this.tokenExpiry = null;
  }

  async checkTokenExpiry() {
    try {
      const response = await this.request('GET', '/api/v1/auth/status');
      this.tokenExpiry = response.user.tokenExpiry;
      
      if (this.tokenExpiry && Date.now() > new Date(this.tokenExpiry)) {
        throw new Error('Token expired');
      }
    } catch (error) {
      if (error.status === 401) {
        throw new Error('Token invalid or expired');
      }
      throw error;
    }
  }
}

Network Security

HTTPS Only

// Always use HTTPS in production
const MEMOS_HOST = 'https://memos.example.com'; // ✅ Secure
// const MEMOS_HOST = 'http://memos.example.com'; // ❌ Insecure

Certificate Validation

import requests

# Enable certificate verification (default)
response = requests.get(
    'https://memos.example.com/api/v1/memos',
    headers={'Authorization': f'Bearer {token}'},
    verify=True  # Always True in production
)

# Custom CA bundle if needed
response = requests.get(
    'https://memos.example.com/api/v1/memos',
    headers={'Authorization': f'Bearer {token}'},
    verify='/path/to/ca-bundle.crt'
)

Error Handling

Authentication Errors

async function handleAuthError(response) {
  if (response.status === 401) {
    const error = await response.json();
    
    switch (error.code) {
      case 'UNAUTHENTICATED':
        throw new Error('Missing or invalid token');
      case 'TOKEN_EXPIRED':
        throw new Error('Token has expired');
      case 'TOKEN_REVOKED':
        throw new Error('Token has been revoked');
      default:
        throw new Error('Authentication failed');
    }
  }
}

// Usage
try {
  const response = await fetch('/api/v1/memos', {
    headers: { 'Authorization': `Bearer ${token}` }
  });
  
  if (!response.ok) {
    await handleAuthError(response);
  }
  
  return await response.json();
} catch (error) {
  console.error('Authentication error:', error.message);
  // Handle token renewal or user re-authentication
}

Retry Logic

async function apiRequestWithRetry(url, options, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch(url, options);
      
      if (response.status === 401 && i < maxRetries - 1) {
        // Try to refresh token
        await refreshToken();
        options.headers['Authorization'] = `Bearer ${getNewToken()}`;
        continue;
      }
      
      return response;
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      await sleep(1000 * Math.pow(2, i)); // Exponential backoff
    }
  }
}

Advanced Authentication

Custom Authentication Middleware

Express.js Middleware

const memosAuth = (req, res, next) => {
  const token = req.headers.authorization?.replace('Bearer ', '');
  
  if (!token) {
    return res.status(401).json({ error: 'Missing token' });
  }
  
  // Verify token with Memos API
  fetch(`${MEMOS_HOST}/api/v1/auth/status`, {
    headers: { 'Authorization': `Bearer ${token}` }
  })
  .then(response => {
    if (response.ok) {
      return response.json();
    }
    throw new Error('Invalid token');
  })
  .then(data => {
    req.user = data.user;
    next();
  })
  .catch(error => {
    res.status(401).json({ error: 'Authentication failed' });
  });
};

// Usage
app.get('/api/user-memos', memosAuth, (req, res) => {
  // req.user contains authenticated user info
  res.json({ user: req.user });
});

Proxy Authentication

# Nginx proxy with authentication headers
location /api/ {
    proxy_pass http://memos-backend;
    proxy_set_header Authorization $http_authorization;
    proxy_set_header X-Original-URI $request_uri;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

Testing Authentication

Test Script

#!/bin/bash
# Test authentication setup

MEMOS_HOST="https://your-memos-instance.com"
MEMOS_TOKEN="your-access-token"

echo "Testing Memos API authentication..."

# Test 1: Check authentication status
echo "1. Checking auth status..."
response=$(curl -s -w "%{http_code}" -H "Authorization: Bearer $MEMOS_TOKEN" "$MEMOS_HOST/api/v1/auth/status")
status_code=$(echo "$response" | tail -n1)

if [ "$status_code" = "200" ]; then
    echo "✅ Authentication successful"
else
    echo "❌ Authentication failed (HTTP $status_code)"
    exit 1
fi

# Test 2: Test API access
echo "2. Testing API access..."
memos_response=$(curl -s -w "%{http_code}" -H "Authorization: Bearer $MEMOS_TOKEN" "$MEMOS_HOST/api/v1/memos?limit=1")
memos_status=$(echo "$memos_response" | tail -n1)

if [ "$memos_status" = "200" ]; then
    echo "✅ API access successful"
else
    echo "❌ API access failed (HTTP $memos_status)"
    exit 1
fi

echo "🎉 All authentication tests passed!"

Next Steps


Need help with authentication? Check the troubleshooting guide or ask in our community discussions.