Quickstart della REST API di Shager: da una chiave nuova a un post tracciato su 40 account in sette chiamate
La REST API di Shager in pratica, con curl e TypeScript: autenticazione, workspace, canali, media, bozze per account, programmazione, stato e clic.

La REST API di Shager si trova su https://app.shager.ai/api/v1, si autentica con Authorization: Bearer shgr_live_..., restituisce ogni risultato come { "data": ... } e ogni errore come { "error": { "code", "message" } }. Una distribuzione completa richiede sette chiamate: verificare la chiave, scegliere un workspace, elencarne i canali, caricare i media, creare una bozza con testi diversi per account, programmarla, poi leggere stato e clic. La specifica leggibile dalle macchine è il documento OpenAPI 3.1.
Tutto quello che segue funziona così com'è, una volta impostate due variabili d'ambiente. Le stesse operazioni esistono come strumenti MCP con gli stessi nomi, quindi ciò che impari qui vale anche per Claude Code, Codex o Cursor.
Cosa ti serve prima della prima chiamata?
Una chiave. Può crearla una persona su https://app.shager.ai/dashboard/agents con gli ambiti necessari, oppure il tuo agente può creare un ambiente di prova con la registrazione autonoma. Gli ambiti funzionano così:
| Ambito | Consente |
|---|---|
read |
whoami, elenco di workspace, canali, gruppi e post, lettura di un post, analytics |
write |
Creazione di workspace, gruppi, media, bozze, link di collegamento, richieste di approvazione, annullamenti |
publish |
Programmazione, compreso create_post con schedule_at |
export SHAGER_API_KEY="shgr_live_your_key_here"
export API="https://app.shager.ai/api/v1"

Chiamata 1: chi sono?
curl -s "$API/me" -H "Authorization: Bearer $SHAGER_API_KEY"
{ "data": { "organization_id": "...", "organization": "Northwind Agency", "sandbox": false, "scopes": ["read", "write"] } }
Se sandbox è true, la programmazione e i link di collegamento restano bloccati finché una persona non rivendica la chiave. Se in scopes manca publish, puoi preparare bozze ma non programmarle. Saperlo subito ti evita più avanti un 403 difficile da interpretare.
Chiamata 2: scegli o crea un workspace
Di solito un workspace corrisponde a un cliente o a un brand.
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}'
Imposta sempre timezone (un nome IANA) al momento della creazione. Gli orari di programmazione vengono salvati con il loro offset, e il fuso del workspace determina come Shager li mostra alle persone. approval_required: true significa che i post devono essere approvati dal cliente prima di poter essere programmati: un buon default per i clienti nuovi.
Esporta l'id dalla risposta, così i comandi successivi funzionano:
export WS="the_workspace_id"
Chiamata 3: elenca canali e gruppi
curl -s "$API/workspaces/$WS/channels" -H "Authorization: Bearer $SHAGER_API_KEY"
curl -s "$API/workspaces/$WS/groups" -H "Authorization: Bearer $SHAGER_API_KEY"
Ogni canale restituisce id, network, name, handle, status, i formats che supporta e connected_by_client. I gruppi restituiscono id, name e channel_ids. Con i gruppi raggiungi quaranta account senza elencare quaranta id: passa group_ids a create_post e tutti i canali attivi del gruppo vengono inclusi.
Se il workspace non ha ancora canali, crea un link di collegamento con POST /workspaces/{workspace_id}/connect-links e invialo al titolare dell'account. Sarà lui a collegare Facebook, Instagram, YouTube, X, LinkedIn o TikTok, senza accedere a Shager.

Chiamata 4: carica i media da un URL
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
}'
Shager scarica il file lato server e restituisce id, kind, mime_type e size_bytes. Per Reel, Short e storie, passa width, height e duration_ms quando li conosci. Così Shager può verificare le regole di formato di ogni social prima che il post venga programmato, invece di fallire al momento dell'invio.
Chiamata 5: crea una bozza con testi diversi per account
È la chiamata che conta davvero. Eccola in TypeScript con un semplice fetch, insieme a una piccola funzione di supporto che riutilizzerai:
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
Cosa ti dà ciascun campo:
per_channelassegna a ogni account il proprio testo. In pratica li scrive un agente; il modello qui sopra è solo un segnaposto per quel passaggio, e il perché è spiegato in come evitare i blocchi per spam quando pubblichi su larga scala.formatviene applicato dove il social lo supporta, altrimenti la scelta traSHORTeREELviene risolta social per social.youtube_titleeyoutube_privacyriguardano solo i canali YouTube.cta_urlgenera un link breve e un set di UTM separati per ogni account, conutm_contentche identifica l'account. È la base dell'attribuzione dei contatti per account.stagger_secondsimposta una pausa casuale tra gli account dello stesso social. Il valore predefinito è[30, 90].
Senza schedule_at, il risultato è una bozza. Conserva il suo id per le chiamate successive con export POST_ID="the_post_id".
Chiamata 6: approvazione o programmazione
Se il workspace richiede l'approvazione, invia la bozza al cliente. La risposta contiene un approval_url che il revisore apre senza accedere:
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}'
Una volta approvata (o se l'approvazione non è richiesta), programmala. Serve l'ambito publish, e schedule_at deve essere in formato ISO 8601 con offset:
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"}'
A questo punto Shager verifica ogni destinazione rispetto alle regole del suo social. Se anche un solo account rifiuterebbe il post, ricevi un 422 rejected con il motivo, e nulla viene messo in coda.
Chiamata 7: controlla stato e clic
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 restituisce una riga per destinazione con state, dispatch_at, published_at, l'url pubblicato ed eventuali error. Gli analytics restituiscono total_clicks, by_account e by_post, contando i clic sui link tracciati di Shager con il traffico dei bot escluso. Per annullare un post programmato la cui pubblicazione non è ancora iniziata: POST /workspaces/{workspace_id}/posts/{post_id}/cancel.
Quali errori deve gestire il tuo codice?
| Stato e codice | Significato | Cosa fare |
|---|---|---|
401 unauthorized |
Header mancante o malformato | Invia Authorization: Bearer shgr_... |
403 forbidden |
Alla chiave manca l'ambito necessario | Crea una bozza, oppure chiedi a una persona il permesso publish |
403 sandbox_unclaimed |
Chiave da registrazione autonoma non rivendicata | Mostra il link di rivendicazione a una persona |
404 not_found |
Id di workspace, post o canale errato | Rifai l'elenco: gli id sono per workspace |
409 not_scheduled |
Annullamento di un post non programmato | Leggi prima lo stato del post |
422 rejected |
Una regola del social non è rispettata | Mostra il messaggio: indica il problema |
429 rate_limited |
Oltre 120 richieste al minuto per chiave | Rallenta e riprova |
Un client ben progettato raramente si scontra con il limite di richieste. Un solo create_post copre centinaia di account, quindi non dovrebbe servirti una chiamata per account.
Domande frequenti
Esiste un SDK?
Non uno dedicato. Il documento OpenAPI 3.1 funziona con i generatori standard se vuoi client tipizzati, e l'API è abbastanza piccola che di solito basta una funzione di supporto come quella qui sopra. È in arrivo una CLI di Shager.
REST e MCP sono davvero la stessa cosa?
Sì. Ogni strumento MCP corrisponde a un'operazione REST con lo stesso nome, gli stessi input, gli stessi ambiti, lo stesso limite di richieste e lo stesso registro delle chiamate. Usa REST da script e servizi, MCP dai client per agenti. L'elenco completo degli strumenti è nella pagina per agenti.
Come pubblico testi diversi su Facebook e YouTube nello stesso post?
Usa per_channel con l'id e il testo di ogni canale, e youtube_title per il titolo su YouTube. Un solo oggetto post, tante destinazioni, ognuna con il suo testo, il suo formato e, se serve, il suo primo commento.
Il prossimo passo
Quando questa sequenza funziona senza intoppi a mano, trasformala in qualcosa che un agente possa ripetere. Creare una skill di Claude per Shager mostra come raccogliere queste chiamate e le regole che le accompagnano in una skill riutilizzabile.


