Questo articolo raccoglie script API Notiondesk pronti all'uso per le attività che i team automatizzano più frequentemente: pubblicare le modifiche di Notion da una pipeline CI, eseguire il backup di ogni articolo in formato Markdown, inviare feedback negativi a Slack, segnalare le ricerche a cui il centro assistenza non riesce a rispondere, trovare le sincronizzazioni non riuscite e applicare un unico marchio a diversi centri assistenza. Copia una ricetta, imposta due variabili d'ambiente ed eseguila.
Prima di iniziare
- Crea una chiave API con solo gli ambiti necessari alla ricetta. Ogni ricetta li nomina. Vedi Autenticazione con l'API Notiondesk tramite chiavi API .
- Trova il tuo centro assistenza
idconGET /sites. Vedi Leggi i tuoi centri assistenza con l'API Notiondesk .
- Impostali entrambi come variabili d'ambiente:
export NOTIONDESK_API_KEY="nd_wsk_..."
export NOTIONDESK_SITE_ID="c74781f6-1aea-4e17-ab23-bbb8b4d174b6"Le ricette JavaScript richiedono Node.js 18 o versioni successive.
Un piccolo aiutante condiviso
Salva questo file come notiondesk.mjs accanto ai tuoi script. Invia le intestazioni, attende la risposta dell'API 429 rate_limited e legge ogni pagina di una lista.
Sincronizza il tuo centro assistenza da GitHub Actions
Ambito: sync
Pubblica le modifiche apportate a Notion tramite un flusso di lavoro di GitHub Actions, ad esempio subito dopo una release. Memorizza la chiave come segreto del repository NOTIONDESK_API_KEY e l'ID del centro assistenza come variabile del repository NOTIONDESK_SITE_ID.
export const SITE_ID = process.env.NOTIONDESK_SITE_ID;
export async function api(path, options = {}) {
for (let attempt = 0; attempt < 5; attempt++) {
const response = await fetch(`https://api.notiondesk.so${path}`, {
...options,
headers: {
Authorization: `Bearer ${process.env.NOTIONDESK_API_KEY}`,
"nd-version": "2026-10-16",
...options.headers,
},
});
if (response.status === 429) {
const seconds = Number(response.headers.get("Retry-After")) || 5;
await new Promise((resolve) => setTimeout(resolve, seconds * 1000));
continue;
}
if (!response.ok) {
throw new Error(`${response.status} ${await response.text()}`);
}
return response.status === 204 ? null : response.json();
}
throw new Error(`Still rate limited on ${path}`);
}
export async function listAll(path) {
const items = [];
let cursor = null;
do {
const url = new URL(`https://api.notiondesk.so${path}`);
url.searchParams.set("page_size", "100");
if (cursor) {
url.searchParams.set("cursor", cursor);
}
const page = await api(`${url.pathname}${url.search}`);
items.push(...page.data);
cursor = page.has_more ? page.next_cursor : null;
} while (cursor);
return items;
}name: Sync help center
on:
workflow_dispatch:
release:
types: [published]
jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: Trigger a Notiondesk sync
run: |
status=$(curl -s -o response.json -w "%{http_code}" -X POST \
"https://api.notiondesk.so/sites/${{ vars.NOTIONDESK_SITE_ID }}/sync" \
-H "Authorization: Bearer ${{ secrets.NOTIONDESK_API_KEY }}" \
-H "nd-version: 2026-10-16" \
-H "Idempotency-Key: github-${{ github.run_id }}-${{ github.run_attempt }}")
if [ "$status" = "409" ]; then echo "A sync is already running."; exit 0; fi
if [ "$status" != "204" ]; then cat response.json; exit 1; fi
echo "Sync queued."204significa che la sincronizzazione è in coda. Viene eseguita in background.
409 conflict_errorsignifica che una sincronizzazione del centro assistenza è già in coda o in esecuzione, quindi il passaggio ha esito positivo senza avviarne un altro.
- Il parametro
Idempotency-Keyfa sì che una richiesta ritentata non venga eseguita due volte.
Esegui il backup di ogni articolo in markdown
Ambito: read
Salva ogni articolo, in ogni lingua supportata dal tuo centro assistenza, come file Markdown:
Il backup include bozze e articoli archiviati. Le letture sono limitate a 120 richieste al minuto, quindi un centro assistenza con 500 articoli richiede circa cinque minuti per lingua; il sistema di assistenza attende automaticamente al raggiungimento del limite.
Invia feedback negativi su Slack ogni giorno
Ambito: solo read
Pubblica i feedback negativi delle ultime 24 ore su un canale Slack tramite un webhook in entrata . Imposta SLACK_WEBHOOK_URL, quindi esegui lo script una volta al giorno.
L'elenco dei feedback è ordinato dal più recente al più vecchio, quindi lo script interrompe la paginazione alla prima voce più vecchia di 24 ore.
import { mkdir, writeFile } from "node:fs/promises";
import { api, listAll, SITE_ID } from "./notiondesk.mjs";
const site = await api(`/sites/${SITE_ID}`);
for (const locale of site.supported_locales) {
await mkdir(`backup/${locale}`, { recursive: true });
const articles = await listAll(`/sites/${SITE_ID}/articles?locale=${locale}`);
for (const article of articles) {
const full = await api(`/sites/${SITE_ID}/articles/${encodeURIComponent(article.id)}?locale=${locale}`);
const frontMatter = `---\ntitle: ${JSON.stringify(full.title ?? "")}\nstatus: ${full.status}\nurl: ${full.url ?? ""}\n---\n\n`;
await writeFile(`backup/${locale}/${encodeURIComponent(article.id)}.md`, frontMatter + full.text);
if (full.truncated) {
console.warn(`${locale}/${article.id} is longer than 200,000 bytes and was truncated`);
}
}
console.log(`${locale}: ${articles.length} articles saved`);
}import { api, SITE_ID } from "./notiondesk.mjs";
const since = Date.now() - 24 * 60 * 60 * 1000;
const escapeSlack = (text) => (text ?? "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
async function feedbackSince(timestamp) {
const entries = [];
let cursor = null;
do {
const query = new URLSearchParams({ page_size: "100" });
if (cursor) {
query.set("cursor", cursor);
}
const page = await api(`/sites/${SITE_ID}/feedback?${query}`);
const recent = page.data.filter((entry) => Date.parse(entry.submitted_at) >= timestamp);
entries.push(...recent);
cursor = page.has_more && recent.length === page.data.length ? page.next_cursor : null;
} while (cursor);
return entries;
}
const negative = (await feedbackSince(since)).filter(
(entry) => entry.reaction_group === "negative" || entry.reaction_group === "not_helpful",
);
if (negative.length > 0) {
const lines = negative.map(
(entry) => `• *${escapeSlack(entry.article_title)}* (${entry.article_locale}): ${escapeSlack(entry.message) || "_no message_"}`,
);
await fetch(process.env.SLACK_WEBHOOK_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ text: `${negative.length} negative feedback in the last 24 hours\n${lines.join("\n")}` }),
});
}readSegnala ogni settimana le ricerche senza risultati.
Ambito: read
Elenca le ricerche effettuate dai lettori la scorsa settimana senza trovare un articolo. Ogni query è una potenziale fonte di spunto per un nuovo articolo o per un titolo più accattivante.
Ogni elenco di query può contenere fino a 50 righe. I report analitici condividono inoltre un budget di 30 richieste al minuto per ogni centro assistenza. Vedi Ottenere analisi del centro assistenza con l'API Notiondesk .
Trova gli articoli la cui ultima sincronizzazione non è riuscita
Ambito: read, più sync per riprovare
Correggi prima ogni articolo in Notion, poi sincronizzalo nuovamente con POST /sites/{site_id}/articles/{article_id}/sync.
Applicare un unico marchio a ogni centro di assistenza.
Ambito: read e admin
Copia i colori e i caratteri di un centro assistenza in tutti gli altri centri assistenza della tua area di lavoro. Imposta SOURCE_SITE_ID sul centro assistenza da cui copiare.
Ogni aggiornamento del marchio aggiorna l'intero centro assistenza di destinazione, quindi lo script invia una richiesta per ogni centro assistenza. Un colore memorizzato in un formato breve come #fff viene rifiutato: correggilo prima nella dashboard del centro assistenza di origine. Vedi Personalizzare il marchio e i temi del centro assistenza con l'API Notiondesk .
Importa i reindirizzamenti dopo una migrazione
Ambito: read e write
Stai passando da un altro strumento del centro assistenza? Lo script di importazione dei reindirizzamenti legge un file CSV contenente i vecchi percorsi e crea un reindirizzamento per ogni riga. Vedi Gestire i reindirizzamenti di pagina con l'API Notiondesk .
import { api, SITE_ID } from "./notiondesk.mjs";
const end = new Date();
end.setUTCHours(0, 0, 0, 0);
const start = new Date(end.getTime() - 7 * 24 * 60 * 60 * 1000);
const toApiDate = (date) => `${date.toISOString().slice(0, 19)}Z`;
const query = new URLSearchParams({
start_date: toApiDate(start),
end_date: toApiDate(end),
date_scale: "day",
});
const report = await api(`/sites/${SITE_ID}/analytics/search?${query}`);
console.log(`Searches: ${report.searches.total} (${report.searches.percentage_difference}% vs previous week)`);
console.log(`Without results: ${report.searches_without_results.total}`);
console.log(`Click-through rate: ${report.click_through_rate.current.toFixed(1)}%`);
for (const { query: text, searches } of report.searches_without_results.queries) {
console.log(`${searches}\t${text}`);
}import { api, listAll, SITE_ID } from "./notiondesk.mjs";
const articles = await listAll(`/sites/${SITE_ID}/articles`);
const failed = articles.filter((article) => article.sync_failed || article.sync_timeout);
for (const article of failed) {
const reason = article.sync_timeout ? "timed out" : article.sync_error_code ?? "failed";
console.log(`${article.title}: ${reason} ${article.notion_article_page_url ?? ""}`);
}import { createHash } from "node:crypto";
import { api, listAll } from "./notiondesk.mjs";
const sourceId = process.env.SOURCE_SITE_ID;
const { colors, primary_font, secondary_font } = await api(`/sites/${sourceId}/settings/customize`);
const body = JSON.stringify({ colors, primary_font, secondary_font });
const bodyHash = createHash("sha256").update(body).digest("hex");
const sites = await listAll("/sites");
for (const site of sites.filter((candidate) => candidate.id !== sourceId)) {
await api(`/sites/${site.id}/settings/customize`, {
method: "PATCH",
headers: { "Content-Type": "application/json", "Idempotency-Key": `brand-${site.id}-${bodyHash}` },
body,
});
console.log(`Updated ${site.full_domain}`);
}Domande frequenti
Quale linguaggio posso utilizzare con l'API Notiondesk?
Qualsiasi linguaggio in grado di inviare richieste HTTPS. L'API è una semplice REST con JSON, quindi JavaScript, Python, PHP, Go o Ruby funzionano tutti.
Dove devo eseguire questi script?
Su un server, un'attività pianificata o una pipeline CI, con la chiave memorizzata come segreto. Non eseguirli mai in un browser.
Posso utilizzare questi script con OAuth al posto di una chiave API?
Sì. Sostituisci la chiave API con un token di accesso OAuth nell'intestazione Authorization e aggiornalo prima della sua scadenza. Vedi Connettere la tua app a Notiondesk con OAuth .