Guida per sviluppatori · REST e MCP

Sincronizza la tua base di conoscenza

Continua a scrivere nel tuo sistema attuale e invia le modifiche a Sonny. Questa guida crea una categoria Fatturazione, sincronizza un articolo come bozza e poi lo pubblica per un pubblico di clienti verificati.

1. Scegli un canale e crea una chiave

  1. Apri Canali → il tuo canale. Copia l'ID dopo /app/sources/ nell'URL della dashboard. L'API chiama questo ID del canale sourceId; è diverso dal siteId del widget e dallo slug pubblico del centro assistenza.
  2. In Impostazioni → Sviluppatori → Chiavi API → Crea chiave, concedi kb:read e kb:write. Limita Accesso ai canali al canale che intendi sincronizzare. Salva la chiave, mostrata una sola volta, nel gestore dei segreti del tuo server.
  3. Imposta le variabili qui sotto nell'ambiente del tuo backend. Questi esempi usano curl; sostituisci gli ID in maiuscolo con i valori restituiti da Sonny. Eseguili su un canale di prova prima di sincronizzare un centro assistenza pubblicato.
bash
# Load SONNY_API_KEY from your server's secret manager first.
# SOURCE_ID is the ID in the channel dashboard URL: /app/sources/SOURCE_ID
export SOURCE_ID="YOUR_SOURCE_ID"
export BASE="https://www.usesonny.com/api/v1/sources/$SOURCE_ID"

Rilevamento facoltativo: GET /api/v1/sources e lo strumento MCP list_sources restituiscono i canali accessibili e richiedono conversations:read. Non ti serve questo ambito se hai già l'ID del canale. Il collegamento del dominio resta un'operazione da dashboard.

Configurazione completa della chiave API

2. Crea o aggiorna la categoria

Usa nell'URL un ID stabile del tuo sistema di origine. Inviare di nuovo lo stesso ID esterno aggiorna la categoria esistente invece di crearne un duplicato. La prima creazione inizializza automaticamente il centro assistenza di questo canale.

bash
curl --fail-with-body -X PUT "$BASE/categories/by-external-id/billing" \
  -H "Authorization: Bearer $SONNY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Billing","position":0}'

Aspettati 201 alla creazione o 200 all'aggiornamento, con la categoria in data. Salva data.id quando ti serve l'ID interno della categoria in Sonny. Codifica gli ID esterni per l'URL e mantienili stabili anche quando cambiano i titoli.

3. Sincronizza un articolo come bozza

bash
curl --fail-with-body -X PUT "$BASE/articles/by-external-id/billing-guide" \
  -H "Authorization: Bearer $SONNY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"Manage billing","body":"## Find your invoices\n\nOpen **Settings → Billing** in your account.","bodyFormat":"markdown","categoryExternalId":"billing","status":"draft"}'

La risposta contiene data.id e l'HTML ripulito in data.body. Apri l'articolo in Canali → il tuo canale → Centro assistenza per controllarne la formattazione. Il Markdown viene convertito in HTML; è accettato anche HTML grezzo con bodyFormat: "html" (il valore predefinito). Il titolo dell'articolo è separato dai titoli nel corpo.

Usa categoryExternalId per fare riferimento alla categoria del passaggio 2, oppure categoryId per il suo ID Sonny. Indicane solo uno. Imposta uno dei due su null per togliere la categoria a un articolo. Ogni upsert richiede title e body; PATCH è disponibile per modifiche parziali. I nuovi articoli sono bozze per impostazione predefinita quando status è omesso.

4. Imposta l'accesso, poi pubblica

Per questo esempio di centro privato, collega prima il sistema di accesso dei clienti verificati. Crea il pubblico e salva il data.id restituito:

bash
curl --fail-with-body -X POST "$BASE/audiences" \
  -H "Authorization: Bearer $SONNY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Pro customers","rules":{"match":"all","conditions":[{"trait":"plan","op":"in","value":["pro","business"]}]}}'

Sostituisci AUDIENCE_ID, ARTICLE_ID e l'URL di login qui sotto. La prima richiesta rende il centro assistenza Pubblicato e lo riserva ai clienti Pro. La seconda pubblica l'articolo. Il flusso di login della tua app deve scambiare un JWT firmato del cliente con il centro assistenza; impostare solo signInUrl non fa accedere nessuno.

bash
curl --fail-with-body -X PATCH "$BASE/help-center" \
  -H "Authorization: Bearer $SONNY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"enabled":true,"title":"Acme Help Center","audience":"verified","audienceId":"AUDIENCE_ID","signInUrl":"https://app.example.com/login"}'

# Replace ARTICLE_ID and AUDIENCE_ID with the returned data.id values.
curl --fail-with-body -X PATCH "$BASE/articles/ARTICLE_ID" \
  -H "Authorization: Bearer $SONNY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"audienceId":"AUDIENCE_ID","status":"published"}'

Usa l'URL restituito da GET /help-center per provare la vista del lettore con accesso e un browser senza accesso. Le chiavi API e MCP agiscono come staff e possono leggere i contenuti entro i loro ambiti; una loro risposta positiva non dimostra che un cliente possa vedere l'articolo.

Per un centro pubblico, usa audience: "everyone" e audienceId: null sul centro e sull'articolo, e controlla anche l'accesso della categoria. Per rimuovere solo un gruppo con nome, invia audienceId: null; l'accesso riservato ai verificati resta a meno che tu non cambi esplicitamente audience. Un audienceId omesso mantiene l'assegnazione precedente. Tutte le restrizioni di centro assistenza, categoria e articolo devono essere soddisfatte.

Le regole con nome usano match: "all" o "any", fino a 10 condizioni e valori con tipo rigoroso. Operatori: eq, neq, in, not_in, exists, not_exists, gt, gte, lt, lte. I confronti numerici accettano numeri; in/not_in accettano array da 1 a 100 valori scalari; exists/not_exists omettono value. Gli attributi mancanti non soddisfano i confronti. Vedi comportamento delle regole, ereditarietà e risoluzione dei problemi di accesso.

5. Mantieni aggiornata la sincronizzazione

Ripeti le scritture senza duplicare i contenuti

Ripeti PUT con lo stesso ID esterno ogni volta che la tua fonte cambia. Gli ID esterni sono univoci all'interno di un centro assistenza. I campi omessi in un aggiornamento mantengono i valori attuali. Ometti status negli upsert successivi per conservare la pubblicazione; inviare esplicitamente draft ritira un articolo pubblicato. publishedAt viene impostato alla prima pubblicazione e non cambia più. È di sola lettura, quindi non si possono importare date di pubblicazione storiche. I contenuti invariati e le modifiche ai soli metadati non attivano nuovi embedding inutili.

Leggi modifiche e statistiche

bash
curl --fail-with-body --get "$BASE/articles" \
  -H "Authorization: Bearer $SONNY_API_KEY" \
  --data-urlencode "page=1" --data-urlencode "limit=100" \
  --data-urlencode "status=published" \
  --data-urlencode "updatedSince=2026-09-01T00:00:00Z"

# Get the full sanitized HTML and read-only analytics for one article.
curl --fail-with-body "$BASE/articles/ARTICLE_ID" \
  -H "Authorization: Bearer $SONNY_API_KEY"

Gli elenchi di articoli e categorie restituiscono data più pagination: page, limit, total. Le pagine partono da 1; limit vale 50 per impostazione predefinita e accetta fino a 100. Mantieni gli stessi filtri e richiedi pagine finché page × limit non raggiunge total. Entrambi gli elenchi accettano externalId e updatedSince; gli articoli accettano anche categoryId e status (draft o published). Usa per updatedSince un timestamp ISO con fuso orario; include i record aggiornati in quell'istante.

Leggi il dettaglio di ogni articolo per ottenere il body completo, viewCount, helpfulYes e helpfulNo. Queste statistiche sono di sola lettura. updatedSince elenca i record attuali; non segnala le eliminazioni. Tieni traccia delle rimozioni nel tuo sistema di origine ed elimina esplicitamente il record Sonny corrispondente quando serve.

Carica un'immagine

bash
curl --fail-with-body -X POST "$BASE/media" \
  -H "Authorization: Bearer $SONNY_API_KEY" \
  -F "file=@billing.png;type=image/png"

Usa il data.url restituito nella sintassi Markdown per le immagini o in un elemento HTML img, poi sincronizza il body dell'articolo. I caricamenti accettano JPEG, PNG, GIF e WebP fino a 25 MB e richiedono un centro assistenza inizializzato. Gli URL caricati sono pubblici; la restrizione di pubblico di un articolo non protegge l'URL dell'immagine. MCP usa upload_kb_media con sourceId, fileName, contentType e dataBase64 del file, invece di un form multipart.

Ordina articoli e categorie

bash
curl --fail-with-body -X PUT "$BASE/articles/reorder" \
  -H "Authorization: Bearer $SONNY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"ids":["ARTICLE_ID_1","ARTICLE_ID_2"]}'

Per prima cosa recupera tutte le pagine senza filtri di stato, categoria o data, comprese le bozze. Sostituisci gli ID di esempio con ogni ID di articolo attuale, esattamente una volta, nell'ordine desiderato. Usa /categories/reorder per l'elenco completo delle categorie. Un riordino riuscito restituisce 204 senza corpo. Se l'elenco è cambiato o contiene ID estranei, duplicati o mancanti, aggiornalo prima di riprovare. Puoi anche impostare un position non negativo nelle singole scritture.

Usa lo stesso flusso tramite MCP

  1. Collega il tuo client MCP e scegli lo spazio di lavoro corretto. Verifica che abbia kb:read e kb:write, con accesso al tuo canale.
  2. Usa l'ID del canale della dashboard, oppure chiama list_sources se la tua credenziale ha anche conversations:read. Non usare mai al suo posto il siteId del widget.
  3. Esegui in ordine i primi cinque esempi qui sotto. Sostituisci SOURCE_ID con l'ID del tuo canale e ARTICLE_ID/AUDIENCE_ID con il data.id di ciascun risultato precedente. Sostituisci l'URL di login e collega l'accesso prima di pubblicare.
  4. Usa list_kb_articles per controllare le modifiche. Esegui un riordino solo dopo aver raccolto l'elenco completo non filtrato, comprese le bozze. Aggiorna i tuoi elenchi dopo le scritture riuscite.

Ogni esempio mostra il nome dello strumento e i suoi argomenti. I campi di upsert vanno direttamente negli argomenti; gli strumenti di aggiornamento mettono i campi modificati dentro updates. Sono input di chiamate agli strumenti, non richieste HTTP all'endpoint MCP.

1. upsert_kb_category
Chiamata a uno strumento MCP
{
  "name": "upsert_kb_category",
  "arguments": {
    "sourceId": "SOURCE_ID",
    "externalId": "billing",
    "name": "Billing",
    "position": 0
  }
}
2. upsert_kb_article
Chiamata a uno strumento MCP
{
  "name": "upsert_kb_article",
  "arguments": {
    "sourceId": "SOURCE_ID",
    "externalId": "billing-guide",
    "title": "Manage billing",
    "body": "## Find your invoices\n\nOpen **Settings → Billing** in your account.",
    "bodyFormat": "markdown",
    "categoryExternalId": "billing",
    "status": "draft"
  }
}
3. create_kb_audience
Chiamata a uno strumento MCP
{
  "name": "create_kb_audience",
  "arguments": {
    "sourceId": "SOURCE_ID",
    "name": "Pro customers",
    "rules": {
      "match": "all",
      "conditions": [
        {
          "trait": "plan",
          "op": "in",
          "value": [
            "pro",
            "business"
          ]
        }
      ]
    }
  }
}
4. update_help_center
Chiamata a uno strumento MCP
{
  "name": "update_help_center",
  "arguments": {
    "sourceId": "SOURCE_ID",
    "updates": {
      "enabled": true,
      "title": "Acme Help Center",
      "audience": "verified",
      "audienceId": "AUDIENCE_ID",
      "signInUrl": "https://app.example.com/login"
    }
  }
}
5. update_kb_article
Chiamata a uno strumento MCP
{
  "name": "update_kb_article",
  "arguments": {
    "sourceId": "SOURCE_ID",
    "articleId": "ARTICLE_ID",
    "updates": {
      "audience": "verified",
      "audienceId": "AUDIENCE_ID",
      "status": "published"
    }
  }
}
6. list_kb_articles
Chiamata a uno strumento MCP
{
  "name": "list_kb_articles",
  "arguments": {
    "sourceId": "SOURCE_ID",
    "page": 1,
    "limit": 100,
    "updatedSince": "2026-09-01T00:00:00Z"
  }
}
7. reorder_kb_articles
Chiamata a uno strumento MCP
{
  "name": "reorder_kb_articles",
  "arguments": {
    "sourceId": "SOURCE_ID",
    "ids": [
      "ARTICLE_ID_1",
      "ARTICLE_ID_2"
    ]
  }
}
8. update_kb_audience
Chiamata a uno strumento MCP
{
  "name": "update_kb_audience",
  "arguments": {
    "sourceId": "SOURCE_ID",
    "audienceId": "AUDIENCE_ID",
    "updates": {
      "name": "Paid customers"
    }
  }
}

Operazioni correlate

I percorsi REST qui sotto sono relativi a BASE. Consulta il riferimento API per ogni campo e schema di risposta.

Operazioni REST e MCP correlate per la base di conoscenza
AttivitàRESTMCP
Elenca / ottieni articoliGET /articles · GET /articles/{articleId}list_kb_articles · get_kb_article
Elenca / ottieni categorieGET /categories · GET /categories/{categoryId}list_kb_categories · get_kb_category
Crea senza ID esternoPOST /articles · POST /categoriescreate_kb_article · create_kb_category
Modifica contenuti esistentiPATCH /articles/{articleId} · PATCH /categories/{categoryId}update_kb_article · update_kb_category
Ordina le categoriePUT /categories/reorderreorder_kb_categories
Leggi / modifica le impostazioni del centro assistenzaGET /help-center · PATCH /help-centerget_help_center · update_help_center
Elenca / modifica i pubbliciGET /audiences · PATCH /audiences/{audienceId}list_kb_audiences · update_kb_audience
EliminaDELETE /articles/{articleId} · /categories/{categoryId} · /audiences/{audienceId}delete_kb_article · delete_kb_category · delete_kb_audience

L'eliminazione di un articolo è definitiva. Eliminare una categoria toglie la categoria ai suoi articoli e rimuove la restrizione ereditata da quella categoria. Eliminare un pubblico rimuove le sue regole sugli attributi, lasciando i contenuti collegati riservati ai clienti verificati. Controlla l'accesso interessato prima di eliminare; usa le bozze per ritirare gli articoli che vuoi conservare.

Risolvere i problemi di sincronizzazione

400 · Richiesta non valida
Controlla title/body o name obbligatori, i nomi esatti dei campi, i tipi delle regole validi e gli ID completi per il riordino. Invia categoryId o categoryExternalId, non entrambi.
401 · Non autorizzato
Fornisci una chiave Bearer valida. Controlla se è scaduta o è stata revocata.
403 · Accesso negato
Controlla gli ambiti della chiave, i permessi del proprietario della chiave e la fatturazione dello spazio di lavoro. Ricollega un client MCP con l'accesso necessario se i suoi strumenti sono di sola lettura.
404 · Non trovato
Verifica che la sorgente appartenga allo spazio di lavoro selezionato e sia consentita dalla credenziale. Inizializza un nuovo centro assistenza creando prima dei contenuti; le letture non lo creano.
409 · Conflitto
Recupera di nuovo i contenuti attuali e risolvi l'ID in conflitto o l'ordine non aggiornato prima di riprovare.
413 · Troppo grande
Riduci l'immagine sotto il limite di 25 MB.
429 · Limite di richieste raggiunto
Attendi il tempo indicato da Retry-After prima di riprovare. Usa RateLimit-Limit, RateLimit-Remaining e RateLimit-Reset per regolare il ritmo delle richieste.

Guide correlate