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​

Included:

  • 100 total chart renders
  • 30-day expiration
  • All chart types and formats

Not included:

  • Account management
  • Usage analytics
  • 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

    # Don't: hardcode keys
    const apiKey = "live_abc123...";

    # Do: read from 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: