Fibersals API v1.0

Documentação da API do MDShare

MDShare publica, compartilha e revisa documentos Markdown. Cada documento recebe um ID único de 8 caracteres e uma URL curta para compartilhamento.

Visão Geral #

O MDShare foi projetado para ser usado por:

Características Principais

Meus documentos #

O painel de documentos mostra os arquivos que pertencem à sua conta e os que compartilharam com você. A mesma listagem está disponível em GET /api/docs. Use uma sessão ou um token de agente com proprietário definido. O token lista os documentos do proprietário e os compartilhados com ele, desde que tenha escopo de leitura, comentário, publicação ou administração. O escopo delete sozinho não abre a listagem.

GET /api/docs?scope=shared&q=proposta&visibility=private&page=1&limit=20

Você pode combinar scope=mine|shared, visibility=private|link, password=yes|no, pending=yes|no, owner=email e q. A busca procura título, ID, e-mail e nome do proprietário. A página tem até 100 documentos.

A resposta traz docs, total, page, limit, pages, counts e owners. Cada documento informa proprietário, criador, papel concedido, visibilidade, senha, datas, revisão e tópicos pendentes. Para um agente, o papel concedido e os escopos do token são verificados juntos em cada ação. openComments conta comentários principais abertos; respostas não criam outra pendência. Em documentos antigos, o criador pode estar sem registro.

Sugestões de texto #

Use POST /api/{id}/comments com kind: "suggestion" para propor uma substituição. Envie body e anchor como em um comentário, além dos campos abaixo:

{
  "kind": "suggestion",
  "body": "Atualizar prazo após a vistoria.",
  "anchor": { "quote": "30 dias", "start": 125, "end": 132 },
  "original": "30 dias",
  "proposed": "45 dias",
  "sourceStart": 120,
  "sourceEnd": 127,
  "baseRevision": 2,
  "hash": "sha256_do_markdown_original_em_hexadecimal"
}

sourceStart e sourceEnd apontam para o Markdown fonte, enquanto a âncora identifica o trecho renderizado. O hash SHA-256 e a revisão impedem uma sugestão sobre texto que já mudou. A resposta de comentários inclui kind, original e proposed para comparar as versões.

Presença e alterações #

GET /api/{id}/activity?revision=2 lista quem está lendo a revisão e devolve version, que muda com os comentários. Para aparecer na lista, envie POST /api/{id}/activity com {"clientId":"cliente_12345678","revision":2} periodicamente. A presença expira após 35 segundos. Use "leave":true para sair. Uma seleção opcional usa quote, start, end, prefix e suffix. Apenas pessoas que podem ler o documento usam essa rota.

Transcrição de voz #

Quem pode comentar pode enviar uma gravação em multipart/form-data para POST /api/{id}/transcribe, no campo audio. A resposta traz o texto transcrito. Depois, envie esse texto para /comments. A API aceita apenas WAV PCM mono, 16 kHz e 16 bits, com até dois minutos. O limite padrão é 8 MiB por gravação e 20 transcrições por dia por identidade. Acessos por senha compartilham a cota do IP. Sem serviço configurado, a resposta é 503.

Atualização segura #

Leia o documento antes de editar. GET /api/{id} informa a revisão e devolve um ETag como "rev-3". Envie baseRevision: 3 no JSON de PUT /api/update ou o mesmo valor no cabeçalho If-Match. Essa precondição é obrigatória: sem ela, a API responde 428. Se outra pessoa publicou uma revisão antes, a API responde 409 e informa a revisão atual. Leia a versão nova e concilie as alterações antes de tentar novamente.

Se a rede falhar durante a publicação, repita a chamada com a mesma Idempotency-Key e o mesmo conteúdo. A API devolve o resultado anterior sem criar outra revisão. Uma chave repetida com conteúdo diferente recebe 409.

curl -X PUT https://mdshare-a2m.pages.dev/api/update \
  -H 'Content-Type: application/json' \
  -H 'If-Match: "rev-3"' \
  -H 'Idempotency-Key: proposta-2026-revisao-4' \
  -d '{"id":"8f14e45f","markdown":"# Proposta atualizada"}'

Primeiros passos #

Publique um documento

curl -X POST \
  -H "Authorization: Bearer $MDSHARE_TOKEN" \
  -H "Content-Type: text/markdown" \
  -d '# Meu Primeiro Documento

Este é um teste do **MDShare**!' \
  https://mdshare-a2m.pages.dev/api/upload

Resposta:

{
  "success": true,
  "id": "8f14e45f",
  "url": "https://mdshare-a2m.pages.dev/view/8f14e45f",
  "revision": 1
}

Autenticação #

Publicação e leitura têm regras diferentes Para publicar, use um token de agente com escopo publish ou um IP autorizado. Para ler um documento privado, é preciso ter acesso ao próprio documento.

Há dois caminhos para autenticar uma automação:

  1. Token de agente (recomendado) — Authorization: Bearer mds_.... Funciona de qualquer IP, tem escopo (read, publish, comment, delete, admin) e pode ser revogado sozinho. É o caminho para agentes com egress variável, que não cabem numa lista de IPs.
  2. Whitelist de IP — mantida para a rede da Fibersals; nada a enviar.

Quem administra cria e revoga tokens:

curl -X POST https://mdshare-a2m.pages.dev/api/tokens \
  -H "Content-Type: application/json" \
  -d '{"name":"Bot comercial","scopes":["publish"],"owner":"bot@fibersals.com.br"}'

curl -X DELETE https://mdshare-a2m.pages.dev/api/tokens/{id}
O token aparece uma vez Só o hash fica guardado. Criar e revogar exige conta da casa ou IP na whitelist — nunca outro token, para que um token de publicação não consiga criar um de administração.

IPs Autorizados:

Endpoints públicos (não requerem whitelist):

Os comentários usam uma segunda camada de autorização, por documento: quem tem a senha comenta, mesmo de fora da whitelist. Veja Comentários.

URL Base #

https://mdshare-a2m.pages.dev

Todas as requisições à API devem usar HTTPS.

Upload de Markdown #

POST /api/upload

Cria um novo documento Markdown e retorna um ID único e URL para visualização.

Content-Types Suportados

Content-Type Descrição Suporta Imagens
multipart/form-data Upload de arquivo .md com imagens Sim
application/json JSON com markdown e imagens base64 Sim (base64)
text/markdown Markdown direto no body Não
text/plain Texto plano tratado como Markdown Não

Parâmetros (multipart/form-data)

Campo Tipo Obrigatório Descrição
markdown File ou String Sim Conteúdo Markdown
images[] File[] Não Array de imagens

Parâmetros (application/json)

Campo Tipo Obrigatório Descrição
markdown String Sim Conteúdo Markdown
images Array Não Array de objetos {name, data}
password String Não Senha para acesso pelo link; entregue a senha separadamente

Exemplo 1: Multipart Form Data

curl -X POST \
  -F "markdown=@documento.md" \
  -F "images[]=@diagrama.png" \
  -F "images[]=@screenshot.jpg" \
  https://mdshare-a2m.pages.dev/api/upload

Exemplo 2: JSON

curl -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "markdown": "# Título\n\nConteúdo do documento."
  }' \
  https://mdshare-a2m.pages.dev/api/upload

Exemplo 3: JSON com Imagens Base64

curl -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "markdown": "# Com Imagem\n\n![Logo](logo.png)",
    "images": [
      {
        "name": "logo.png",
        "data": "data:image/png;base64,iVBORw0KGgo..."
      }
    ]
  }' \
  https://mdshare-a2m.pages.dev/api/upload

Exemplo 4: Texto Direto

curl -X POST \
  -H "Content-Type: text/markdown" \
  -d '# Hello World

Este é um documento simples.' \
  https://mdshare-a2m.pages.dev/api/upload

Resposta de Sucesso (200 OK)

{
  "success": true,
  "id": "8f14e45f",
  "url": "https://mdshare-a2m.pages.dev/view/8f14e45f",
  "revision": 1
}

Resposta de Erro (400 Bad Request)

{
  "success": false,
  "error": "No markdown content provided"
}

Atualizar Markdown #

PUT /api/update

Atualiza um documento existente. Cria uma nova revisão mantendo o histórico. Envie a revisão que serviu de base à edição em baseRevision ou no cabeçalho If-Match.

Dica Também aceita POST para compatibilidade com clientes que não suportam PUT.

Parâmetros

Campo Tipo Obrigatório Descrição
id String Sim ID do documento (8 caracteres hex)
markdown String ou File Sim Novo conteúdo Markdown
baseRevision Inteiro Sim, se não enviar If-Match Revisão lida antes de editar. Aceito em JSON e multipart; falta de precondição retorna 428 e revisão desatualizada retorna 409.
images[] File[] ou Array Não Novas imagens a adicionar

Exemplo: JSON

curl -X PUT \
  -H "Content-Type: application/json" \
  -d '{
    "id": "8f14e45f",
    "baseRevision": 1,
    "markdown": "# Documento Atualizado\n\nEsta é a revisão 2."
  }' \
  https://mdshare-a2m.pages.dev/api/update

Exemplo: Multipart Form Data

curl -X PUT \
  -F "id=8f14e45f" \
  -F "baseRevision=1" \
  -F "markdown=@documento_atualizado.md" \
  -F "images[]=@nova_imagem.png" \
  https://mdshare-a2m.pages.dev/api/update

Resposta de Sucesso

{
  "success": true,
  "id": "8f14e45f",
  "url": "https://mdshare-a2m.pages.dev/view/8f14e45f",
  "revision": 2
}

Resposta de Erro (404 Not Found)

{
  "success": false,
  "error": "Markdown not found with this ID"
}

Obter Markdown #

GET /api/{id}

Retorna o conteúdo Markdown e metadados de um documento.

Parâmetros de URL

Parâmetro Descrição
id ID do documento (8 caracteres hex)

Query Parameters

Parâmetro Tipo Descrição
revision Integer Número da revisão específica
format String raw para obter texto puro

Exemplo: Obter documento atual

curl https://mdshare-a2m.pages.dev/api/8f14e45f

Exemplo: Obter revisão específica

curl "https://mdshare-a2m.pages.dev/api/8f14e45f?revision=1"

Exemplo: Obter markdown puro

curl "https://mdshare-a2m.pages.dev/api/8f14e45f?format=raw"

Resposta (JSON)

{
  "success": true,
  "id": "8f14e45f",
  "created": "2025-01-30T12:00:00.000Z",
  "updated": "2025-01-30T14:30:00.000Z",
  "revision": 2,
  "totalRevisions": 2,
  "images": ["diagrama.png", "screenshot.jpg"],
  "content": "# Título\n\nConteúdo do documento..."
}

Resposta (format=raw)

# Título

Conteúdo do documento...

Visualizar Documento #

GET /view/{id}

Renderiza o documento Markdown em uma página HTML bonita e interativa.

Público Este endpoint não requer whitelist de IP. Documentos privados exigem propriedade, convite ou senha válida. Uma conta da Fibersals, por si só, não abre um documento privado protegido por senha. Documentos abertos por link e sem senha podem ser lidos por quem tem o endereço.

Query Parameters

Parâmetro Tipo Descrição
revision Integer Visualizar revisão específica

Recursos do Visualizador

  • Toggle de tema claro/escuro (salvo no localStorage)
  • Dropdown para selecionar revisões
  • Syntax highlighting automático para código
  • Renderização de imagens embarcadas
  • Suporte a GitHub Flavored Markdown
  • Design responsivo para mobile

Exemplo de URL

https://mdshare-a2m.pages.dev/view/8f14e45f
https://mdshare-a2m.pages.dev/view/8f14e45f?revision=1

Acessar Imagens #

GET /img/{id}/{filename}

Retorna uma imagem associada a um documento.

Público Este endpoint não requer whitelist de IP. A imagem segue as permissões do documento.

Formatos Suportados

  • PNG (image/png)
  • JPEG (image/jpeg)
  • GIF (image/gif)
  • WebP (image/webp)
  • SVG (image/svg+xml)
  • ICO (image/x-icon)
  • BMP (image/bmp)

Exemplo

https://mdshare-a2m.pages.dev/img/8f14e45f/diagrama.png

Uso no Markdown

![Diagrama](diagrama.png)
![Screenshot](screenshot.jpg)

As imagens são automaticamente resolvidas para o caminho correto no visualizador.

Comentários #

Comentários ficam presos a um trecho do documento, não à posição numa página. Cada comentário guarda o texto citado, o contexto ao redor e a posição na revisão em que foi criado. Consulte revisões anteriores para ler comentários antigos.

Quem pode comentar Quem tem papel de comentar ou gerenciar, um token de agente autorizado ou a senha do documento pode comentar. O papel viewer não permite comentar pelo login, mesmo em um documento protegido aberto por link; informar a senha gera uma autorização própria. Documentos abertos por link permitem leitura, mas a escrita exige permissão.

Obter um token com a senha

curl -X POST https://mdshare-a2m.pages.dev/api/auth \
  -H "Content-Type: application/json" \
  -d '{"id": "8f14e45f", "password": "minha-senha"}'
{
  "success": true,
  "token": "a1b2c3...",
  "expiresIn": 86400
}

Envie o token em todas as chamadas de comentário: Authorization: Bearer <token>

GET /api/{id}/comments

Lista os comentários do documento, em ordem de criação.

Query Parameters

Parâmetro Tipo Descrição
status String open ou resolved
includeDeleted Boolean Inclui os excluídos. Exige administrador

Exemplo

curl https://mdshare-a2m.pages.dev/api/8f14e45f/comments \
  -H "Authorization: Bearer $TOKEN"

Resposta

{
  "success": true,
  "docId": "8f14e45f",
  "revision": 3,
  "total": 1,
  "comments": [
    {
      "id": "3f9a1c2b7d4e5061",
      "docId": "8f14e45f",
      "parentId": null,
      "revision": 2,
      "author": "Eng. Marina",
      "body": "Isso já foi tratado na vistoria de 2025?",
      "anchor": {
        "quote": "infiltração na face sul",
        "prefix": "O telhado apresenta ",
        "suffix": ", com manchas visíveis",
        "start": 61,
        "end": 84
      },
      "status": "open",
      "created": "2026-08-12T12:00:00.000Z",
      "updated": null,
      "deleted": null
    }
  ]
}
POST /api/{id}/comments

Cria um comentário, ancorado num trecho ou geral.

Body (JSON)

Campo Tipo Obrigatório Descrição
body String Sim Texto do comentário (até 5000 caracteres)
images Array Não Imagens do comentário ou da sugestão: objetos com name e data em Base64
author String Não Nome de quem comenta. Padrão: Anônimo
anchor Object Não Trecho comentado. Sem ele, o comentário é geral do documento
parentId String Não Id do comentário respondido (threads de um nível)
revision Integer Não Revisão em que o trecho foi lido. Padrão: a atual

Objeto anchor

Campo Descrição
quote O trecho exato comentado
prefix / suffix Até 32 caracteres antes e depois — desempatam trechos repetidos
start / end Offsets no texto plano renderizado

Exemplo

curl -X POST https://mdshare-a2m.pages.dev/api/8f14e45f/comments \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "author": "Eng. Marina",
    "body": "Isso já foi tratado na vistoria de 2025?",
    "anchor": {
      "quote": "infiltração na face sul",
      "prefix": "O telhado apresenta ",
      "suffix": ", com manchas visíveis",
      "start": 61,
      "end": 84
    }
  }'

Resposta 201 com o comentário criado em comment.

Mencionar pessoas

Digite @ e escolha alguém pelo nome ou email. A busca mostra usuários Fibersals registrados que podem ler o documento. Funciona em comentários, respostas, edição e justificativas de sugestões. Mencionar uma pessoa não concede acesso.

Pela API, consulte GET /api/{id}/mentions?q=nome e escreva @pessoa@fibersals.com.br em body. A senha anônima não permite buscar pessoas nem gerar emails. Emails sem o @ extra, código, imagens e o Markdown proposto da sugestão não geram avisos.

A resposta inclui mentions e notificationStatus: {state,total,sent}. Aviso pendente significa que ele aguarda envio. Enviado significa que o serviço de email o aceitou, sem garantir chegada à caixa de entrada. Uma falha de email não apaga o comentário salvo. Sem integração configurada, os avisos ficam pendentes.

Cada comentário aceita até dez destinatários e cada identidade pode gerar até 50 avisos por dia. Repetir o envio ou editar o comentário não repete um aviso já aceito para aquela pessoa. Remover a menção, excluir o comentário ou revogar o acesso cancela um envio pendente. O email abre a revisão e a conversa com a autorização normal do documento.

Colar imagens

Cole a imagem no campo de comentário, resposta, justificativa ou texto proposto da sugestão. Confira a prévia e remova o que não quiser enviar. O MDShare guarda as imagens quando você envia a anotação; se o envio falhar, o rascunho continua disponível.

Pela API, envie images: [{"name":"foto.png","data":"BASE64"}] e use ![Vistoria](mdshare-image:foto.png) em body ou proposed. A resposta troca essa referência por uma URL local /img/{id}/{arquivo}. O trecho original da sugestão e seus offsets não mudam.

Cada anotação aceita até cinco imagens PNG, JPEG, GIF ou WebP, com até 8 MiB por imagem e 20 MiB no total. SVG não é aceito nesse fluxo. Cada arquivo precisa aparecer no corpo ou no texto proposto. Os limites de texto também valem após a troca das referências.

As imagens seguem as permissões atuais do documento. Elas permanecem disponíveis até excluir o documento, mesmo após excluir a anotação, para preservar sugestões já incorporadas ao Markdown e revisões anteriores. Comentários mostram imagens locais e mantêm o restante como texto.

Limite 20 comentários por hora, por IP e por documento. IPs na whitelist não têm limite.
PATCH /api/{id}/comments/{commentId}

Resolve, reabre ou edita o texto de um comentário.

curl -X PATCH https://mdshare-a2m.pages.dev/api/8f14e45f/comments/3f9a1c2b7d4e5061 \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"status": "resolved"}'
DELETE /api/{id}/comments/{commentId}

Exclui o comentário e suas respostas. Por padrão é exclusão lógica: some da listagem e continua visível para auditoria com ?includeDeleted=true.

Query Parameters

Parâmetro Descrição
hard true apaga a linha de vez. Exige administrador
curl -X DELETE https://mdshare-a2m.pages.dev/api/8f14e45f/comments/3f9a1c2b7d4e5061 \
  -H "Authorization: Bearer $TOKEN"

Excluir Documento e Revisão #

Irreversível Exclusão exige o papel de gerente no documento; agentes precisam também do escopo delete. Um token só com publish não apaga. Não há desfazer: o conteúdo sai do KV, do R2 e do banco de comentários.
DELETE /api/{id}?confirm={id}

Apaga o documento inteiro: metadados, todas as revisões, as imagens e os comentários. O confirm precisa repetir o id do documento.

curl -X DELETE "https://mdshare-a2m.pages.dev/api/8f14e45f?confirm=8f14e45f"

Resposta

{
  "success": true,
  "id": "8f14e45f",
  "deleted": true,
  "deletedObjects": 4,
  "deletedComments": 2
}
DELETE /api/{id}?revision={n}

Remove uma revisão antiga do histórico. A revisão atual e a última restante não podem ser apagadas — publique uma nova antes.

curl -X DELETE "https://mdshare-a2m.pages.dev/api/8f14e45f?revision=2"

Resposta

{
  "success": true,
  "id": "8f14e45f",
  "deletedRevision": 2,
  "revisions": [1, 3]
}

Integração com LLMs #

🤖 Instruções para LLMs

Se você é um LLM (Claude, GPT, etc.), use esta API para compartilhar outputs longos com o usuário através de links curtos e legíveis.

Quando Usar

Fluxo Recomendado

  1. Gere o conteúdo Markdown
  2. Faça POST para /api/upload
  3. Apresente o link url ao usuário
  4. Se precisar atualizar, use PUT para /api/update com o id e a baseRevision lida antes da edição

Exemplo de Uso por LLM

# Passo 1: Gerar o Markdown
markdown_content = """
# Relatório de Análise

## Sumário Executivo
Esta análise examina os dados de vendas Q4 2024...

## Dados
| Métrica | Valor |
|---------|-------|
| Vendas  | $1.2M |
| Crescimento | +15% |

## Conclusões
- Ponto 1
- Ponto 2
"""

# Passo 2: Enviar para MDShare
import requests

response = requests.post(
    "https://mdshare-a2m.pages.dev/api/upload",
    json={"markdown": markdown_content}
)

result = response.json()
# result = {"success": true, "id": "abc12345", "url": "https://...", "revision": 1}

# Passo 3: Apresentar ao usuário
print(f"Seu relatório está disponível em: {result['url']}")

Resposta Sugerida para o Usuário

Gerei o relatório completo de análise. Você pode visualizá-lo aqui:

📄 https://mdshare-a2m.pages.dev/view/abc12345

O documento inclui:
- Sumário executivo
- Tabelas de dados
- Conclusões e recomendações

O link é permanente e você pode compartilhá-lo com sua equipe.

Proteção por Senha #

Quem gerencia o documento pode alterar a senha no painel ou usar PUT /api/{id}/password com {"password":"nova senha"}. Para remover, use DELETE /api/{id}/password. A troca invalida o acesso feito com a senha anterior e não cria uma revisão do texto.

Acesso por senha Quem recebe o link e a senha pode abrir o documento, mesmo fora da rede da Fibersals.

Envie a senha por um canal separado do link. Quem gerencia o documento pode trocá-la ou removê-la.

Criar Documento com Senha

curl -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "markdown": "# Documento Confidencial\n\nConteúdo secreto aqui...",
    "password": "minhasenha123"
  }' \
  https://mdshare-a2m.pages.dev/api/upload

Resposta

{
  "success": true,
  "id": "8f14e45f",
  "url": "https://mdshare-a2m.pages.dev/view/8f14e45f",
  "revision": 1,
  "isPublic": true,
  "message": "Documento protegido por senha. Compartilhe o link e a senha com quem deve acessá-lo."
}

isPublic é um campo legado: nesta resposta ele indica presença de senha, não acesso público. Consulte visibility e hasPassword no catálogo ou em GET /api/{id}/share para distinguir visibilidade e proteção.

Adicionar ou trocar senha

curl -X PUT https://mdshare-a2m.pages.dev/api/8f14e45f/password \
  -H "Content-Type: application/json" \
  -d '{"password":"novasenha456"}'

Remover senha

curl -X DELETE https://mdshare-a2m.pages.dev/api/8f14e45f/password

Como Funciona

Autenticação via API

curl -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "id": "8f14e45f",
    "password": "minhasenha123"
  }' \
  https://mdshare-a2m.pages.dev/api/auth

Resposta

{
  "success": true,
  "token": "abc123def456...",
  "expiresIn": 86400
}

Envie o token da API no cabeçalho Authorization: Bearer <token>. No navegador, digite a senha na tela do documento.

Upload de Imagens #

Existem duas formas de incluir imagens em seus documentos:

Upload e atualização aceitam PNG, JPEG (.jpg ou .jpeg), GIF, WebP e SVG. Cada publicação pode enviar até 20 imagens, com no máximo 8 MiB por arquivo e 25 MiB no total. Os limites consideram os bytes da imagem, após decodificar Base64.

Use nomes sem pastas, barras, .. ou caracteres de controle. Cada imagem precisa ter um nome diferente, inclusive após a conversão de espaços e caracteres especiais para _. No multipart e nas URLs Base64, o MIME informado deve corresponder à extensão; MIME ausente ou application/octet-stream deixa a extensão definir o formato.

Dados Base64 inválidos, nomes repetidos, formatos não aceitos e mais de 20 imagens retornam 400. Arquivo acima de 8 MiB ou conjunto acima de 25 MiB retorna 413. Essas recusas não publicam conteúdo nem imagens.

1. Upload de Arquivos (Multipart Form Data)

curl -X POST \
  -F "markdown=@documento.md" \
  -F "images[]=@imagem1.png" \
  -F "images[]=@imagem2.jpg" \
  https://mdshare-a2m.pages.dev/api/upload

2. Base64 (JSON)

{
  "markdown": "# Documento\n\n![Logo](logo.png)",
  "images": [
    {
      "name": "logo.png",
      "data": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
    }
  ]
}

Referenciando Imagens no Markdown

Use caminhos relativos simples no seu Markdown:

![Descrição da imagem](nome-do-arquivo.png)
![Diagrama](diagrama.svg)
![Screenshot](screenshot.jpg)

O visualizador automaticamente resolve para:

https://mdshare-a2m.pages.dev/img/{id}/nome-do-arquivo.png

Sistema de Revisões #

Cada atualização de documento cria uma nova revisão. O histórico completo é mantido.

Como Funciona

Acessar Revisões Específicas

# Via API
curl "https://mdshare-a2m.pages.dev/api/8f14e45f?revision=1"

# Via Visualizador
https://mdshare-a2m.pages.dev/view/8f14e45f?revision=1

Comparar revisões no desktop

No visualizador, clique em Comparar revisões. A versão antiga aparece à esquerda e a nova à direita, com Markdown renderizado e rolagem sincronizada. Os blocos removidos ficam em vermelho; os adicionados ficam em verde. Um bloco alterado mostra o original em vermelho e o resultado em verde.

A comparação começa com a revisão exibida e a anterior disponível. Você pode escolher outro par nos seletores. Quando não há revisão anterior, o botão fica desativado. A comparação usa as permissões atuais do documento e mantém o texto e os comentários intactos.

Metadados de Revisão

{
  "revision": 3,
  "totalRevisions": 3,
  "created": "2025-01-30T12:00:00.000Z",
  "updated": "2025-01-30T15:45:00.000Z"
}

Comentários Ancorados #

No visualizador, selecione um trecho do documento e clique em Comentar. O comentário aparece no painel lateral e o trecho fica destacado para todo mundo que abrir o link com a senha.

Como a âncora sobrevive a uma nova revisão

Ao abrir o documento, cada âncora é procurada nesta ordem:

  1. O offset guardado ainda aponta para o mesmo texto — usa direto.
  2. O texto existe em outro ponto — o contexto (prefixo e sufixo) escolhe a ocorrência certa.
  3. Só o espaçamento mudou — compara ignorando quebras de linha e espaços repetidos.
  4. O trecho sumiu — o comentário vira órfão: continua na lista, sem destaque, avisando que o trecho não existe nesta revisão.

Ou seja: editar o documento com PUT /api/update não perde comentários, mesmo inserindo parágrafos antes do trecho comentado.

Estados

Ciclo de Revisão com LLM #

É para isto que os comentários existem: um LLM publica, um humano revisa e comenta o trecho exato, o LLM lê os comentários pela API e age em cima deles. O link é permanente, então o ciclo inteiro acontece na mesma URL.

Arquivo para agentes A mesma informação em formato compacto para LLMs está em /llms.txt, e a skill pronta para instalar num agente está em /skill — são os endereços a passar para outro agente que precise operar o MDShare.

Os seis passos

  1. Publicar — POST /api/upload. Guardar o id.
  2. Entregar — mandar /view/{id} para quem revisa (com senha, se for externo).
  3. Revisar — a pessoa seleciona o trecho e comenta no navegador.
  4. Ler — GET /api/{id}?format=commented traz o markdown com cada comentário delimitado no trecho exato (ou GET /api/{id}/comments para JSON).
  5. Agir — corrigir o markdown e publicar com PUT /api/update.
  6. Fechar — PATCH /api/{id}/comments/{commentId} com {"status":"resolved"}.

Repetir do passo 4 até não sobrar comentário aberto.

Exemplo do passo 4 ao 6

TOKEN=$(curl -s -X POST https://mdshare-a2m.pages.dev/api/auth \
  -H "Content-Type: application/json" \
  -d '{"id":"8f14e45f","password":"minha-senha"}' | jq -r .token)

# o que está pendente, e sobre qual trecho
curl -s "https://mdshare-a2m.pages.dev/api/8f14e45f/comments?status=open" \
  -H "Authorization: Bearer $TOKEN" \
  | jq -r '.comments[] | "[\(.id)] \(.author): \(.body)\n  trecho: \(.anchor.quote // "(documento)")"'

# ... aplica as correções e publica a revisão nova ...

curl -X PATCH https://mdshare-a2m.pages.dev/api/8f14e45f/comments/3f9a1c2b7d4e5061 \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"status":"resolved"}'

Markdown comentado

Em vez de casar âncoras por conta própria, o agente pede o documento já anotado:

curl "https://mdshare-a2m.pages.dev/api/8f14e45f?format=commented&status=open"
O telhado apresenta **{{{c1}}}infiltração na face sul{{{/c1}}}**, com manchas.

{{{comments
c1 id=3f9a1c2b7d4e5061 status=open author="Eng. Marina" revision=2
    Isso já foi tratado na vistoria de 2025?
}}}

Os delimitadores cercam exatamente o trecho comentado, e a legenda traz o id que vai no PATCH para resolver. Ao publicar a revisão corrigida, remova os delimitadores — eles são anotação, não conteúdo. Comentário sem trecho aparece como scope=documento; trecho que sumiu, como scope=nao-localizado; trecho repetido sem contexto que o distinga, como scope=aproximado.

Duas regras que evitam retrabalho

  1. Resolva o que você aplicou. Quando a correção altera justamente o trecho comentado, a âncora não encontra mais aquele texto e o comentário passa a aparecer como órfão. É o comportamento correto — marque como resolvido no mesmo momento em que aplicar.
  2. Olhe o campo revision. O comentário pode ter sido escrito sobre uma versão anterior do documento.

Quem assina o comentário

Comentário feito por quem entrou com a conta Google da Fibersals traz authorEmail preenchido — identidade verificada, sem digitar nome. Comentário feito com a senha do documento traz só o nome digitado, e authorEmail nulo. Um agente que precise saber de quem veio a instrução deve olhar esse campo.

Tratamento de Erros #

Todas as respostas de erro seguem o mesmo formato:

{
  "success": false,
  "error": "Descrição do erro"
}

Códigos de Status HTTP

Código Descrição Causa Comum
200 OK Requisição bem sucedida
400 Bad Request Dados inválidos ou faltando
401 Unauthorized Token inválido, revogado ou expirado
403 Forbidden A credencial não dá acesso à operação ou ao documento
404 Not Found Documento ou revisão não existe
409 Conflict Revisão alterada por outra publicação ou documento em alteração
500 Internal Error Erro no servidor

Exemplos de Erros

# Publicação sem credencial autorizada
{
  "success": false,
  "error": "Authentication required to publish"
}

# Markdown vazio
{
  "success": false,
  "error": "No markdown content provided"
}

# ID inválido
{
  "success": false,
  "error": "Invalid ID format"
}

# Documento não encontrado
{
  "success": false,
  "error": "Markdown not found with this ID"
}

# Revisão não encontrada
{
  "success": false,
  "error": "Revision 5 not found. Available: 1-3"
}

Exemplos cURL #

Upload Básico

curl -X POST \
  -H "Content-Type: text/markdown" \
  -d '# Título

Conteúdo do documento.' \
  https://mdshare-a2m.pages.dev/api/upload

Upload de Arquivo

curl -X POST \
  -F "markdown=@meu-documento.md" \
  https://mdshare-a2m.pages.dev/api/upload

Upload com Imagens

curl -X POST \
  -F "markdown=@documento.md" \
  -F "images[]=@img1.png" \
  -F "images[]=@img2.jpg" \
  https://mdshare-a2m.pages.dev/api/upload

Atualizar Documento

curl -X PUT \
  -H "Content-Type: application/json" \
  -d '{"id": "8f14e45f", "baseRevision": 1, "markdown": "# Atualizado\n\nNovo conteúdo."}' \
  https://mdshare-a2m.pages.dev/api/update

Obter Documento

# JSON com metadados
curl https://mdshare-a2m.pages.dev/api/8f14e45f

# Markdown puro
curl "https://mdshare-a2m.pages.dev/api/8f14e45f?format=raw"

# Revisão específica
curl "https://mdshare-a2m.pages.dev/api/8f14e45f?revision=1"

One-liner: Upload de stdin

echo "# Quick Note" | curl -X POST -d @- https://mdshare-a2m.pages.dev/api/upload

Exemplos Python #

Instalação

pip install requests

Upload Simples

import requests

def upload_markdown(content: str) -> dict:
    """Upload markdown content and return response with URL."""
    response = requests.post(
        "https://mdshare-a2m.pages.dev/api/upload",
        json={"markdown": content}
    )
    return response.json()

# Uso
result = upload_markdown("""
# Meu Documento

Este é um exemplo de documento Markdown.

## Seção 1
- Item 1
- Item 2

```python
print("Hello, World!")
```
""")

print(f"URL: {result['url']}")
print(f"ID: {result['id']}")

Upload com Imagens

import requests
import mimetypes
from pathlib import Path

def upload_with_images(markdown: str, images: list[str]) -> dict:
    """Upload markdown with image files."""
    files = [('markdown', ('doc.md', markdown, 'text/markdown'))]

    for img_path in images:
        path = Path(img_path)
        with open(path, 'rb') as f:
            mime = mimetypes.guess_type(path.name)[0] or 'application/octet-stream'
            files.append(('images[]', (path.name, f.read(), mime)))

    response = requests.post(
        "https://mdshare-a2m.pages.dev/api/upload",
        files=files
    )
    return response.json()

# Uso
result = upload_with_images(
    "# Com Imagens\n\n![Diagrama](diagrama.png)",
    ["diagrama.png"]
)

Atualizar Documento

import requests

def update_markdown(doc_id: str, content: str, base_revision: int) -> dict:
    """Update existing markdown document."""
    response = requests.put(
        "https://mdshare-a2m.pages.dev/api/update",
        json={"id": doc_id, "markdown": content, "baseRevision": base_revision}
    )
    return response.json()

# Uso
# Use a revisão lida antes de editar o conteúdo.
result = update_markdown("8f14e45f", "# Atualizado\n\nNovo conteúdo.", base_revision=1)

Obter Documento

import requests

def get_markdown(doc_id: str, revision: int = None) -> dict:
    """Get markdown document by ID."""
    url = f"https://mdshare-a2m.pages.dev/api/{doc_id}"
    if revision:
        url += f"?revision={revision}"

    response = requests.get(url)
    return response.json()

# Uso
doc = get_markdown("8f14e45f")
print(doc['content'])

Classe Helper Completa

import requests
from dataclasses import dataclass
from typing import Optional

@dataclass
class MDShareResult:
    success: bool
    id: str = ""
    url: str = ""
    revision: int = 0
    error: str = ""

class MDShare:
    BASE_URL = "https://mdshare-a2m.pages.dev"

    @classmethod
    def upload(cls, markdown: str) -> MDShareResult:
        """Upload new markdown document."""
        resp = requests.post(
            f"{cls.BASE_URL}/api/upload",
            json={"markdown": markdown}
        )
        data = resp.json()
        return MDShareResult(**data) if data.get('success') else MDShareResult(success=False, error=data.get('error', ''))

    @classmethod
    def update(cls, doc_id: str, markdown: str, base_revision: int) -> MDShareResult:
        """Update existing document."""
        resp = requests.put(
            f"{cls.BASE_URL}/api/update",
            json={"id": doc_id, "markdown": markdown, "baseRevision": base_revision}
        )
        data = resp.json()
        return MDShareResult(**data) if data.get('success') else MDShareResult(success=False, error=data.get('error', ''))

    @classmethod
    def get(cls, doc_id: str, revision: Optional[int] = None) -> dict:
        """Get document content and metadata."""
        url = f"{cls.BASE_URL}/api/{doc_id}"
        if revision:
            url += f"?revision={revision}"
        return requests.get(url).json()

    @classmethod
    def view_url(cls, doc_id: str) -> str:
        """Get viewer URL for document."""
        return f"{cls.BASE_URL}/view/{doc_id}"

# Uso
result = MDShare.upload("# Hello\n\nWorld!")
print(f"Visualize em: {result.url}")

# Atualizar
MDShare.update(result.id, "# Updated\n\nNew content!")

# Obter conteúdo
doc = MDShare.get(result.id)
print(doc['content'])

Exemplos JavaScript #

Upload com Fetch

async function uploadMarkdown(content) {
  const response = await fetch('https://mdshare-a2m.pages.dev/api/upload', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ markdown: content })
  });
  return response.json();
}

// Uso
const result = await uploadMarkdown(`# Meu Documento

Conteúdo aqui...
`);

console.log(`URL: ${result.url}`);
console.log(`ID: ${result.id}`);

Upload com Imagens (FormData)

async function uploadWithImages(markdownFile, imageFiles) {
  const formData = new FormData();
  formData.append('markdown', markdownFile);

  for (const img of imageFiles) {
    formData.append('images[]', img);
  }

  const response = await fetch('https://mdshare-a2m.pages.dev/api/upload', {
    method: 'POST',
    body: formData
  });

  return response.json();
}

// Uso com file inputs
const mdFile = document.getElementById('markdown-input').files[0];
const imgFiles = document.getElementById('images-input').files;
const result = await uploadWithImages(mdFile, imgFiles);

Atualizar Documento

async function updateMarkdown(id, content, baseRevision) {
  const response = await fetch('https://mdshare-a2m.pages.dev/api/update', {
    method: 'PUT',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ id, markdown: content, baseRevision })
  });
  return response.json();
}

// Uso
// Use a revisão lida antes de editar o conteúdo.
await updateMarkdown('8f14e45f', '# Atualizado\n\nNovo conteúdo.', 1);

Obter Documento

async function getMarkdown(id, revision = null) {
  let url = `https://mdshare-a2m.pages.dev/api/${id}`;
  if (revision) url += `?revision=${revision}`;

  const response = await fetch(url);
  return response.json();
}

// Uso
const doc = await getMarkdown('8f14e45f');
console.log(doc.content);

Classe Helper (ES6)

class MDShare {
  static BASE_URL = 'https://mdshare-a2m.pages.dev';

  static async upload(markdown) {
    const res = await fetch(`${this.BASE_URL}/api/upload`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ markdown })
    });
    return res.json();
  }

  static async update(id, markdown, baseRevision) {
    const res = await fetch(`${this.BASE_URL}/api/update`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ id, markdown, baseRevision })
    });
    return res.json();
  }

  static async get(id, revision = null) {
    let url = `${this.BASE_URL}/api/${id}`;
    if (revision) url += `?revision=${revision}`;
    const res = await fetch(url);
    return res.json();
  }

  static viewUrl(id) {
    return `${this.BASE_URL}/view/${id}`;
  }
}

// Uso
const { id, url } = await MDShare.upload('# Hello World');
console.log(`View: ${url}`);

Exemplos MCP Tools #

Para Claude e outros LLMs usando MCP Você pode usar a ferramenta WebFetch ou fazer requisições HTTP diretamente.

Usando WebFetch (se disponível)

# O LLM pode usar WebFetch para enviar markdown
# Exemplo conceitual de como um LLM usaria:

1. Preparar o conteúdo Markdown
2. Usar ferramenta de HTTP POST para:
   URL: https://mdshare-a2m.pages.dev/api/upload
   Body: {"markdown": "# Seu conteúdo aqui"}
   Content-Type: application/json

3. Extrair 'url' da resposta JSON
4. Apresentar o link ao usuário

Fluxo de Trabalho LLM

# Quando o usuário pede um relatório/documento longo:

1. Gerar o conteúdo completo em Markdown
2. Fazer upload via POST /api/upload
3. Obter o link de resposta
4. Responder ao usuário:

   "Aqui está seu relatório completo:
    📄 https://mdshare-a2m.pages.dev/view/abc12345

    O documento inclui todas as seções solicitadas
    e está formatado para fácil leitura."

# Se precisar atualizar:

1. Usar PUT /api/update com o ID existente e a baseRevision lida antes de editar
2. Informar ao usuário que o documento foi atualizado
   (o mesmo link continua funcionando)