SDK
Node.js SDK
安裝 @sofia-post/node,在 Node.js 中呼叫 Sofia API:發佈、排程、上傳媒體、讀取數據洞察,回應和錯誤都帶型別。
官方 Node.js SDK 把 Sofia API v1 封裝在一個類別 Sofia 中:每個路由對應一個方法,請求和回應都有型別,所有失敗都使用同一種錯誤型別。它沒有任何相依套件,可在 Node.js 20 或更高版本上執行。
安裝
SDK 以 @sofia-post/node 套件的名稱發佈在 npm 上。import 和 require 都可以使用。
npm install @sofia-post/node快速開始
用你的金鑰建立用戶端,找到要發佈內容的店家,然後在 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);身分驗證
建構函式接收一個個人金鑰(在設定 › API 的個人存取標籤頁中建立),如果你的應用程式代表其他 Sofia 使用者操作,則接收 OAuth 存取權杖(OAuth2)。SDK 會在每次請求中以 Authorization: Bearer … 傳送它。
用戶端預設連線到 https://api.sofia-post.com。第二個參數可以更改基礎 URL,或傳送請求所用的 fetch:
const sofia = new Sofia(apiKey, { baseUrl: 'http://localhost:3008' });方法
| 方式 | 作用 |
|---|---|
me() | 金鑰代表誰操作、它的 scope,以及是否為測試金鑰 |
platforms() | 各網路的規則:文案長度、媒體、格式 |
establishments() | 金鑰可存取的店家 |
accounts(establishmentId) | 已連接到店家的社群帳號 |
connectionLink(establishmentId, input) | 由 Sofia 代管的頁面,使用者可在其中連接網路 |
insights(establishmentId, period?) | 依網路查看觸及、互動和粉絲數據 |
post(input, options?) | 立即發佈、用 scheduledAt 排程發佈,或儲存為草稿 |
postList(filters?) | 一頁帖文,可依店家、狀態或日期篩選 |
getPost(id) · updatePost(id, input) · deletePost(id) | 讀取、修改或撤回一篇帖文 |
upload(input) · uploadFromUrl(input) | 把檔案或 URL 加入店家的媒體庫 |
mediaList(filters) · getMedia(id) · deleteMedia(id) | 讀取並清理媒體庫 |
createWebhook(input) · webhooks() · deleteWebhook(id) · testWebhook(id) | 在帖文發佈或失敗時收到通知 |
帖文
一篇帖文在每個網路上都有一個目標,各自帶有文案、媒體和格式。請先用 dryRun 檢查:不會寫入任何內容,被拒絕時會列出它違反的每條規則。傳入 idempotencyKey,重試的請求就不會重複發佈。scheduledAt 接受 Date 或 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() });媒體
檔案只需上傳一次,然後把它的 id 放進帖文的 media。upload 接受 Buffer、Uint8Array 或 Blob,除非你指定 contentType,否則會根據檔名判斷內容類型。uploadFromUrl 則由 Sofia 自己下載檔案。
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' });分頁
postList 和 mediaList 每次回傳一頁。把一頁的 nextCursor 作為下一次呼叫的 cursor 傳入;最後一頁沒有它。
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);錯誤
當 API 拒絕呼叫時,每個方法都會擲出 SofiaApiError。它包含 HTTP status、錯誤信封中穩定的 code、details(對被拒絕的帖文,即它違反的每條網路規則)、需要提供給支援團隊的 requestId,以及在 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);
}當 API 完全無法連線時,錯誤的 code 為 network_error,狀態為 0。SDK 從不自動重試:遇到 429 請等待 retryAfterSeconds,遇到 5xx 請放慢重試間隔。
TypeScript
該套件自帶型別:PublishPostInput、Post、Media、SocialAccount、EstablishmentInsights 等都與 Sofia 一起匯出,因此你的編輯器會自動補全每個欄位和每個列舉值。
接下來:API 參考介紹了 SDK 呼叫的每個路由,CLI 則在終端機中操控同一套 API。