Automatiseer veelvoorkomende helpcentrumtaken met de Notiondesk API

Notiondesk API-voorbeelden: synchroniseren vanuit GitHub Actions, artikelen back-uppen als markdown, feedback plaatsen op Slack en zoekopdrachten zonder resultaten rapporteren.

7 min lezen

Dit artikel verzamelt kant-en-klare Notiondesk API-scripts voor de taken die teams het meest automatiseren: Notion-bewerkingen publiceren vanuit een CI-pipeline, van elk artikel een Markdown-back-up maken, negatieve feedback naar Slack sturen, zoekopdrachten rapporteren die uw helpcentrum niet kan beantwoorden, mislukte synchronisaties vinden en één merk toepassen op meerdere helpcentra. Kopieer een recept, stel twee omgevingsvariabelen in en voer het uit.

Voordat je begint

  • Stel beide in als omgevingsvariabelen:
export NOTIONDESK_API_KEY="nd_wsk_..."
export NOTIONDESK_SITE_ID="c74781f6-1aea-4e17-ab23-bbb8b4d174b6"

Voor de JavaScript-recepten is Node.js versie 18 of hoger vereist.

Een kleine, gedeelde helper

Sla dit bestand op als notiondesk.mjs naast je scripts. Het verstuurt de headers, wacht tot de API antwoordt met 429 rate_limited en leest vervolgens elke pagina van een lijst.

Synchroniseer je helpcentrum vanuit GitHub Actions.

Omvang: __GERESERVEERD_30__

Publiceer je Notion-bewerkingen vanuit een GitHub Actions-workflow, bijvoorbeeld direct na een release. Sla de sleutel op als het repositorygeheim NOTIONDESK_API_KEY en de helpcentrum-ID als de repositoryvariabele 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."
  • 204 betekent dat de synchronisatie in de wachtrij staat. Deze wordt op de achtergrond uitgevoerd.
  • 409 conflict_error betekent dat er al een synchronisatie van het helpcentrum in de wachtrij staat of bezig is, waardoor de stap succesvol is en er geen nieuwe hoeft te worden gestart.
  • De Idempotency-Key zorgt ervoor dat een verzoek in de wachtrij twee keer wordt herhaald.
Wanneer automatische vertaling is ingeschakeld, vertaalt elke synchronisatie de gewijzigde inhoud en brengt deze in rekening. Activeer de workflow na daadwerkelijke bewerkingen, niet op korte termijn. Zie Notion-synchronisaties activeren met de Notiondesk API .

Maak van elk artikel een back-up als markdown.

Bereik: __GEVESTIGD_24__

Sla elk artikel, in elke taal die uw helpcentrum ondersteunt, op als een Markdown-bestand:

De back-up bevat concepten en gearchiveerde artikelen. Het aantal leesverzoeken is beperkt tot 120 per minuut, dus een helpcentrum met 500 artikelen duurt ongeveer vijf minuten per taal; de helpdesk wacht automatisch wanneer de limiet is bereikt.

Stuur elke dag negatieve feedback naar Slack.

Bereik: alleen __GERESERVEERD_23__

Plaats de negatieve feedback van de afgelopen 24 uur in een Slack-kanaal via een inkomende webhook . Stel SLACK_WEBHOOK_URL in en voer het script vervolgens eenmaal per dag uit.

De feedbacklijst is gesorteerd op nieuwste, dus het script stopt met pagineren bij de eerste vermelding die ouder is dan 24 uur.

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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");

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")}` }),
  });
}
Feedback wordt geschreven door uw lezers. Voer dit script uit met een sleutel die alleen schrijfrechten heeft, ontsnap aan de tekst voordat u deze verzendt en geef het nooit door aan een LLM die een sleutel heeft waarmee geschreven kan worden. Zie Feedback van lezers exporteren met de Notiondesk API .read

Rapporteer zoekopdrachten zonder resultaten elke week

Omvang: __GERESERVEERD_20__

Maak een lijst van de zoektermen die lezers vorige week gebruikten zonder een artikel te vinden. Elke zoekterm komt in aanmerking voor een nieuw artikel of een betere titel.

Elke querylijst kan maximaal 50 rijen bevatten. Analyserapporten delen ook een budget van 30 aanvragen per minuut per helpcentrum. Zie Helpcentrumanalyses verkrijgen met de Notiondesk API .

Zoek naar artikelen waarvan de laatste synchronisatie is mislukt.

Bereik: read, plus sync om opnieuw te proberen

Corrigeer elk artikel eerst in Notion en synchroniseer het vervolgens opnieuw met POST /sites/{site_id}/articles/{article_id}/sync.

Gebruik één merk voor elk helpcentrum.

Bereik: __GERESERVEERD_16__ en __GERESERVEERD_15__

Kopieer de kleuren en lettertypen van één helpcentrum naar alle andere helpcentra in uw werkruimte. Stel SOURCE_SITE_ID in op het helpcentrum waarvan u wilt kopiëren.

Elke brandingupdate vernieuwt het volledige doelhelpcentrum, dus het script verstuurt één verzoek per helpcentrum. Een kleur die is opgeslagen in een verkorte vorm zoals #fff wordt afgewezen: corrigeer dit eerst in het dashboard van het bronhelpcentrum. Zie Branding en thema's van helpcentra aanpassen met de Notiondesk API .

Importeer omleidingen na een migratie

Bereik: read en write

Stapt u over van een andere helpcentrumtool? Het importscript voor redirects leest een CSV-bestand met oude paden en maakt één redirect per regel aan. Zie Pagina-redirects beheren met de Notiondesk API .

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}`);
}

Veelgestelde vragen

Welke taal kan ik gebruiken met de Notiondesk API?

Elke programmeertaal die HTTPS-verzoeken kan verzenden, is geschikt. De API is een eenvoudige REST-applicatie met JSON, dus JavaScript, Python, PHP, Go en Ruby werken allemaal.

Waar moet ik deze scripts uitvoeren?

Voer ze uit op een server, in een geplande taak of in een CI-pipeline, waarbij de sleutel als geheim is opgeslagen. Voer ze nooit uit in een browser.

Kan ik deze scripts gebruiken met OAuth in plaats van een API-sleutel?

Ja. Vervang de API-sleutel door een OAuth-toegangstoken in de Authorization-header en vernieuw deze voordat deze verloopt. Zie Uw app verbinden met Notiondesk met OAuth .

Wat volgt?

Gerelateerde artikelen

Was deze pagina nuttig?

Still need help? Ask us