Voltar à Documentação

Sutram REST API v1


Visão geral

A REST API do Sutram fornece endpoints JSON para consumo externo do conteúdo de projetos (pastas, arquivos, links). Ela utiliza a mesma autenticação de chave dupla do MCP Server (x-project-key + x-user-key), portanto qualquer projeto com Acesso Remoto habilitado fica automaticamente acessível pela REST API.

Casos de uso

  • Site institucional (dogfooding) — Servir os posts do blog e as páginas de documentação do próprio site sutram.io, com o Sutram como fonte única (SSOT)
  • Dashboards personalizados — Criar páginas de listagem de conteúdo fora do Sutram
  • Aplicativos móveis — Buscar arquivos e links com URLs de download pré-assinadas
  • Automação — Descoberta e recuperação programática de conteúdo via scripts ou CI/CD

URL base

https://sutram.io/api/v1

Como funciona

External Client (app, script, dashboard)
        |
        | HTTPS + JSON
        v
  Sutram REST API
    /api/v1/*
        |
        | Dual Key Authentication
        v
  Project Content (folders, files, links, tags)

Autenticação

Toda requisição deve incluir dois headers:

Header Valor Descrição
x-project-key sk_proj_... Identifica o projeto
x-user-key sk_user_... Identifica o usuário

Ambas as chaves são criadas pela interface web do Sutram.

Exemplo

curl -H "x-project-key: sk_proj_ej8NWMisd2rJgMwAJ22T..." \
     -H "x-user-key: sk_user_zQD0BjH56nQhOgX3uxni..." \
     https://sutram.io/api/v1/project

Respostas de erro

Quando a autenticação falha, a API retorna HTTP 401:

{ "error": "Missing x-project-key header" }
{ "error": "Missing x-user-key header" }
{ "error": "Invalid project key" }
{ "error": "Invalid user key" }
{ "error": "Not a member of this project" }

Permissões

Papel Acesso
Owner Acesso total de leitura
Admin Acesso total de leitura
Member Acesso total de leitura
Viewer Acesso de leitura (a API v1 é somente leitura para todos os papéis)

A REST API v1 é somente leitura para todos os papéis. Como toda a API é de leitura, qualquer membro ativo — inclusive visualizadores — pode consumi-la com chaves válidas.


Endpoints

GET /api/v1/project

Retorna informações sobre o projeto autenticado.

Exemplo de requisição:

curl -H "x-project-key: $PROJECT_KEY" \
     -H "x-user-key: $USER_KEY" \
     https://sutram.io/api/v1/project

Resposta:

{
  "project": {
    "id": "a1b2c3d4-...",
    "name": "Construction Site Alpha",
    "description": "Main project documentation",
    "your_role": "owner",
    "created_at": "2026-01-15T10:00:00Z",
    "updated_at": "2026-02-19T14:30:00Z"
  }
}

GET /api/v1/folders

Lista as pastas do nível raiz do projeto.

Exemplo de requisição:

curl -H "x-project-key: $PROJECT_KEY" \
     -H "x-user-key: $USER_KEY" \
     https://sutram.io/api/v1/folders

Resposta:

{
  "folders": [
    {
      "id": "f1a2b3c4-...",
      "name": "Reports",
      "parent_id": null,
      "tags": {"category": "monthly"},
      "created_at": "2026-01-20T10:00:00Z"
    },
    {
      "id": "f5e6d7c8-...",
      "name": "Photos",
      "parent_id": null,
      "tags": {},
      "created_at": "2026-01-22T08:00:00Z"
    }
  ],
  "meta": {
    "total_count": 2
  }
}

GET /api/v1/folders/:id

Retorna os detalhes de uma pasta, suas subpastas e seus itens de conteúdo.

Parâmetros de caminho:

Parâmetro Tipo Descrição
id UUID ID da pasta

Exemplo de requisição:

curl -H "x-project-key: $PROJECT_KEY" \
     -H "x-user-key: $USER_KEY" \
     https://sutram.io/api/v1/folders/f1a2b3c4-...

Resposta:

{
  "folder": {
    "id": "f1a2b3c4-...",
    "name": "Reports",
    "parent_id": null,
    "path": "/Reports",
    "tags": {"category": "monthly"},
    "created_at": "2026-01-20T10:00:00Z"
  },
  "subfolders": [
    {
      "id": "a1b2c3d4-...",
      "name": "2024",
      "parent_id": "f1a2b3c4-...",
      "tags": {"year": "2024"},
      "created_at": "2026-01-25T12:00:00Z"
    }
  ],
  "items": [
    {
      "id": "d5e6f7a8-...",
      "type": "file",
      "name": "summary",
      "description": null,
      "tags": {"status": "final"},
      "folder_id": "f1a2b3c4-...",
      "created_at": "2026-02-01T10:00:00Z",
      "file": {
        "filename": "summary.pdf",
        "content_type": "application/pdf",
        "file_size": 245000
      }
    }
  ],
  "meta": {
    "subfolder_count": 1,
    "item_count": 1
  }
}

Respostas de erro:

Status Corpo Quando
404 {"error": "Folder not found"} A pasta não existe ou pertence a outro projeto

GET /api/v1/content

Lista itens de conteúdo com filtragem, busca por tags, busca por texto e paginação.

Query parameters:

Parâmetro Tipo Padrão Descrição
folder_id UUID Filtrar por pasta. Sem este parâmetro, retorna apenas itens do nível raiz.
all_folders boolean false Se true, busca em todas as pastas (não apenas na raiz). Necessário para consultas de tag/busca em todo o projeto.
type string Filtrar por tipo de conteúdo: "file", "web_link", "video_link", "audio_link"
tag string Chave de tag única para filtrar (ex.: tag=category)
tag_value string Valor da tag para correspondência (usado com tag). Correspondência de substring, sem distinção de maiúsculas/minúsculas.
tags JSON Múltiplas condições AND como array JSON (ex.: tags=[{"key":"topic","value":"neuro"}])
search string Buscar pelo nome do item (correspondência de substring, sem distinção de maiúsculas/minúsculas)
page integer 1 Número da página
per_page integer 50 Itens por página (máximo: 100)

Comportamento da busca por tags:

  • Correspondência de chave: Correspondência exata, sem distinção de maiúsculas/minúsculas ("Category" corresponde a "category")
  • Correspondência de valor: Correspondência de substring, sem distinção de maiúsculas/minúsculas ("neuro" corresponde a "Neurologia Avançada")
  • Múltiplas condições (parâmetro tags): Todas as condições devem ser satisfeitas (lógica AND)
  • Sem valor: Quando tag_value é omitido, corresponde a qualquer item que possua a chave de tag, independentemente do valor

Exemplo — listar itens da raiz

curl -H "x-project-key: $PROJECT_KEY" \
     -H "x-user-key: $USER_KEY" \
     "https://sutram.io/api/v1/content"

Exemplo — listar itens em uma pasta

curl -H "x-project-key: $PROJECT_KEY" \
     -H "x-user-key: $USER_KEY" \
     "https://sutram.io/api/v1/content?folder_id=f1a2b3c4-..."

Exemplo — filtrar por tipo

curl -H "x-project-key: $PROJECT_KEY" \
     -H "x-user-key: $USER_KEY" \
     "https://sutram.io/api/v1/content?type=video_link&all_folders=true"

Exemplo — busca por tag única

curl -H "x-project-key: $PROJECT_KEY" \
     -H "x-user-key: $USER_KEY" \
     "https://sutram.io/api/v1/content?tag=category&tag_value=cientifico&all_folders=true"

Exemplo — busca AND com múltiplas tags

curl -H "x-project-key: $PROJECT_KEY" \
     -H "x-user-key: $USER_KEY" \
     "https://sutram.io/api/v1/content?tags=%5B%7B%22key%22%3A%22patient%22%2C%22value%22%3A%22jo%C3%A3o%22%7D%2C%7B%22key%22%3A%22year%22%2C%22value%22%3A%222024%22%7D%5D&all_folders=true"

O parâmetro tags acima é a versão codificada em URL de:

[{"key": "patient", "value": "joão"}, {"key": "year", "value": "2024"}]

Exemplo — buscar por nome

curl -H "x-project-key: $PROJECT_KEY" \
     -H "x-user-key: $USER_KEY" \
     "https://sutram.io/api/v1/content?search=report&all_folders=true"

Exemplo — listagem paginada

curl -H "x-project-key: $PROJECT_KEY" \
     -H "x-user-key: $USER_KEY" \
     "https://sutram.io/api/v1/content?page=2&per_page=20"

Resposta:

{
  "items": [
    {
      "id": "d5e6f7a8-...",
      "type": "file",
      "name": "site-plan",
      "description": null,
      "tags": {"category": "planta", "year": "2024"},
      "folder_id": "f1a2b3c4-...",
      "created_at": "2026-02-19T10:00:00Z",
      "file": {
        "filename": "site-plan.pdf",
        "content_type": "application/pdf",
        "file_size": 2450000
      }
    },
    {
      "id": "e5f6a7b8-...",
      "type": "video_link",
      "name": "Site walkthrough",
      "description": null,
      "tags": {"topic": "neurologia"},
      "folder_id": null,
      "created_at": "2026-02-18T14:00:00Z",
      "video_link": {
        "url": "https://youtube.com/watch?v=abc",
        "platform": "youtube",
        "video_id": "abc",
        "thumbnail_url": "https://img.youtube.com/vi/abc/hqdefault.jpg"
      }
    },
    {
      "id": "a1b2c3d4-...",
      "type": "web_link",
      "name": "Documentation",
      "description": "Project reference docs",
      "tags": {},
      "folder_id": null,
      "created_at": "2026-02-17T09:00:00Z",
      "web_link": {
        "url": "https://docs.example.com",
        "fetched_title": "Documentation Hub",
        "favicon_url": "https://docs.example.com/favicon.ico"
      }
    },
    {
      "id": "c9d0e1f2-...",
      "type": "audio_link",
      "name": "Weekly recap",
      "description": null,
      "tags": {"series": "weekly"},
      "folder_id": null,
      "created_at": "2026-02-16T16:00:00Z",
      "audio_link": {
        "url": "https://open.spotify.com/episode/xyz",
        "platform": "spotify",
        "artist_or_author": "Team Alpha",
        "thumbnail_url": "https://i.scdn.co/image/abc"
      }
    }
  ],
  "meta": {
    "page": 1,
    "per_page": 50,
    "total_count": 4,
    "total_pages": 1
  }
}

Campos do contentable por tipo:

O contentable é serializado sob uma chave correspondente ao tipo do item:

Tipo Chave Campos
file file filename, content_type, file_size
web_link web_link url, fetched_title, favicon_url
video_link video_link url, platform, video_id, thumbnail_url
audio_link audio_link url, platform, artist_or_author, thumbnail_url

GET /api/v1/content/:id

Retorna os detalhes completos de um único item de conteúdo, incluindo URLs de download pré-assinadas para arquivos.

Parâmetros de caminho:

Parâmetro Tipo Descrição
id UUID ID do item de conteúdo

Exemplo de requisição:

curl -H "x-project-key: $PROJECT_KEY" \
     -H "x-user-key: $USER_KEY" \
     https://sutram.io/api/v1/content/d5e6f7a8-...

Resposta (arquivo):

{
  "item": {
    "id": "d5e6f7a8-...",
    "type": "file",
    "name": "site-plan",
    "description": null,
    "tags": {"category": "planta", "year": "2024"},
    "folder_id": "f1a2b3c4-...",
    "folder_path": "/Reports/2024",
    "created_at": "2026-02-19T10:00:00Z",
    "file": {
      "filename": "site-plan.pdf",
      "content_type": "application/pdf",
      "file_size": 2450000,
      "version": 1,
      "download_url": "https://storage.example.com/projects/.../site-plan.pdf?X-Amz-..."
    }
  }
}

Resposta (link de vídeo):

{
  "item": {
    "id": "e5f6a7b8-...",
    "type": "video_link",
    "name": "Site walkthrough",
    "description": null,
    "tags": {"topic": "neurologia"},
    "folder_id": null,
    "folder_path": "/",
    "created_at": "2026-02-18T14:00:00Z",
    "video_link": {
      "url": "https://youtube.com/watch?v=abc",
      "platform": "youtube",
      "video_id": "abc",
      "thumbnail_url": "https://img.youtube.com/vi/abc/hqdefault.jpg",
      "duration_seconds": null,
      "fetched_title": "Construction Site Alpha - Full Tour"
    }
  }
}

Resposta (link de áudio):

{
  "item": {
    "id": "c9d0e1f2-...",
    "type": "audio_link",
    "name": "Weekly recap",
    "description": null,
    "tags": {"series": "weekly"},
    "folder_id": null,
    "folder_path": "/",
    "created_at": "2026-02-16T16:00:00Z",
    "audio_link": {
      "url": "https://open.spotify.com/episode/xyz",
      "platform": "spotify",
      "artist_or_author": "Team Alpha",
      "thumbnail_url": "https://i.scdn.co/image/abc",
      "duration_seconds": 1800,
      "fetched_title": "Weekly Standup Recap - Feb 16"
    }
  }
}

Resposta (link web):

{
  "item": {
    "id": "a1b2c3d4-...",
    "type": "web_link",
    "name": "Documentation",
    "description": "Project reference docs",
    "tags": {},
    "folder_id": null,
    "folder_path": "/",
    "created_at": "2026-02-17T09:00:00Z",
    "web_link": {
      "url": "https://docs.example.com",
      "fetched_title": "Documentation Hub",
      "fetched_description": "Complete project documentation",
      "favicon_url": "https://docs.example.com/favicon.ico"
    }
  }
}

Diferenças entre resposta de detalhe e listagem:

O endpoint /content/:id retorna campos adicionais em comparação com o endpoint de listagem:

Campo Listagem (/content) Detalhe (/content/:id)
folder_path Não Sim
file.version Não Sim
file.download_url Não Sim (pré-assinada, temporária)
web_link.fetched_description Não Sim
video_link.duration_seconds Não Sim
video_link.fetched_title Não Sim
audio_link.duration_seconds Não Sim
audio_link.fetched_title Não Sim

Respostas de erro:

Status Corpo Quando
404 {"error": "Content item not found"} O item não existe ou pertence a outro projeto

GET /api/v1/document-classes/:id/documents

Lista os documentos governados de uma Document Class (recurso de governança, plano Max). Por padrão retorna uma linha por família — a última versão que você pode ler — e honra a visibilidade need-to-know: apenas documentos em estados que você tem permissão de ler são retornados.

Parâmetros de caminho:

Parâmetro Tipo Descrição
id UUID ou string UID da Document Class ou o category_id da classe (ex.: docs)

Query parameters:

Parâmetro Sintaxe Efeito
tag[chave] tag[category]=getting-started&tag[lang]=PT Filtro genérico por qualquer metadado do documento (ex.: category, lang). Múltiplas chaves combinam com AND.
state / states state[]=draft&state[]=approved ou state=draft,approved Filtra por estado do ciclo de vida; vários estados = união (OR). Sempre interseccionado com os estados que você pode ler (need-to-know) — nunca alarga a visibilidade.
all_versions all_versions=true false (padrão) = uma linha por família (a última versão legível); true = uma linha por versão.
page / per_page page=2&per_page=100 Paginação. per_page padrão 50, máximo 200.

Estados legíveis e default: sem state, o endpoint retorna a última versão que você pode ler de cada documento — que pode ser uma draft não publicada. Para consumir apenas conteúdo publicado (ex.: um site público), filtre com state=approved.

Exemplo de requisição:

curl -H "x-project-key: $PROJECT_KEY" \
     -H "x-user-key: $USER_KEY" \
     "https://sutram.io/api/v1/document-classes/docs/documents?state=approved"

Resposta:

{
  "category_id": "docs",
  "category_name": "Documentation",
  "documents": [
    {
      "document_id": "a6a01eb3-...",
      "version_id": "117665e1-...",
      "slug": "guia-de-inicio-rapido",
      "name": "guia-de-inicio-rapido",
      "lifecycle_state": "approved",
      "state_label": "Approved",
      "version_key": "1.38.a",
      "metadata": {
        "title": "Guia de Início Rápido",
        "category": "getting-started",
        "excerpt": "Comece a usar o Sutram em poucos minutos...",
        "lang": "PT",
        "published_at": "2026-03-16",
        "sort_order": "10",
        "translation_key": "quick-start-guide"
      }
    }
  ],
  "page": 1,
  "per_page": 50,
  "total_count": 27,
  "total_pages": 1
}

Campos por documento:

Campo Descrição
document_id ID da família do documento (estável entre versões)
version_id ID da versão retornada. É também um ID de pasta — use GET /api/v1/folders/{version_id} para obter o arquivo do corpo (ex.: o .md)
slug / name Identidade calculada do documento
lifecycle_state / state_label Estado do ciclo de vida (id e rótulo legível)
version_key Rótulo da versão (ex.: 1.38.a)
metadata Metadados tipados da classe (título, categoria, resumo, lang, sort_order, etc.)

Obtendo o corpo de um documento:

O corpo de um documento (ex.: o .md) vive na pasta da versão. Combine este endpoint com os endpoints de pasta e conteúdo:

# 1. Liste os documentos governados
curl ... "https://sutram.io/api/v1/document-classes/docs/documents?state=approved"

# 2. Para cada documento, abra a pasta da versão pelo version_id
curl ... "https://sutram.io/api/v1/folders/117665e1-..."

# 3. Obtenha a URL de download pré-assinada do arquivo do corpo
curl ... "https://sutram.io/api/v1/content/{file_id}"

Respostas de erro:

Status Corpo Quando
404 {"error": "Document class not found"} A classe não existe ou pertence a outro projeto

Tags de itens de conteúdo

Tags são pares de chave-valor em formato de texto livre, associados a itens de conteúdo e pastas. Elas permitem metadados estruturados e recursos avançados de busca.

Restrições de tags

Restrição Limite
Máximo de tags por item 50
Comprimento máximo da chave 100 caracteres
Comprimento máximo do valor 500 caracteres
Tipos de chave e valor Apenas strings

Gerenciamento de tags

As tags dos itens de conteúdo são gerenciadas pelas ferramentas do MCP Server:

Ferramenta MCP Descrição
sutram_set_item_tags Definir/substituir todas as tags de um item de conteúdo
sutram_search_items Buscar itens por tag (condição única ou AND com múltiplas condições)
sutram_get_item_tag_keys Listar todas as chaves de tag distintas entre os itens

Consultando tags pela REST API

Use os query parameters tag, tag_value e tags no GET /api/v1/content:

# Find all items tagged with "category"
curl ... "https://sutram.io/api/v1/content?tag=category&all_folders=true"

# Find items where category contains "report"
curl ... "https://sutram.io/api/v1/content?tag=category&tag_value=report&all_folders=true"

# Find items matching multiple conditions (AND)
curl ... "https://sutram.io/api/v1/content?tags=[{\"key\":\"patient\",\"value\":\"joão\"},{\"key\":\"year\",\"value\":\"2024\"}]&all_folders=true"

Comparação: REST API vs. MCP Server

Funcionalidade REST API (/api/v1) MCP Server (/mcp)
Protocolo HTTP/JSON JSON-RPC 2.0 (MCP)
Ler conteúdo Sim Sim
Buscar por tags Sim Sim
Listar documentos governados Sim Sim
URLs de download Sim (pré-assinadas) Sim (pré-assinadas)
Upload de arquivos Não Sim
Criar links Não Sim
Criar pastas Não Sim
Definir tags Não Sim
Mover/renomear/excluir Não Sim
Autenticação Mesma chave dupla Mesma chave dupla
Ideal para Aplicativos externos, dashboards, scripts Assistentes de IA, automação

Limites de requisições

A REST API aplica limites de requisição por projeto e por IP:

  • Por projeto (por hora) — Determinado pelo plano do proprietário do projeto (api_rate_limit_per_hour). O padrão é 1.000 requisições/hora, chegando a 10.000/hora nos planos superiores. Ao exceder, a API responde HTTP 429 com o corpo {"error": "Rate limit exceeded", "retry_after": <segundos>}.
  • Headers de limite — Respostas bem-sucedidas incluem x-ratelimit-limit e x-ratelimit-remaining; a resposta 429 inclui retry-after (em segundos).
  • Por IP — Um limite adicional por endereço IP protege contra abuso e força bruta.

Ao receber um 429, respeite o header retry-after e aplique recuo (backoff) antes de repetir a requisição.


Versionamento

A API é versionada pelo caminho da URL (/api/v1). Mudanças que quebram compatibilidade serão introduzidas em novas versões (/api/v2). Adições que não quebram compatibilidade (novos campos, novos parâmetros opcionais) podem ser adicionadas à versão atual sem aviso prévio.


Exemplos

Buscar todos os links de vídeo de um projeto

curl -H "x-project-key: $PROJECT_KEY" \
     -H "x-user-key: $USER_KEY" \
     "https://sutram.io/api/v1/content?type=video_link&all_folders=true"

Buscar conteúdo marcado com um tópico específico

curl -H "x-project-key: $PROJECT_KEY" \
     -H "x-user-key: $USER_KEY" \
     "https://sutram.io/api/v1/content?tag=topic&tag_value=neurologia&all_folders=true"

Navegar pela hierarquia de pastas

# 1. List root folders
curl ... "https://sutram.io/api/v1/folders"

# 2. Open a specific folder
curl ... "https://sutram.io/api/v1/folders/f1a2b3c4-..."

# 3. Get download URL for a file
curl ... "https://sutram.io/api/v1/content/d5e6f7a8-..."

Paginar por todo o conteúdo

# Page 1
curl ... "https://sutram.io/api/v1/content?page=1&per_page=20&all_folders=true"

# Page 2
curl ... "https://sutram.io/api/v1/content?page=2&per_page=20&all_folders=true"

# Continue until page >= total_pages from meta

Integração com JavaScript (fetch)

const PROJECT_KEY = "sk_proj_...";
const USER_KEY = "sk_user_...";
const BASE_URL = "https://sutram.io/api/v1";

async function fetchContent(params = {}) {
  const query = new URLSearchParams(params).toString();
  const url = `${BASE_URL}/content${query ? "?" + query : ""}`;

  const response = await fetch(url, {
    headers: {
      "x-project-key": PROJECT_KEY,
      "x-user-key": USER_KEY,
    },
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(error.error);
  }

  return response.json();
}

// List all video links
const videos = await fetchContent({ type: "video_link", all_folders: "true" });
console.log(`Found ${videos.meta.total_count} videos`);

// Search by tag
const tagged = await fetchContent({
  tag: "topic",
  tag_value: "neurologia",
  all_folders: "true",
});

Integração com Python (requests)

import requests

PROJECT_KEY = "sk_proj_..."
USER_KEY = "sk_user_..."
BASE_URL = "https://sutram.io/api/v1"

headers = {
    "x-project-key": PROJECT_KEY,
    "x-user-key": USER_KEY,
}

# List root folders
folders = requests.get(f"{BASE_URL}/folders", headers=headers).json()

# Get content in a folder
folder_id = folders["folders"][0]["id"]
content = requests.get(
    f"{BASE_URL}/content",
    headers=headers,
    params={"folder_id": folder_id},
).json()

# Search by multiple tags
import json
tags = json.dumps([
    {"key": "patient", "value": "joão"},
    {"key": "year", "value": "2024"},
])
results = requests.get(
    f"{BASE_URL}/content",
    headers=headers,
    params={"tags": tags, "all_folders": "true"},
).json()

print(f"Found {results['meta']['total_count']} items")

Solução de problemas

401 Unauthorized

  • Verifique se ambos os headers x-project-key e x-user-key estão presentes
  • Confirme que as chaves não foram revogadas
  • Certifique-se de que você é um membro ativo do projeto (visualizadores têm acesso de leitura; apenas não-membros recebem 401)

404 Not Found

  • Verifique se o ID da pasta/conteúdo pertence ao projeto autenticado
  • Confirme que o item não foi excluído

429 Too Many Requests

  • Você excedeu o limite de requisições por hora do projeto (ver Limites de requisições)
  • Leia o header retry-after da resposta e aguarde o número de segundos indicado antes de repetir
  • Acompanhe x-ratelimit-remaining nas respostas para evitar atingir o limite
  • Se o volume for legítimo, considere um plano com cota maior

Resultados vazios na busca por tags

  • Adicione all_folders=true para buscar em todas as pastas (sem isso, apenas itens da raiz são retornados)
  • A busca por tags não diferencia maiúsculas de minúsculas — verifique erros de digitação nos nomes das chaves
  • Use GET /api/v1/content?tag=SUA_CHAVE&all_folders=true sem valor primeiro para verificar se a chave de tag existe

Parâmetro tags retorna erro

  • O parâmetro tags deve ser um array JSON válido: [{"key": "...", "value": "..."}]
  • Codifique a string JSON em URL ao passá-la como query parameter
  • Cada condição deve ter um campo key; value é opcional