Passo a passo para desenvolvedores · REST e MCP

Sincronize sua base de conhecimento

Continue escrevendo no sistema que você já usa e envie as alterações para o Sonny. Este passo a passo cria uma categoria Faturamento, sincroniza um artigo como rascunho e depois o publica para um público de clientes verificados.

1. Escolha um canal e crie uma chave

  1. Abra Canais → seu canal. Copie o ID que aparece depois de /app/sources/ na URL do painel. A API chama esse ID de canal de sourceId; ele é diferente do siteId do widget e do slug público da central de ajuda.
  2. Em Configurações → Desenvolvedor → Chaves de API → Criar chave, conceda kb:read e kb:write. Limite o acesso a canais ao canal que você pretende sincronizar. Guarde a chave, exibida uma única vez, no gerenciador de segredos do seu servidor.
  3. Defina as variáveis abaixo no ambiente do seu backend. Estes exemplos usam curl; substitua os IDs em maiúsculas pelos valores retornados pelo Sonny. Teste-os em um canal de testes antes de sincronizar uma central de ajuda publicada.
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"

Descoberta opcional: GET /api/v1/sources e a ferramenta MCP list_sources retornam os canais acessíveis e exigem conversations:read. Você não precisa desse escopo se já tiver o ID do canal. A conexão de domínio continua sendo feita no painel.

Configuração completa das chaves de API

2. Crie ou atualize a categoria

Use na URL um ID estável do seu sistema de origem. Enviar o mesmo ID externo novamente atualiza a categoria existente em vez de criar uma duplicata. A primeira criação inicializa automaticamente a central de ajuda deste canal.

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}'

Espere 201 na criação ou 200 na atualização, com a categoria em data. Guarde data.id quando precisar do ID interno da categoria no Sonny. Codifique os IDs externos para URL e mantenha-os estáveis mesmo quando os títulos mudarem.

3. Sincronize um artigo como rascunho

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"}'

A resposta contém data.id e o HTML sanitizado em data.body. Abra o artigo em Canais → seu canal → Central de ajuda para conferir a formatação. O Markdown é convertido em HTML; HTML puro também é aceito com bodyFormat: "html" (o padrão). O título do artigo é separado dos títulos dentro do corpo.

Use categoryExternalId para se referir à categoria do passo 2, ou categoryId para o ID dela no Sonny. Informe apenas um. Defina qualquer um deles como null para tirar o artigo da categoria. Todo upsert precisa de title e body; PATCH está disponível para edições parciais. Novos artigos são criados como rascunho quando status é omitido.

4. Defina o acesso e publique

Para este exemplo de central privada, conecte primeiro o login verificado dos clientes. Crie o público e guarde o data.id retornado:

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"]}]}}'

Substitua AUDIENCE_ID, ARTICLE_ID e a URL de login abaixo. A primeira requisição coloca a central de ajuda como Publicado e a restringe a clientes Pro. A segunda publica o artigo. O fluxo de login do seu app precisa trocar um JWT assinado do cliente com a central de ajuda; definir signInUrl sozinho não faz o login de ninguém.

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"}'

Use a URL retornada por GET /help-center para testar a visão do leitor conectado e a de um navegador sem login. Chaves de API e MCP atuam como equipe e podem ler o conteúdo dentro dos seus escopos; uma resposta bem-sucedida delas não prova que um cliente consegue ver o artigo.

Para uma central pública, use audience: "everyone" e audienceId: null na central e no artigo, e confira também o acesso da categoria. Para remover apenas um grupo nomeado, envie audienceId: null; o acesso só para verificados continua, a menos que você altere audience explicitamente. Omitir audienceId mantém a atribuição anterior. Todas as restrições da central de ajuda, da categoria e do artigo precisam corresponder.

Regras nomeadas usam match: "all" ou "any", até 10 condições e valores com tipo estrito. Operadores: eq, neq, in, not_in, exists, not_exists, gt, gte, lt, lte. Comparações numéricas recebem números; in/not_in recebem arrays de 1 a 100 valores escalares; exists/not_exists omitem value. Atributos ausentes reprovam nas comparações. Veja o comportamento das regras, a herança e a solução de problemas de acesso.

5. Mantenha a sincronização em dia

Repita gravações sem duplicar conteúdo

Repita o PUT com o mesmo ID externo sempre que sua origem mudar. IDs externos são únicos dentro de uma central de ajuda. Campos omitidos na atualização mantêm os valores atuais. Omita status nos upserts seguintes para preservar a publicação; enviar draft explicitamente retira um artigo publicado. publishedAt é definido na primeira publicação e não muda depois. Ele é somente leitura, então não é possível importar datas históricas de publicação. Conteúdo sem alterações e edições só de metadados não disparam novos embeddings desnecessários.

Leia alterações e estatísticas

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"

As listas de artigos e categorias retornam data e mais pagination: page, limit, total. As páginas começam em 1; limit é 50 por padrão e aceita até 100. Mantenha os mesmos filtros e peça páginas até page × limit alcançar total. As duas listas aceitam externalId e updatedSince; artigos também aceitam categoryId e status (draft ou published). Use um timestamp ISO com fuso horário em updatedSince; ele inclui registros atualizados nesse exato momento.

Leia o detalhe de cada artigo para obter o body completo, viewCount, helpfulYes e helpfulNo. Essas estatísticas são somente leitura. updatedSince lista os registros atuais; ele não informa exclusões. Acompanhe as remoções no seu sistema de origem e exclua explicitamente o registro correspondente no Sonny quando for o caso.

Envie uma imagem

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

Use o data.url retornado na sintaxe de imagem do Markdown ou em um elemento img do HTML e depois sincronize o corpo do artigo. Os envios aceitam JPEG, PNG, GIF e WebP de até 25 MB e exigem uma central de ajuda inicializada. As URLs enviadas são públicas; a restrição de público de um artigo não protege a URL da imagem. No MCP, use upload_kb_media com sourceId, fileName, contentType e o dataBase64 do arquivo, em vez de multipart form data.

Organize artigos e categorias

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"]}'

Primeiro, busque todas as páginas sem filtros de status, categoria ou data, incluindo os rascunhos. Substitua os IDs de exemplo por todos os IDs de artigo atuais, cada um exatamente uma vez, na ordem desejada. Use /categories/reorder para a lista completa de categorias. Uma reordenação bem-sucedida retorna 204 sem corpo. Se a lista mudou ou contém IDs de outro lugar, duplicados ou ausentes, atualize-a antes de tentar de novo. Você também pode definir uma position não negativa em gravações individuais.

Use o mesmo fluxo pelo MCP

  1. Conecte seu cliente MCP e escolha o espaço de trabalho correto. Confirme que ele tem kb:read e kb:write, com acesso ao seu canal.
  2. Use o ID do canal que aparece no painel ou chame list_sources se a sua credencial também tiver conversations:read. Nunca use o siteId do widget no lugar dele.
  3. Execute os cinco primeiros exemplos abaixo, na ordem. Substitua SOURCE_ID pelo ID do seu canal e ARTICLE_ID/AUDIENCE_ID pelo data.id de cada resultado anterior. Substitua a URL de login e conecte o login antes de publicar.
  4. Use list_kb_articles para revisar as alterações. Só execute uma reordenação depois de reunir a lista completa e sem filtros, incluindo os rascunhos. Atualize suas listas depois de gravações bem-sucedidas.

Cada exemplo mostra o nome da ferramenta e seus argumentos. Os campos de upsert vão direto nos argumentos; as ferramentas de atualização colocam os campos alterados dentro de updates. Estas são entradas de chamadas de ferramenta, não uma requisição HTTP ao endpoint MCP.

1. upsert_kb_category
Chamada de ferramenta MCP
{
  "name": "upsert_kb_category",
  "arguments": {
    "sourceId": "SOURCE_ID",
    "externalId": "billing",
    "name": "Billing",
    "position": 0
  }
}
2. upsert_kb_article
Chamada de ferramenta 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
Chamada de ferramenta 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
Chamada de ferramenta 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
Chamada de ferramenta MCP
{
  "name": "update_kb_article",
  "arguments": {
    "sourceId": "SOURCE_ID",
    "articleId": "ARTICLE_ID",
    "updates": {
      "audience": "verified",
      "audienceId": "AUDIENCE_ID",
      "status": "published"
    }
  }
}
6. list_kb_articles
Chamada de ferramenta MCP
{
  "name": "list_kb_articles",
  "arguments": {
    "sourceId": "SOURCE_ID",
    "page": 1,
    "limit": 100,
    "updatedSince": "2026-09-01T00:00:00Z"
  }
}
7. reorder_kb_articles
Chamada de ferramenta MCP
{
  "name": "reorder_kb_articles",
  "arguments": {
    "sourceId": "SOURCE_ID",
    "ids": [
      "ARTICLE_ID_1",
      "ARTICLE_ID_2"
    ]
  }
}
8. update_kb_audience
Chamada de ferramenta MCP
{
  "name": "update_kb_audience",
  "arguments": {
    "sourceId": "SOURCE_ID",
    "audienceId": "AUDIENCE_ID",
    "updates": {
      "name": "Paid customers"
    }
  }
}

Operações relacionadas

Os caminhos REST abaixo são relativos a BASE. Consulte a referência da API para ver todos os campos e esquemas de resposta.

Operações REST e MCP relacionadas à base de conhecimento
TarefaRESTMCP
Listar / obter artigosGET /articles · GET /articles/{articleId}list_kb_articles · get_kb_article
Listar / obter categoriasGET /categories · GET /categories/{categoryId}list_kb_categories · get_kb_category
Criar sem ID externoPOST /articles · POST /categoriescreate_kb_article · create_kb_category
Editar conteúdo existentePATCH /articles/{articleId} · PATCH /categories/{categoryId}update_kb_article · update_kb_category
Ordenar categoriasPUT /categories/reorderreorder_kb_categories
Ler / editar configurações da central de ajudaGET /help-center · PATCH /help-centerget_help_center · update_help_center
Listar / editar públicosGET /audiences · PATCH /audiences/{audienceId}list_kb_audiences · update_kb_audience
ExcluirDELETE /articles/{articleId} · /categories/{categoryId} · /audiences/{audienceId}delete_kb_article · delete_kb_category · delete_kb_audience

Excluir um artigo é permanente. Excluir uma categoria tira os artigos dela de qualquer categoria e remove a restrição herdada dessa categoria. Excluir um público remove as regras de atributos dele, e o conteúdo vinculado continua só para clientes verificados. Revise o acesso afetado antes de excluir; use rascunhos para retirar artigos que você quer manter.

Solucione problemas de sincronização

400 · Requisição inválida
Confira os campos obrigatórios title/body ou name, os nomes exatos dos campos, os tipos de regra válidos e se a lista de IDs da reordenação está completa. Envie categoryId ou categoryExternalId, não os dois.
401 · Não autorizado
Envie uma chave Bearer válida. Verifique se ela expirou ou foi revogada.
403 · Proibido
Confira os escopos da chave, as permissões do dono da chave e o faturamento do espaço de trabalho. Reconecte o cliente MCP com o acesso necessário se as ferramentas dele forem somente leitura.
404 · Não encontrado
Confirme que a origem pertence ao espaço de trabalho selecionado e é permitida pela credencial. Inicialize uma nova central de ajuda criando conteúdo primeiro; leituras não a criam.
409 · Conflito
Busque de novo o conteúdo atual e resolva o ID em conflito ou a ordem desatualizada antes de tentar novamente.
413 · Grande demais
Reduza a imagem para menos que o limite de 25 MB.
429 · Limite de requisições atingido
Aguarde o tempo de Retry-After antes de tentar de novo. Use RateLimit-Limit, RateLimit-Remaining e RateLimit-Reset para controlar o ritmo das requisições.

Documentos relacionados