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
- 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.
- 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.
- 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.
# 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 API2. 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.
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
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:
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.
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
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
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
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
- 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.
- 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.
- 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.
- 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
{
"name": "upsert_kb_category",
"arguments": {
"sourceId": "SOURCE_ID",
"externalId": "billing",
"name": "Billing",
"position": 0
}
}2. upsert_kb_article
{
"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
{
"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
{
"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
{
"name": "update_kb_article",
"arguments": {
"sourceId": "SOURCE_ID",
"articleId": "ARTICLE_ID",
"updates": {
"audience": "verified",
"audienceId": "AUDIENCE_ID",
"status": "published"
}
}
}6. list_kb_articles
{
"name": "list_kb_articles",
"arguments": {
"sourceId": "SOURCE_ID",
"page": 1,
"limit": 100,
"updatedSince": "2026-09-01T00:00:00Z"
}
}7. reorder_kb_articles
{
"name": "reorder_kb_articles",
"arguments": {
"sourceId": "SOURCE_ID",
"ids": [
"ARTICLE_ID_1",
"ARTICLE_ID_2"
]
}
}8. update_kb_audience
{
"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.
| Tarefa | REST | MCP |
|---|---|---|
| Listar / obter artigos | GET /articles · GET /articles/{articleId} | list_kb_articles · get_kb_article |
| Listar / obter categorias | GET /categories · GET /categories/{categoryId} | list_kb_categories · get_kb_category |
| Criar sem ID externo | POST /articles · POST /categories | create_kb_article · create_kb_category |
| Editar conteúdo existente | PATCH /articles/{articleId} · PATCH /categories/{categoryId} | update_kb_article · update_kb_category |
| Ordenar categorias | PUT /categories/reorder | reorder_kb_categories |
| Ler / editar configurações da central de ajuda | GET /help-center · PATCH /help-center | get_help_center · update_help_center |
| Listar / editar públicos | GET /audiences · PATCH /audiences/{audienceId} | list_kb_audiences · update_kb_audience |
| Excluir | DELETE /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
- Central de ajuda
Publique respostas, organize categorias, escolha quem pode ler e mantenha sua central de ajuda atualizada.
- Públicos da central de ajuda
Crie grupos de clientes, torne sua central de ajuda privada e teste quem pode ler cada resposta.
- API públicaBeta
Faça a triagem e sincronize conversas, compartilhe o contexto dos clientes, leia relatórios e envie arquivos com chaves de API de escopo limitado.
- Sonny MCPBeta
Conecte assistentes de IA aos fluxos da caixa de entrada, memórias de clientes, relatórios e arquivos com OAuth ou chaves de escopo limitado.