SDKs
Node.js-SDK
Installiere @sofia-post/node und nutze die Sofia API aus Node.js: veröffentlichen, planen, Medien hochladen, Statistiken lesen – mit typisierten Fehlern.
Das offizielle Node.js-SDK verpackt die Sofia API v1 in einer einzigen Klasse, Sofia: eine Methode pro Route, typisierte Anfragen und Antworten und ein einziger Fehlertyp für jeden Fehlschlag. Es hat keine Abhängigkeiten und läuft ab Node.js 20.
Installation
Das SDK liegt auf npm als Paket @sofia-post/node. Es funktioniert mit import und require.
npm install @sofia-post/nodeSchnellstart
Erstelle einen Client mit deinem Schlüssel, such den Betrieb, für den du veröffentlichen willst, und plane dann einen Beitrag auf 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);Authentifizierung
Der Konstruktor nimmt einen persönlichen Schlüssel, den du unter Einstellungen › API, Tab Persönlicher Zugriff, erstellst, oder ein OAuth-Zugriffstoken, wenn deine Anwendung für andere Sofia-Nutzer handelt (OAuth2). Das SDK sendet ihn bei jeder Anfrage als Authorization: Bearer ….
Der Client spricht standardmäßig mit https://api.sofia-post.com. Das zweite Argument ändert die Basis-URL oder das fetch, mit dem er Anfragen sendet:
const sofia = new Sofia(apiKey, { baseUrl: 'http://localhost:3008' });Methoden
| Methode | Was sie tut |
|---|---|
me() | Für wen der Schlüssel handelt, seine Scopes und ob es ein Testschlüssel ist |
platforms() | Die Regeln jedes Netzwerks: Textlänge, Medien, Formate |
establishments() | Die Betriebe, die der Schlüssel erreicht |
accounts(establishmentId) | Die mit einem Betrieb verbundenen Social-Media-Konten |
connectionLink(establishmentId, input) | Eine von Sofia gehostete Seite, auf der die Person ein Netzwerk verbindet |
insights(establishmentId, period?) | Reichweite, Interaktion und Follower, Netzwerk für Netzwerk |
post(input, options?) | Jetzt veröffentlichen, mit scheduledAt planen oder einen Entwurf speichern |
postList(filters?) | Eine Seite mit Beiträgen, gefiltert nach Betrieb, Status oder Datum |
getPost(id) · updatePost(id, input) · deletePost(id) | Einen Beitrag lesen, ändern oder zurückziehen |
upload(input) · uploadFromUrl(input) | Eine Datei oder eine URL zur Mediathek des Betriebs hinzufügen |
mediaList(filters) · getMedia(id) · deleteMedia(id) | Die Mediathek lesen und aufräumen |
createWebhook(input) · webhooks() · deleteWebhook(id) · testWebhook(id) | Benachrichtigt werden, wenn ein Beitrag veröffentlicht wird oder fehlschlägt |
Beiträge
Ein Beitrag hat pro Netzwerk ein Ziel, jeweils mit eigenem Text, eigenen Medien und eigenem Format. Prüfe ihn zuerst mit dryRun: Es wird nichts geschrieben, und eine Ablehnung listet jede verletzte Regel auf. Gib einen idempotencyKey mit, damit eine wiederholte Anfrage nie doppelt veröffentlicht. scheduledAt akzeptiert ein Date oder einen ISO-8601-String.
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() });Medien
Lade die Datei einmal hoch und trage dann ihre id in die media eines Beitrags ein. upload nimmt einen Buffer, ein Uint8Array oder einen Blob und liest den Content-Type aus dem Dateinamen, sofern du keinen contentType angibst. Bei uploadFromUrl lädt Sofia die Datei selbst herunter.
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' });Paginierung
postList und mediaList antworten Seite für Seite. Übergib das nextCursor einer Seite als cursor des nächsten Aufrufs; die letzte Seite hat keins.
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);Fehler
Jede Methode wirft einen SofiaApiError, wenn die API den Aufruf ablehnt. Er enthält den HTTP-status, den stabilen code des Fehler-Umschlags, seine details (bei einem abgelehnten Beitrag jede verletzte Netzwerkregel), die requestId für den Support und bei einem 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);
}Ist die API gar nicht erreichbar, hat der Fehler den Code network_error und den Status 0. Das SDK wiederholt nie von selbst: Warte bei einem 429 retryAfterSeconds ab und erhöhe bei einem 5xx die Abstände zwischen den Versuchen.
TypeScript
Das Paket bringt seine Typen mit: PublishPostInput, Post, Media, SocialAccount, EstablishmentInsights und die übrigen werden neben Sofia exportiert, damit dein Editor jedes Feld und jeden Enum-Wert vervollständigt.
Als Nächstes: Die API-Referenz beschreibt jede Route, die das SDK aufruft, und die CLI steuert dieselbe API vom Terminal aus.