⚡ Developer API v1

TenTags REST API Reference

Generate styled HTML, XLSX (Excel), PDF, and DOCX (Word) documents programmatically from your backend, microservices, or CLI using your TENTAGS_API_TOKEN.

🔑 Authentication

Bearer Auth

The TenTags API uses Bearer Tokens to authenticate HTTP requests. You can generate and manage your TENTAGS_API_TOKEN directly in your User Profile.

How to pass your API Token:

Include your token in the Authorization header or X-TenTags-Api-Key header of all API requests:

Authorization: Bearer tt_live_8f3d...32charshere
X-TenTags-Api-Key: tt_live_8f3d...32charshere
🔐 Security Notice: Keep your API token secure! Never expose your TENTAGS_API_TOKEN in client-side code, public repositories, or frontend bundles. Always send requests from a secure backend environment.
Core Endpoint

Generate Document

POST /api/generate

Compiles TenTags layout code into a downloadable file stream in your chosen format (HTML, XLSX, PDF, or DOCX).

POST https://tentags.org/api/generate

Request Headers

Header Type Description
Authorization* string Bearer tt_live_... formatted API token.
Content-Type* string Must be application/json.

JSON Request Body Parameters

Parameter Type Description
style_csv string Raw style TenTags CSV string. Required if template_id is not provided.
data_csv string Raw data TenTags CSV string. Required if template_id is not provided.
template_id integer ID of a saved template from your library. Replaces manual style_csv and data_csv.
format string Output format: "html", "xlsx", "pdf", "docx". Default is "html".
orientation string Page orientation: "auto", "portrait", "landscape". Applicable for PDF/DOCX.
page_size string Page geometry: "auto", "a4", "a3", "letter". Applicable for PDF/DOCX.
streamlog boolean If true, returns a Server-Sent Events (SSE) stream with real-time progress updates instead of a direct file response. Default is false. See Streaming section.

💻 Code Examples & SDK Integration

Select your programming language to see complete working examples for generating documents using /api/generate:

curl -X POST https://tentags.org/api/generate \
  -H "Authorization: Bearer YOUR_TENTAGS_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "style_csv": "<bg=#0f172a><color=#ffffff><center></center></color></bg>;",
    "data_csv": "Sales Report 2026;",
    "format": "pdf",
    "orientation": "auto",
    "streamlog": true
  }' \
  --no-buffer 2>&1 | while IFS= read -r line; do
    case "$line" in
      data:*) echo "$line" | sed 's/^data: //' ;;
    esac
  done
import requests, json, base64

url = "https://tentags.org/api/generate"
headers = {
    "Authorization": "Bearer YOUR_TENTAGS_API_TOKEN",
    "Content-Type": "application/json"
}

payload = {
    "style_csv": "<bg=#0f172a><color=#ffffff><center></center></color></bg>;",
    "data_csv": "Sales Report 2026;",
    "format": "docx",
    "orientation": "portrait",
    "streamlog": True
}

response = requests.post(url, headers=headers, json=payload, stream=True)

for line in response.iter_lines(decode_unicode=True):
    if not line:
        continue
    if line.startswith(": "):
        continue  # heartbeat — ignore
    if line.startswith("id: "):
        continue  # event ID — for reconnection
    if line.startswith("data: "):
        event = json.loads(line[6:])
        if event.get("done"):
            content = base64.b64decode(event["content_b64"])
            with open("report.docx", "wb") as f:
                f.write(content)
            print(f"Done! {event['mime']}, {len(content)} bytes")
        elif event.get("type") == "error":
            print(f"Error: {event['message']}")
        else:
            print(event["message"])
const fs = require('fs');

async function generateDocument() {
  const response = await fetch('https://tentags.org/api/generate', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_TENTAGS_API_TOKEN',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      style_csv: '<bg=#0f172a><color=#ffffff><center></center></color></bg>;',
      data_csv: 'Sales Report 2026;',
      format: 'xlsx',
      streamlog: true
    })
  });

  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  let buffer = '';

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    buffer += decoder.decode(value, { stream: true });
    const lines = buffer.split('\n');
    buffer = lines.pop();

    for (const line of lines) {
      if (line.startsWith('data: ')) {
        const event = JSON.parse(line.slice(6));
        if (event.done) {
          const content = Buffer.from(event.content_b64, 'base64');
          fs.writeFileSync('report.xlsx', content);
          console.log(`Done! ${event.mime}, ${content.length} bytes`);
        } else if (event.type === 'error') {
          console.error(`Error: ${event.message}`);
        } else {
          console.log(event.message);
        }
      }
    }
  }
}

generateDocument();
<?php
$url = "https://tentags.org/api/generate";
$token = "YOUR_TENTAGS_API_TOKEN";

$data = [
    "style_csv" => "<bg=#0f172a><color=#ffffff><center></center></color></bg>;",
    "data_csv"  => "Sales Report 2026;",
    "format"    => "html"
];

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "Authorization: Bearer $token",
    "Content-Type: application/json"
]);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$result = curl_exec($ch);
curl_close($ch);

echo $result;
?>
package main

import (
	"bufio"
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
)

func main() {
	payload := map[string]interface{}{
		"style_csv": "<bg=#0f172a><color=#ffffff><center></center></color></bg>;",
		"data_csv":  "Sales Report 2026;",
		"format":    "pdf",
		"streamlog": true,
	}
	body, _ := json.Marshal(payload)

	req, _ := http.NewRequest("POST", "https://tentags.org/api/generate", bytes.NewBuffer(body))
	req.Header.Set("Authorization", "Bearer YOUR_TENTAGS_API_TOKEN")
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil || resp.StatusCode != 200 {
		panic("Failed to generate document")
	}
	defer resp.Body.Close()

	scanner := bufio.NewScanner(resp.Body)
	for scanner.Scan() {
		line := scanner.Text()
		if !strings.HasPrefix(line, "data: ") {
			continue
		}
		var event map[string]interface{}
		json.Unmarshal([]byte(line[6:]), &event)

		if done, ok := event["done"].(bool); ok && done {
			b64, _ := event["content_b64"].(string)
			decoded, _ := base64.StdEncoding.DecodeString(b64)
			os.WriteFile("report.pdf", decoded, 0644)
			fmt.Printf("Done! %s, %d bytes\n", event["mime"], len(decoded))
		} else if event["type"] == "error" {
			fmt.Printf("Error: %s\n", event["message"])
		} else {
			fmt.Println(event["message"])
		}
	}
}
Advanced

📡 Streaming & Real-Time Progress

SSE over POST

Set "streamlog": true in your request body to receive a Server-Sent Events (SSE) stream instead of a direct file. The server sends progress events as the document renders, then the final file as Base64.

How it works: The server renders your document in a background thread and pushes SSE events to your client in real time. Each event has an incrementing id. A heartbeat comment (: heartbeat) is sent every 20 seconds to keep the connection alive through proxies.

SSE Event Format

Event Fields Description
progress message, current, total, unit A rendering step completed (e.g. "Creating PDF table object").
heartbeat none (SSE comment) Sent every 20s. Client should ignore it.
done done: true, mime, filename, content_b64 Document ready. Decode content_b64 to get the file bytes.
error type: "error", message Compilation or render failed.

Example: What the client receives

id: 1
data: {"message": "Starting PDF document generation."}

id: 2
data: {"message": "Creating PDF table object."}

id: 3
data: {"message": "Building PDF document."}

id: 4
data: {"done": true, "mime": "application/pdf", "filename": "document.pdf", "content_b64": "JVBERi0x..."}

Python Example

import requests, json, base64

response = requests.post(
    "https://tentags.org/api/generate",
    headers={
        "Authorization": "Bearer YOUR_TENTAGS_API_TOKEN",
        "Content-Type": "application/json"
    },
    json={
        "style_csv": "...",
        "data_csv": "...",
        "format": "pdf",
        "orientation": "auto",
        "streamlog": True
    },
    stream=True   # important: must be True
)

for line in response.iter_lines(decode_unicode=True):
    if not line:
        continue

    if line.startswith(": "):
        continue  # heartbeat — ignore

    if line.startswith("id: "):
        continue  # event ID — for reconnection tracking

    if line.startswith("data: "):
        event = json.loads(line[6:])

        if event.get("done"):
            content = base64.b64decode(event["content_b64"])
            with open("document.pdf", "wb") as f:
                f.write(content)
            print(f"Done! {event['mime']}, {len(content)} bytes")

        elif event.get("type") == "error":
            print(f"Error: {event['message']}")

        else:
            print(event["message"])

Node.js Example

const fs = require('fs');

async function generateWithProgress() {
  const response = await fetch('https://tentags.org/api/generate', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_TENTAGS_API_TOKEN',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      style_csv: '...',
      data_csv: '...',
      format: 'pdf',
      streamlog: true
    })
  });

  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  let buffer = '';

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    buffer += decoder.decode(value, { stream: true });
    const lines = buffer.split('\n');
    buffer = lines.pop();

    for (const line of lines) {
      if (line.startsWith('data: ')) {
        const event = JSON.parse(line.slice(6));

        if (event.done) {
          const content = Buffer.from(event.content_b64, 'base64');
          fs.writeFileSync('document.pdf', content);
          console.log(`Done! ${event.mime}, ${content.length} bytes`);
        } else if (event.type === 'error') {
          console.error(`Error: ${event.message}`);
        } else {
          console.log(event.message);
        }
      }
    }
  }
}

generateWithProgress();

cURL Example

curl -X POST https://tentags.org/api/generate \
  -H "Authorization: Bearer YOUR_TENTAGS_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "style_csv": "...",
    "data_csv": "...",
    "format": "pdf",
    "streamlog": true
  }' \
  --no-buffer 2>&1 | while IFS= read -r line; do
    case "$line" in
      data:*) echo "$line" | sed 's/^data: //' ;;
    esac
  done
Tip: For small documents, all events may arrive in a single TCP packet — this is normal. The streaming mode is most useful for large or multi-table documents where rendering takes noticeable time.

📊 Quotas & Subscription Limits

API quotas are enforced per UTC calendar month based on your active subscription plan:

Plan Tier Monthly API Requests Allowed Formats Price
Free Plan 20 requests / mo HTML, XLSX $0
Starter Plan 500 requests / mo HTML, XLSX, PDF, DOCX $2.00 / mo
Pro Plan 10,000 requests / mo HTML, XLSX, PDF, DOCX $40.00 / mo
Advanced Plan 100,000 requests / mo HTML, XLSX, PDF, DOCX $120.00 / mo

⚠️ Error Handling

The TenTags API uses standard HTTP response status codes to indicate the success or failure of an API request:

Status Code Meaning Description
200 OK Success Document generated and returned successfully.
401 Unauthorized Invalid Token Missing or invalid TENTAGS_API_TOKEN.
403 Forbidden Format Restricted Requested export format (e.g. PDF/DOCX) is not available on Free tier.
429 Limit Exceeded Quota Exceeded Monthly API request limit reached for your plan.