Desarrolladores
Publicar en tu propia API
Contrato HTTP que debe implementar tu web para que Blogenia le envíe los artículos: rutas, cabeceras, cuerpo, respuesta, tiempos y reintentos, con un receptor de ejemplo.
Si tu web tiene su propia API, Blogenia puede publicar en ella cada artículo: lo crea al publicarlo, lo actualiza cuando lo editas y lo retira si lo despublicas o lo borras. Tu web recibe el artículo en HTML y decide cómo mostrarlo.
Si tu web es WordPress no necesitas esto: elige WordPress en la misma pantalla.
Conectar tu API
- En el panel, abre Blog y la pestaña Publicación. Solo el propietario de la cuenta puede configurarla.
- Elige API propia e indica la dirección de tu web, la clave de acceso y, si cambian, las rutas.
- Guarda y comprueba la conexión.
| Campo | Reglas |
|---|---|
| Dirección de la web | https, sin usuario, parámetros ni fragmento, y puerto 443. Debe ser una dirección pública. |
| Clave de acceso | Se envía como Authorization: Bearer <clave>. Se guarda cifrada y no se vuelve a mostrar. |
| Ruta de artículos | Por defecto /articles. Debe empezar por / y llevar solo letras, números, _, - y /. |
| Ruta de comprobación | Por defecto /health. Mismas reglas. |
Lo que debe implementar tu API
| Operación | Petición | Cuándo |
|---|---|---|
| Comprobar | GET {ruta de comprobación} | Al comprobar la conexión. |
| Crear | POST {ruta de artículos} | Se publica un artículo por primera vez. |
| Actualizar | PUT {ruta de artículos}/{id} | Se edita un artículo ya publicado. |
| Despublicar | PUT {ruta de artículos}/{id} con status: "draft" | El artículo deja de estar publicado. |
| Borrar | DELETE {ruta de artículos}/{id} | Se borra el artículo en Blogenia. |
| Consultar | GET {ruta de artículos}/{id} | Solo al conciliar a mano una creación dudosa (ver más abajo). |
{id} es el identificador que tu API devolvió al crear el artículo.
Cabeceras
| Cabecera | Valor |
|---|---|
Authorization | Bearer <clave de acceso> |
Accept | application/json |
Content-Type | application/json |
Idempotency-Key | En POST, PUT y DELETE: 64 caracteres hexadecimales. Los reintentos de la misma operación llevan la misma clave. |
Cuerpo de POST y PUT
Campos
| Campo | Tipo | Descripción |
|---|---|---|
sourceId | string | UUID estable del artículo en Blogenia. Úsalo para no crear duplicados. |
title | string | Título. |
slug | string | Slug del artículo. |
content | string | Contenido en HTML. Los enlaces a otros artículos ya publicados en tu API apuntan a su url; los enlaces a artículos aún no publicados allí llegan sin href. |
excerpt | string | Extracto. Cadena vacía si no tiene. |
status | string | published, o draft al despublicar. |
DELETE no lleva cuerpo.
Tu respuesta
Responde con un código 2xx y un objeto JSON. GET de comprobación solo necesita un objeto JSON cualquiera.
Campos de la respuesta a POST y PUT
| Campo | Tipo | Descripción |
|---|---|---|
id | string | number | Identificador del artículo en tu API: un entero positivo o un texto de hasta 200 caracteres con letras, números, _ y -. En PUT debe ser el mismo del que está en la ruta. |
url | string | Opcional. URL pública del artículo en tu web. Solo se guarda si es https, del mismo origen que la dirección configurada y de menos de 2048 caracteres. |
DELETE: cualquier2xxvale (también sin cuerpo).404y410se tratan como «ya borrado».- No seguimos redirecciones: un
3xxcuenta como error. Configura la dirección definitiva.
{
"sourceId": "0b8e2f6c-6d63-4d0e-9c1a-5d8f3c1d7a11",
"title": "¿Cuánto cuesta reformar una cocina?",
"slug": "cuanto-cuesta-reformar-una-cocina",
"content": "<h2>Precio medio</h2><p>…</p>",
"excerpt": "Precios orientativos y qué incluye cada partida.",
"status": "published"
}{
"id": "42",
"url": "https://tuweb.example/blog/cuanto-cuesta-reformar-una-cocina"
}Tiempos, tamaños y reintentos
| Concepto | Valor |
|---|---|
| Tiempo máximo por petición | 15 segundos. |
| Cuerpo de la petición | Hasta 2 MiB. |
| Respuesta | Hasta 256 KB. |
| Actualizar, despublicar y borrar | Un error de red, un 5xx o un 429 se reintenta solo, hasta 3 intentos en total: tras 1 y 2 minutos. Otros 4xx no se reintentan. |
| Crear | Un 429 se reintenta igual; otro 4xx es un fallo definitivo. Un error de red o un 5xx no se reintenta: Blogenia no sabe si tu API llegó a crear el artículo y pide al propietario que lo revise en el panel antes de repetir. |
Un fallo no automático se puede reintentar a mano desde el artículo en el panel.
Creaciones dudosas
Si un POST termina en error de red o 5xx, el propietario revisa en tu web si el artículo se creó. Si existe, indica su id en el panel y Blogenia lo enlaza tras comprobar que GET {ruta de artículos}/{id} devuelve el mismo id y el mismo slug. Implementa ese GET y haz el POST idempotente por sourceId o Idempotency-Key para evitar duplicados.
Receptor de ejemplo
Un servidor Express mínimo que cumple el contrato. Guarda los artículos en memoria: úsalo como punto de partida.
import express from 'express';
const TOKEN = process.env.BLOGENIA_TOKEN; // la clave de acceso que pegas en Blogenia
const articles = new Map(); // id -> artículo
const bySource = new Map(); // sourceId -> id
let nextId = 1;
const app = express();
app.use(express.json({ limit: '3mb' }));
// Autenticación Bearer
app.use((req, res, next) => {
if (req.get('authorization') !== 'Bearer ' + TOKEN) return res.sendStatus(401);
next();
});
// Ruta de comprobación
app.get('/health', (_req, res) => res.json({ ok: true }));
// Crear (idempotente por sourceId)
app.post('/articles', (req, res) => {
const { sourceId, title, slug, content, excerpt, status } = req.body;
let id = bySource.get(sourceId);
if (!id) {
id = String(nextId++);
bySource.set(sourceId, id);
}
articles.set(id, { id, sourceId, title, slug, content, excerpt, status });
res.status(201).json({ id, url: 'https://tuweb.example/blog/' + slug });
});
// Consultar (lo usa Blogenia al conciliar)
app.get('/articles/:id', (req, res) => {
const a = articles.get(req.params.id);
if (!a) return res.sendStatus(404);
res.json({ id: a.id, slug: a.slug, url: 'https://tuweb.example/blog/' + a.slug });
});
// Actualizar o despublicar (status: "draft")
app.put('/articles/:id', (req, res) => {
if (!articles.has(req.params.id)) return res.sendStatus(404);
const { sourceId, title, slug, content, excerpt, status } = req.body;
const id = req.params.id;
articles.set(id, { id, sourceId, title, slug, content, excerpt, status });
res.json({ id, url: 'https://tuweb.example/blog/' + slug });
});
// Borrar
app.delete('/articles/:id', (req, res) => {
articles.delete(req.params.id);
res.sendStatus(204);
});
app.listen(3000);