Skip to main content

Authentication & Security

Chartly provides multiple authentication methods to fit different use cases, from quick trials to production deployments with advanced security requirements.

Authentication Methods Overview

MethodUse CaseSecuritySetup
Trial API KeyTesting, demosBasicNo signup
API KeysProduction appsStandardAccount required via dashboard
Signed URLsPublic sharingHighHMAC signing

Note: Production API keys are managed through the Chartly Dashboard. Account creation, login, and API key management are handled via the web interface.

Trial API Keys

Perfect for testing and evaluation without any signup requirements.

Getting a Trial Key

curl -X POST "https://api.chartly.dev/signup/anon"

Response:

{
"api_key": "trial_abc123def456xyz789"
}

Trial Limitations

  • 100 total chart renders
  • 30-day expiration
  • All chart types and formats
  • ❌ No account management
  • ❌ No usage analytics
  • ❌ No API key regeneration

Using Trial Keys

curl "https://api.chartly.dev/v1/chart" \
-H "X-Api-Key: trial_abc123def456xyz789" \
-H "Content-Type: application/json" \
-d '{"chart": {...}, "width": 400, "height": 300}'

Storage and Management

Trial keys are automatically stored in your browser's localStorage when generated through the web interface:

// Checking for existing trial key
const existingKey = localStorage.getItem('trial_api_key');
const keyCreated = localStorage.getItem('trial_api_key_created');

// Keys expire after 30 days
const isExpired = (Date.now() - parseInt(keyCreated)) > (30 * 24 * 60 * 60 * 1000);

Production API Keys

Full-featured API keys for production applications with account management and analytics.

Getting Production Keys

Production API keys are managed through the Chartly Dashboard:

  1. Create Account - Sign up at chartly.dev
  2. Access Dashboard - Log in to your account
  3. Generate Keys - Create API keys for different environments
  4. Monitor Usage - Track analytics and billing

API Key Best Practices

  1. Separate Keys by Environment

    • Use different API keys for development, staging, and production
    • Label keys clearly for easy identification
  2. Rotate Keys Regularly

    • Generate new keys periodically via the dashboard
    • Delete unused or compromised keys immediately
  3. Secure Storage

    • Store keys in environment variables, not in code
    • Use secret management services in production
  4. Monitor Usage

    • Check your dashboard for usage analytics
    • Set up alerts for unusual activity

Environment Variable Setup

# .env file
CHARTLY_API_KEY=live_your_production_key_here

# Development environment
CHARTLY_DEV_API_KEY=live_your_development_key_here
// Node.js usage
const apiKey = process.env.CHARTLY_API_KEY;

const response = await fetch('https://api.chartly.dev/v1/chart', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Api-Key': apiKey
},
body: JSON.stringify({
chart: chartConfig,
width: 600,
height: 400
})
});

Signed URLs

Secure, time-limited URLs for public sharing without exposing your API keys. Perfect for email reports, public dashboards, and sharing charts with external parties.

How Signed URLs Work

  1. Generate a URL with chart parameters
  2. Add expiration timestamp
  3. Create HMAC-SHA256 signature using your secret key
  4. Append signature to URL

Generating Signed URLs

const crypto = require('crypto');

function createSignedChartUrl(chartConfig, options = {}) {
const {
width = 600,
height = 400,
format = 'png',
backgroundColor = 'white',
expirationMinutes = 60,
secretKey = process.env.CHARTLY_SECRET_KEY
} = options;

// Create URL parameters
const params = new URLSearchParams({
chart: JSON.stringify(chartConfig),
width: width.toString(),
height: height.toString(),
format,
backgroundColor
});

// Add expiration timestamp (Unix timestamp)
const exp = Math.floor(Date.now() / 1000) + (expirationMinutes * 60);
params.append('exp', exp.toString());

// Create signature
const message = params.toString();
const signature = crypto
.createHmac('sha256', secretKey)
.update(message)
.digest('hex');

params.append('sig', signature);

return `https://api.chartly.dev/v1/chart?${params.toString()}`;
}

// Usage
const chartConfig = {
type: 'bar',
data: {
labels: ['Q1', 'Q2', 'Q3', 'Q4'],
datasets: [{
label: 'Revenue',
data: [100, 120, 115, 134]
}]
},
options: { responsive: false }
};

const signedUrl = createSignedChartUrl(chartConfig, {
width: 800,
height: 400,
expirationMinutes: 120 // 2 hours
});

Using Signed URLs

The generated URL can be used directly in any context:

<!-- In HTML emails -->
<img src="https://api.chartly.dev/v1/chart?chart=...&exp=1642262400&sig=abc123..."
alt="Quarterly Revenue Chart"
width="800"
height="400">
<!-- In Slack/Discord -->
![Revenue Chart](https://api.chartly.dev/v1/chart?chart=...&exp=1642262400&sig=abc123...)

Signed URL Security

  • Time-limited: URLs automatically expire
  • Tamper-proof: Any modification invalidates the signature
  • Secret-based: Requires your secret key to generate
  • No API key exposure: Safe for public sharing

Managing Secret Keys

Your secret key should be:

  • Stored securely (environment variables)
  • Never committed to version control
  • Rotated periodically
  • Different for each environment
# Environment setup
CHARTLY_SECRET_KEY=your-256-bit-secret-key-here
CHARTLY_SECRET_KEY_DEV=your-development-secret-key-here

Error Handling

Authentication Errors

Error CodeStatusDescriptionSolution
unauthorized401Invalid API keyCheck key format and validity
forbidden403Insufficient permissionsCheck account status
trial_limit_reached429Trial usage exhaustedUpgrade account
rate_limit_exceeded429Too many requestsImplement rate limiting

Error Response Format

{
"error": "unauthorized",
"detail": "Invalid or missing API key"
}

Handling Authentication Errors

async function makeChartRequest(chartConfig) {
try {
const response = await fetch('https://api.chartly.dev/v1/chart', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Api-Key': process.env.CHARTLY_API_KEY
},
body: JSON.stringify(chartConfig)
});

if (response.status === 401) {
throw new Error('Invalid API key - please check your credentials');
}

if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After');
throw new Error(`Rate limit exceeded. Retry after ${retryAfter} seconds`);
}

if (!response.ok) {
const error = await response.json();
throw new Error(`Chart generation failed: ${error.detail}`);
}

return await response.blob();
} catch (error) {
console.error('Chart generation error:', error.message);
throw error;
}
}

Security Best Practices

API Key Security

  1. Environment Variables

    # Never hardcode keys
    ❌ const apiKey = "live_abc123...";

    # Use environment variables
    ✅ const apiKey = process.env.CHARTLY_API_KEY;
  2. Secure Transmission

    • Always use HTTPS
    • Never log API keys
    • Don't include keys in URLs (except signed URLs)
  3. Access Control

    • Use different keys for different environments
    • Regularly rotate keys via the dashboard
    • Monitor usage for unusual activity

Signed URL Security

  1. Short Expiration Times

    // For emails: 24-48 hours
    expirationMinutes: 24 * 60

    // For real-time sharing: 1-2 hours
    expirationMinutes: 120
  2. Strong Secret Keys

    # Generate secure random keys
    openssl rand -hex 32
  3. Signature Validation

    • Always validate expiration
    • Verify signature before serving
    • Use constant-time comparison

Integration Examples

React Hook for Authentication

import { useState, useEffect } from 'react';

function useChartlyAuth() {
const [apiKey, setApiKey] = useState(null);
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [loading, setLoading] = useState(true);

useEffect(() => {
// Check for stored trial key
const trialKey = localStorage.getItem('trial_api_key');
const keyCreated = localStorage.getItem('trial_api_key_created');

if (trialKey && keyCreated) {
const isExpired = (Date.now() - parseInt(keyCreated)) > (30 * 24 * 60 * 60 * 1000);

if (!isExpired) {
setApiKey(trialKey);
setIsAuthenticated(true);
} else {
localStorage.removeItem('trial_api_key');
localStorage.removeItem('trial_api_key_created');
}
}

setLoading(false);
}, []);

const generateTrialKey = async () => {
try {
const response = await fetch('https://api.chartly.dev/signup/anon', {
method: 'POST'
});

const data = await response.json();

localStorage.setItem('trial_api_key', data.api_key);
localStorage.setItem('trial_api_key_created', Date.now().toString());

setApiKey(data.api_key);
setIsAuthenticated(true);

return data.api_key;
} catch (error) {
console.error('Failed to generate trial key:', error);
throw error;
}
};

return {
apiKey,
isAuthenticated,
loading,
generateTrialKey
};
}

Express.js Middleware

function chartlyAuth(req, res, next) {
const apiKey = req.headers['x-api-key'] || process.env.CHARTLY_API_KEY;

if (!apiKey) {
return res.status(401).json({
error: 'unauthorized',
detail: 'API key required'
});
}

req.chartlyApiKey = apiKey;
next();
}

// Usage
app.post('/generate-chart', chartlyAuth, async (req, res) => {
try {
const response = await fetch('https://api.chartly.dev/v1/chart', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Api-Key': req.chartlyApiKey
},
body: JSON.stringify(req.body)
});

const chartBuffer = await response.buffer();
res.setHeader('Content-Type', 'image/png');
res.send(chartBuffer);
} catch (error) {
res.status(500).json({ error: 'Chart generation failed' });
}
});

Next Steps: