PanoramicaPanoramica dell'API

Panoramica

Panoramica dell'API

Ogni endpoint di Sofia API v1 con parametri, scope ed esempi, e un playground per provarli uno a uno con la tua chiave API.

URL di base https://api.sofia-post.com/api · Documento OpenAPI

Sofia API dà al tuo codice accesso al tuo account Sofia: le attività dei tuoi Workspace, le loro reti collegate, la loro libreria media, i loro post e le loro statistiche. È un’API REST su HTTPS; richieste e risposte sono in JSON. Permette di:

  • Pubblicare: subito, a una data o come bozza, su una o più reti alla volta.
  • Verificare prima di pubblicare: una simulazione ti dice se ogni rete accetterebbe il post, senza scrivere nulla.
  • Gestire i media: carica un file o importalo da un URL nella libreria media di un’attività.
  • Seguire i post: leggi lo stato di ogni rete, il suo permalink o il motivo del fallimento. Un webhook firmato può anche avvisarti.
  • Leggere le statistiche: gli ultimi numeri raccolti da Sofia per ogni account collegato.
  • Collegare le reti: crea un link ospitato che permette al titolare di collegare una rete mancante.

SDK e integrazioni

  • CLI `sofia`: la CLI, pubblicata su npm con il nome sofia-post, per controllare Sofia da un terminale.
  • Server MCP: collega Sofia ai tuoi agenti IA.
  • Zapier: l’integrazione Sofia si collega con la tua chiave personale. Offre i trigger «Post Published» e «Post Failed», l’azione «Create Post» (pubblicare, programmare o salvare una bozza) e la ricerca «Find Post». Non è ancora stata sottoposta alla directory pubblica di Zapier.

Autenticazione

Ogni chiamata porta un token Bearer nell’header Authorization. Sofia rilegge i diritti collegati a ogni chiamata: una persona rimossa da un Workspace, o un’autorizzazione revocata, ferma il token dalla richiesta successiva.

Ci sono due modi per autenticarti:

MetodoPerToken
Chiave personaleAutomatizzare il tuo account, script, agentisofia_pk_live_…, valida fino alla scadenza
OAuth, authorization code + PKCEUn prodotto che agisce per i suoi utenti, sul Workspace che scelgonosofia_live_at_…, un’ora, rinnovabile
OAuth, client credentialsUn tuo server che automatizza un Workspace di cui sei titolaresofia_live_at_…, un’ora

Chiave API

Se automatizzi il tuo Workspace, basta una chiave personale. Nell’app Sofia apri Impostazioni › API, scheda Accesso personale, e crea una chiave. Scegli il suo nome, la sua scadenza e i suoi permessi. La chiave viene mostrata una sola volta: copiala e conservala in un vault di segreti, mai nel tuo codice.

Crea una chiave in Impostazioni › API › Accesso personale. Agisce per te su tutti i tuoi Workspace: l’attività che indichi determina il Workspace, e il tuo ruolo al suo interno decide cosa la chiave può fare.

Invia la chiave nell’header Authorization, come token Bearer:

curl https://api.sofia-post.com/api/v1/establishments \
  -H "Authorization: Bearer sofia_pk_live_…"

`GET /v1/me` ti indica per chi agisce la chiave, i suoi Workspace e i suoi permessi.

Una chiamata senza il permesso (lo scope) richiesto risponde 403 insufficient_scope.

Token OAuth2

Per un prodotto SaaS che agisce per i suoi utenti sui loro Workspace, registra un client OAuth: ogni persona autorizza la tua applicazione sul Workspace che sceglie. I token di accesso OAuth iniziano con sofia_live_at_…, durano un’ora e si usano allo stesso modo:

curl https://api.sofia-post.com/api/v1/establishments \
  -H "Authorization: Bearer sofia_live_at_…"

Crei un’app a cui si collegano altri account Sofia? Il suo flusso authorization code con PKCE, le sue client credentials e i suoi scope sono descritti in OAuth2.

URL di base

Ogni endpoint si trova sotto un unico URL di base:

https://api.sofia-post.com/api/v1

Limiti di frequenza

Sofia API conta le richieste su tre finestre: 15 minuti, 24 ore e 30 giorni.

Token15 minuti24 ore30 giorni
Chiave personale (per account)1501.00010.000
Client OAuth10050015.000
Client OAuth, per persona autorizzata (fino a 1.000)+10+25+100

Header

Ogni risposta ti dice cosa resta, nel formato IETF:

RateLimit: "15m";r=144;t=43, "24h";r=994;t=13543, "30d";r=9994;t=13543
RateLimit-Policy: "15m";q=150;w=900, "24h";q=1000;w=86400, "30d";q=10000;w=2592000

r è il numero di richieste rimanenti, t i secondi prima del reset della finestra, q la quota e w la finestra in secondi.

Quando una finestra è piena

La risposta è 429 rate_limit_exceeded, con un header Retry-After e la finestra esaurita in details. Aspetta questo intervallo prima di riprovare.

Usare l’API in modo efficiente

  • Registra un webhook invece di interrogare un post in continuazione.
  • Usa limit fino a 100 per leggere le liste con meno pagine.
  • Metti in cache ciò che cambia raramente: piattaforme, attività, account collegati.

Errori

Ogni errore risponde con uno stato HTTP e la stessa busta JSON. code è il contratto che legge il tuo codice; message è scritto per una persona e può cambiare.

{
  "error": {
    "code": "invalid_request",
    "message": "The request is invalid",
    "details": [
      { "field": "targets[0].text", "code": "required", "message": "targets[0].text is required" }
    ],
    "docUrl": "https://www.sofia-post.com/developers/errors#invalid_request",
    "requestId": "2aab5943-6333-4caf-8478-25a85ebd4abf"
  }
}
  • details dice di più: il campo rifiutato, il motivo di una rete, la finestra di limite esaurita.
  • docUrl porta alla spiegazione del codice in questa pagina.
  • requestId identifica la richiesta: comunicalo all’assistenza.

Codici di errore

StatoCodiceSignificato
400invalid_requestIl body, la query o un parametro non è valido. Un dettaglio per campo rifiutato: field, code (required, is_string, max…) e message.
400invalid_jsonIl body non è un JSON valido.
400invalid_cursorIl cursore non è stato restituito da Sofia API.
400webhook_url_invalidL'indirizzo del webhook non è https, o porta a un indirizzo privato.
400webhook_event_invalidEvento webhook sconosciuto.
400application_name_invalidImpostazioni › API: il nome del client manca o è troppo lungo.
400application_profile_invalidImpostazioni › API: un campo del profilo del client non è valido; il dettaglio lo indica.
400logo_invalidImpostazioni › API: il logo è rifiutato (almeno 180×180 px, PNG, JPEG o WebP, massimo 2 MB).
401invalid_tokenIl token manca, è sconosciuto, scaduto o revocato, oppure la persona dietro di esso ha perso i diritti che porta. Vedi l'header WWW-Authenticate.
401unauthorizedÈ richiesta l'autenticazione.
402subscription_requiredUna scrittura reale (simulazione compresa) richiede un abbonamento attivo o una prova.
403insufficient_scopeAl token manca lo scope dell'endpoint; il dettaglio lo indica.
403insufficient_roleIl tuo ruolo in questo Workspace non consente questa azione.
403live_mode_requiredUna scrittura reale con un token di test.
403route_closed_to_applicationsQuesto endpoint non è aperto alle applicazioni.
403forbiddenL'accesso è rifiutato.
403session_requiredImpostazioni › API: questa azione richiede una sessione Sofia, non un token.
403impersonation_forbiddenImpostazioni › API: questa azione è rifiutata in una sessione presa in prestito.
403workspace_owner_requiredSolo il proprietario del Workspace può farlo.
403workspace_member_requiredNon sei membro di questo Workspace.
404route_not_foundNessun endpoint risponde a questo metodo e a questo percorso.
404establishment_not_foundAttività sconosciuta, o fuori dalla portata del token.
404media_not_foundMedia sconosciuto, o fuori dalla portata del token.
404post_not_foundPubblicazione sconosciuta, o fuori dalla portata del token.
404webhook_not_foundWebhook sconosciuto, o di un'altra applicazione.
404not_foundLa risorsa non esiste.
404application_not_foundImpostazioni › API: client sconosciuto.
404grant_not_foundAutorizzazione sconosciuta o già revocata.
404personal_key_not_foundImpostazioni › API: chiave personale sconosciuta.
405method_not_allowedQuesto metodo non è consentito su questo endpoint.
409post_in_flightUna rete sta inviando la pubblicazione: riprova l'eliminazione un momento dopo.
409post_not_editableUna rete ha già inviato la pubblicazione: non può più essere modificata.
409account_already_connectedQuesta rete è già collegata all'attività.
409webhook_limit_reachedL'applicazione ha già 10 webhook.
409idempotency_in_progressLa prima richiesta con questa Idempotency-Key è ancora in corso.
409conflictLa richiesta è in conflitto con lo stato attuale della risorsa.
409scope_removal_forbiddenImpostazioni › API: un permesso di un client può essere aggiunto ma non rimosso.
409public_client_has_no_secretImpostazioni › API: un client pubblico non ha un secret.
413payload_too_largeIl file supera il limite di caricamento.
415unsupported_media_typeTipo di contenuto non supportato.
422post_refusedUna rete, l'attività o il tuo piano rifiuterebbe la pubblicazione. Un dettaglio per motivo, ognuno con il suo message.
422media_import_refusedNon è stato possibile importare il media da questo URL; il dettaglio dà il motivo.
422media_upload_refusedIl file caricato non è un'immagine o un video supportato.
422platform_not_offeredQuesta rete non è offerta per il collegamento.
422idempotency_key_reusedQuesta Idempotency-Key è stata usata con un body diverso.
422unprocessable_entityLa richiesta è comprensibile ma non può essere elaborata.
429rate_limit_exceededUna finestra di limite è piena: attendi Retry-After.

Motivi di un post rifiutato

Un post_refused elenca ogni motivo in details[].code:

CodiceSignificato
unsupported_platformQuesta rete non è supportata.
duplicate_platformLa stessa rete è indicata due volte.
unsupported_formatQuesta rete non offre questo formato.
caption_too_longIl testo è troppo lungo (max, actual).
too_many_hashtagsTroppi hashtag (max, actual).
too_many_mentionsTroppe menzioni (max, actual).
too_many_thread_postsIl thread è troppo lungo (max, actual).
too_many_mediaTroppi media (max, actual).
media_mix_refusedFoto e video non si possono mescolare su questa rete.
media_requiredQuesta rete richiede una foto o un video.
media_type_refusedQuesta rete non accetta questo tipo di media.
scheduled_in_pastscheduledAt è nel passato.
scheduled_at_invalidUna pubblicazione programmata richiede uno scheduledAt futuro.
account_not_connectedNessun account collegato su questa rete per questa attività.
account_ambiguousSu questa rete sono collegati più account: indicane uno con accountId.
media_not_foundIl media non è nella mediateca di questa attività.
media_url_invalidL'URL del media deve essere un indirizzo https pubblico.
platform_unavailableQuesta rete non è ancora aperta su Sofia.
publication_quota_exceededLa quota di pubblicazioni del tuo piano è raggiunta.

Gestire gli errori

  • Basa la tua logica su code, mai su message.
  • Riprova un 429 dopo Retry-After, e un 5xx con un backoff esponenziale.
  • Non riprovare mai un 4xx identico: correggi prima la richiesta.
  • Registra requestId a ogni fallimento.

Paginazione

Le liste (GET /v1/posts, GET /v1/media) arrivano dalle più recenti, una pagina alla volta. limit fissa la dimensione della pagina, da 1 a 100 (20 per impostazione predefinita).

{
  "data": [ … ],
  "nextCursor": "WyIyMDI2LTEwLTI3VDE1OjExOjU5LjAxN1oiLCIx…"
}

Passa nextCursor come cursor per leggere la pagina successiva. Quando nextCursor è assente, sei arrivato alla fine.

let cursor;
do {
  const url = new URL('https://api.sofia-post.com/api/v1/posts');
  url.searchParams.set('limit', '100');
  if (cursor) url.searchParams.set('cursor', cursor);
  const page = await globalThis.fetch(url, { headers: { Authorization: `Bearer ${key}` } }).then((r) => r.json());
  handle(page.data);
  cursor = page.nextCursor;
} while (cursor);

Un cursore è opaco: non costruirlo né modificarlo mai. Un cursore che Sofia non ha restituito risponde 400 invalid_cursor.

Idempotenza

Un errore di rete può lasciarti nel dubbio sulla creazione di un post. Invia un header Idempotency-Key con POST /v1/posts e POST /v1/media, e riprova con la stessa chiave: Sofia risponde ciò che ha risposto la prima volta e non crea duplicati.

curl -X POST https://api.sofia-post.com/api/v1/posts \
  -H "Authorization: Bearer $SOFIA_API_KEY" \
  -H 'Content-Type: application/json' \
  -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
  -d @post.json
  • Usa una nuova chiave casuale (un UUID) per ogni nuovo post, e la stessa chiave per ogni nuovo tentativo di quel post.
  • La stessa chiave con un corpo diverso risponde 422 idempotency_key_reused.
  • Finché la prima richiesta è in corso, la stessa chiave risponde 409 idempotency_in_progress: riprova poco dopo.

Modalità test e simulazione

Simulazione. Aggiungi ?dry_run=true a POST /v1/posts: Sofia esegue i controlli di un vero post (le regole di ogni rete, l’account collegato, i media, la data e il tuo piano) senza salvare né inviare nulla. Il dettaglio delle risposte è nella pagina Pubblicare.

Modalità test. Un’applicazione creata in modalità test (token sofia_test_at_…) legge il tuo vero Workspace ma non vi scrive mai: i suoi post sono simulati e valutati con le stesse regole, e ogni scrittura reale risponde 403 live_mode_required. Le chiavi e i client creati in Impostazioni › API sono in modalità live; usa la simulazione per verificare senza pubblicare.

OAuth

Account

Piattaforme

Attività

Media

Pubblicazioni

Webhook