Documentación para desarrolladores

Documentación de la API de StoryMint

Todo lo que necesitas para generar libros infantiles con IA de forma programática. Marca personalizada, varios formatos y ejemplos de código en todos los lenguajes principales.

El acceso a la API requiere un plan de pago. Solo los planes Developer, Max y Escuelas y organizaciones incluyen claves API y generación programática de libros. Comparar planes →

Inicio rápido

Crea tu primer libro con unas pocas llamadas a la API.

Configuración

  1. Crea tu cuenta en storymint.ca/signup
  2. Suscríbete al plan Developer, Max o Escuelas y organizaciones (se requiere acceso a la API)
  3. Crea una clave API en tu panel de API
  4. Haz tu primera solicitud — mira los ejemplos más abajo
  5. Consulta el estado hasta que sea status: "done" y luego descarga

5 formatos de libro

Libros para colorear, libros ilustrados, cómics, cómics por capítulos y novelas Beta.

Marca personalizada

Define el nombre del autor, la serie y el estilo de ilustración, y elige entre más de 8 estilos visuales para tu marca.

Libros más largos en planes superiores

Los límites de páginas y capítulos por libro dependen de tu plan. Consulta los límites de uso.

Descargas en PDF

Descarga el libro terminado en PDF en los planes elegibles; obtén el texto y las URL de las imágenes de cada página en JSON.

Generación de audiolibros

Genera un audiolibro narrado con IA a partir de un libro terminado (Max y Escuelas y organizaciones).

Edición por chat con IA

Envía instrucciones de edición en lenguaje natural y la IA reescribe las páginas en tiempo real.

Ruta de integración con menos código

  1. Verifica primero la autenticación con GET /api/ping para confirmar que la clave y el plan son válidos.
  2. Genera con POST /api/generate.
  3. Consulta GET /api/status/{job_id} hasta que veas done.
  4. Obtén o descarga con GET /api/book/{job_id} para el JSON o GET /api/download/{job_id} para el PDF.

Autenticación

Toda solicitud debe incluir uno de estos dos métodos de autenticación:

Opción 1 — Clave API (recomendada)

Envía tu clave en el encabezado X-API-Key. Las claves API heredan el plan y el límite de uso de tu cuenta. Funcionan en los endpoints de libros (crear, seguir, leer, descargar, editar y narrar libros); los endpoints de la cuenta, como el perfil, las claves API, los equipos, los créditos y la facturación, necesitan el token de sesión de la Opción 2.

X-API-Key: sm_your_key_here

Opción 2 — Token Bearer de Firebase

Si estás construyendo un frontend, envía el token de ID de Firebase:

Authorization: Bearer <firebase_id_token>

Seguridad: las claves API se cifran con hash SHA-256 antes de almacenarse — nunca guardamos tu clave en texto plano. Trata tu clave como si fuera una contraseña.

URL base de la API de StoryMint

https://api.storymint.ca

Este es el endpoint actual de la API de StoryMint. Todas las rutas que siguen son relativas a él. Ejemplo: POST https://api.storymint.ca/api/generate

Límites de solicitudes

Los límites de solicitudes se aplican por cuenta según tu plan:

PlanLibros / mesMáx. páginas ilustradasMáx. páginas para colorearMáx. páginas de cómicMáx. capítulos de novelaAPI
Free1 de prueba (vista previa)vista previavista previa———
Plus15504010020—
Max50504010030Incluida
Developer1005040——150 llamadas de prueba
Escuelas y org.9,9991004010040Incluida

Cuando superas tu límite de uso, la API devuelve 402 con error_code: "quota" y un mensaje error fácil de leer. Los límites se reinician el día 1 de cada mes. Los límites de páginas son por libro, no por mes.

Generar un libro

POST /api/generate Cuota

Inicia un trabajo asíncrono de generación de libro. Devuelve un job_id que usarás para consultar el estado y obtener el resultado.

Cuerpo de la solicitud (JSON)

ParámetroTipoObligatorioDescripción
prompt string obligatorio Descripción de la historia, hasta 600 caracteres. Por ejemplo: "Un pequeño mapache valiente que aprende a compartir con sus amigos del bosque"
book_format string opcional Uno de: coloring, picture, comic, chapter_comic, novel Beta. El valor predeterminado es picture. Consulta los formatos.
author string opcional Nombre del autor impreso en la portada y en la página de título. Ideal para marca personalizada.
series string opcional Nombre de la serie si este libro pertenece a una colección con marca. Se muestra en la portada.
art_style string opcional Estilo visual de las ilustraciones. Consulta Estilos de ilustración. Predeterminado: cartoon. Los libros para colorear usan lineart_simple (formas grandes para manos pequeñas), lineart_bold (predeterminado) o lineart_fine.
story_model string opcional Ajuste de calidad para el texto de la historia. Por ahora el único ajuste es standard, que también es el predeterminado, así que puedes omitirlo. Consulta los ajustes de calidad.
image_model string opcional Ajuste de calidad para las ilustraciones. Por ahora el único ajuste es standard (el predeterminado).
coloring_variant string opcional Solo libros para colorear: pure (solo dibujos, predeterminado) o story (una frase corta debajo de cada dibujo).
num_pages integer opcional Número de páginas (libro ilustrado/cómic) o de capítulos (novela). Se ajusta a los límites de tu plan — los cómics de pago llegan a 100 páginas, los libros ilustrados alcanzan 100 páginas en Escuelas y organizaciones, y las novelas están en beta con topes de capítulos según el plan.
save_mode string opcional Se ignora para la duración del almacenamiento en los planes que incluyen la biblioteca en la nube: esos libros siempre se guardan en cloud_library y nunca expiran. Los planes que no la incluyen usan siempre temporary_cloud (se conservan 7 días). Envía local_until_export para omitir por completo el guardado en la nube.
attachments array<string> opcional Ids de archivos adjuntos devueltos por POST /api/attachments. Las imágenes de referencia se describen con visión por IA y pueden guiar el renderizado imagen a imagen de SD Forge.

Archivos adjuntos de referencia

Sube los archivos antes de llamar a /api/generate. StoryMint acepta hasta 5 archivos de 10 MB cada uno: PNG, JPEG, WebP, GIF, PDF, TXT o Markdown.

curl -X POST https://es.storymint.ca/api/attachments \
  -H "Authorization: Bearer $TOKEN" \
  -F "files=@character-reference.png"

La respuesta es { "ids": ["abc123ef"] }. Envía esos ids en el campo attachments cuando inicies la generación.

Respuesta

{
  "job_id": "a1b2c3d4",
  "status": "running"
}

Cómo se ve — vista previa del prompt

Así se ve una solicitud a la API aplicada a la pantalla Crear de StoryMint. Tu llamada a la API define todos estos campos de forma programática:

Tu solicitud a la API → pantalla Crear de StoryMint
Generar
Formato: picture
Páginas: 12
Estilo: watercolor
Autor: My Brand
Serie: Forest Friends
Calidad: standard

Cada etiqueta de arriba corresponde a un campo JSON del cuerpo de la solicitud. El prompt es el área de texto y las opciones se convierten en pares clave-valor.

Ejemplo completo — crear un libro ilustrado con tu marca

import requests

API_KEY = "sm_your_key_here"
BASE    = "https://api.storymint.ca"

resp = requests.post(
    f"{BASE}/api/generate",
    headers={"X-API-Key": API_KEY},
    json={
        "prompt": "A brave little raccoon who learns to share",
        "book_format": "picture",
        "author": "My Brand Publishing",
        "series": "Forest Friends",
        "art_style": "watercolor",
        "num_pages": 12,
    },
)
data = resp.json()
print(f"Job started: {data['job_id']}")
const API_KEY = "sm_your_key_here";
const BASE    = "https://api.storymint.ca";

const resp = await fetch(`${BASE}/api/generate`, {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-API-Key": API_KEY,
  },
  body: JSON.stringify({
    prompt: "A brave little raccoon who learns to share",
    book_format: "picture",
    author: "My Brand Publishing",
    series: "Forest Friends",
    art_style: "watercolor",
    num_pages: 12,
  }),
});
const data = await resp.json();
console.log("Job started:", data.job_id);
import java.net.http.*;
import java.net.URI;

HttpClient client = HttpClient.newHttpClient();
String body = """
  {
    "prompt": "A brave little raccoon who learns to share",
    "book_format": "picture",
    "author": "My Brand Publishing",
    "series": "Forest Friends",
    "art_style": "watercolor",
    "num_pages": 12
  }
  """;

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.storymint.ca/api/generate"))
    .header("Content-Type", "application/json")
    .header("X-API-Key", "sm_your_key_here")
    .POST(HttpRequest.BodyPublishers.ofString(body))
    .build();

HttpResponse<String> response = client.send(request,
    HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
curl -X POST https://api.storymint.ca/api/generate \
  -H "Content-Type: application/json" \
  -H "X-API-Key: sm_your_key_here" \
  -d '{
    "prompt": "A brave little raccoon who learns to share",
    "book_format": "picture",
    "author": "My Brand Publishing",
    "series": "Forest Friends",
    "art_style": "watercolor",
    "num_pages": 12
  }'
using System.Net.Http;
using System.Text;
using System.Text.Json;

var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "sm_your_key_here");

var payload = new {
    prompt = "A brave little raccoon who learns to share",
    book_format = "picture",
    author = "My Brand Publishing",
    series = "Forest Friends",
    art_style = "watercolor",
    num_pages = 12,
};

var content = new StringContent(
    JsonSerializer.Serialize(payload),
    Encoding.UTF8, "application/json");

var resp = await client.PostAsync(
    "https://api.storymint.ca/api/generate",
    content);

var json = await resp.Content.ReadAsStringAsync();
Console.WriteLine(json);
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
    "io"
)

func main() {
    body, _ := json.Marshal(map[string]interface{}{
        "prompt":      "A brave little raccoon who learns to share",
        "book_format": "picture",
        "author":      "My Brand Publishing",
        "series":      "Forest Friends",
        "art_style":   "watercolor",
        "num_pages":   12,
    })
    req, _ := http.NewRequest("POST",
        "https://api.storymint.ca/api/generate",
        bytes.NewBuffer(body))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("X-API-Key", "sm_your_key_here")

    resp, _ := http.DefaultClient.Do(req)
    defer resp.Body.Close()
    data, _ := io.ReadAll(resp.Body)
    fmt.Println(string(data))
}

Consultar estado

GET /api/status/{job_id}

Consulta este endpoint cada pocos segundos para seguir el progreso hasta que status sea done o failed. El tiempo que tarda un libro depende de su formato y su extensión.

Respuesta

{
  "status": "running",
  "step": "Generating illustrations",
  "msg": "Drawing page 4 of 12...",
  "pct": 45,
  "title": "Rusty's Big Day",
  "format": "picture",
  "can_download": false
}

Valores de estado: running → done o error.

Ejemplo de sondeo (Python)

import time, requests

API_KEY = "sm_your_key_here"
BASE    = "https://api.storymint.ca"
job_id  = "a1b2c3d4"  # devuelto por /api/generate

while True:
    r = requests.get(f"{BASE}/api/status/{job_id}",
                     headers={"X-API-Key": API_KEY})
    info = r.json()
    print(f"[{info.get('pct', 0)}%] {info.get('msg', '')}")

    if info["status"] == "done":
        print(f"Book ready! Title: {info.get('title')}")
        break
    elif info["status"] == "error":
        print(f"Error: {info.get('msg')}")
        break

    time.sleep(3)  # consulta cada 3 segundos

Progreso por SSE

GET /api/stream/{job_id}

Flujo de Server-Sent Events en tiempo real. Más eficiente que el sondeo — recibes las actualizaciones a medida que ocurren.

Formato de los eventos

data: {"step":"generating","msg":"Writing page 3...","pct":25}
data: {"step":"illustrating","msg":"Drawing page 3...","pct":40}
data: {"step":"done","msg":"Book complete!","pct":100}

Ejemplo en JavaScript

const BASE   = "https://api.storymint.ca";
const jobId  = "a1b2c3d4";

const evtSrc = new EventSource(`${BASE}/api/stream/${jobId}`);
evtSrc.onmessage = (e) => {
  const data = JSON.parse(e.data);
  console.log(`[${data.pct}%] ${data.msg}`);
  if (data.step === "done" || data.step === "error") {
    evtSrc.close();
  }
};

Obtener datos del libro

GET /api/book/{job_id}

Obtén la vista previa completa del libro: páginas, portada, texto, metadatos y URLs de las imágenes. Solo está disponible cuando el estado del trabajo es done.

Respuesta (libro ilustrado)

{
  "title": "Rusty's Big Day",
  "author": "My Brand Publishing",
  "series": "Forest Friends",
  "format": "picture",
  "pages": [
    {
      "page_num": 1,
      "text": "Once upon a time, in a hollow oak tree...",
      "image_url": "/api/book/a1b2c3d4/image/page_01.png"
    }
  ],
  "cover_url": "/api/book/a1b2c3d4/image/cover.jpg",
  "can_download": true,
  "metadata": {
    "total_pages": 12,
    "art_style": "watercolor"
  }
}

Edición por chat

POST /api/book/{job_id}/chat

Envía instrucciones en lenguaje natural para modificar un libro terminado. La IA reescribe las páginas afectadas.

Cuerpo de la solicitud

{
  "message": "Make the ending happier and add a rainbow"
}

Respuesta

{
  "assistant_message": "I've updated pages 10-12 with a happier ending featuring a rainbow celebration!",
  "pages": [ ... ]
}

Ejemplo en Python

resp = requests.post(
    f"{BASE}/api/book/{job_id}/chat",
    headers={"X-API-Key": API_KEY},
    json={"message": "Change the main character's name to Luna"},
)
edit = resp.json()
print(edit["assistant_message"])

Descargar el PDF

GET /api/download/{job_id} Planes de pago

Descarga el libro terminado en PDF (application/pdf). Requiere un plan de pago elegible (Plus, Max, Developer o Escuelas y organizaciones) o un crédito de libro para ese libro.

Ejemplo de descarga en Python

resp = requests.get(
    f"{BASE}/api/download/{job_id}",
    headers={"X-API-Key": API_KEY},
)
if resp.status_code == 200:
    with open("my_book.pdf", "wb") as f:
        f.write(resp.content)
    print("Downloaded!")
else:
    print("Error:", resp.json())

Planificador de historias (Plan Mode)

POST /api/plan-questions Max+

Genera preguntas aclaratorias y respuestas sugeridas para un borrador de prompt de libro. Disponible en los planes Max y Escuelas y organizaciones.

{ "prompt": "A city gardener finds a tiny door under a tomato plant" }
POST /api/plan-enhance Max+

Combina el prompt original con las respuestas del Modo Plan y devuelve un prompt mejorado para /api/generate.

{
  "prompt": "A city gardener finds a tiny door under a tomato plant",
  "answers": [
    { "question": "Who opens the door?", "answer": "Maya, a curious gardener" }
  ]
}

Audiolibro

POST /api/audiobook/{job_id} Max+

Genera un audiolibro narrado con IA a partir de un libro terminado. Disponible en los planes Max y Escuelas y organizaciones. Si la narración no está disponible en este momento, la API devuelve un error con error_code en lugar de un archivo incompleto.

Respuesta

Devuelve el archivo MP3 directamente como audio/mpeg. Guárdalo en disco:

resp = requests.post(
    f"{BASE}/api/audiobook/{job_id}",
    headers={"X-API-Key": API_KEY},
)
with open("audiobook.mp3", "wb") as f:
    f.write(resp.content)

Listar ajustes de calidad

GET /api/models

Lista los ajustes de calidad disponibles en tu plan. Pasa el id de un ajuste en story_model o image_model al generar, u omite ambos para usar standard.

Respuesta

{
  "presets": [
    { "id": "standard", "label": "Estándar" }
  ]
}

StoryMint elige y actualiza los servicios de IA que usa por dentro, así que los ajustes se mantienen estables aunque esos servicios cambien.

Verificar autenticación

GET /api/ping

Ejecuta una verificación de autenticación de bajo costo antes de generar. Confirma el acceso a la API, te indica qué plan está asociado a la clave e incrementa el contador de pings independiente que usa el plan Developer.

Respuesta

{
  "status": "ok",
  "tier": "developer",
  "plan_display_name": "Developer",
  "api_test_calls_this_month": 12,
  "api_test_calls_limit": 150,
  "timestamp": "2026-03-27T18:41:22.115000+00:00"
}

Developer incluye 150 llamadas de ping al mes. Max y Escuelas y organizaciones incluyen acceso a la API y por ahora no tienen un tope de pings independiente.

Plan y uso

GET /api/tier

Obtén tu plan actual, los contadores de uso, los límites y la cuota restante.

Respuesta

{
  "tier": "developer",
  "plan_display_name": "Developer",
  "books_this_month": 12,
  "books_limit": 100,
  "next_reset": "2026-04-01",
  "api_access": true,
  "api_test_calls_this_month": 12,
  "api_test_calls_limit": 150,
  "full_download": true,
  "max_pages": 50,
  "novel_max_chapters": 0,
  "org_type": ""
}

Marca personalizada

StoryMint está diseñado para la generación de libros de marca blanca. Usa estos campos para poner tu identidad en cada libro:

CampoDónde apareceEjemplo
author Portada, página de título, metadatos del PDF "Maple Leaf Publishing"
series Subtítulo de la portada, lomo (si aplica) "Tiny Explorers Vol. 3"
art_style Todas las ilustraciones del libro "watercolor"
book_format Diseño y estructura de todo el libro "novel"

Flujo de trabajo de marca

# Crea una serie de libros con tu marca
for topic in ["Space", "Ocean", "Jungle"]:
    resp = requests.post(f"{BASE}/api/generate",
        headers={"X-API-Key": API_KEY},
        json={
            "prompt": f"A child explores the {topic.lower()}",
            "author": "Your Publishing Company",
            "series": f"Little Explorers: {topic}",
            "art_style": "watercolor",
            "book_format": "picture",
        })
    print(f"Started {topic}: {resp.json()['job_id']}")

Personalización del perfil

GET PUT /api/profile

Consulta o actualiza tu perfil de editor (nombre visible, biografía, sitio web). Esta información aparece en las publicaciones del mercado.

requests.put(f"{BASE}/api/profile",
    headers={"Authorization": f"Bearer {ID_TOKEN}"},
    json={
        "display_name": "Maple Leaf Publishing",
        "bio": "We create magical stories for curious kids.",
        "website": "https://mapleleaf.example.com",
    })

Estilos de ilustración

Elige un estilo visual para todas las ilustraciones de tu libro. Envía el valor de art_style en tu solicitud de generación.

cartoon

Estilo de caricatura brillante y llamativo. Ideal para los más pequeños (de 3 a 7 años). Predeterminado.

watercolor

Pinturas suaves en acuarela. Elegante y encantador, ideal para marcas premium.

sketch

Ilustraciones a lápiz con un aire hecho a mano.

pixel

Estilo retro de pixel art. Divertido para historias de videojuegos o nostálgicas.

flat

Ilustraciones modernas de diseño plano con líneas limpias y colores sólidos.

manga

Estilo manga japonés con personajes expresivos. Ideal para cómics por capítulos.

western

Estilo de cómic occidental con entintado dinámico y viñetas vibrantes.

graphic_novel

Estética de novela gráfica para lectores mayores, con viñetas detalladas y cinematográficas.

Planes y límites de páginas

Los límites de páginas y capítulos son por libro; la cantidad de libros es por mes. Esto es lo que desbloquea cada plan:

Free

$0
  • Una vista previa de un libro ilustrado o un libro para colorear
  • Sin cómics ni novelas
  • Sin acceso a la API

Plus

CA$9.99/mes
  • 50 páginas ilustradas máx.
  • 100 páginas de cómic máx.
  • 20 capítulos de novela máx. Beta
  • 15 libros / mes

Escuelas y organizaciones

CA$99.99/mes
  • 100 páginas ilustradas máx.
  • 100 páginas de cómic máx.
  • 40 capítulos de novela máx. Beta
  • 9,999 libros / mes
  • 10 puestos para docentes o miembros del equipo incluidos

Developer

CA$25/mes
  • 50 páginas ilustradas máx.
  • 100 libros / mes
  • 150 llamadas de prueba a la API
  • Acceso completo a la API
  • Sin cómics ni novelas

Escala visual de páginas

Mira cómo cambia el máximo de páginas según el plan:

—Free
50Plus
50Max
50Dev
100Escuelas

Las páginas de cómic llegan hasta 100 en los planes creativos de pago. Las novelas están en Beta y llegan a un máximo de 40 capítulos en Escuelas y organizaciones.

Formatos de libro

FormatoValorResultadoPáginas máx.Ideal para
Libro para colorear coloring Dibujos en blanco y negro, una escena por página, portada a color; frases opcionales (coloring_variant: "story") Hasta 40 páginas (Free: vista previa) Niños de 2 a 12 años, según el estilo de línea
Libro ilustrado picture Ilustraciones a página completa con texto superpuesto Hasta 100 páginas (según el plan) Niños de 2 a 7 años, historias sencillas
Cómic comic Páginas de cómic con viñetas y globos de diálogo Hasta 100 páginas Niños de 6 a 12 años, historias de acción
Cómic por capítulos (manga) chapter_comic Cómic de varios capítulos con una portada por capítulo Hasta 100 páginas A partir de 8 años, historias por entregas
Novela Beta novel Texto extenso con portada generada por IA y PDF con formato 5–40 capítulos (según el plan) A partir de 10 años, 15k–60k+ palabras

Beta La generación de novelas está actualmente en beta. Las novelas usan IA para escribir libros completos por capítulos (15,000–60,000+ palabras) con portadas generadas automáticamente. Durante la beta:

  • Una novela tarda más en crearse que un libro ilustrado, y una novela larga tarda más que una corta
  • Las novelas más largas están disponibles en los planes superiores (consulta los límites de arriba)
  • Puedes editar novelas por chat, aunque funciona mejor para ajustes pequeños
  • El PDF incluye un formato profesional con encabezados de capítulo y números de página
  • La cantidad de novelas al mes depende del plan: Plus 3, Max 10, Escuelas y organizaciones 9,999

Gestión de claves API

GET /api/keys

Lista todas tus claves API con su prefijo, etiqueta, fechas de creación y de último uso, y estado.

POST /api/keys

Crea una nueva clave API. La clave completa (sm_...) se devuelve solo una vez — guárdala de inmediato.

Solicitud

{ "label": "My Production App" }

Respuesta

{
  "key": "sm_abc123xyz...",
  "key_id": "k_8f2e1a",
  "label": "My Production App"
}
DELETE /api/keys/{key_id}

Revoca una clave API. Queda inactiva de forma permanente. Crea una nueva si la necesitas.

Equipos (Escuelas y organizaciones)

Los usuarios de Escuelas y organizaciones pueden crear espacios de trabajo compartidos con puestos para docentes o miembros del equipo, invitar colaboradores por correo electrónico y compartir libros terminados en una biblioteca común.

GET POST /api/teams

GET lista tus equipos. POST crea un equipo nuevo.

Crear un equipo

resp = requests.post(f"{BASE}/api/teams",
    headers={"Authorization": f"Bearer {ID_TOKEN}"},
    json={"name": "Oakwood Elementary"})
team = resp.json()
print(f"Workspace: {team['id']} ({team['workspace_type']}), seats: {team['max_seats']}")
POST /api/teams/{team_id}/invite

Invita a un miembro por correo electrónico. La respuesta incluye un ID de invitación que puedes usar en los flujos de aceptación.

{
  "email": "teacher@school.edu"
}
POST /api/teams/invite/{invite_id}/accept

Acepta una invitación pendiente después de que el usuario invitado inicie sesión. Devuelve el objeto del espacio de trabajo actualizado.

GET /api/teams/{team_id}/books

Lista todos los libros de la biblioteca compartida del equipo.

POST /api/teams/{team_id}/books/{book_id}

Comparte un libro terminado en la biblioteca del espacio de trabajo compartido para que todos los miembros puedan acceder a él.

Ejemplo completo — Python

Flujo completo de principio a fin: generar, consultar el estado y descargar.

import requests, time

API_KEY = "sm_your_key_here"
BASE    = "https://api.storymint.ca"
HEADERS = {"X-API-Key": API_KEY, "Content-Type": "application/json"}

# 1. Generar
gen = requests.post(f"{BASE}/api/generate", headers=HEADERS, json={
    "prompt": "A cat astronaut discovers a candy planet",
    "book_format": "comic",
    "author": "Starlight Studios",
    "art_style": "cartoon",
    "num_pages": 30,
}).json()
job_id = gen["job_id"]
print(f"Job: {job_id}")

# 2. Consultar hasta que termine
while True:
    status = requests.get(f"{BASE}/api/status/{job_id}",
                          headers=HEADERS).json()
    print(f"  [{status.get('pct', 0):3d}%] {status.get('msg', '')}")
    if status["status"] in ("done", "error"):
        break
    time.sleep(3)

if status["status"] == "error":
    print("Generation failed:", status.get("msg"))
    exit(1)

# 3. Obtener los datos del libro
book = requests.get(f"{BASE}/api/book/{job_id}",
                    headers=HEADERS).json()
print(f"\nTitle: {book['title']}")
print(f"Pages: {len(book.get('pages', []))}")

# 4. Edición con IA
edit = requests.post(f"{BASE}/api/book/{job_id}/chat",
    headers=HEADERS,
    json={"message": "Add more humor to page 3"}).json()
print(f"Edit: {edit.get('assistant_message')}")

# 5. Descargar el PDF
dl = requests.get(f"{BASE}/api/download/{job_id}",
                  headers=HEADERS)
if dl.status_code == 200:
    with open(f"{book['title']}.pdf", "wb") as f:
        f.write(dl.content)
    print("Downloaded!")
else:
    print("Download error:", dl.json())

Ejemplo completo — JavaScript (Node.js / navegador)

const API_KEY = "sm_your_key_here";
const BASE    = "https://api.storymint.ca";
const headers = {
  "Content-Type": "application/json",
  "X-API-Key": API_KEY,
};

// 1. Generar
const genResp = await fetch(`${BASE}/api/generate`, {
  method: "POST", headers,
  body: JSON.stringify({
    prompt: "A cat astronaut discovers a candy planet",
    book_format: "comic",
    author: "Starlight Studios",
    art_style: "cartoon",
    num_pages: 30,
  }),
});
const { job_id } = await genResp.json();
console.log("Job:", job_id);

// 2. Consultar el estado
let status;
do {
  await new Promise(r => setTimeout(r, 3000));
  const r = await fetch(`${BASE}/api/status/${job_id}`, { headers });
  status = await r.json();
  console.log(`  [${status.pct x 0}%] ${status.msg x ""}`);
} while (status.status === "running");

// 3. Obtener el libro
const book = await (await fetch(`${BASE}/api/book/${job_id}`,
  { headers })).json();
console.log("Title:", book.title);

// 4. Edición por chat
const edit = await (await fetch(`${BASE}/api/book/${job_id}/chat`, {
  method: "POST", headers,
  body: JSON.stringify({ message: "Make the ending funnier" }),
})).json();
console.log("Edit:", edit.assistant_message);

// 5. Descargar (Node.js)
const dlResp = await fetch(`${BASE}/api/download/${job_id}`,
  { headers });
if (dlResp.ok) {
  const fs = await import("fs");
  const buf = Buffer.from(await dlResp.arrayBuffer());
  fs.writeFileSync(`${book.title}.pdf`, buf);
  console.log("Downloaded!");
}

// 5b. Descargar (navegador)
// const blob = await dlResp.blob();
// const a = document.createElement("a");
// a.href = URL.createObjectURL(blob);
// a.download = `${book.title}.pdf`;
// a.click();

Ejemplo completo — Java

import java.net.http.*;
import java.net.URI;
import java.nio.file.*;

public class StoryMintExample {
    static final String API_KEY = "sm_your_key_here";
    static final String BASE = "https://api.storymint.ca";
    static final HttpClient client = HttpClient.newHttpClient();

    static HttpRequest.Builder req(String path) {
        return HttpRequest.newBuilder()
            .uri(URI.create(BASE + path))
            .header("X-API-Key", API_KEY)
            .header("Content-Type", "application/json");
    }

    public static void main(String[] args) throws Exception {
        // 1. Generar
        String body = """
            {
              "prompt": "A cat astronaut discovers a candy planet",
              "book_format": "comic",
              "author": "Starlight Studios",
              "art_style": "cartoon",
              "num_pages": 30
            }""";
        var genResp = client.send(
            req("/api/generate").POST(
                HttpRequest.BodyPublishers.ofString(body)).build(),
            HttpResponse.BodyHandlers.ofString());
        System.out.println("Generate: " + genResp.body());
        // Extrae el job_id de la respuesta JSON

        String jobId = "PARSE_FROM_RESPONSE";

        // 2. Consultar el estado
        while (true) {
            var statusResp = client.send(
                req("/api/status/" + jobId).GET().build(),
                HttpResponse.BodyHandlers.ofString());
            System.out.println("Status: " + statusResp.body());
            if (statusResp.body().contains("\"done\"") ||
                statusResp.body().contains("\"error\"")) break;
            Thread.sleep(3000);
        }

        // 3. Descargar
        var dlResp = client.send(
            req("/api/download/" + jobId).GET().build(),
            HttpResponse.BodyHandlers.ofByteArray());
        if (dlResp.statusCode() == 200) {
            Files.write(Path.of("book.pdf"), dlResp.body());
            System.out.println("Downloaded!");
        }
    }
}

Ejemplo completo — cURL

# 1. Generar un libro
JOB=$(curl -s -X POST \
  https://api.storymint.ca/api/generate \
  -H "Content-Type: application/json" \
  -H "X-API-Key: sm_your_key_here" \
  -d '{
    "prompt": "A cat astronaut discovers a candy planet",
    "book_format": "comic",
    "author": "Starlight Studios",
    "art_style": "cartoon",
    "num_pages": 30
  }')
echo "Generate response: $JOB"
JOB_ID=$(echo $JOB | grep -o '"job_id":"[^"]*"' | cut -d'"' -f4)

# 2. Consultar el estado hasta que termine
while true; do
  STATUS=$(curl -s \
    https://api.storymint.ca/api/status/$JOB_ID \
    -H "X-API-Key: sm_your_key_here")
  echo "Status: $STATUS"
  echo "$STATUS" | grep -q '"done"\|"error"' && break
  sleep 3
done

# 3. Obtener los datos del libro
curl -s \
  https://api.storymint.ca/api/book/$JOB_ID \
  -H "X-API-Key: sm_your_key_here" | python3 -m json.tool

# 4. Descargar el PDF
curl -o book.pdf \
  https://api.storymint.ca/api/download/$JOB_ID \
  -H "X-API-Key: sm_your_key_here"

Ejemplo completo — C#

using System.Net.Http;
using System.Text;
using System.Text.Json;

var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "sm_your_key_here");
var baseUrl = "https://api.storymint.ca";

// 1. Generar
var genPayload = JsonSerializer.Serialize(new {
    prompt = "A cat astronaut discovers a candy planet",
    book_format = "comic",
    author = "Starlight Studios",
    art_style = "cartoon",
    num_pages = 30,
});
var genResp = await client.PostAsync($"{baseUrl}/api/generate",
    new StringContent(genPayload, Encoding.UTF8, "application/json"));
var genJson = JsonDocument.Parse(await genResp.Content.ReadAsStringAsync());
var jobId = genJson.RootElement.GetProperty("job_id").GetString();
Console.WriteLine($"Job: {jobId}");

// 2. Consultar el estado
while (true) {
    var statusResp = await client.GetStringAsync($"{baseUrl}/api/status/{jobId}");
    Console.WriteLine($"Status: {statusResp}");
    if (statusResp.Contains("\"done\"") || statusResp.Contains("\"error\"")) break;
    await Task.Delay(3000);
}

// 3. Descargar
var dlResp = await client.GetAsync($"{baseUrl}/api/download/{jobId}");
if (dlResp.IsSuccessStatusCode) {
    var bytes = await dlResp.Content.ReadAsByteArrayAsync();
    await File.WriteAllBytesAsync("book.pdf", bytes);
    Console.WriteLine("Downloaded!");
}

Ejemplo completo — Go

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "os"
    "time"
)

const apiKey = "sm_your_key_here"
const base = "https://api.storymint.ca"

func apiReq(method, path string, body interface{}) (*http.Response, error) {
    var buf io.Reader
    if body != nil {
        b, _ := json.Marshal(body)
        buf = bytes.NewBuffer(b)
    }
    req, _ := http.NewRequest(method, base+path, buf)
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("X-API-Key", apiKey)
    return http.DefaultClient.Do(req)
}

func main() {
    // 1. Generar
    resp, _ := apiReq("POST", "/api/generate", map[string]interface{}{
        "prompt":      "A cat astronaut discovers a candy planet",
        "book_format": "comic",
        "author":      "Starlight Studios",
        "art_style":   "cartoon",
        "num_pages":   30,
    })
    var gen map[string]interface{}
    json.NewDecoder(resp.Body).Decode(&gen)
    resp.Body.Close()
    jobID := gen["job_id"].(string)
    fmt.Println("Job:", jobID)

    // 2. Consultar el estado
    for {
        resp, _ := apiReq("GET", "/api/status/"+jobID, nil)
        var st map[string]interface{}
        json.NewDecoder(resp.Body).Decode(&st)
        resp.Body.Close()
        fmt.Printf("  [%.0f%%] %s\n", st["pct"], st["msg"])
        if st["status"] == "done" || st["status"] == "error" {
            break
        }
        time.Sleep(3 * time.Second)
    }

    // 3. Descargar
    resp, _ = apiReq("GET", "/api/download/"+jobID, nil)
    defer resp.Body.Close()
    if resp.StatusCode == 200 {
        f, _ := os.Create("book.pdf")
        io.Copy(f, resp.Body)
        f.Close()
        fmt.Println("Downloaded!")
    }
}

Códigos de error

  • 200 Éxito
  • 201 Recurso creado (clave API, equipo)
  • 400 Solicitud incorrecta — faltan parámetros o no son válidos
  • 401 No autorizado — falta la clave API / token o no es válida
  • 403 Prohibido — se superó el límite de uso o la función no está disponible en tu plan
  • 404 No encontrado — job_id o recurso no válido
  • 429 Demasiadas solicitudes — se alcanzó el límite de solicitudes
  • 500 Error interno del servidor — vuelve a intentarlo en unos segundos
  • 503 Servicio no disponible — el motor de generación está temporalmente fuera de línea

Formato de la respuesta de error

{
  "error": "Descriptive error message here",
  "detail": "Additional context (optional)"
}

Manejo de errores (Python)

resp = requests.post(f"{BASE}/api/generate",
    headers=HEADERS, json=payload)

if resp.status_code == 200:
    job_id = resp.json()["job_id"]
elif resp.status_code == 403:
    print("Quota exceeded:", resp.json().get("error"))
elif resp.status_code == 401:
    print("Invalid API key. Check your X-API-Key header.")
else:
    print(f"Error {resp.status_code}:", resp.json())

Webhooks

Los webhooks de generación completada todavía no están disponibles en esta versión pública. En su lugar, usa la consulta de estado o la transmisión por SSE para seguir el progreso del trabajo.

¿Te interesa? Escríbenos a support@storymint.ca.