Skip to content

API Reference

The CloviCharts API lets you integrate CloviCharts into your own applications and automations. All endpoints are served over HTTPS from https://clovicharts.clovitek.com.

Authentication

Authenticate every request with a Bearer token in the Authorization header. Generate a token from your account settings on the CloviCharts dashboard.

curl https://clovicharts.clovitek.com/api/me \
  -H "Authorization: Bearer $CLOVI_TOKEN"
import requests

resp = requests.get(
    "https://clovicharts.clovitek.com/api/me",
    headers={"Authorization": f"Bearer {token}"},
)
resp.raise_for_status()
print(resp.json())
const resp = await fetch("https://clovicharts.clovitek.com/api/me", {
  headers: { Authorization: `Bearer ${token}` },
});
const data = await resp.json();
console.log(data);
import axios from "axios";

const { data } = await axios.get(
  "https://clovicharts.clovitek.com/api/me",
  { headers: { Authorization: `Bearer ${token}` } }
);
console.log(data);

Keep your token secret

Treat your API token like a password. Send it only over HTTPS and never commit it to source control — load it from an environment variable instead.

Responses & errors

All responses are JSON. Successful calls return 2xx; client errors return 4xx with a JSON body describing the problem. Common status codes:

Status Meaning
200 Success
400 Bad request — check your parameters
401 Missing or invalid token
404 Resource not found
429 Rate limit exceeded — slow down and retry
500 Server error — retry or contact support

CloviCharts API Reference

Service: CloviCharts
Port: 8966
Engine: chartjs-node-canvas
Base URL: http://localhost:8966 (internal) / https://clovicharts.clovitek.com (production)

Rate limit: 120 requests/minute on all /api/ routes (express-rate-limit).

Supported chart types: bar, line, pie, donut, scatter, radar, polar, sparkline, competitive_radar, funnel, bullet, slope, heatcalendar, lollipop, waterfall

Themes: clovitek (dark navy + cyan, default), dark (Catppuccin), light (white)

Output formats: png (default), webp (requires sharp npm package; falls back to png if unavailable)


Authentication

CloviCharts uses the shared CloviTek SSO system. Two auth methods are accepted, checked in order:

  1. cl_session cookie — JWT signed with JWT_SECRET from /root/.api_keys/master.env. This is the primary method for browser sessions authenticated via dash.clovitek.com/login.
  2. X-User-Id internal header — Numeric user ID passed by trusted internal services (sets role: "user", isAdmin: false). No token verification is performed for this path.

Unauthenticated requests to protected routes receive:

{ "error": "Authentication required", "login_url": "https://dash.clovitek.com/login" }

Admin-only routes additionally require the JWT role field to be one of: super_admin, admin, platform_admin. Non-admin authenticated users receive:

{ "error": "Admin access required" }


Route Overview

Method Path Auth Notes
GET /health None Service health check
GET /app None (shell handles redirect) Serves app HTML shell
GET /api/auth/me Session cookie Auth probe / user info
GET /api/chart-types None Chart type catalog
GET /api/chart-types/examples Required Render preview for all types
POST /api/charts/generate Required Generate a single chart
GET /api/charts/gallery None Recent chart gallery
POST /api/charts/recommend Required AI chart type recommendation
POST /api/charts/batch Required Generate up to 10 charts
GET /api/charts/:id None Chart metadata by UUID
DELETE /api/charts/:id Admin Delete chart files
GET /charts/:filename None Static file — serve chart image

Application Routes (Internal / Browser)

These routes serve the web application shell. They are not part of a public developer API.


GET /app

Serves the CloviCharts HTML application shell (shell/app.html). Unauthed browser sessions are redirected to dash.clovitek.com/login by the client-side SSO guard — no server-side redirect occurs on this route.

Auth: None (server-side); client-side SSO guard redirects unauthenticated users.


API Routes


GET /health

Returns service health and runtime counters. No authentication required.

Auth: None
Parameters: None

Example:

curl http://localhost:8966/health

Response:

{
  "status": "ok",
  "service": "CloviCharts",
  "version": "1.0.0",
  "port": 8966,
  "charts_rendered": 42,
  "uptime_seconds": 3600,
  "supported_types": 15,
  "output_dir": "/root/clovicharts/output"
}


GET /api/auth/me

Auth probe used by the client-side SSO guard to confirm the session is valid and retrieve the current user's profile.

Auth: cl_session cookie (required)
Parameters: None

Example:

curl -H "Cookie: cl_session=<jwt>" http://localhost:8966/api/auth/me

Response:

{
  "ok": true,
  "user": {
    "id": 1,
    "email": "[email protected]",
    "role": "super_admin",
    "name": "Vitaly"
  }
}

Errors: 401 Unauthorized


GET /api/chart-types

Returns the full catalog of supported chart types with metadata and example configurations. No authentication required.

Auth: None
Parameters: None

Example:

curl http://localhost:8966/api/chart-types

Response:

{
  "version": "1.0.0",
  "engine": "chartjs-node-canvas",
  "total": 15,
  "chart_types": [
    {
      "type": "bar",
      "label": "Bar Chart",
      "formats": ["png", "webp"],
      "example_config": {
        "type": "bar",
        "data": {
          "labels": ["Q1", "Q2", "Q3", "Q4"],
          "datasets": [{ "label": "Revenue ($k)", "data": [120, 190, 150, 220] }]
        },
        "options": { "width": 800, "height": 500, "theme": "clovitek" }
      }
    }
  ]
}


GET /api/chart-types/examples

Renders a 400×280 preview PNG for every chart type and returns their public URLs. Files are written to the output directory as example_<type>.png.

Auth: Required (cl_session cookie or X-User-Id header)
Parameters: None

Example:

curl -H "Cookie: cl_session=<jwt>" http://localhost:8966/api/chart-types/examples

Response:

{
  "examples": [
    { "type": "bar",  "label": "Bar Chart",  "url": "http://localhost:8966/charts/example_bar.png" },
    { "type": "line", "label": "Line Chart", "url": "http://localhost:8966/charts/example_line.png" }
  ]
}

On per-type render failure, the entry contains "error": "<message>" instead of "url". Other types in the array are still returned.

Errors: 401 Unauthorized


POST /api/charts/generate

Generate a single chart image. Returns a URL, metadata, and optionally a base64-encoded image. Chart files are persisted to disk alongside a .json metadata file.

Auth: Required (cl_session cookie or X-User-Id header)

Body:

{
  "type": "bar",
  "data": {
    "labels": ["Q1", "Q2", "Q3", "Q4"],
    "datasets": [
      { "label": "Revenue ($k)", "data": [120, 190, 150, 220] }
    ]
  },
  "options": {
    "width": 800,
    "height": 500,
    "theme": "clovitek",
    "format": "png",
    "title": "Quarterly Revenue",
    "ai_narrative": false,
    "ai_title": false,
    "base64": false
  }
}

Parameters:

Field Type Required Description
type string Yes Chart type. Must be one of the 15 supported types.
data object Yes ChartJS-compatible data object.
data.datasets array Yes Non-empty array of dataset objects.
data.labels array No Category labels (most chart types).
options.width integer No Output width in px. Default 800. Max 2400.
options.height integer No Output height in px. Default 500. Max 1600.
options.theme string No "clovitek" (default), "dark", or "light".
options.format string No "png" (default) or "webp".
options.title string No Chart title text.
options.ai_narrative boolean No If true, Gemini generates a one-sentence insight embedded in the chart. Failures are non-fatal.
options.ai_title boolean No If true and no title provided, Gemini generates a short title (max 6 words).
options.base64 boolean No If true, includes raw base64 image data in the response.

Constraints: width × height must not exceed 3,840,000 px.

Response headers: X-Chart-ID, X-Chart-URL, X-Render-Ms, X-AI-Narrative (only when a narrative was generated)

Example:

curl -X POST http://localhost:8966/api/charts/generate \
  -H "Cookie: cl_session=<jwt>" \
  -H "Content-Type: application/json" \
  -d '{"type":"bar","data":{"labels":["Q1","Q2","Q3","Q4"],"datasets":[{"label":"Revenue","data":[120,190,150,220]}]}}'

Response:

{
  "chart_id": "550e8400-e29b-41d4-a716-446655440000",
  "url": "http://localhost:8966/charts/550e8400-e29b-41d4-a716-446655440000.png",
  "narrative": "Sales peaked in Q4 driven by holiday demand.",
  "title": "Quarterly Revenue",
  "width": 800,
  "height": 500,
  "theme": "clovitek",
  "format": "png",
  "render_ms": 142,
  "created_at": "2026-06-16T12:00:00.000Z"
}

narrative and title are null when not requested or when AI generation fails. base64 key is omitted unless options.base64 is true.

Errors:

Status Error code Cause
400 MISSING_TYPE type field absent
400 MISSING_DATA data field absent
400 DATASET_EMPTY data.datasets missing or empty array
400 INVALID_CHART_TYPE type not in supported list
400 CHART_TOO_LARGE width × height > 3,840,000
401 Not authenticated
500 RENDER_FAILED chartjs-node-canvas rendering error

GET /api/charts/gallery

Returns the last 20 charts from the in-memory gallery (capped at 100 most recent). The gallery resets on server restart; it is not persisted to disk.

Auth: None
Parameters: None

Example:

curl http://localhost:8966/api/charts/gallery

Response:

{
  "charts": [
    {
      "chart_id": "550e8400-e29b-41d4-a716-446655440000",
      "type": "bar",
      "theme": "clovitek",
      "format": "png",
      "width": 800,
      "height": 500,
      "title": "Quarterly Revenue",
      "narrative": null,
      "url": "http://localhost:8966/charts/550e8400-e29b-41d4-a716-446655440000.png",
      "created_at": "2026-06-16T12:00:00.000Z",
      "render_ms": 142
    }
  ],
  "total": 1
}


POST /api/charts/recommend

Sends column descriptors to Gemini and returns a recommended chart type with reasoning. Falls back to bar if Gemini is unreachable or the API key is absent.

Auth: Required (cl_session cookie or X-User-Id header)

Body:

{
  "columns": [
    { "name": "month",    "type": "string" },
    { "name": "revenue",  "type": "number" },
    { "name": "expenses", "type": "number" }
  ]
}

Parameters:

Field Type Required Description
columns array Yes Array of column descriptors. Each entry must have name (string) and type (string).

Example:

curl -X POST http://localhost:8966/api/charts/recommend \
  -H "Cookie: cl_session=<jwt>" \
  -H "Content-Type: application/json" \
  -d '{"columns":[{"name":"month","type":"string"},{"name":"revenue","type":"number"}]}'

Response:

{
  "recommended": "line",
  "reason": "Two numeric series over a time-ordered string axis suit a line chart.",
  "alternatives": ["bar", "scatter"]
}

Fallback response when AI is unavailable:

{ "recommended": "bar", "reason": "AI unavailable — defaulted to bar", "alternatives": ["line", "pie"] }

Errors:

Status Error code Cause
400 MISSING_COLUMNS columns field absent or not an array
401 Not authenticated

POST /api/charts/batch

Generate up to 10 charts in a single request. Each entry uses the same shape as the /api/charts/generate body. Per-entry failures do not abort the batch; failed entries receive status: "error". Batch renders always output png; ai_narrative is not applied per entry.

Auth: Required (cl_session cookie or X-User-Id header)

Body:

{
  "charts": [
    {
      "type": "bar",
      "data": { "labels": ["A", "B"], "datasets": [{ "label": "X", "data": [1, 2] }] },
      "options": { "width": 600, "height": 400, "theme": "light" }
    },
    {
      "type": "pie",
      "data": { "labels": ["A", "B", "C"], "datasets": [{ "data": [30, 50, 20] }] }
    }
  ]
}

Parameters:

Field Type Required Description
charts array Yes Array of chart specs. Maximum 10 entries.

Example:

curl -X POST http://localhost:8966/api/charts/batch \
  -H "Cookie: cl_session=<jwt>" \
  -H "Content-Type: application/json" \
  -d '{"charts":[{"type":"bar","data":{"labels":["A","B"],"datasets":[{"data":[1,2]}]}}]}'

Response:

{
  "charts": [
    { "status": "ok",    "chart_id": "uuid1", "url": "http://localhost:8966/charts/uuid1.png", "render_ms": 110 },
    { "status": "error", "error": "Invalid type: xyz" }
  ],
  "total": 2,
  "failed": 1
}

Errors:

Status Error code Cause
400 MISSING_CHARTS charts field absent or not an array
400 BATCH_TOO_LARGE More than 10 charts in the array
401 Not authenticated

GET /api/charts/:id

Retrieve the stored metadata JSON for a previously generated chart. The .png, .webp, or .json extension is stripped from the id parameter automatically.

Auth: None
Path parameters: id — chart UUID

Example:

curl http://localhost:8966/api/charts/550e8400-e29b-41d4-a716-446655440000
# or with extension — both work:
curl http://localhost:8966/api/charts/550e8400-e29b-41d4-a716-446655440000.png

Response:

{
  "chart_id": "550e8400-e29b-41d4-a716-446655440000",
  "type": "bar",
  "theme": "clovitek",
  "format": "png",
  "width": 800,
  "height": 500,
  "title": "Quarterly Revenue",
  "narrative": null,
  "url": "http://localhost:8966/charts/550e8400-e29b-41d4-a716-446655440000.png",
  "created_at": "2026-06-16T12:00:00.000Z",
  "render_ms": 142
}

Errors:

Status Error code Cause
404 NOT_FOUND No metadata file found for the given ID

DELETE /api/charts/:id

Delete all files associated with a chart (<id>.png, <id>.webp, <id>.json) and remove it from the in-memory gallery. Requires admin role.

Auth: Admin required (cl_session cookie with role in super_admin, admin, or platform_admin)
Path parameters: id — chart UUID (extensions stripped automatically)

Example:

curl -X DELETE http://localhost:8966/api/charts/550e8400-e29b-41d4-a716-446655440000 \
  -H "Cookie: cl_session=<admin-jwt>"

Response:

{ "deleted": true, "id": "550e8400-e29b-41d4-a716-446655440000" }

Errors:

Status Error code Cause
401 Not authenticated
403 Authenticated but not an admin role
404 NOT_FOUND No files found for the given ID

Static File Route

GET /charts/:filename

Serves rendered chart image files directly from the output directory. Files are cached for 1 day (Cache-Control: public, max-age=86400).

Auth: None
Examples: - GET /charts/550e8400-e29b-41d4-a716-446655440000.png - GET /charts/550e8400-e29b-41d4-a716-446655440000.webp - GET /charts/example_bar.png


Notes

Public Developer API

All routes listed above are internal application routes using the CloviTek SSO (cl_session JWT cookie) or the trusted internal X-User-Id header. There is currently no public developer API with Bearer token authentication. A public API (API key / Bearer token auth) is planned for a future release.

AI Features

AI narrative and AI title generation use Google Gemini (gemini-2.5-flash) via the GOOGLE_GEMINI key in /root/.api_keys/master.env. When the key is absent or Gemini is unreachable, both features degrade gracefully: ai_narrative returns null and ai_title falls back to no title. These failures never cause the chart render itself to fail.

Persistence

Each chart render writes two files to the output directory: - <uuid>.<ext> — the rendered image (png or webp) - <uuid>.json — the chart metadata

The in-memory gallery holds up to the 100 most recent charts and is lost on restart. Metadata JSON files on disk persist across restarts and are served via GET /api/charts/:id.