SDKs
SDK de Node.js
Instala @sofia-post/node y llama a la API de Sofia desde Node.js: publica, programa, sube archivos y lee estadísticas, con respuestas y errores tipados.
El SDK oficial de Node.js envuelve la API v1 de Sofia en una sola clase, Sofia: un método por ruta, solicitudes y respuestas tipadas, y un único tipo de error para cualquier fallo. No tiene dependencias y funciona con Node.js 20 o posterior.
Instalación
El SDK se publica en npm como el paquete @sofia-post/node. Funciona tanto con import como con require.
npm install @sofia-post/nodeInicio rápido
Crea un cliente con tu clave, localiza el establecimiento para el que vas a publicar y programa una publicación en Instagram:
import { Sofia } from '@sofia-post/node';
const sofia = new Sofia(process.env.SOFIA_API_KEY);
const [establishment] = await sofia.establishments();
const post = await sofia.post({
establishmentId: establishment.id,
targets: [{ platform: 'instagram', text: 'Tarte tatin du jour 🍎' }],
scheduledAt: new Date('2026-12-01T09:00:00Z'),
});
console.log(post.id, post.status);Autenticación
El constructor recibe una clave personal, que se crea en Ajustes › API, pestaña Acceso personal, o un token de acceso OAuth si tu aplicación actúa por otros usuarios de Sofia (OAuth2). El SDK la envía como Authorization: Bearer … en cada solicitud.
El cliente habla con https://api.sofia-post.com por defecto. El segundo argumento cambia la URL base o el fetch con el que envía las solicitudes:
const sofia = new Sofia(apiKey, { baseUrl: 'http://localhost:3008' });Métodos
| Método | Qué hace |
|---|---|
me() | Para quién actúa la clave, sus scopes y si es una clave de prueba |
platforms() | Las reglas de cada red: longitud del texto, archivos, formatos |
establishments() | Los establecimientos a los que llega la clave |
accounts(establishmentId) | Las cuentas sociales conectadas a un establecimiento |
connectionLink(establishmentId, input) | Una página alojada por Sofia donde la persona conecta una red |
insights(establishmentId, period?) | Alcance, interacción y seguidores, red por red |
post(input, options?) | Publicar ahora, programar con scheduledAt o guardar un borrador |
postList(filters?) | Una página de publicaciones, filtradas por establecimiento, estado o fechas |
getPost(id) · updatePost(id, input) · deletePost(id) | Leer, modificar o retirar una publicación |
upload(input) · uploadFromUrl(input) | Añadir un archivo o una URL a la biblioteca de archivos del establecimiento |
mediaList(filters) · getMedia(id) · deleteMedia(id) | Leer y limpiar la biblioteca de archivos |
createWebhook(input) · webhooks() · deleteWebhook(id) · testWebhook(id) | Recibir un aviso cuando una publicación sale o falla |
Publicaciones
Una publicación tiene un destino por red, cada uno con su propio texto, archivos y formato. Compruébala primero con dryRun: no se escribe nada, y un rechazo enumera cada regla que incumple. Pasa una idempotencyKey para que una solicitud repetida nunca publique dos veces. scheduledAt acepta una Date o una cadena ISO 8601.
import type { PublishPostInput } from '@sofia-post/node';
const input: PublishPostInput = {
establishmentId,
targets: [
{ platform: 'instagram', text: 'Menu du jour', media: [{ id: media.id }] },
{ platform: 'facebook', text: 'Menu du jour : tarte tatin et café' },
],
};
await sofia.post(input, { dryRun: true });
await sofia.post(input, { idempotencyKey: crypto.randomUUID() });Archivos
Sube el archivo una vez y pon su id en los media de una publicación. upload acepta un Buffer, un Uint8Array o un Blob, y lee el tipo de contenido del nombre del archivo salvo que indiques contentType. Con uploadFromUrl, Sofia descarga el archivo ella misma.
import { readFile } from 'node:fs/promises';
const media = await sofia.upload({
establishmentId,
file: await readFile('tarte-tatin.jpg'),
fileName: 'tarte-tatin.jpg',
});
const imported = await sofia.uploadFromUrl({ establishmentId, url: 'https://example.com/menu.png' });Paginación
postList y mediaList responden una página cada vez. Pasa el nextCursor de una página como cursor de la siguiente llamada; la última página no lo tiene.
let cursor: string | undefined;
do {
const page = await sofia.postList({ establishmentId, status: 'published', limit: 100, cursor });
page.data.forEach((post) => console.log(post.id));
cursor = page.nextCursor;
} while (cursor);Errores
Cada método lanza un SofiaApiError cuando la API rechaza la llamada. Incluye el status HTTP, el code estable del sobre de error, sus details (en una publicación rechazada, cada regla de red que incumple), el requestId que debes dar al soporte y, en un 429, retryAfterSeconds.
import { SofiaApiError } from '@sofia-post/node';
try {
await sofia.post(input);
} catch (error) {
if (!(error instanceof SofiaApiError)) throw error;
if (error.code === 'post_refused') console.log(error.details);
if (error.status === 429) await wait(error.retryAfterSeconds);
}Cuando la API no es accesible en absoluto, el error tiene el código network_error y el estado 0. El SDK nunca reintenta por su cuenta: espera retryAfterSeconds en un 429 y espacia los reintentos en un 5xx.
TypeScript
El paquete incluye sus tipos: PublishPostInput, Post, Media, SocialAccount, EstablishmentInsights y los demás se exportan junto a Sofia, para que tu editor autocomplete cada campo y cada valor de enumeración.
A continuación: la referencia de la API describe cada ruta que llama el SDK, y la CLI maneja la misma API desde un terminal.