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.appContent Type
All requests must include:
Content-Type: application/jsonQuick 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.docxAuthentication
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/jsonKeep 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.
| Scope | Description |
|---|---|
| students:read | List and view student records |
| students:write | Create, update, and delete students |
| forms:read | List and view form definitions |
| templates:read | List and view template metadata |
| documents:read | List files in storage |
| documents:generate | Generate documents from templates |
| documents:download | Download files from storage |
| credits:read | View 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
| Code | Name | Description |
|---|---|---|
| 400 | Bad Request | Invalid or missing parameters. Check the error message and hint for details. |
| 401 | Unauthorized | Missing, invalid, revoked, or expired API key. |
| 403 | Forbidden | Your API key doesn't have the required scope for this action. |
| 404 | Not Found | Resource not found (student, template, form, file). |
| 409 | Conflict | Duplicate record (e.g., student with same identifier already exists). |
| 413 | Payload Too Large | Request body exceeds the 10MB limit. |
| 415 | Unsupported Media Type | Missing or wrong Content-Type header (must be application/json). |
| 422 | Unprocessable Entity | Document generation failed (e.g., template variable mismatch). |
| 429 | Too Many Requests | Rate limit exceeded. Check X-RateLimit-* headers and retry_after. |
| 500 | Internal Server Error | Unexpected 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.
| Header | Description |
|---|---|
| X-RateLimit-Limit | Maximum requests per minute for your key |
| X-RateLimit-Remaining | Requests remaining in the current window |
| X-RateLimit-Reset | Unix 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.
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" },
# ...
# ]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.
curl "https://api.autocrat.app/api/templates" \
-H "X-API-Key: ak_live_your_key"
# Response: [{ "name": "contrato_formacion", ... }]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" },
# ...
# ]
# }
# }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.docx3. Quick document generation (no students needed)
Generate documents on the fly by passing data directly — no need to create student records first.
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 instead4. 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!")