API Reference

The Autocrat REST API lets you manage students, generate documents, and interact with your organization's data programmatically. All endpoints are under /api and require API key authentication.

Base URL

https://api.autocrat.app

Content Type

All requests must include:

Content-Type: application/json

Quick Start

# List your students
curl https://api.autocrat.app/api/students \
  -H "X-API-Key: ak_live_your_key_here"

# Generate a document (stateless)
curl -X POST https://api.autocrat.app/api/generate/stateless \
  -H "X-API-Key: ak_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "template_name": "certificado",
    "data": [{"NOMBRE": "Ana Garcia", "DNI": "12345678A"}]
  }' \
  --output certificado.docx

Authentication

All API requests require an API key sent via the X-API-Key header. You can create and manage API keys in Settings → Advanced.

GET /api/students HTTP/1.1
Host: api.autocrat.app
X-API-Key: ak_live_your_64_character_hex_key_here
Content-Type: application/json

Keep your API keys secret

Never expose API keys in frontend code, public repositories, or client-side applications. API keys should only be used in server-to-server communication.

Scopes

Each API key has a set of scopes that determine what it can do. If a request requires a scope your key doesn't have, the API returns 403 Forbidden with the missing scope.

ScopeDescription
students:readList and view student records
students:writeCreate, update, and delete students
forms:readList and view form definitions
templates:readList and view template metadata
documents:readList files in storage
documents:generateGenerate documents from templates
documents:downloadDownload files from storage
credits:readView credit balance and usage stats

Error Handling

All errors follow a consistent format. The hint field (when present) provides actionable guidance to fix the issue.

{
  "error": {
    "code": "validation_error",
    "message": "template_name is required",
    "hint": "Use GET /api/templates to see available template names",
    "expected_body": {
      "template_name": "<string>",
      "data": [
        {
          "VAR_NAME": "value"
        }
      ]
    }
  }
}

HTTP Status Codes

CodeNameDescription
400Bad RequestInvalid or missing parameters. Check the error message and hint for details.
401UnauthorizedMissing, invalid, revoked, or expired API key.
403ForbiddenYour API key doesn't have the required scope for this action.
404Not FoundResource not found (student, template, form, file).
409ConflictDuplicate record (e.g., student with same identifier already exists).
413Payload Too LargeRequest body exceeds the 10MB limit.
415Unsupported Media TypeMissing or wrong Content-Type header (must be application/json).
422Unprocessable EntityDocument generation failed (e.g., template variable mismatch).
429Too Many RequestsRate limit exceeded. Check X-RateLimit-* headers and retry_after.
500Internal Server ErrorUnexpected server error. Contact support if persistent.

Students

Forms

Templates

Documents

Generate (Stateless)

Credits

Rate Limits

Each API key has a per-minute request limit (default: 100 requests/minute). The current state is communicated via response headers on every request.

HeaderDescription
X-RateLimit-LimitMaximum requests per minute for your key
X-RateLimit-RemainingRequests remaining in the current window
X-RateLimit-ResetUnix timestamp when the window resets

When rate limited

{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Rate limit of 100 requests per minute exceeded",
    "retry_after": 23
  }
}

Wait retry_after seconds before retrying. Implement exponential backoff for production integrations.

Common Workflows

End-to-end examples showing how to combine multiple endpoints to accomplish real tasks.

1. Import students from your CRM

Sync students from an external system. First discover the form structure, then batch import.

1Get the form schema to know which fields to send
curl "https://api.autocrat.app/api/forms" \
  -H "X-API-Key: ak_live_your_key"

# Response: find your form_id and its fields
# { "data": [{ "id": "550e8400-...", "title": "Matriculacion 2026" }] }

curl "https://api.autocrat.app/api/forms/550e8400-e29b-41d4-a716-446655440000" \
  -H "X-API-Key: ak_live_your_key"

# Response: the schema tells you which field is the identifier
# "fields": [
#   { "field_id": "DNI", "type": "identifier" },
#   { "field_id": "NOMBRE", "type": "text" },
#   ...
# ]
2Batch import students using the field IDs from the schema
curl -X POST "https://api.autocrat.app/api/students/batch" \
  -H "X-API-Key: ak_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "form_id": "550e8400-e29b-41d4-a716-446655440000",
    "on_conflict": "update",
    "students": [
      {
        "DNI": "12345678A",
        "NOMBRE": "Ana",
        "APELLIDOS": "Garcia Lopez",
        "EMAIL": "ana@example.com"
      },
      {
        "DNI": "87654321B",
        "NOMBRE": "Carlos",
        "APELLIDOS": "Lopez Martinez",
        "EMAIL": "carlos@example.com"
      }
    ]
  }'

# Response:
# { "data": { "created": 2, "updated": 0, "skipped": 0, "errors": [] } }

2. Generate and download documents for students

Generate contracts/certificates for existing students and download them.

1Check available templates and their variables
curl "https://api.autocrat.app/api/templates" \
  -H "X-API-Key: ak_live_your_key"

# Response: [{ "name": "contrato_formacion", ... }]
2Generate documents for specific students
curl -X POST "https://api.autocrat.app/api/documents/generate" \
  -H "X-API-Key: ak_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "template_name": "contrato_formacion",
    "form_id": "550e8400-e29b-41d4-a716-446655440000",
    "identifier_values": ["12345678A", "87654321B"],
    "output_folder": "contratos_marzo_2026",
    "variables": {
      "FECHA_INICIO": "01/04/2026",
      "CURSO": "Desarrollo Web Full Stack"
    }
  }'

# Response:
# {
#   "data": {
#     "generated": 2,
#     "results": [
#       { "identifier_value": "12345678A", "status": "generated",
#         "path": "anexos/org-id/contratos_marzo_2026/contrato_formacion_12345678A.docx" },
#       ...
#     ]
#   }
# }
3Download all generated files as ZIP
curl -X POST "https://api.autocrat.app/api/documents/download-zip" \
  -H "X-API-Key: ak_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{ "folder": "contratos_marzo_2026" }' \
  --output contratos_marzo_2026.zip

# Or download a single file:
curl "https://api.autocrat.app/api/documents/download?path=anexos/org-id/contratos_marzo_2026/contrato_formacion_12345678A.docx" \
  -H "X-API-Key: ak_live_your_key" \
  --output contrato_ana.docx

3. Quick document generation (no students needed)

Generate documents on the fly by passing data directly — no need to create student records first.

Single request, instant download
curl -X POST "https://api.autocrat.app/api/generate/stateless" \
  -H "X-API-Key: ak_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "template_name": "certificado_asistencia",
    "data": [
      {
        "NOMBRE": "Ana Garcia Lopez",
        "DNI": "12345678A",
        "CURSO": "React Avanzado",
        "HORAS": "40",
        "FECHA": "28/03/2026"
      }
    ]
  }' \
  --output certificado_ana.docx

# Returns the .docx file directly (no storage, no student records)
# For multiple elements, returns a .zip file instead

4. Full JavaScript integration example

A complete Node.js script that imports students from a CSV-like array and generates their documents.

const API_KEY = "ak_live_your_key";
const BASE_URL = "https://api.autocrat.app";

// Helper function for API calls
async function api(method, path, body) {
  const opts = {
    method,
    headers: {
      "X-API-Key": API_KEY,
      "Content-Type": "application/json",
    },
  };
  if (body) opts.body = JSON.stringify(body);

  const res = await fetch(BASE_URL + path, opts);

  // Check rate limit headers
  const remaining = res.headers.get("X-RateLimit-Remaining");
  if (remaining && parseInt(remaining) < 5) {
    console.warn(`Rate limit warning: ${remaining} requests remaining`);
  }

  if (!res.ok) {
    const err = await res.json();
    throw new Error(`${res.status}: ${err.error?.message} — ${err.error?.hint || ""}`);
  }
  return res;
}

// ─── Step 1: Get form schema ───
const formsRes = await api("GET", "/api/forms");
const forms = (await formsRes.json()).data;
const form = forms.find(f => f.title.includes("Matriculacion"));
console.log("Using form:", form.id, form.title);

// ─── Step 2: Import students ───
const students = [
  { DNI: "12345678A", NOMBRE: "Ana", APELLIDOS: "Garcia", EMAIL: "ana@test.com" },
  { DNI: "87654321B", NOMBRE: "Carlos", APELLIDOS: "Lopez", EMAIL: "carlos@test.com" },
  { DNI: "11223344C", NOMBRE: "Maria", APELLIDOS: "Rodriguez", EMAIL: "maria@test.com" },
];

const batchRes = await api("POST", "/api/students/batch", {
  form_id: form.id,
  students: students,
  on_conflict: "update",  // Update if they already exist
});
const batchResult = (await batchRes.json()).data;
console.log(`Imported: ${batchResult.created} created, ${batchResult.updated} updated`);

// ─── Step 3: Generate contracts ───
const genRes = await api("POST", "/api/documents/generate", {
  template_name: "contrato_formacion",
  form_id: form.id,
  identifier_values: students.map(s => s.DNI),
  output_folder: "contratos_abril_2026",
  variables: {
    FECHA_INICIO: "01/04/2026",
    CURSO: "Desarrollo Web Full Stack",
    DURACION: "400 horas",
  },
});
const genResult = (await genRes.json()).data;
console.log(`Generated: ${genResult.generated} documents`);

// ─── Step 4: Download as ZIP ───
const zipRes = await api("POST", "/api/documents/download-zip", {
  folder: "contratos_abril_2026",
});
const blob = await zipRes.blob();
// In Node.js: fs.writeFileSync("contratos.zip", Buffer.from(await blob.arrayBuffer()));
console.log("Downloaded ZIP:", blob.size, "bytes");

5. Full Python integration example

The same workflow in Python using the requests library.

import requests

API_KEY = "ak_live_your_key"
BASE_URL = "https://api.autocrat.app"
HEADERS = {"X-API-Key": API_KEY, "Content-Type": "application/json"}


def api(method, path, json=None):
    """Make an API call with error handling."""
    res = requests.request(method, f"{BASE_URL}{path}", headers=HEADERS, json=json)
    if not res.ok:
        err = res.json().get("error", {})
        raise Exception(f'{res.status_code}: {err.get("message")} — {err.get("hint", "")}')
    return res


# Step 1: Find the form
forms = api("GET", "/api/forms").json()["data"]
form = next(f for f in forms if "Matriculacion" in f["title"])
print(f"Using form: {form['id']} — {form['title']}")

# Step 2: Import students
students = [
    {"DNI": "12345678A", "NOMBRE": "Ana", "APELLIDOS": "Garcia", "EMAIL": "ana@test.com"},
    {"DNI": "87654321B", "NOMBRE": "Carlos", "APELLIDOS": "Lopez", "EMAIL": "carlos@test.com"},
]

result = api("POST", "/api/students/batch", {
    "form_id": form["id"],
    "students": students,
    "on_conflict": "update",
}).json()["data"]
print(f"Imported: {result['created']} created, {result['updated']} updated")

# Step 3: Generate documents
gen = api("POST", "/api/documents/generate", {
    "template_name": "contrato_formacion",
    "form_id": form["id"],
    "identifier_values": [s["DNI"] for s in students],
    "output_folder": "contratos_abril_2026",
    "variables": {
        "FECHA_INICIO": "01/04/2026",
        "CURSO": "Desarrollo Web Full Stack",
    },
}).json()["data"]
print(f"Generated: {gen['generated']} documents")

# Step 4: Download ZIP
zip_data = api("POST", "/api/documents/download-zip", {
    "folder": "contratos_abril_2026",
})
with open("contratos.zip", "wb") as f:
    f.write(zip_data.content)
print(f"Downloaded: {len(zip_data.content)} bytes")

# --- Or generate stateless (no students needed) ---
doc = requests.post(
    f"{BASE_URL}/api/generate/stateless",
    headers=HEADERS,
    json={
        "template_name": "certificado_asistencia",
        "data": [{"NOMBRE": "Ana Garcia", "DNI": "12345678A", "CURSO": "React", "HORAS": "40"}],
    },
)
with open("certificado.docx", "wb") as f:
    f.write(doc.content)
print("Stateless document saved!")