# Guia rápido da API REST do Shager: de uma chave nova a um post rastreável em 40 contas, em sete chamadas

_2026-09-19 · Um tour prático pela API REST do Shager em curl e TypeScript: autenticação, workspaces, canais, mídia, rascunhos por conta, agendamento, status e cliques._

A API REST do Shager fica em `https://app.shager.ai/api/v1`, autentica com `Authorization: Bearer shgr_live_...`, devolve todo resultado como `{ "data": ... }` e todo erro como `{ "error": { "code", "message" } }`. Uma execução completa de distribuição são sete chamadas: conferir a chave, escolher um workspace, listar os canais, enviar a mídia, criar um rascunho com legendas por conta, agendar e depois ler o status e os cliques. A especificação legível por máquina é o [documento OpenAPI 3.1](https://app.shager.ai/api/v1/openapi.json).

Tudo o que está abaixo roda do jeito que está escrito, assim que você define duas variáveis de ambiente. As mesmas operações existem como ferramentas MCP com os mesmos nomes, então o que você aprende aqui vale também para Claude Code, Codex ou Cursor.

## Do que você precisa antes da primeira chamada?

De uma chave. Ou uma pessoa cria uma em https://app.shager.ai/dashboard/agents com os escopos de que você precisa, ou o seu agente cria um ambiente de teste pelo [cadastro feito pelo próprio agente](/en/blog/agent-self-signup-and-claim). Os escopos funcionam assim:

| Escopo | Permite |
|---|---|
| `read` | `whoami`, listar workspaces, canais, grupos e posts, ler um post, analytics |
| `write` | Criar workspaces, grupos, mídias, rascunhos de posts, links de conexão, pedidos de aprovação, cancelar |
| `publish` | Agendar, incluindo `create_post` com `schedule_at` |

```bash
export SHAGER_API_KEY="shgr_live_your_key_here"
export API="https://app.shager.ai/api/v1"
```

## Chamada 1: quem sou eu?

```bash
curl -s "$API/me" -H "Authorization: Bearer $SHAGER_API_KEY"
```

```json
{ "data": { "organization_id": "...", "organization": "Northwind Agency", "sandbox": false, "scopes": ["read", "write"] } }
```

Se `sandbox` for true, o agendamento e os links de conexão ficam bloqueados até uma pessoa assumir a chave. Se `publish` não estiver em `scopes`, você consegue montar rascunhos, mas não agendá-los. Saber disso logo de cara evita um 403 confuso mais tarde.

## Chamada 2: escolha ou crie um workspace

Um workspace costuma ser um cliente ou uma marca.

```bash
curl -s "$API/workspaces" -H "Authorization: Bearer $SHAGER_API_KEY"

curl -s -X POST "$API/workspaces" \
  -H "Authorization: Bearer $SHAGER_API_KEY" -H "Content-Type: application/json" \
  -d '{"name": "Summit Physio", "timezone": "Europe/London", "approval_required": true}'
```

Sempre defina o `timezone` (um nome IANA) na criação. Os horários de agendamento são guardados com o offset, e o fuso do workspace é como o Shager os mostra para as pessoas. `approval_required: true` significa que os posts precisam ser aprovados pelo cliente antes de serem agendados, um bom padrão para clientes novos.

Exporte o id da resposta para que os próximos comandos funcionem:

```bash
export WS="the_workspace_id"
```

## Chamada 3: liste canais e grupos

```bash
curl -s "$API/workspaces/$WS/channels" -H "Authorization: Bearer $SHAGER_API_KEY"
curl -s "$API/workspaces/$WS/groups" -H "Authorization: Bearer $SHAGER_API_KEY"
```

Cada canal volta com `id`, `network`, `name`, `handle`, `status`, os `formats` que ele suporta e `connected_by_client`. Os grupos devolvem `id`, `name` e `channel_ids`. Grupos são a forma de falar com quarenta contas sem listar quarenta ids: passe `group_ids` para `create_post` e todos os canais ativos do grupo entram.

Se o workspace ainda não tem canais, crie um link de conexão com `POST /workspaces/{workspace_id}/connect-links` e envie para o dono da conta. Ele mesmo conecta Facebook, Instagram, YouTube, X, LinkedIn ou TikTok, sem login no Shager.

## Chamada 4: envie a mídia a partir de uma URL

```bash
curl -s -X POST "$API/workspaces/$WS/media" \
  -H "Authorization: Bearer $SHAGER_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "url": "https://cdn.summitphysio.example/video/knee-mobility.mp4",
    "file_name": "knee-mobility.mp4",
    "width": 1080, "height": 1920, "duration_ms": 42000
  }'
```

O Shager baixa o arquivo no servidor e devolve `id`, `kind`, `mime_type` e `size_bytes`. Para Reels, Shorts e stories, passe `width`, `height` e `duration_ms` quando souber esses valores. Assim o Shager confere as regras de formato de cada rede antes de o post ser agendado, em vez de falhar na hora do envio.

## Chamada 5: crie um rascunho com legendas por conta

Esta é a chamada que importa. Aqui está em TypeScript com `fetch` puro, incluindo uma pequena função auxiliar que você vai reaproveitar:

```typescript
const API = "https://app.shager.ai/api/v1";

async function shager<T>(method: string, path: string, body?: unknown): Promise<T> {
  const res = await fetch(`${API}${path}`, {
    method,
    headers: {
      Authorization: `Bearer ${process.env.SHAGER_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: body ? JSON.stringify(body) : undefined,
  });
  const json = await res.json();
  if (!res.ok) {
    const err = new Error(`${res.status} ${json.error.code}: ${json.error.message}`);
    (err as any).status = res.status;
    throw err;
  }
  return json.data as T;
}

const WS = process.env.SHAGER_WORKSPACE_ID!;
const mediaId = process.env.SHAGER_MEDIA_ID!; // the id returned by call 4
const channels = await shager<Array<{ id: string; name: string; network: string; status: string }>>(
  "GET", `/workspaces/${WS}/channels`,
);

const post = await shager<{ id: string; status: string }>("POST", `/workspaces/${WS}/posts`, {
  label: "Knee mobility, week 42",
  caption: "Three minutes a day for stiffer knees. Full routine in the video.",
  channel_ids: channels.map((c) => c.id),
  media_ids: [mediaId],
  format: "REEL",
  youtube_title: "3-minute knee mobility routine",
  youtube_privacy: "public",
  per_channel: channels.map((c) => ({
    channel_id: c.id,
    caption: `${c.name}: three minutes a day for stiffer knees. Our physios show the routine.`,
  })),
  cta_url: "https://summitphysio.example/book",
  cta_text: "Book an assessment",
  campaign: "knee-mobility-oct",
  first_comment: "Book an assessment: https://summitphysio.example/book",
  stagger_seconds: [45, 120],
});

console.log(post.id, post.status); // DRAFT
```

O que cada campo traz para você:

1. `per_channel` dá a cada conta a própria legenda. Na prática, quem escreve essas legendas é um agente; o template acima ocupa o lugar dessa etapa, e o raciocínio está em [como evitar bloqueios por spam em escala](/en/blog/avoid-spam-blocks-when-posting-at-scale).
2. `format` é aplicado onde a rede suporta; caso contrário, `SHORT` ou `REEL` é resolvido por rede. `youtube_title` e `youtube_privacy` só afetam canais do YouTube.
3. `cta_url` gera um link curto e um conjunto de UTMs separados para cada conta, com `utm_content` identificando a conta. É sobre isso que se constrói a [atribuição de leads por conta](/en/blog/utm-per-account-lead-attribution).
4. `stagger_seconds` define uma pausa aleatória entre contas da mesma rede. O padrão é `[30, 90]`.

Sem `schedule_at`, o resultado é um rascunho. Guarde o id para as próximas chamadas com `export POST_ID="the_post_id"`.

## Chamada 6: aprovação ou agendamento

Se o workspace exige aprovação, envie para o cliente. A resposta inclui uma `approval_url` que o revisor abre sem fazer login:

```bash
curl -s -X POST "$API/workspaces/$WS/posts/$POST_ID/approval" \
  -H "Authorization: Bearer $SHAGER_API_KEY" -H "Content-Type: application/json" \
  -d '{"reviewer_name": "Dana Ellis", "reviewer_email": "dana@summitphysio.example", "send_email": true}'
```

Depois de aprovado (ou se a aprovação não for exigida), agende. Isso exige o escopo `publish`, e `schedule_at` precisa estar em ISO 8601 com offset:

```bash
curl -s -X POST "$API/workspaces/$WS/posts/$POST_ID/schedule" \
  -H "Authorization: Bearer $SHAGER_API_KEY" -H "Content-Type: application/json" \
  -d '{"schedule_at": "2026-10-20T08:30:00+01:00"}'
```

Nesse momento, o Shager valida cada destino contra as regras da rede. Se alguma conta fosse rejeitar o post, você recebe um 422 `rejected` com o motivo, e nada entra na fila.

## Chamada 7: confira o status e os cliques

```bash
curl -s "$API/workspaces/$WS/posts/$POST_ID" -H "Authorization: Bearer $SHAGER_API_KEY"
curl -s "$API/workspaces/$WS/posts?status=SCHEDULED&limit=50" -H "Authorization: Bearer $SHAGER_API_KEY"
curl -s "$API/workspaces/$WS/analytics?from=2026-10-01T00:00:00Z&to=2026-10-31T23:59:59Z" \
  -H "Authorization: Bearer $SHAGER_API_KEY"
```

`get_post` devolve uma linha por destino com `state`, `dispatch_at`, `published_at`, a `url` publicada e qualquer `error`. O analytics devolve `total_clicks`, `by_account` e `by_post`, contando os cliques nos links rastreáveis do Shager, sem o tráfego de bots. Para cancelar um post agendado que ainda não começou a ser publicado, use `POST /workspaces/{workspace_id}/posts/{post_id}/cancel`.

## Quais erros o seu código deve tratar?

| Status e código | Significado | O que fazer |
|---|---|---|
| 401 `unauthorized` | Cabeçalho ausente ou malformado | Envie `Authorization: Bearer shgr_...` |
| 403 `forbidden` | A chave não tem o escopo necessário | Crie um rascunho, ou peça `publish` a uma pessoa |
| 403 `sandbox_unclaimed` | Chave de cadastro do agente ainda não assumida | Mostre o link de transferência a uma pessoa |
| 404 `not_found` | Id de workspace, post ou canal errado | Liste de novo; os ids são por workspace |
| 409 `not_scheduled` | Cancelamento de um post que não está agendado | Leia o status do post antes |
| 422 `rejected` | Uma regra da rede falhou | Mostre a mensagem; ela diz qual é o problema |
| 429 `rate_limited` | Mais de 120 requisições por minuto por chave | Espere um pouco e tente de novo |

O limite de requisições raramente incomoda um cliente bem projetado. Um único `create_post` cobre centenas de contas, então você não deveria precisar de uma chamada por conta.

## Perguntas frequentes

### Existe um SDK?

Não um dedicado. O documento OpenAPI 3.1 funciona com os geradores padrão, se você quiser clientes tipados, e a API é pequena o bastante para que uma função auxiliar como a de cima costume ser tudo de que você precisa. Uma CLI do Shager está a caminho.

### REST e MCP são mesmo a mesma coisa?

Sim. Cada ferramenta MCP corresponde a uma operação REST com o mesmo nome, as mesmas entradas, os mesmos escopos, o mesmo limite de requisições e o mesmo registro de auditoria. Use REST em scripts e serviços, e MCP em clientes de agentes. A lista completa de ferramentas está na [página para agentes](/en/agents).

### Como publicar textos diferentes no Facebook e no YouTube no mesmo post?

Use `per_channel` com o id e a legenda de cada canal, e `youtube_title` para o título do YouTube. Um objeto de post, muitos destinos, cada um com a própria legenda, o próprio formato e, se precisar, o próprio primeiro comentário.

## Próximo passo

Quando essa sequência rodar sem problemas à mão, transforme-a em algo que um agente possa repetir. [Como criar uma skill do Claude para o Shager](/en/blog/build-a-claude-skill-for-shager) mostra como empacotar essas chamadas, e as regras em volta delas, numa skill reutilizável.

---
Source: https://shager.ai/pt/blog/shager-rest-api-quickstart
